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
19 namespace content {
20
21 struct 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
AudioEntry()44 AudioInputRendererHost::AudioEntry::AudioEntry()
45 : stream_id(0),
46 shared_memory_segment_count(0),
47 pending_close(false) {
48 }
49
~AudioEntry()50 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
51
AudioInputRendererHost(media::AudioManager * audio_manager,MediaStreamManager * media_stream_manager,AudioMirroringManager * audio_mirroring_manager,media::UserInputMonitor * user_input_monitor)52 AudioInputRendererHost::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
~AudioInputRendererHost()64 AudioInputRendererHost::~AudioInputRendererHost() {
65 DCHECK(audio_entries_.empty());
66 }
67
OnChannelClosing()68 void AudioInputRendererHost::OnChannelClosing() {
69 // Since the IPC channel is gone, close all requested audio streams.
70 DeleteEntries();
71 }
72
OnDestruct() const73 void AudioInputRendererHost::OnDestruct() const {
74 BrowserThread::DeleteOnIOThread::Destruct(this);
75 }
76
OnCreated(media::AudioInputController * controller)77 void 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
OnRecording(media::AudioInputController * controller)88 void 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
OnError(media::AudioInputController * controller)99 void 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
OnData(media::AudioInputController * controller,const uint8 * data,uint32 size)109 void AudioInputRendererHost::OnData(media::AudioInputController* controller,
110 const uint8* data,
111 uint32 size) {
112 NOTREACHED() << "Only low-latency mode is supported.";
113 }
114
DoCompleteCreation(media::AudioInputController * controller)115 void 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
DoSendRecordingMessage(media::AudioInputController * controller)169 void 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
DoHandleError(media::AudioInputController * controller)176 void 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
OnMessageReceived(const IPC::Message & message,bool * message_was_ok)188 bool 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
OnCreateStream(int stream_id,int render_view_id,int session_id,const AudioInputHostMsg_CreateStream_Config & config)202 void 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
OnRecordStream(int stream_id)315 void 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
OnCloseStream(int stream_id)328 void 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
OnSetVolume(int stream_id,double volume)337 void 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
SendErrorMessage(int stream_id)350 void AudioInputRendererHost::SendErrorMessage(int stream_id) {
351 Send(new AudioInputMsg_NotifyStreamStateChanged(
352 stream_id, media::AudioInputIPCDelegate::kError));
353 }
354
DeleteEntries()355 void 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
CloseAndDeleteStream(AudioEntry * entry)364 void 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
DeleteEntry(AudioEntry * entry)375 void 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
DeleteEntryOnError(AudioEntry * entry)385 void 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
LookupById(int stream_id)394 AudioInputRendererHost::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
LookupByController(media::AudioInputController * controller)404 AudioInputRendererHost::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