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.
5 #include "content/browser/renderer_host/media/audio_input_renderer_host.h"
8 #include "base/memory/shared_memory.h"
9 #include "base/metrics/histogram.h"
10 #include "base/numerics/safe_math.h"
11 #include "base/process/process.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/browser/media/capture/web_contents_audio_input_stream.h"
14 #include "content/browser/media/capture/web_contents_capture_util.h"
15 #include "content/browser/media/media_internals.h"
16 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
17 #include "content/browser/renderer_host/media/audio_input_sync_writer.h"
18 #include "content/browser/renderer_host/media/media_stream_manager.h"
19 #include "media/audio/audio_manager_base.h"
20 #include "media/base/audio_bus.h"
24 void LogMessage(int stream_id
, const std::string
& msg
, bool add_prefix
) {
25 std::ostringstream oss
;
26 oss
<< "[stream_id=" << stream_id
<< "] ";
30 content::MediaStreamManager::SendMessageToNativeLog(oss
.str());
31 DVLOG(1) << oss
.str();
38 struct AudioInputRendererHost::AudioEntry
{
42 // The AudioInputController that manages the audio input stream.
43 scoped_refptr
<media::AudioInputController
> controller
;
45 // The audio input stream ID in the render view.
48 // Shared memory for transmission of the audio data. It has
49 // |shared_memory_segment_count| equal lengthed segments.
50 base::SharedMemory shared_memory
;
51 int shared_memory_segment_count
;
53 // The synchronous writer to be used by the controller. We have the
54 // ownership of the writer.
55 scoped_ptr
<media::AudioInputController::SyncWriter
> writer
;
57 // Set to true after we called Close() for the controller.
60 // If this entry's layout has a keyboard mic channel.
61 bool has_keyboard_mic_
;
64 AudioInputRendererHost::AudioEntry::AudioEntry()
66 shared_memory_segment_count(0),
68 has_keyboard_mic_(false) {
71 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
73 AudioInputRendererHost::AudioInputRendererHost(
74 media::AudioManager
* audio_manager
,
75 MediaStreamManager
* media_stream_manager
,
76 AudioMirroringManager
* audio_mirroring_manager
,
77 media::UserInputMonitor
* user_input_monitor
)
78 : BrowserMessageFilter(AudioMsgStart
),
79 audio_manager_(audio_manager
),
80 media_stream_manager_(media_stream_manager
),
81 audio_mirroring_manager_(audio_mirroring_manager
),
82 user_input_monitor_(user_input_monitor
),
83 audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
84 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER
)) {}
86 AudioInputRendererHost::~AudioInputRendererHost() {
87 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
88 DCHECK(audio_entries_
.empty());
91 void AudioInputRendererHost::OnChannelClosing() {
92 // Since the IPC sender is gone, close all requested audio streams.
96 void AudioInputRendererHost::OnDestruct() const {
97 BrowserThread::DeleteOnIOThread::Destruct(this);
100 void AudioInputRendererHost::OnCreated(
101 media::AudioInputController
* controller
) {
102 BrowserThread::PostTask(
106 &AudioInputRendererHost::DoCompleteCreation
,
108 make_scoped_refptr(controller
)));
111 void AudioInputRendererHost::OnRecording(
112 media::AudioInputController
* controller
) {
113 BrowserThread::PostTask(
117 &AudioInputRendererHost::DoSendRecordingMessage
,
119 make_scoped_refptr(controller
)));
122 void AudioInputRendererHost::OnError(media::AudioInputController
* controller
,
123 media::AudioInputController::ErrorCode error_code
) {
124 BrowserThread::PostTask(
128 &AudioInputRendererHost::DoHandleError
,
130 make_scoped_refptr(controller
),
134 void AudioInputRendererHost::OnData(media::AudioInputController
* controller
,
135 const media::AudioBus
* data
) {
136 NOTREACHED() << "Only low-latency mode is supported.";
139 void AudioInputRendererHost::OnLog(media::AudioInputController
* controller
,
140 const std::string
& message
) {
141 BrowserThread::PostTask(BrowserThread::IO
,
143 base::Bind(&AudioInputRendererHost::DoLog
,
145 make_scoped_refptr(controller
),
149 void AudioInputRendererHost::DoCompleteCreation(
150 media::AudioInputController
* controller
) {
151 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
153 AudioEntry
* entry
= LookupByController(controller
);
155 NOTREACHED() << "AudioInputController is invalid.";
160 NOTREACHED() << "Renderer process handle is invalid.";
161 DeleteEntryOnError(entry
, INVALID_PEER_HANDLE
);
165 if (!entry
->controller
->SharedMemoryAndSyncSocketMode()) {
166 NOTREACHED() << "Only shared-memory/sync-socket mode is supported.";
167 DeleteEntryOnError(entry
, INVALID_LATENCY_MODE
);
171 // Once the audio stream is created then complete the creation process by
172 // mapping shared memory and sharing with the renderer process.
173 base::SharedMemoryHandle foreign_memory_handle
;
174 if (!entry
->shared_memory
.ShareToProcess(PeerHandle(),
175 &foreign_memory_handle
)) {
176 // If we failed to map and share the shared memory then close the audio
177 // stream and send an error message.
178 DeleteEntryOnError(entry
, MEMORY_SHARING_FAILED
);
182 AudioInputSyncWriter
* writer
=
183 static_cast<AudioInputSyncWriter
*>(entry
->writer
.get());
185 base::SyncSocket::TransitDescriptor socket_transit_descriptor
;
187 // If we failed to prepare the sync socket for the renderer then we fail
188 // the construction of audio input stream.
189 if (!writer
->PrepareForeignSocket(PeerHandle(), &socket_transit_descriptor
)) {
190 DeleteEntryOnError(entry
, SYNC_SOCKET_ERROR
);
194 LogMessage(entry
->stream_id
,
195 "DoCompleteCreation: IPC channel and stream are now open",
198 Send(new AudioInputMsg_NotifyStreamCreated(
199 entry
->stream_id
, foreign_memory_handle
, socket_transit_descriptor
,
200 entry
->shared_memory
.requested_size(),
201 entry
->shared_memory_segment_count
));
204 void AudioInputRendererHost::DoSendRecordingMessage(
205 media::AudioInputController
* controller
) {
206 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
207 // TODO(henrika): See crbug.com/115262 for details on why this method
208 // should be implemented.
209 AudioEntry
* entry
= LookupByController(controller
);
211 NOTREACHED() << "AudioInputController is invalid.";
215 entry
->stream_id
, "DoSendRecordingMessage: stream is now started", true);
218 void AudioInputRendererHost::DoHandleError(
219 media::AudioInputController
* controller
,
220 media::AudioInputController::ErrorCode error_code
) {
221 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
222 AudioEntry
* entry
= LookupByController(controller
);
224 NOTREACHED() << "AudioInputController is invalid.";
228 // This is a fix for crbug.com/357501. The error can be triggered when closing
229 // the lid on Macs, which causes more problems than it fixes.
230 // Also, in crbug.com/357569, the goal is to remove usage of the error since
231 // it was added to solve a crash on Windows that no longer can be reproduced.
232 if (error_code
== media::AudioInputController::NO_DATA_ERROR
) {
233 // TODO(henrika): it might be possible to do something other than just
234 // logging when we detect many NO_DATA_ERROR calls for a stream.
235 LogMessage(entry
->stream_id
, "AIC::DoCheckForNoData: NO_DATA_ERROR", false);
239 std::ostringstream oss
;
240 oss
<< "AIC reports error_code=" << error_code
;
241 LogMessage(entry
->stream_id
, oss
.str(), false);
243 audio_log_
->OnError(entry
->stream_id
);
244 DeleteEntryOnError(entry
, AUDIO_INPUT_CONTROLLER_ERROR
);
247 void AudioInputRendererHost::DoLog(media::AudioInputController
* controller
,
248 const std::string
& message
) {
249 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
250 AudioEntry
* entry
= LookupByController(controller
);
252 NOTREACHED() << "AudioInputController is invalid.";
256 // Add stream ID and current audio level reported by AIC to native log.
257 LogMessage(entry
->stream_id
, message
, false);
260 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message
& message
) {
262 IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost
, message
)
263 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream
, OnCreateStream
)
264 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream
, OnRecordStream
)
265 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream
, OnCloseStream
)
266 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume
, OnSetVolume
)
267 IPC_MESSAGE_UNHANDLED(handled
= false)
268 IPC_END_MESSAGE_MAP()
273 void AudioInputRendererHost::OnCreateStream(
277 const AudioInputHostMsg_CreateStream_Config
& config
) {
278 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
280 #if defined(OS_CHROMEOS)
281 if (config
.params
.channel_layout() ==
282 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
283 media_stream_manager_
->audio_input_device_manager()
284 ->RegisterKeyboardMicStream(
285 base::Bind(&AudioInputRendererHost::DoCreateStream
, this, stream_id
,
286 render_frame_id
, session_id
, config
));
288 DoCreateStream(stream_id
, render_frame_id
, session_id
, config
);
291 DoCreateStream(stream_id
, render_frame_id
, session_id
, config
);
295 void AudioInputRendererHost::DoCreateStream(
299 const AudioInputHostMsg_CreateStream_Config
& config
) {
300 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
302 std::ostringstream oss
;
303 oss
<< "[stream_id=" << stream_id
<< "] "
304 << "AIRH::OnCreateStream(render_frame_id=" << render_frame_id
305 << ", session_id=" << session_id
<< ")";
306 DCHECK_GT(render_frame_id
, 0);
308 // media::AudioParameters is validated in the deserializer.
309 if (LookupById(stream_id
) != NULL
) {
310 SendErrorMessage(stream_id
, STREAM_ALREADY_EXISTS
);
311 MaybeUnregisterKeyboardMicStream(config
);
315 media::AudioParameters
audio_params(config
.params
);
316 if (media_stream_manager_
->audio_input_device_manager()->
317 ShouldUseFakeDevice()) {
319 media::AudioParameters::AUDIO_FAKE
,
320 config
.params
.channel_layout(), config
.params
.channels(),
321 config
.params
.sample_rate(), config
.params
.bits_per_sample(),
322 config
.params
.frames_per_buffer());
325 // Check if we have the permission to open the device and which device to use.
326 std::string device_name
;
327 std::string device_id
= media::AudioManagerBase::kDefaultDeviceId
;
328 if (audio_params
.format() != media::AudioParameters::AUDIO_FAKE
) {
329 const StreamDeviceInfo
* info
= media_stream_manager_
->
330 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id
);
332 SendErrorMessage(stream_id
, PERMISSION_DENIED
);
333 DLOG(WARNING
) << "No permission has been granted to input stream with "
334 << "session_id=" << session_id
;
335 MaybeUnregisterKeyboardMicStream(config
);
339 device_id
= info
->device
.id
;
340 device_name
= info
->device
.name
;
341 oss
<< ": device_name=" << device_name
;
344 // Create a new AudioEntry structure.
345 scoped_ptr
<AudioEntry
> entry(new AudioEntry());
347 const uint32 segment_size
=
348 (sizeof(media::AudioInputBufferParameters
) +
349 media::AudioBus::CalculateMemorySize(audio_params
));
350 entry
->shared_memory_segment_count
= config
.shared_memory_count
;
352 // Create the shared memory and share it with the renderer process
353 // using a new SyncWriter object.
354 base::CheckedNumeric
<uint32
> size
= segment_size
;
355 size
*= entry
->shared_memory_segment_count
;
356 if (!size
.IsValid() ||
357 !entry
->shared_memory
.CreateAndMapAnonymous(size
.ValueOrDie())) {
358 // If creation of shared memory failed then send an error message.
359 SendErrorMessage(stream_id
, SHARED_MEMORY_CREATE_FAILED
);
360 MaybeUnregisterKeyboardMicStream(config
);
364 scoped_ptr
<AudioInputSyncWriter
> writer(new AudioInputSyncWriter(
365 &entry
->shared_memory
, entry
->shared_memory_segment_count
, audio_params
));
367 if (!writer
->Init()) {
368 SendErrorMessage(stream_id
, SYNC_WRITER_INIT_FAILED
);
369 MaybeUnregisterKeyboardMicStream(config
);
373 // If we have successfully created the SyncWriter then assign it to the
374 // entry and construct an AudioInputController.
375 entry
->writer
.reset(writer
.release());
376 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id
)) {
377 entry
->controller
= media::AudioInputController::CreateForStream(
378 audio_manager_
->GetTaskRunner(),
380 WebContentsAudioInputStream::Create(
383 audio_manager_
->GetWorkerTaskRunner(),
384 audio_mirroring_manager_
),
386 user_input_monitor_
);
388 DCHECK_EQ(config
.params
.format(),
389 media::AudioParameters::AUDIO_PCM_LOW_LATENCY
);
390 entry
->controller
= media::AudioInputController::CreateLowLatency(
397 config
.automatic_gain_control
);
398 oss
<< ", AGC=" << config
.automatic_gain_control
;
401 if (!entry
->controller
.get()) {
402 SendErrorMessage(stream_id
, STREAM_CREATE_ERROR
);
403 MaybeUnregisterKeyboardMicStream(config
);
407 #if defined(OS_CHROMEOS)
408 if (config
.params
.channel_layout() ==
409 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
410 entry
->has_keyboard_mic_
= true;
414 MediaStreamManager::SendMessageToNativeLog(oss
.str());
415 DVLOG(1) << oss
.str();
417 // Since the controller was created successfully, create an entry and add it
419 entry
->stream_id
= stream_id
;
420 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
421 audio_log_
->OnCreated(stream_id
, audio_params
, device_id
);
423 // TODO(dalecurtis): Wire up input streams to show WebContents titles, needs
424 // AudioInputRendererHost converted to use RenderProcessID & RenderFrameID
425 // instead of RenderViewID.
428 void AudioInputRendererHost::OnRecordStream(int stream_id
) {
429 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
430 LogMessage(stream_id
, "OnRecordStream", true);
432 AudioEntry
* entry
= LookupById(stream_id
);
434 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
438 entry
->controller
->Record();
439 audio_log_
->OnStarted(stream_id
);
442 void AudioInputRendererHost::OnCloseStream(int stream_id
) {
443 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
444 LogMessage(stream_id
, "OnCloseStream", true);
446 AudioEntry
* entry
= LookupById(stream_id
);
449 CloseAndDeleteStream(entry
);
452 void AudioInputRendererHost::OnSetVolume(int stream_id
, double volume
) {
453 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
455 AudioEntry
* entry
= LookupById(stream_id
);
457 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
461 entry
->controller
->SetVolume(volume
);
462 audio_log_
->OnSetVolume(stream_id
, volume
);
465 void AudioInputRendererHost::SendErrorMessage(
466 int stream_id
, ErrorCode error_code
) {
467 std::string err_msg
=
468 base::StringPrintf("SendErrorMessage(error_code=%d)", error_code
);
469 LogMessage(stream_id
, err_msg
, true);
471 Send(new AudioInputMsg_NotifyStreamStateChanged(
472 stream_id
, media::AudioInputIPCDelegate::kError
));
475 void AudioInputRendererHost::DeleteEntries() {
476 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
478 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
479 i
!= audio_entries_
.end(); ++i
) {
480 CloseAndDeleteStream(i
->second
);
484 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
485 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
487 if (!entry
->pending_close
) {
488 LogMessage(entry
->stream_id
, "CloseAndDeleteStream", true);
489 entry
->controller
->Close(base::Bind(&AudioInputRendererHost::DeleteEntry
,
491 entry
->pending_close
= true;
492 audio_log_
->OnClosed(entry
->stream_id
);
496 void AudioInputRendererHost::DeleteEntry(AudioEntry
* entry
) {
497 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
498 LogMessage(entry
->stream_id
, "DeleteEntry: stream is now closed", true);
500 #if defined(OS_CHROMEOS)
501 if (entry
->has_keyboard_mic_
) {
502 media_stream_manager_
->audio_input_device_manager()
503 ->UnregisterKeyboardMicStream();
507 // Delete the entry when this method goes out of scope.
508 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
510 // Erase the entry from the map.
511 audio_entries_
.erase(entry
->stream_id
);
514 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry
* entry
,
515 ErrorCode error_code
) {
516 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
518 // Sends the error message first before we close the stream because
519 // |entry| is destroyed in DeleteEntry().
520 SendErrorMessage(entry
->stream_id
, error_code
);
521 CloseAndDeleteStream(entry
);
524 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupById(
526 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
528 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
529 if (i
!= audio_entries_
.end())
534 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupByController(
535 media::AudioInputController
* controller
) {
536 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
538 // Iterate the map of entries.
539 // TODO(hclam): Implement a faster look up method.
540 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
541 i
!= audio_entries_
.end(); ++i
) {
542 if (controller
== i
->second
->controller
.get())
548 void AudioInputRendererHost::MaybeUnregisterKeyboardMicStream(
549 const AudioInputHostMsg_CreateStream_Config
& config
) {
550 #if defined(OS_CHROMEOS)
551 if (config
.params
.channel_layout() ==
552 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC
) {
553 media_stream_manager_
->audio_input_device_manager()
554 ->UnregisterKeyboardMicStream();
559 } // namespace content