Upstream version 11.39.250.0
[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/numerics/safe_math.h"
11 #include "base/process/process.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/browser/media/capture/web_contents_audio_input_stream.h"
14 #include "content/browser/media/capture/web_contents_capture_util.h"
15 #include "content/browser/media/media_internals.h"
16 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
17 #include "content/browser/renderer_host/media/audio_input_sync_writer.h"
18 #include "content/browser/renderer_host/media/media_stream_manager.h"
19 #include "media/audio/audio_manager_base.h"
20 #include "media/base/audio_bus.h"
21
22 namespace {
23
24 void LogMessage(int stream_id, const std::string& msg, bool add_prefix) {
25   std::ostringstream oss;
26   oss << "[stream_id=" << stream_id << "] ";
27   if (add_prefix)
28     oss << "AIRH::";
29   oss << msg;
30   content::MediaStreamManager::SendMessageToNativeLog(oss.str());
31   DVLOG(1) << oss.str();
32 }
33
34 }  // namespace
35
36 namespace content {
37
38 struct AudioInputRendererHost::AudioEntry {
39   AudioEntry();
40   ~AudioEntry();
41
42   // The AudioInputController that manages the audio input stream.
43   scoped_refptr<media::AudioInputController> controller;
44
45   // The audio input stream ID in the render view.
46   int stream_id;
47
48   // Shared memory for transmission of the audio data. It has
49   // |shared_memory_segment_count| equal lengthed segments.
50   base::SharedMemory shared_memory;
51   int shared_memory_segment_count;
52
53   // The synchronous writer to be used by the controller. We have the
54   // ownership of the writer.
55   scoped_ptr<media::AudioInputController::SyncWriter> writer;
56
57   // Set to true after we called Close() for the controller.
58   bool pending_close;
59
60   // If this entry's layout has a keyboard mic channel.
61   bool has_keyboard_mic_;
62 };
63
64 AudioInputRendererHost::AudioEntry::AudioEntry()
65     : stream_id(0),
66       shared_memory_segment_count(0),
67       pending_close(false),
68       has_keyboard_mic_(false) {
69 }
70
71 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
72
73 AudioInputRendererHost::AudioInputRendererHost(
74     media::AudioManager* audio_manager,
75     MediaStreamManager* media_stream_manager,
76     AudioMirroringManager* audio_mirroring_manager,
77     media::UserInputMonitor* user_input_monitor)
78     : BrowserMessageFilter(AudioMsgStart),
79       audio_manager_(audio_manager),
80       media_stream_manager_(media_stream_manager),
81       audio_mirroring_manager_(audio_mirroring_manager),
82       user_input_monitor_(user_input_monitor),
83       audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
84           media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)) {}
85
86 AudioInputRendererHost::~AudioInputRendererHost() {
87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
88   DCHECK(audio_entries_.empty());
89 }
90
91 void AudioInputRendererHost::OnChannelClosing() {
92   // Since the IPC sender is gone, close all requested audio streams.
93   DeleteEntries();
94 }
95
96 void AudioInputRendererHost::OnDestruct() const {
97   BrowserThread::DeleteOnIOThread::Destruct(this);
98 }
99
100 void AudioInputRendererHost::OnCreated(
101     media::AudioInputController* controller) {
102   BrowserThread::PostTask(
103       BrowserThread::IO,
104       FROM_HERE,
105       base::Bind(
106           &AudioInputRendererHost::DoCompleteCreation,
107           this,
108           make_scoped_refptr(controller)));
109 }
110
111 void AudioInputRendererHost::OnRecording(
112     media::AudioInputController* controller) {
113   BrowserThread::PostTask(
114       BrowserThread::IO,
115       FROM_HERE,
116       base::Bind(
117           &AudioInputRendererHost::DoSendRecordingMessage,
118           this,
119           make_scoped_refptr(controller)));
120 }
121
122 void AudioInputRendererHost::OnError(media::AudioInputController* controller,
123     media::AudioInputController::ErrorCode error_code) {
124   BrowserThread::PostTask(
125       BrowserThread::IO,
126       FROM_HERE,
127       base::Bind(
128           &AudioInputRendererHost::DoHandleError,
129           this,
130           make_scoped_refptr(controller),
131           error_code));
132 }
133
134 void AudioInputRendererHost::OnData(media::AudioInputController* controller,
135                                     const media::AudioBus* data) {
136   NOTREACHED() << "Only low-latency mode is supported.";
137 }
138
139 void AudioInputRendererHost::OnLog(media::AudioInputController* controller,
140                                    const std::string& message) {
141   BrowserThread::PostTask(BrowserThread::IO,
142                           FROM_HERE,
143                           base::Bind(&AudioInputRendererHost::DoLog,
144                                      this,
145                                      make_scoped_refptr(controller),
146                                      message));
147 }
148
149 void AudioInputRendererHost::DoCompleteCreation(
150     media::AudioInputController* controller) {
151   DCHECK_CURRENTLY_ON(BrowserThread::IO);
152
153   AudioEntry* entry = LookupByController(controller);
154   if (!entry) {
155     NOTREACHED() << "AudioInputController is invalid.";
156     return;
157   }
158
159   if (!PeerHandle()) {
160     NOTREACHED() << "Renderer process handle is invalid.";
161     DeleteEntryOnError(entry, INVALID_PEER_HANDLE);
162     return;
163   }
164
165   if (!entry->controller->SharedMemoryAndSyncSocketMode()) {
166     NOTREACHED() << "Only shared-memory/sync-socket mode is supported.";
167     DeleteEntryOnError(entry, INVALID_LATENCY_MODE);
168     return;
169   }
170
171   // Once the audio stream is created then complete the creation process by
172   // mapping shared memory and sharing with the renderer process.
173   base::SharedMemoryHandle foreign_memory_handle;
174   if (!entry->shared_memory.ShareToProcess(PeerHandle(),
175                                            &foreign_memory_handle)) {
176     // If we failed to map and share the shared memory then close the audio
177     // stream and send an error message.
178     DeleteEntryOnError(entry, MEMORY_SHARING_FAILED);
179     return;
180   }
181
182   AudioInputSyncWriter* writer =
183       static_cast<AudioInputSyncWriter*>(entry->writer.get());
184
185   base::SyncSocket::TransitDescriptor socket_transit_descriptor;
186
187   // If we failed to prepare the sync socket for the renderer then we fail
188   // the construction of audio input stream.
189   if (!writer->PrepareForeignSocket(PeerHandle(), &socket_transit_descriptor)) {
190     DeleteEntryOnError(entry, SYNC_SOCKET_ERROR);
191     return;
192   }
193
194   LogMessage(entry->stream_id,
195              "DoCompleteCreation: IPC channel and stream are now open",
196              true);
197
198   Send(new AudioInputMsg_NotifyStreamCreated(
199       entry->stream_id, foreign_memory_handle, socket_transit_descriptor,
200       entry->shared_memory.requested_size(),
201       entry->shared_memory_segment_count));
202 }
203
204 void AudioInputRendererHost::DoSendRecordingMessage(
205     media::AudioInputController* controller) {
206   DCHECK_CURRENTLY_ON(BrowserThread::IO);
207   // TODO(henrika): See crbug.com/115262 for details on why this method
208   // should be implemented.
209   AudioEntry* entry = LookupByController(controller);
210   if (!entry) {
211     NOTREACHED() << "AudioInputController is invalid.";
212     return;
213   }
214   LogMessage(
215       entry->stream_id, "DoSendRecordingMessage: stream is now started", true);
216 }
217
218 void AudioInputRendererHost::DoHandleError(
219     media::AudioInputController* controller,
220     media::AudioInputController::ErrorCode error_code) {
221   DCHECK_CURRENTLY_ON(BrowserThread::IO);
222   AudioEntry* entry = LookupByController(controller);
223   if (!entry) {
224     NOTREACHED() << "AudioInputController is invalid.";
225     return;
226   }
227
228   // This is a fix for crbug.com/357501. The error can be triggered when closing
229   // the lid on Macs, which causes more problems than it fixes.
230   // Also, in crbug.com/357569, the goal is to remove usage of the error since
231   // it was added to solve a crash on Windows that no longer can be reproduced.
232   if (error_code == media::AudioInputController::NO_DATA_ERROR) {
233     // TODO(henrika): it might be possible to do something other than just
234     // logging when we detect many NO_DATA_ERROR calls for a stream.
235     LogMessage(entry->stream_id, "AIC::DoCheckForNoData: NO_DATA_ERROR", false);
236     return;
237   }
238
239   std::ostringstream oss;
240   oss << "AIC reports error_code=" << error_code;
241   LogMessage(entry->stream_id, oss.str(), false);
242
243   audio_log_->OnError(entry->stream_id);
244   DeleteEntryOnError(entry, AUDIO_INPUT_CONTROLLER_ERROR);
245 }
246
247 void AudioInputRendererHost::DoLog(media::AudioInputController* controller,
248                                    const std::string& message) {
249   DCHECK_CURRENTLY_ON(BrowserThread::IO);
250   AudioEntry* entry = LookupByController(controller);
251   if (!entry) {
252     NOTREACHED() << "AudioInputController is invalid.";
253     return;
254   }
255
256   // Add stream ID and current audio level reported by AIC to native log.
257   LogMessage(entry->stream_id, message, false);
258 }
259
260 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message) {
261   bool handled = true;
262   IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost, message)
263     IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream)
264     IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream)
265     IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream)
266     IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume)
267     IPC_MESSAGE_UNHANDLED(handled = false)
268   IPC_END_MESSAGE_MAP()
269
270   return handled;
271 }
272
273 void AudioInputRendererHost::OnCreateStream(
274     int stream_id,
275     int render_view_id,
276     int session_id,
277     const AudioInputHostMsg_CreateStream_Config& config) {
278   DCHECK_CURRENTLY_ON(BrowserThread::IO);
279
280 #if defined(OS_CHROMEOS)
281   if (config.params.channel_layout() ==
282       media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
283     media_stream_manager_->audio_input_device_manager()
284         ->RegisterKeyboardMicStream(
285             base::Bind(&AudioInputRendererHost::DoCreateStream,
286                        this,
287                        stream_id,
288                        render_view_id,
289                        session_id,
290                        config));
291   } else {
292     DoCreateStream(stream_id, render_view_id, session_id, config);
293   }
294 #else
295   DoCreateStream(stream_id, render_view_id, session_id, config);
296 #endif
297 }
298
299 void AudioInputRendererHost::DoCreateStream(
300     int stream_id,
301     int render_view_id,
302     int session_id,
303     const AudioInputHostMsg_CreateStream_Config& config) {
304   DCHECK_CURRENTLY_ON(BrowserThread::IO);
305
306   std::ostringstream oss;
307   oss << "[stream_id=" << stream_id << "] "
308       << "AIRH::OnCreateStream(render_view_id=" << render_view_id
309       << ", session_id=" << session_id << ")";
310   DCHECK_GT(render_view_id, 0);
311
312   // media::AudioParameters is validated in the deserializer.
313   if (LookupById(stream_id) != NULL) {
314     SendErrorMessage(stream_id, STREAM_ALREADY_EXISTS);
315     MaybeUnregisterKeyboardMicStream(config);
316     return;
317   }
318
319   media::AudioParameters audio_params(config.params);
320   if (media_stream_manager_->audio_input_device_manager()->
321       ShouldUseFakeDevice()) {
322     audio_params.Reset(
323         media::AudioParameters::AUDIO_FAKE,
324         config.params.channel_layout(), config.params.channels(),
325         config.params.sample_rate(), config.params.bits_per_sample(),
326         config.params.frames_per_buffer());
327   }
328
329   // Check if we have the permission to open the device and which device to use.
330   std::string device_name;
331   std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
332   if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
333     const StreamDeviceInfo* info = media_stream_manager_->
334         audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
335     if (!info) {
336       SendErrorMessage(stream_id, PERMISSION_DENIED);
337       DLOG(WARNING) << "No permission has been granted to input stream with "
338                     << "session_id=" << session_id;
339       MaybeUnregisterKeyboardMicStream(config);
340       return;
341     }
342
343     device_id = info->device.id;
344     device_name = info->device.name;
345     oss << ": device_name=" << device_name;
346   }
347
348   // Create a new AudioEntry structure.
349   scoped_ptr<AudioEntry> entry(new AudioEntry());
350
351   const uint32 segment_size =
352       (sizeof(media::AudioInputBufferParameters) +
353        media::AudioBus::CalculateMemorySize(audio_params));
354   entry->shared_memory_segment_count = config.shared_memory_count;
355
356   // Create the shared memory and share it with the renderer process
357   // using a new SyncWriter object.
358   base::CheckedNumeric<uint32> size = segment_size;
359   size *= entry->shared_memory_segment_count;
360   if (!size.IsValid() ||
361       !entry->shared_memory.CreateAndMapAnonymous(size.ValueOrDie())) {
362     // If creation of shared memory failed then send an error message.
363     SendErrorMessage(stream_id, SHARED_MEMORY_CREATE_FAILED);
364     MaybeUnregisterKeyboardMicStream(config);
365     return;
366   }
367
368   scoped_ptr<AudioInputSyncWriter> writer(new AudioInputSyncWriter(
369       &entry->shared_memory, entry->shared_memory_segment_count, audio_params));
370
371   if (!writer->Init()) {
372     SendErrorMessage(stream_id, SYNC_WRITER_INIT_FAILED);
373     MaybeUnregisterKeyboardMicStream(config);
374     return;
375   }
376
377   // If we have successfully created the SyncWriter then assign it to the
378   // entry and construct an AudioInputController.
379   entry->writer.reset(writer.release());
380   if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
381     entry->controller = media::AudioInputController::CreateForStream(
382         audio_manager_->GetTaskRunner(),
383         this,
384         WebContentsAudioInputStream::Create(
385             device_id,
386             audio_params,
387             audio_manager_->GetWorkerTaskRunner(),
388             audio_mirroring_manager_),
389         entry->writer.get(),
390         user_input_monitor_);
391   } else {
392     // TODO(henrika): replace CreateLowLatency() with Create() as soon
393     // as satish has ensured that Speech Input also uses the default low-
394     // latency path. See crbug.com/112472 for details.
395     entry->controller =
396         media::AudioInputController::CreateLowLatency(audio_manager_,
397                                                       this,
398                                                       audio_params,
399                                                       device_id,
400                                                       entry->writer.get(),
401                                                       user_input_monitor_);
402   }
403
404   if (!entry->controller.get()) {
405     SendErrorMessage(stream_id, STREAM_CREATE_ERROR);
406     MaybeUnregisterKeyboardMicStream(config);
407     return;
408   }
409
410   // Set the initial AGC state for the audio input stream. Note that, the AGC
411   // is only supported in AUDIO_PCM_LOW_LATENCY mode.
412   if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) {
413     entry->controller->SetAutomaticGainControl(config.automatic_gain_control);
414     oss << ", AGC=" << config.automatic_gain_control;
415   }
416
417 #if defined(OS_CHROMEOS)
418   if (config.params.channel_layout() ==
419           media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
420     entry->has_keyboard_mic_ = true;
421   }
422 #endif
423
424   MediaStreamManager::SendMessageToNativeLog(oss.str());
425   DVLOG(1) << oss.str();
426
427   // Since the controller was created successfully, create an entry and add it
428   // to the map.
429   entry->stream_id = stream_id;
430   audio_entries_.insert(std::make_pair(stream_id, entry.release()));
431   audio_log_->OnCreated(stream_id, audio_params, device_id);
432 }
433
434 void AudioInputRendererHost::OnRecordStream(int stream_id) {
435   DCHECK_CURRENTLY_ON(BrowserThread::IO);
436   LogMessage(stream_id, "OnRecordStream", true);
437
438   AudioEntry* entry = LookupById(stream_id);
439   if (!entry) {
440     SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
441     return;
442   }
443
444   entry->controller->Record();
445   audio_log_->OnStarted(stream_id);
446 }
447
448 void AudioInputRendererHost::OnCloseStream(int stream_id) {
449   DCHECK_CURRENTLY_ON(BrowserThread::IO);
450   LogMessage(stream_id, "OnCloseStream", true);
451
452   AudioEntry* entry = LookupById(stream_id);
453
454   if (entry)
455     CloseAndDeleteStream(entry);
456 }
457
458 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
459   DCHECK_CURRENTLY_ON(BrowserThread::IO);
460
461   AudioEntry* entry = LookupById(stream_id);
462   if (!entry) {
463     SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
464     return;
465   }
466
467   entry->controller->SetVolume(volume);
468   audio_log_->OnSetVolume(stream_id, volume);
469 }
470
471 void AudioInputRendererHost::SendErrorMessage(
472     int stream_id, ErrorCode error_code) {
473   std::string err_msg =
474       base::StringPrintf("SendErrorMessage(error_code=%d)", error_code);
475   LogMessage(stream_id, err_msg, true);
476
477   Send(new AudioInputMsg_NotifyStreamStateChanged(
478       stream_id, media::AudioInputIPCDelegate::kError));
479 }
480
481 void AudioInputRendererHost::DeleteEntries() {
482   DCHECK_CURRENTLY_ON(BrowserThread::IO);
483
484   for (AudioEntryMap::iterator i = audio_entries_.begin();
485        i != audio_entries_.end(); ++i) {
486     CloseAndDeleteStream(i->second);
487   }
488 }
489
490 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
491   DCHECK_CURRENTLY_ON(BrowserThread::IO);
492
493   if (!entry->pending_close) {
494     LogMessage(entry->stream_id, "CloseAndDeleteStream", true);
495     entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
496                                         this, entry));
497     entry->pending_close = true;
498     audio_log_->OnClosed(entry->stream_id);
499   }
500 }
501
502 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
503   DCHECK_CURRENTLY_ON(BrowserThread::IO);
504   LogMessage(entry->stream_id, "DeleteEntry: stream is now closed", true);
505
506 #if defined(OS_CHROMEOS)
507   if (entry->has_keyboard_mic_) {
508     media_stream_manager_->audio_input_device_manager()
509         ->UnregisterKeyboardMicStream();
510   }
511 #endif
512
513   // Delete the entry when this method goes out of scope.
514   scoped_ptr<AudioEntry> entry_deleter(entry);
515
516   // Erase the entry from the map.
517   audio_entries_.erase(entry->stream_id);
518 }
519
520 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry,
521     ErrorCode error_code) {
522   DCHECK_CURRENTLY_ON(BrowserThread::IO);
523
524   // Sends the error message first before we close the stream because
525   // |entry| is destroyed in DeleteEntry().
526   SendErrorMessage(entry->stream_id, error_code);
527   CloseAndDeleteStream(entry);
528 }
529
530 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
531     int stream_id) {
532   DCHECK_CURRENTLY_ON(BrowserThread::IO);
533
534   AudioEntryMap::iterator i = audio_entries_.find(stream_id);
535   if (i != audio_entries_.end())
536     return i->second;
537   return NULL;
538 }
539
540 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
541     media::AudioInputController* controller) {
542   DCHECK_CURRENTLY_ON(BrowserThread::IO);
543
544   // Iterate the map of entries.
545   // TODO(hclam): Implement a faster look up method.
546   for (AudioEntryMap::iterator i = audio_entries_.begin();
547        i != audio_entries_.end(); ++i) {
548     if (controller == i->second->controller.get())
549       return i->second;
550   }
551   return NULL;
552 }
553
554 void AudioInputRendererHost::MaybeUnregisterKeyboardMicStream(
555     const AudioInputHostMsg_CreateStream_Config& config) {
556 #if defined(OS_CHROMEOS)
557   if (config.params.channel_layout() ==
558       media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
559     media_stream_manager_->audio_input_device_manager()
560         ->UnregisterKeyboardMicStream();
561   }
562 #endif
563 }
564
565 }  // namespace content