blob: 4a2f731a81c300fc7390832ae60132917dc69659 [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001// 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"
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01008#include "base/memory/shared_memory.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +00009#include "base/metrics/histogram.h"
Ben Murdocha3f7b4e2013-07-24 10:36:34 +010010#include "base/process/process.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000011#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"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000014#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
15#include "content/browser/renderer_host/media/web_contents_capture_util.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000016#include "media/audio/audio_manager_base.h"
Torne (Richard Coles)58218062012-11-14 11:43:16 +000017
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
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000030 // Shared memory for transmission of the audio data. It has
31 // |shared_memory_segment_count| equal lengthed segments.
Torne (Richard Coles)58218062012-11-14 11:43:16 +000032 base::SharedMemory shared_memory;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000033 int shared_memory_segment_count;
Torne (Richard Coles)58218062012-11-14 11:43:16 +000034
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),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000045 shared_memory_segment_count(0),
Torne (Richard Coles)58218062012-11-14 11:43:16 +000046 pending_close(false) {
47}
48
49AudioInputRendererHost::AudioEntry::~AudioEntry() {}
50
51AudioInputRendererHost::AudioInputRendererHost(
52 media::AudioManager* audio_manager,
Ben Murdocheb525c52013-07-10 11:40:50 +010053 MediaStreamManager* media_stream_manager,
54 AudioMirroringManager* audio_mirroring_manager)
Torne (Richard Coles)58218062012-11-14 11:43:16 +000055 : audio_manager_(audio_manager),
Ben Murdocheb525c52013-07-10 11:40:50 +010056 media_stream_manager_(media_stream_manager),
57 audio_mirroring_manager_(audio_mirroring_manager) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000058}
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
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000097void AudioInputRendererHost::OnError(media::AudioInputController* controller) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +000098 BrowserThread::PostTask(
99 BrowserThread::IO,
100 FROM_HERE,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000101 base::Bind(
102 &AudioInputRendererHost::DoHandleError,
103 this,
104 make_scoped_refptr(controller)));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000105}
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
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100121 if (!PeerHandle()) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000122 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;
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100136 if (!entry->shared_memory.ShareToProcess(PeerHandle(),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000137 &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.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100155 if (!writer->PrepareForeignSocketHandle(PeerHandle(),
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000156 &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,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100163 entry->shared_memory.requested_size(),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000164 entry->shared_memory_segment_count));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000165}
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(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000175 media::AudioInputController* controller) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000176 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)
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000189 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}
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000198
199void AudioInputRendererHost::OnCreateStream(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000200 int stream_id,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100201 int render_view_id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000202 int session_id,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100203 const AudioInputHostMsg_CreateStream_Config& config) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100205
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
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000212 // media::AudioParameters is validated in the deserializer.
213 if (LookupById(stream_id) != NULL) {
214 SendErrorMessage(stream_id);
215 return;
216 }
217
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100218 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
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000228 // Check if we have the permission to open the device and which device to use.
229 std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100230 if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000231 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
Ben Murdochbb1529c2013-08-08 10:24:53 +0100240 device_id = info->device.id;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000242
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000243 // Create a new AudioEntry structure.
244 scoped_ptr<AudioEntry> entry(new AudioEntry());
245
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100246 const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) +
247 audio_params.GetBytesPerBuffer());
248 entry->shared_memory_segment_count = config.shared_memory_count;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000249
250 // Create the shared memory and share it with the renderer process
251 // using a new SyncWriter object.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000252 if (!entry->shared_memory.CreateAndMapAnonymous(
253 segment_size * entry->shared_memory_segment_count)) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000254 // If creation of shared memory failed then send an error message.
255 SendErrorMessage(stream_id);
256 return;
257 }
258
259 scoped_ptr<AudioInputSyncWriter> writer(
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000260 new AudioInputSyncWriter(&entry->shared_memory,
261 entry->shared_memory_segment_count));
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000262
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.
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000270 entry->writer.reset(writer.release());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000271 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
272 entry->controller = media::AudioInputController::CreateForStream(
Ben Murdocheb525c52013-07-10 11:40:50 +0100273 audio_manager_->GetMessageLoop(),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000274 this,
275 WebContentsAudioInputStream::Create(
Ben Murdocheb525c52013-07-10 11:40:50 +0100276 device_id, audio_params, audio_manager_->GetWorkerLoop(),
277 audio_mirroring_manager_),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000278 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 }
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000290
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100291 if (!entry->controller.get()) {
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000292 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.
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100298 if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY)
299 entry->controller->SetAutomaticGainControl(config.automatic_gain_control);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000300
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100301 // Since the controller was created successfully, create an entry and add it
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000302 // to the map.
303 entry->stream_id = stream_id;
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000304 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);
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000326}
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
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000354void 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) {
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100401 if (controller == i->second->controller.get())
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000402 return i->second;
403 }
404 return NULL;
405}
406
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000407} // namespace content