- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / media / audio_input_renderer_host.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/media/audio_input_renderer_host.h"
6
7 #include "base/bind.h"
8 #include "base/memory/shared_memory.h"
9 #include "base/metrics/histogram.h"
10 #include "base/process/process.h"
11 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
12 #include "content/browser/renderer_host/media/audio_input_sync_writer.h"
13 #include "content/browser/renderer_host/media/media_stream_manager.h"
14 #include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
15 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
16 #include "media/audio/audio_manager_base.h"
17
18 namespace content {
19
20 struct AudioInputRendererHost::AudioEntry {
21   AudioEntry();
22   ~AudioEntry();
23
24   // The AudioInputController that manages the audio input stream.
25   scoped_refptr<media::AudioInputController> controller;
26
27   // The audio input stream ID in the render view.
28   int stream_id;
29
30   // Shared memory for transmission of the audio data. It has
31   // |shared_memory_segment_count| equal lengthed segments.
32   base::SharedMemory shared_memory;
33   int shared_memory_segment_count;
34
35   // The synchronous writer to be used by the controller. We have the
36   // ownership of the writer.
37   scoped_ptr<media::AudioInputController::SyncWriter> writer;
38
39   // Set to true after we called Close() for the controller.
40   bool pending_close;
41 };
42
43 AudioInputRendererHost::AudioEntry::AudioEntry()
44     : stream_id(0),
45       shared_memory_segment_count(0),
46       pending_close(false) {
47 }
48
49 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
50
51 AudioInputRendererHost::AudioInputRendererHost(
52     media::AudioManager* audio_manager,
53     MediaStreamManager* media_stream_manager,
54     AudioMirroringManager* audio_mirroring_manager,
55     media::UserInputMonitor* user_input_monitor)
56     : audio_manager_(audio_manager),
57       media_stream_manager_(media_stream_manager),
58       audio_mirroring_manager_(audio_mirroring_manager),
59       user_input_monitor_(user_input_monitor) {}
60
61 AudioInputRendererHost::~AudioInputRendererHost() {
62   DCHECK(audio_entries_.empty());
63 }
64
65 void AudioInputRendererHost::OnChannelClosing() {
66   // Since the IPC channel is gone, close all requested audio streams.
67   DeleteEntries();
68 }
69
70 void AudioInputRendererHost::OnDestruct() const {
71   BrowserThread::DeleteOnIOThread::Destruct(this);
72 }
73
74 void AudioInputRendererHost::OnCreated(
75     media::AudioInputController* controller) {
76   BrowserThread::PostTask(
77       BrowserThread::IO,
78       FROM_HERE,
79       base::Bind(
80           &AudioInputRendererHost::DoCompleteCreation,
81           this,
82           make_scoped_refptr(controller)));
83 }
84
85 void AudioInputRendererHost::OnRecording(
86     media::AudioInputController* controller) {
87   BrowserThread::PostTask(
88       BrowserThread::IO,
89       FROM_HERE,
90       base::Bind(
91           &AudioInputRendererHost::DoSendRecordingMessage,
92           this,
93           make_scoped_refptr(controller)));
94 }
95
96 void AudioInputRendererHost::OnError(media::AudioInputController* controller) {
97   BrowserThread::PostTask(
98       BrowserThread::IO,
99       FROM_HERE,
100       base::Bind(
101           &AudioInputRendererHost::DoHandleError,
102           this,
103           make_scoped_refptr(controller)));
104 }
105
106 void AudioInputRendererHost::OnData(media::AudioInputController* controller,
107                                     const uint8* data,
108                                     uint32 size) {
109   NOTREACHED() << "Only low-latency mode is supported.";
110 }
111
112 void AudioInputRendererHost::DoCompleteCreation(
113     media::AudioInputController* controller) {
114   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
115
116   AudioEntry* entry = LookupByController(controller);
117   if (!entry)
118     return;
119
120   if (!PeerHandle()) {
121     NOTREACHED() << "Renderer process handle is invalid.";
122     DeleteEntryOnError(entry);
123     return;
124   }
125
126   if (!entry->controller->LowLatencyMode()) {
127     NOTREACHED() << "Only low-latency mode is supported.";
128     DeleteEntryOnError(entry);
129     return;
130   }
131
132   // Once the audio stream is created then complete the creation process by
133   // mapping shared memory and sharing with the renderer process.
134   base::SharedMemoryHandle foreign_memory_handle;
135   if (!entry->shared_memory.ShareToProcess(PeerHandle(),
136                                            &foreign_memory_handle)) {
137     // If we failed to map and share the shared memory then close the audio
138     // stream and send an error message.
139     DeleteEntryOnError(entry);
140     return;
141   }
142
143   AudioInputSyncWriter* writer =
144       static_cast<AudioInputSyncWriter*>(entry->writer.get());
145
146 #if defined(OS_WIN)
147   base::SyncSocket::Handle foreign_socket_handle;
148 #else
149   base::FileDescriptor foreign_socket_handle;
150 #endif
151
152   // If we failed to prepare the sync socket for the renderer then we fail
153   // the construction of audio input stream.
154   if (!writer->PrepareForeignSocketHandle(PeerHandle(),
155                                           &foreign_socket_handle)) {
156     DeleteEntryOnError(entry);
157     return;
158   }
159
160   Send(new AudioInputMsg_NotifyStreamCreated(entry->stream_id,
161       foreign_memory_handle, foreign_socket_handle,
162       entry->shared_memory.requested_size(),
163       entry->shared_memory_segment_count));
164 }
165
166 void AudioInputRendererHost::DoSendRecordingMessage(
167     media::AudioInputController* controller) {
168   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
169   // TODO(henrika): See crbug.com/115262 for details on why this method
170   // should be implemented.
171 }
172
173 void AudioInputRendererHost::DoHandleError(
174     media::AudioInputController* controller) {
175   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
176
177   AudioEntry* entry = LookupByController(controller);
178   if (!entry)
179     return;
180
181   DeleteEntryOnError(entry);
182 }
183
184 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message,
185                                                bool* message_was_ok) {
186   bool handled = true;
187   IPC_BEGIN_MESSAGE_MAP_EX(AudioInputRendererHost, message, *message_was_ok)
188     IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream)
189     IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream)
190     IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream)
191     IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume)
192     IPC_MESSAGE_UNHANDLED(handled = false)
193   IPC_END_MESSAGE_MAP_EX()
194
195   return handled;
196 }
197
198 void AudioInputRendererHost::OnCreateStream(
199     int stream_id,
200     int render_view_id,
201     int session_id,
202     const AudioInputHostMsg_CreateStream_Config& config) {
203   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
204
205   DVLOG(1) << "AudioInputRendererHost@" << this
206            << "::OnCreateStream(stream_id=" << stream_id
207            << ", render_view_id=" << render_view_id
208            << ", session_id=" << session_id << ")";
209   DCHECK_GT(render_view_id, 0);
210
211   // media::AudioParameters is validated in the deserializer.
212   if (LookupById(stream_id) != NULL) {
213     SendErrorMessage(stream_id);
214     return;
215   }
216
217   media::AudioParameters audio_params(config.params);
218   if (media_stream_manager_->audio_input_device_manager()->
219       ShouldUseFakeDevice()) {
220     audio_params.Reset(
221         media::AudioParameters::AUDIO_FAKE,
222         config.params.channel_layout(), config.params.channels(), 0,
223         config.params.sample_rate(), config.params.bits_per_sample(),
224         config.params.frames_per_buffer());
225   }
226
227   // Check if we have the permission to open the device and which device to use.
228   std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
229   if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
230     const StreamDeviceInfo* info = media_stream_manager_->
231         audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
232     if (!info) {
233       SendErrorMessage(stream_id);
234       DLOG(WARNING) << "No permission has been granted to input stream with "
235                     << "session_id=" << session_id;
236       return;
237     }
238
239     device_id = info->device.id;
240   }
241
242   // Create a new AudioEntry structure.
243   scoped_ptr<AudioEntry> entry(new AudioEntry());
244
245   const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) +
246                                audio_params.GetBytesPerBuffer());
247   entry->shared_memory_segment_count = config.shared_memory_count;
248
249   // Create the shared memory and share it with the renderer process
250   // using a new SyncWriter object.
251   if (!entry->shared_memory.CreateAndMapAnonymous(
252       segment_size * entry->shared_memory_segment_count)) {
253     // If creation of shared memory failed then send an error message.
254     SendErrorMessage(stream_id);
255     return;
256   }
257
258   scoped_ptr<AudioInputSyncWriter> writer(
259       new AudioInputSyncWriter(&entry->shared_memory,
260                                entry->shared_memory_segment_count));
261
262   if (!writer->Init()) {
263     SendErrorMessage(stream_id);
264     return;
265   }
266
267   // If we have successfully created the SyncWriter then assign it to the
268   // entry and construct an AudioInputController.
269   entry->writer.reset(writer.release());
270   if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
271     entry->controller = media::AudioInputController::CreateForStream(
272         audio_manager_->GetMessageLoop(),
273         this,
274         WebContentsAudioInputStream::Create(device_id,
275                                             audio_params,
276                                             audio_manager_->GetWorkerLoop(),
277                                             audio_mirroring_manager_),
278         entry->writer.get(),
279         user_input_monitor_);
280   } else {
281     // TODO(henrika): replace CreateLowLatency() with Create() as soon
282     // as satish has ensured that Speech Input also uses the default low-
283     // latency path. See crbug.com/112472 for details.
284     entry->controller =
285         media::AudioInputController::CreateLowLatency(audio_manager_,
286                                                       this,
287                                                       audio_params,
288                                                       device_id,
289                                                       entry->writer.get(),
290                                                       user_input_monitor_);
291   }
292
293   if (!entry->controller.get()) {
294     SendErrorMessage(stream_id);
295     return;
296   }
297
298   // Set the initial AGC state for the audio input stream. Note that, the AGC
299   // is only supported in AUDIO_PCM_LOW_LATENCY mode.
300   if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY)
301     entry->controller->SetAutomaticGainControl(config.automatic_gain_control);
302
303   // Since the controller was created successfully, create an entry and add it
304   // to the map.
305   entry->stream_id = stream_id;
306   audio_entries_.insert(std::make_pair(stream_id, entry.release()));
307 }
308
309 void AudioInputRendererHost::OnRecordStream(int stream_id) {
310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
311
312   AudioEntry* entry = LookupById(stream_id);
313   if (!entry) {
314     SendErrorMessage(stream_id);
315     return;
316   }
317
318   entry->controller->Record();
319 }
320
321 void AudioInputRendererHost::OnCloseStream(int stream_id) {
322   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
323
324   AudioEntry* entry = LookupById(stream_id);
325
326   if (entry)
327     CloseAndDeleteStream(entry);
328 }
329
330 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
331   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
332
333   AudioEntry* entry = LookupById(stream_id);
334   if (!entry) {
335     SendErrorMessage(stream_id);
336     return;
337   }
338
339   entry->controller->SetVolume(volume);
340 }
341
342 void AudioInputRendererHost::SendErrorMessage(int stream_id) {
343   Send(new AudioInputMsg_NotifyStreamStateChanged(
344       stream_id, media::AudioInputIPCDelegate::kError));
345 }
346
347 void AudioInputRendererHost::DeleteEntries() {
348   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
349
350   for (AudioEntryMap::iterator i = audio_entries_.begin();
351        i != audio_entries_.end(); ++i) {
352     CloseAndDeleteStream(i->second);
353   }
354 }
355
356 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
357   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
358
359   if (!entry->pending_close) {
360     entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
361                                         this, entry));
362     entry->pending_close = true;
363   }
364 }
365
366 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
367   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
368
369   // Delete the entry when this method goes out of scope.
370   scoped_ptr<AudioEntry> entry_deleter(entry);
371
372   // Erase the entry from the map.
373   audio_entries_.erase(entry->stream_id);
374 }
375
376 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry) {
377   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
378
379   // Sends the error message first before we close the stream because
380   // |entry| is destroyed in DeleteEntry().
381   SendErrorMessage(entry->stream_id);
382   CloseAndDeleteStream(entry);
383 }
384
385 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
386     int stream_id) {
387   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
388
389   AudioEntryMap::iterator i = audio_entries_.find(stream_id);
390   if (i != audio_entries_.end())
391     return i->second;
392   return NULL;
393 }
394
395 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
396     media::AudioInputController* controller) {
397   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
398
399   // Iterate the map of entries.
400   // TODO(hclam): Implement a faster look up method.
401   for (AudioEntryMap::iterator i = audio_entries_.begin();
402        i != audio_entries_.end(); ++i) {
403     if (controller == i->second->controller.get())
404       return i->second;
405   }
406   return NULL;
407 }
408
409 }  // namespace content