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