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