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
22namespace {
23
24void 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
36namespace content {
37
38struct 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
64AudioInputRendererHost::AudioEntry::AudioEntry()
65    : stream_id(0),
66      shared_memory_segment_count(0),
67      pending_close(false),
68      has_keyboard_mic_(false) {
69}
70
71AudioInputRendererHost::AudioEntry::~AudioEntry() {}
72
73AudioInputRendererHost::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
86AudioInputRendererHost::~AudioInputRendererHost() {
87  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
88  DCHECK(audio_entries_.empty());
89}
90
91void AudioInputRendererHost::OnChannelClosing() {
92  // Since the IPC sender is gone, close all requested audio streams.
93  DeleteEntries();
94}
95
96void AudioInputRendererHost::OnDestruct() const {
97  BrowserThread::DeleteOnIOThread::Destruct(this);
98}
99
100void 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
111void 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
122void 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
134void AudioInputRendererHost::OnData(media::AudioInputController* controller,
135                                    const media::AudioBus* data) {
136  NOTREACHED() << "Only low-latency mode is supported.";
137}
138
139void 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
149void 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
204void 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
218void 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
247void 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
260bool 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
273void 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
299void 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
434void 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
448void 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
458void 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
471void 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
481void 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
490void 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
502void 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
520void 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
530AudioInputRendererHost::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
540AudioInputRendererHost::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
554void 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
566