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.
61 AudioInputRendererHost::AudioEntry::AudioEntry()
63 shared_memory_segment_count(0),
64 pending_close(false) {
67 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
69 AudioInputRendererHost::AudioInputRendererHost(
70 media::AudioManager
* audio_manager
,
71 MediaStreamManager
* media_stream_manager
,
72 AudioMirroringManager
* audio_mirroring_manager
,
73 media::UserInputMonitor
* user_input_monitor
)
74 : BrowserMessageFilter(AudioMsgStart
),
75 audio_manager_(audio_manager
),
76 media_stream_manager_(media_stream_manager
),
77 audio_mirroring_manager_(audio_mirroring_manager
),
78 user_input_monitor_(user_input_monitor
),
79 audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
80 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER
)) {}
82 AudioInputRendererHost::~AudioInputRendererHost() {
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
84 DCHECK(audio_entries_
.empty());
87 void AudioInputRendererHost::OnChannelClosing() {
88 // Since the IPC sender is gone, close all requested audio streams.
92 void AudioInputRendererHost::OnDestruct() const {
93 BrowserThread::DeleteOnIOThread::Destruct(this);
96 void AudioInputRendererHost::OnCreated(
97 media::AudioInputController
* controller
) {
98 BrowserThread::PostTask(
102 &AudioInputRendererHost::DoCompleteCreation
,
104 make_scoped_refptr(controller
)));
107 void AudioInputRendererHost::OnRecording(
108 media::AudioInputController
* controller
) {
109 BrowserThread::PostTask(
113 &AudioInputRendererHost::DoSendRecordingMessage
,
115 make_scoped_refptr(controller
)));
118 void AudioInputRendererHost::OnError(media::AudioInputController
* controller
,
119 media::AudioInputController::ErrorCode error_code
) {
120 BrowserThread::PostTask(
124 &AudioInputRendererHost::DoHandleError
,
126 make_scoped_refptr(controller
),
130 void AudioInputRendererHost::OnData(media::AudioInputController
* controller
,
131 const media::AudioBus
* data
) {
132 NOTREACHED() << "Only low-latency mode is supported.";
135 void AudioInputRendererHost::OnLog(media::AudioInputController
* controller
,
136 const std::string
& message
) {
137 BrowserThread::PostTask(BrowserThread::IO
,
139 base::Bind(&AudioInputRendererHost::DoLog
,
141 make_scoped_refptr(controller
),
145 void AudioInputRendererHost::DoCompleteCreation(
146 media::AudioInputController
* controller
) {
147 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
149 AudioEntry
* entry
= LookupByController(controller
);
151 NOTREACHED() << "AudioInputController is invalid.";
156 NOTREACHED() << "Renderer process handle is invalid.";
157 DeleteEntryOnError(entry
, INVALID_PEER_HANDLE
);
161 if (!entry
->controller
->SharedMemoryAndSyncSocketMode()) {
162 NOTREACHED() << "Only shared-memory/sync-socket mode is supported.";
163 DeleteEntryOnError(entry
, INVALID_LATENCY_MODE
);
167 // Once the audio stream is created then complete the creation process by
168 // mapping shared memory and sharing with the renderer process.
169 base::SharedMemoryHandle foreign_memory_handle
;
170 if (!entry
->shared_memory
.ShareToProcess(PeerHandle(),
171 &foreign_memory_handle
)) {
172 // If we failed to map and share the shared memory then close the audio
173 // stream and send an error message.
174 DeleteEntryOnError(entry
, MEMORY_SHARING_FAILED
);
178 AudioInputSyncWriter
* writer
=
179 static_cast<AudioInputSyncWriter
*>(entry
->writer
.get());
181 base::SyncSocket::TransitDescriptor socket_transit_descriptor
;
183 // If we failed to prepare the sync socket for the renderer then we fail
184 // the construction of audio input stream.
185 if (!writer
->PrepareForeignSocket(PeerHandle(), &socket_transit_descriptor
)) {
186 DeleteEntryOnError(entry
, SYNC_SOCKET_ERROR
);
190 LogMessage(entry
->stream_id
,
191 "DoCompleteCreation: IPC channel and stream are now open",
194 Send(new AudioInputMsg_NotifyStreamCreated(
195 entry
->stream_id
, foreign_memory_handle
, socket_transit_descriptor
,
196 entry
->shared_memory
.requested_size(),
197 entry
->shared_memory_segment_count
));
200 void AudioInputRendererHost::DoSendRecordingMessage(
201 media::AudioInputController
* controller
) {
202 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
203 // TODO(henrika): See crbug.com/115262 for details on why this method
204 // should be implemented.
205 AudioEntry
* entry
= LookupByController(controller
);
207 NOTREACHED() << "AudioInputController is invalid.";
211 entry
->stream_id
, "DoSendRecordingMessage: stream is now started", true);
214 void AudioInputRendererHost::DoHandleError(
215 media::AudioInputController
* controller
,
216 media::AudioInputController::ErrorCode error_code
) {
217 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
218 AudioEntry
* entry
= LookupByController(controller
);
220 NOTREACHED() << "AudioInputController is invalid.";
224 // This is a fix for crbug.com/357501. The error can be triggered when closing
225 // the lid on Macs, which causes more problems than it fixes.
226 // Also, in crbug.com/357569, the goal is to remove usage of the error since
227 // it was added to solve a crash on Windows that no longer can be reproduced.
228 if (error_code
== media::AudioInputController::NO_DATA_ERROR
) {
229 // TODO(henrika): it might be possible to do something other than just
230 // logging when we detect many NO_DATA_ERROR calls for a stream.
231 LogMessage(entry
->stream_id
, "AIC::DoCheckForNoData: NO_DATA_ERROR", false);
235 std::ostringstream oss
;
236 oss
<< "AIC reports error_code=" << error_code
;
237 LogMessage(entry
->stream_id
, oss
.str(), false);
239 audio_log_
->OnError(entry
->stream_id
);
240 DeleteEntryOnError(entry
, AUDIO_INPUT_CONTROLLER_ERROR
);
243 void AudioInputRendererHost::DoLog(media::AudioInputController
* controller
,
244 const std::string
& message
) {
245 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
246 AudioEntry
* entry
= LookupByController(controller
);
248 NOTREACHED() << "AudioInputController is invalid.";
252 // Add stream ID and current audio level reported by AIC to native log.
253 LogMessage(entry
->stream_id
, message
, false);
256 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message
& message
) {
258 IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost
, message
)
259 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream
, OnCreateStream
)
260 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream
, OnRecordStream
)
261 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream
, OnCloseStream
)
262 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume
, OnSetVolume
)
263 IPC_MESSAGE_UNHANDLED(handled
= false)
264 IPC_END_MESSAGE_MAP()
269 void AudioInputRendererHost::OnCreateStream(
273 const AudioInputHostMsg_CreateStream_Config
& config
) {
274 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
276 std::ostringstream oss
;
277 oss
<< "[stream_id=" << stream_id
<< "] "
278 << "AIRH::OnCreateStream(render_view_id=" << render_view_id
279 << ", session_id=" << session_id
<< ")";
280 DCHECK_GT(render_view_id
, 0);
282 // media::AudioParameters is validated in the deserializer.
283 if (LookupById(stream_id
) != NULL
) {
284 SendErrorMessage(stream_id
, STREAM_ALREADY_EXISTS
);
288 media::AudioParameters
audio_params(config
.params
);
289 if (media_stream_manager_
->audio_input_device_manager()->
290 ShouldUseFakeDevice()) {
292 media::AudioParameters::AUDIO_FAKE
,
293 config
.params
.channel_layout(), config
.params
.channels(),
294 config
.params
.sample_rate(), config
.params
.bits_per_sample(),
295 config
.params
.frames_per_buffer());
298 // Check if we have the permission to open the device and which device to use.
299 std::string device_name
;
300 std::string device_id
= media::AudioManagerBase::kDefaultDeviceId
;
301 if (audio_params
.format() != media::AudioParameters::AUDIO_FAKE
) {
302 const StreamDeviceInfo
* info
= media_stream_manager_
->
303 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id
);
305 SendErrorMessage(stream_id
, PERMISSION_DENIED
);
306 DLOG(WARNING
) << "No permission has been granted to input stream with "
307 << "session_id=" << session_id
;
311 device_id
= info
->device
.id
;
312 device_name
= info
->device
.name
;
313 oss
<< ": device_name=" << device_name
;
316 // Create a new AudioEntry structure.
317 scoped_ptr
<AudioEntry
> entry(new AudioEntry());
319 const uint32 segment_size
=
320 (sizeof(media::AudioInputBufferParameters
) +
321 media::AudioBus::CalculateMemorySize(audio_params
));
322 entry
->shared_memory_segment_count
= config
.shared_memory_count
;
324 // Create the shared memory and share it with the renderer process
325 // using a new SyncWriter object.
326 base::CheckedNumeric
<uint32
> size
= segment_size
;
327 size
*= entry
->shared_memory_segment_count
;
328 if (!size
.IsValid() ||
329 !entry
->shared_memory
.CreateAndMapAnonymous(size
.ValueOrDie())) {
330 // If creation of shared memory failed then send an error message.
331 SendErrorMessage(stream_id
, SHARED_MEMORY_CREATE_FAILED
);
335 scoped_ptr
<AudioInputSyncWriter
> writer(new AudioInputSyncWriter(
336 &entry
->shared_memory
, entry
->shared_memory_segment_count
, audio_params
));
338 if (!writer
->Init()) {
339 SendErrorMessage(stream_id
, SYNC_WRITER_INIT_FAILED
);
343 // If we have successfully created the SyncWriter then assign it to the
344 // entry and construct an AudioInputController.
345 entry
->writer
.reset(writer
.release());
346 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id
)) {
347 entry
->controller
= media::AudioInputController::CreateForStream(
348 audio_manager_
->GetTaskRunner(),
350 WebContentsAudioInputStream::Create(
353 audio_manager_
->GetWorkerTaskRunner(),
354 audio_mirroring_manager_
),
356 user_input_monitor_
);
358 // TODO(henrika): replace CreateLowLatency() with Create() as soon
359 // as satish has ensured that Speech Input also uses the default low-
360 // latency path. See crbug.com/112472 for details.
362 media::AudioInputController::CreateLowLatency(audio_manager_
,
367 user_input_monitor_
);
370 if (!entry
->controller
.get()) {
371 SendErrorMessage(stream_id
, STREAM_CREATE_ERROR
);
375 // Set the initial AGC state for the audio input stream. Note that, the AGC
376 // is only supported in AUDIO_PCM_LOW_LATENCY mode.
377 if (config
.params
.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY
) {
378 entry
->controller
->SetAutomaticGainControl(config
.automatic_gain_control
);
379 oss
<< ", AGC=" << config
.automatic_gain_control
;
382 MediaStreamManager::SendMessageToNativeLog(oss
.str());
383 DVLOG(1) << oss
.str();
385 // Since the controller was created successfully, create an entry and add it
387 entry
->stream_id
= stream_id
;
388 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
389 audio_log_
->OnCreated(stream_id
, audio_params
, device_id
);
392 void AudioInputRendererHost::OnRecordStream(int stream_id
) {
393 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
394 LogMessage(stream_id
, "OnRecordStream", true);
396 AudioEntry
* entry
= LookupById(stream_id
);
398 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
402 entry
->controller
->Record();
403 audio_log_
->OnStarted(stream_id
);
406 void AudioInputRendererHost::OnCloseStream(int stream_id
) {
407 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
408 LogMessage(stream_id
, "OnCloseStream", true);
410 AudioEntry
* entry
= LookupById(stream_id
);
413 CloseAndDeleteStream(entry
);
416 void AudioInputRendererHost::OnSetVolume(int stream_id
, double volume
) {
417 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
419 AudioEntry
* entry
= LookupById(stream_id
);
421 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
425 entry
->controller
->SetVolume(volume
);
426 audio_log_
->OnSetVolume(stream_id
, volume
);
429 void AudioInputRendererHost::SendErrorMessage(
430 int stream_id
, ErrorCode error_code
) {
431 std::string err_msg
=
432 base::StringPrintf("SendErrorMessage(error_code=%d)", error_code
);
433 LogMessage(stream_id
, err_msg
, true);
435 Send(new AudioInputMsg_NotifyStreamStateChanged(
436 stream_id
, media::AudioInputIPCDelegate::kError
));
439 void AudioInputRendererHost::DeleteEntries() {
440 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
442 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
443 i
!= audio_entries_
.end(); ++i
) {
444 CloseAndDeleteStream(i
->second
);
448 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
449 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
451 if (!entry
->pending_close
) {
452 LogMessage(entry
->stream_id
, "CloseAndDeleteStream", true);
453 entry
->controller
->Close(base::Bind(&AudioInputRendererHost::DeleteEntry
,
455 entry
->pending_close
= true;
456 audio_log_
->OnClosed(entry
->stream_id
);
460 void AudioInputRendererHost::DeleteEntry(AudioEntry
* entry
) {
461 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
462 LogMessage(entry
->stream_id
, "DeleteEntry: stream is now closed", true);
464 // Delete the entry when this method goes out of scope.
465 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
467 // Erase the entry from the map.
468 audio_entries_
.erase(entry
->stream_id
);
471 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry
* entry
,
472 ErrorCode error_code
) {
473 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
475 // Sends the error message first before we close the stream because
476 // |entry| is destroyed in DeleteEntry().
477 SendErrorMessage(entry
->stream_id
, error_code
);
478 CloseAndDeleteStream(entry
);
481 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupById(
483 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
485 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
486 if (i
!= audio_entries_
.end())
491 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupByController(
492 media::AudioInputController
* controller
) {
493 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
495 // Iterate the map of entries.
496 // TODO(hclam): Implement a faster look up method.
497 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
498 i
!= audio_entries_
.end(); ++i
) {
499 if (controller
== i
->second
->controller
.get())
505 } // namespace content