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());
182 base::SyncSocket::Handle foreign_socket_handle;
184 base::FileDescriptor foreign_socket_handle;
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->PrepareForeignSocketHandle(PeerHandle(),
190 &foreign_socket_handle)) {
191 DeleteEntryOnError(entry, SYNC_SOCKET_ERROR);
195 LogMessage(entry->stream_id,
196 "DoCompleteCreation => IPC channel and stream are now open",
199 Send(new AudioInputMsg_NotifyStreamCreated(entry->stream_id,
200 foreign_memory_handle, foreign_socket_handle,
201 entry->shared_memory.requested_size(),
202 entry->shared_memory_segment_count));
205 void AudioInputRendererHost::DoSendRecordingMessage(
206 media::AudioInputController* controller) {
207 DCHECK_CURRENTLY_ON(BrowserThread::IO);
208 // TODO(henrika): See crbug.com/115262 for details on why this method
209 // should be implemented.
210 AudioEntry* entry = LookupByController(controller);
212 NOTREACHED() << "AudioInputController is invalid.";
215 LogMessage(entry->stream_id,
216 "DoSendRecordingMessage => stream is now started",
220 void AudioInputRendererHost::DoHandleError(
221 media::AudioInputController* controller,
222 media::AudioInputController::ErrorCode error_code) {
223 DCHECK_CURRENTLY_ON(BrowserThread::IO);
224 AudioEntry* entry = LookupByController(controller);
226 NOTREACHED() << "AudioInputController is invalid.";
230 // This is a fix for crbug.com/357501. The error can be triggered when closing
231 // the lid on Macs, which causes more problems than it fixes.
232 // Also, in crbug.com/357569, the goal is to remove usage of the error since
233 // it was added to solve a crash on Windows that no longer can be reproduced.
234 if (error_code == media::AudioInputController::NO_DATA_ERROR) {
235 // TODO(henrika): it might be possible to do something other than just
236 // logging when we detect many NO_DATA_ERROR calls for a stream.
237 LogMessage(entry->stream_id, "AIC => NO_DATA_ERROR", false);
241 std::ostringstream oss;
242 oss << "AIC reports error_code=" << error_code;
243 LogMessage(entry->stream_id, oss.str(), false);
245 audio_log_->OnError(entry->stream_id);
246 DeleteEntryOnError(entry, AUDIO_INPUT_CONTROLLER_ERROR);
249 void AudioInputRendererHost::DoLog(media::AudioInputController* controller,
250 const std::string& message) {
251 DCHECK_CURRENTLY_ON(BrowserThread::IO);
252 AudioEntry* entry = LookupByController(controller);
254 NOTREACHED() << "AudioInputController is invalid.";
258 // Add stream ID and current audio level reported by AIC to native log.
259 LogMessage(entry->stream_id, message, false);
262 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message) {
264 IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost, message)
265 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream)
266 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream)
267 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream)
268 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume)
269 IPC_MESSAGE_UNHANDLED(handled = false)
270 IPC_END_MESSAGE_MAP()
275 void AudioInputRendererHost::OnCreateStream(
279 const AudioInputHostMsg_CreateStream_Config& config) {
280 DCHECK_CURRENTLY_ON(BrowserThread::IO);
282 std::ostringstream oss;
283 oss << "[stream_id=" << stream_id << "] "
284 << "AIRH::OnCreateStream(render_view_id=" << render_view_id
285 << ", session_id=" << session_id << ")";
286 DCHECK_GT(render_view_id, 0);
288 // media::AudioParameters is validated in the deserializer.
289 if (LookupById(stream_id) != NULL) {
290 SendErrorMessage(stream_id, STREAM_ALREADY_EXISTS);
294 media::AudioParameters audio_params(config.params);
295 if (media_stream_manager_->audio_input_device_manager()->
296 ShouldUseFakeDevice()) {
298 media::AudioParameters::AUDIO_FAKE,
299 config.params.channel_layout(), config.params.channels(), 0,
300 config.params.sample_rate(), config.params.bits_per_sample(),
301 config.params.frames_per_buffer());
304 // Check if we have the permission to open the device and which device to use.
305 std::string device_name;
306 std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
307 if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
308 const StreamDeviceInfo* info = media_stream_manager_->
309 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
311 SendErrorMessage(stream_id, PERMISSION_DENIED);
312 DLOG(WARNING) << "No permission has been granted to input stream with "
313 << "session_id=" << session_id;
317 device_id = info->device.id;
318 device_name = info->device.name;
319 oss << ": device_name=" << device_name;
322 // Create a new AudioEntry structure.
323 scoped_ptr<AudioEntry> entry(new AudioEntry());
325 const uint32 segment_size =
326 (sizeof(media::AudioInputBufferParameters) +
327 media::AudioBus::CalculateMemorySize(audio_params));
328 entry->shared_memory_segment_count = config.shared_memory_count;
330 // Create the shared memory and share it with the renderer process
331 // using a new SyncWriter object.
332 base::CheckedNumeric<uint32> size = segment_size;
333 size *= entry->shared_memory_segment_count;
334 if (!size.IsValid() ||
335 !entry->shared_memory.CreateAndMapAnonymous(size.ValueOrDie())) {
336 // If creation of shared memory failed then send an error message.
337 SendErrorMessage(stream_id, SHARED_MEMORY_CREATE_FAILED);
341 scoped_ptr<AudioInputSyncWriter> writer(new AudioInputSyncWriter(
342 &entry->shared_memory, entry->shared_memory_segment_count, audio_params));
344 if (!writer->Init()) {
345 SendErrorMessage(stream_id, SYNC_WRITER_INIT_FAILED);
349 // If we have successfully created the SyncWriter then assign it to the
350 // entry and construct an AudioInputController.
351 entry->writer.reset(writer.release());
352 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
353 entry->controller = media::AudioInputController::CreateForStream(
354 audio_manager_->GetTaskRunner(),
356 WebContentsAudioInputStream::Create(
359 audio_manager_->GetWorkerTaskRunner(),
360 audio_mirroring_manager_),
362 user_input_monitor_);
364 // TODO(henrika): replace CreateLowLatency() with Create() as soon
365 // as satish has ensured that Speech Input also uses the default low-
366 // latency path. See crbug.com/112472 for details.
368 media::AudioInputController::CreateLowLatency(audio_manager_,
373 user_input_monitor_);
376 if (!entry->controller.get()) {
377 SendErrorMessage(stream_id, STREAM_CREATE_ERROR);
381 // Set the initial AGC state for the audio input stream. Note that, the AGC
382 // is only supported in AUDIO_PCM_LOW_LATENCY mode.
383 if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) {
384 entry->controller->SetAutomaticGainControl(config.automatic_gain_control);
385 oss << ", AGC=" << config.automatic_gain_control;
388 MediaStreamManager::SendMessageToNativeLog(oss.str());
389 DVLOG(1) << oss.str();
391 // Since the controller was created successfully, create an entry and add it
393 entry->stream_id = stream_id;
394 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
395 audio_log_->OnCreated(stream_id, audio_params, device_id);
398 void AudioInputRendererHost::OnRecordStream(int stream_id) {
399 DCHECK_CURRENTLY_ON(BrowserThread::IO);
400 LogMessage(stream_id, "OnRecordStream", true);
402 AudioEntry* entry = LookupById(stream_id);
404 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
408 entry->controller->Record();
409 audio_log_->OnStarted(stream_id);
412 void AudioInputRendererHost::OnCloseStream(int stream_id) {
413 DCHECK_CURRENTLY_ON(BrowserThread::IO);
414 LogMessage(stream_id, "OnCloseStream", true);
416 AudioEntry* entry = LookupById(stream_id);
419 CloseAndDeleteStream(entry);
422 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
423 DCHECK_CURRENTLY_ON(BrowserThread::IO);
425 AudioEntry* entry = LookupById(stream_id);
427 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
431 entry->controller->SetVolume(volume);
432 audio_log_->OnSetVolume(stream_id, volume);
435 void AudioInputRendererHost::SendErrorMessage(
436 int stream_id, ErrorCode error_code) {
437 std::string err_msg =
438 base::StringPrintf("SendErrorMessage(error_code=%d)", error_code);
439 LogMessage(stream_id, err_msg, true);
441 Send(new AudioInputMsg_NotifyStreamStateChanged(
442 stream_id, media::AudioInputIPCDelegate::kError));
445 void AudioInputRendererHost::DeleteEntries() {
446 DCHECK_CURRENTLY_ON(BrowserThread::IO);
448 for (AudioEntryMap::iterator i = audio_entries_.begin();
449 i != audio_entries_.end(); ++i) {
450 CloseAndDeleteStream(i->second);
454 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
455 DCHECK_CURRENTLY_ON(BrowserThread::IO);
457 if (!entry->pending_close) {
458 LogMessage(entry->stream_id, "CloseAndDeleteStream", true);
459 entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
461 entry->pending_close = true;
462 audio_log_->OnClosed(entry->stream_id);
466 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
467 DCHECK_CURRENTLY_ON(BrowserThread::IO);
468 LogMessage(entry->stream_id, "DeleteEntry => stream is now closed", true);
470 // Delete the entry when this method goes out of scope.
471 scoped_ptr<AudioEntry> entry_deleter(entry);
473 // Erase the entry from the map.
474 audio_entries_.erase(entry->stream_id);
477 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry,
478 ErrorCode error_code) {
479 DCHECK_CURRENTLY_ON(BrowserThread::IO);
481 // Sends the error message first before we close the stream because
482 // |entry| is destroyed in DeleteEntry().
483 SendErrorMessage(entry->stream_id, error_code);
484 CloseAndDeleteStream(entry);
487 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
489 DCHECK_CURRENTLY_ON(BrowserThread::IO);
491 AudioEntryMap::iterator i = audio_entries_.find(stream_id);
492 if (i != audio_entries_.end())
497 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
498 media::AudioInputController* controller) {
499 DCHECK_CURRENTLY_ON(BrowserThread::IO);
501 // Iterate the map of entries.
502 // TODO(hclam): Implement a faster look up method.
503 for (AudioEntryMap::iterator i = audio_entries_.begin();
504 i != audio_entries_.end(); ++i) {
505 if (controller == i->second->controller.get())
511 } // namespace content