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