audio_input_renderer_host.cc revision bb1529ce867d8845a77ec7cdf3e3003ef1771a40
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
18namespace content {
19
20struct 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
43AudioInputRendererHost::AudioEntry::AudioEntry()
44    : stream_id(0),
45      shared_memory_segment_count(0),
46      pending_close(false) {
47}
48
49AudioInputRendererHost::AudioEntry::~AudioEntry() {}
50
51AudioInputRendererHost::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
60AudioInputRendererHost::~AudioInputRendererHost() {
61  DCHECK(audio_entries_.empty());
62}
63
64void AudioInputRendererHost::OnChannelClosing() {
65  BrowserMessageFilter::OnChannelClosing();
66
67  // Since the IPC channel is gone, close all requested audio streams.
68  DeleteEntries();
69}
70
71void AudioInputRendererHost::OnDestruct() const {
72  BrowserThread::DeleteOnIOThread::Destruct(this);
73}
74
75void 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
86void 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
97void 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
107void AudioInputRendererHost::OnData(media::AudioInputController* controller,
108                                    const uint8* data,
109                                    uint32 size) {
110  NOTREACHED() << "Only low-latency mode is supported.";
111}
112
113void 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
167void 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
174void 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
185bool 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
199void 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
307void 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
319void 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
328void 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
340void AudioInputRendererHost::SendErrorMessage(int stream_id) {
341  Send(new AudioInputMsg_NotifyStreamStateChanged(
342      stream_id, media::AudioInputIPCDelegate::kError));
343}
344
345void 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
354void 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
364void 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
374void 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
383AudioInputRendererHost::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
393AudioInputRendererHost::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