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