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"
23 struct AudioInputRendererHost::AudioEntry
{
27 // The AudioInputController that manages the audio input stream.
28 scoped_refptr
<media::AudioInputController
> controller
;
30 // The audio input stream ID in the render view.
33 // Shared memory for transmission of the audio data. It has
34 // |shared_memory_segment_count| equal lengthed segments.
35 base::SharedMemory shared_memory
;
36 int shared_memory_segment_count
;
38 // The synchronous writer to be used by the controller. We have the
39 // ownership of the writer.
40 scoped_ptr
<media::AudioInputController::SyncWriter
> writer
;
42 // Set to true after we called Close() for the controller.
46 AudioInputRendererHost::AudioEntry::AudioEntry()
48 shared_memory_segment_count(0),
49 pending_close(false) {
52 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
54 AudioInputRendererHost::AudioInputRendererHost(
55 media::AudioManager
* audio_manager
,
56 MediaStreamManager
* media_stream_manager
,
57 AudioMirroringManager
* audio_mirroring_manager
,
58 media::UserInputMonitor
* user_input_monitor
)
59 : BrowserMessageFilter(AudioMsgStart
),
60 audio_manager_(audio_manager
),
61 media_stream_manager_(media_stream_manager
),
62 audio_mirroring_manager_(audio_mirroring_manager
),
63 user_input_monitor_(user_input_monitor
),
64 audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
65 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER
)) {}
67 AudioInputRendererHost::~AudioInputRendererHost() {
68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
69 DCHECK(audio_entries_
.empty());
72 void AudioInputRendererHost::OnChannelClosing() {
73 // Since the IPC channel is gone, close all requested audio streams.
77 void AudioInputRendererHost::OnDestruct() const {
78 BrowserThread::DeleteOnIOThread::Destruct(this);
81 void AudioInputRendererHost::OnCreated(
82 media::AudioInputController
* controller
) {
83 BrowserThread::PostTask(
87 &AudioInputRendererHost::DoCompleteCreation
,
89 make_scoped_refptr(controller
)));
92 void AudioInputRendererHost::OnRecording(
93 media::AudioInputController
* controller
) {
94 BrowserThread::PostTask(
98 &AudioInputRendererHost::DoSendRecordingMessage
,
100 make_scoped_refptr(controller
)));
103 void AudioInputRendererHost::OnError(media::AudioInputController
* controller
,
104 media::AudioInputController::ErrorCode error_code
) {
105 BrowserThread::PostTask(
109 &AudioInputRendererHost::DoHandleError
,
111 make_scoped_refptr(controller
),
115 void AudioInputRendererHost::OnData(media::AudioInputController
* controller
,
118 NOTREACHED() << "Only low-latency mode is supported.";
121 void AudioInputRendererHost::DoCompleteCreation(
122 media::AudioInputController
* controller
) {
123 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
125 AudioEntry
* entry
= LookupByController(controller
);
130 NOTREACHED() << "Renderer process handle is invalid.";
131 DeleteEntryOnError(entry
, INVALID_PEER_HANDLE
);
135 if (!entry
->controller
->SharedMemoryAndSyncSocketMode()) {
136 NOTREACHED() << "Only shared-memory/sync-socket mode is supported.";
137 DeleteEntryOnError(entry
, INVALID_LATENCY_MODE
);
141 // Once the audio stream is created then complete the creation process by
142 // mapping shared memory and sharing with the renderer process.
143 base::SharedMemoryHandle foreign_memory_handle
;
144 if (!entry
->shared_memory
.ShareToProcess(PeerHandle(),
145 &foreign_memory_handle
)) {
146 // If we failed to map and share the shared memory then close the audio
147 // stream and send an error message.
148 DeleteEntryOnError(entry
, MEMORY_SHARING_FAILED
);
152 AudioInputSyncWriter
* writer
=
153 static_cast<AudioInputSyncWriter
*>(entry
->writer
.get());
156 base::SyncSocket::Handle foreign_socket_handle
;
158 base::FileDescriptor foreign_socket_handle
;
161 // If we failed to prepare the sync socket for the renderer then we fail
162 // the construction of audio input stream.
163 if (!writer
->PrepareForeignSocketHandle(PeerHandle(),
164 &foreign_socket_handle
)) {
165 DeleteEntryOnError(entry
, SYNC_SOCKET_ERROR
);
169 Send(new AudioInputMsg_NotifyStreamCreated(entry
->stream_id
,
170 foreign_memory_handle
, foreign_socket_handle
,
171 entry
->shared_memory
.requested_size(),
172 entry
->shared_memory_segment_count
));
175 void AudioInputRendererHost::DoSendRecordingMessage(
176 media::AudioInputController
* controller
) {
177 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
178 // TODO(henrika): See crbug.com/115262 for details on why this method
179 // should be implemented.
182 void AudioInputRendererHost::DoHandleError(
183 media::AudioInputController
* controller
,
184 media::AudioInputController::ErrorCode error_code
) {
185 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
186 // Log all errors even it is ignored later.
187 MediaStreamManager::SendMessageToNativeLog(
188 base::StringPrintf("AudioInputController error: %d", error_code
));
190 // This is a fix for crbug.com/357501. The error can be triggered when closing
191 // the lid on Macs, which causes more problems than it fixes.
192 // Also, in crbug.com/357569, the goal is to remove usage of the error since
193 // it was added to solve a crash on Windows that no longer can be reproduced.
194 if (error_code
== media::AudioInputController::NO_DATA_ERROR
) {
195 DVLOG(1) << "AudioInputRendererHost@" << this << "::DoHandleError: "
196 << "NO_DATA_ERROR ignored.";
200 AudioEntry
* entry
= LookupByController(controller
);
204 audio_log_
->OnError(entry
->stream_id
);
205 DeleteEntryOnError(entry
, AUDIO_INPUT_CONTROLLER_ERROR
);
208 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message
& message
,
209 bool* message_was_ok
) {
211 IPC_BEGIN_MESSAGE_MAP_EX(AudioInputRendererHost
, message
, *message_was_ok
)
212 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream
, OnCreateStream
)
213 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream
, OnRecordStream
)
214 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream
, OnCloseStream
)
215 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume
, OnSetVolume
)
216 IPC_MESSAGE_UNHANDLED(handled
= false)
217 IPC_END_MESSAGE_MAP_EX()
222 void AudioInputRendererHost::OnCreateStream(
226 const AudioInputHostMsg_CreateStream_Config
& config
) {
227 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
229 DVLOG(1) << "AudioInputRendererHost@" << this
230 << "::OnCreateStream(stream_id=" << stream_id
231 << ", render_view_id=" << render_view_id
232 << ", session_id=" << session_id
<< ")";
233 DCHECK_GT(render_view_id
, 0);
235 // media::AudioParameters is validated in the deserializer.
236 if (LookupById(stream_id
) != NULL
) {
237 SendErrorMessage(stream_id
, STREAM_ALREADY_EXISTS
);
241 media::AudioParameters
audio_params(config
.params
);
242 if (media_stream_manager_
->audio_input_device_manager()->
243 ShouldUseFakeDevice()) {
245 media::AudioParameters::AUDIO_FAKE
,
246 config
.params
.channel_layout(), config
.params
.channels(), 0,
247 config
.params
.sample_rate(), config
.params
.bits_per_sample(),
248 config
.params
.frames_per_buffer());
251 // Check if we have the permission to open the device and which device to use.
252 std::string device_name
;
253 std::string device_id
= media::AudioManagerBase::kDefaultDeviceId
;
254 if (audio_params
.format() != media::AudioParameters::AUDIO_FAKE
) {
255 const StreamDeviceInfo
* info
= media_stream_manager_
->
256 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id
);
258 SendErrorMessage(stream_id
, PERMISSION_DENIED
);
259 DLOG(WARNING
) << "No permission has been granted to input stream with "
260 << "session_id=" << session_id
;
264 device_id
= info
->device
.id
;
265 device_name
= info
->device
.name
;
268 // Create a new AudioEntry structure.
269 scoped_ptr
<AudioEntry
> entry(new AudioEntry());
271 const uint32 segment_size
= (sizeof(media::AudioInputBufferParameters
) +
272 audio_params
.GetBytesPerBuffer());
273 entry
->shared_memory_segment_count
= config
.shared_memory_count
;
275 // Create the shared memory and share it with the renderer process
276 // using a new SyncWriter object.
277 base::CheckedNumeric
<uint32
> size
= segment_size
;
278 size
*= entry
->shared_memory_segment_count
;
279 if (!size
.IsValid() ||
280 !entry
->shared_memory
.CreateAndMapAnonymous(size
.ValueOrDie())) {
281 // If creation of shared memory failed then send an error message.
282 SendErrorMessage(stream_id
, SHARED_MEMORY_CREATE_FAILED
);
286 scoped_ptr
<AudioInputSyncWriter
> writer(
287 new AudioInputSyncWriter(&entry
->shared_memory
,
288 entry
->shared_memory_segment_count
));
290 if (!writer
->Init()) {
291 SendErrorMessage(stream_id
, SYNC_WRITER_INIT_FAILED
);
295 // If we have successfully created the SyncWriter then assign it to the
296 // entry and construct an AudioInputController.
297 entry
->writer
.reset(writer
.release());
298 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id
)) {
299 entry
->controller
= media::AudioInputController::CreateForStream(
300 audio_manager_
->GetTaskRunner(),
302 WebContentsAudioInputStream::Create(
305 audio_manager_
->GetWorkerTaskRunner(),
306 audio_mirroring_manager_
),
308 user_input_monitor_
);
310 // TODO(henrika): replace CreateLowLatency() with Create() as soon
311 // as satish has ensured that Speech Input also uses the default low-
312 // latency path. See crbug.com/112472 for details.
314 media::AudioInputController::CreateLowLatency(audio_manager_
,
319 user_input_monitor_
);
322 if (!entry
->controller
.get()) {
323 SendErrorMessage(stream_id
, STREAM_CREATE_ERROR
);
327 // Set the initial AGC state for the audio input stream. Note that, the AGC
328 // is only supported in AUDIO_PCM_LOW_LATENCY mode.
329 if (config
.params
.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY
)
330 entry
->controller
->SetAutomaticGainControl(config
.automatic_gain_control
);
332 // Since the controller was created successfully, create an entry and add it
334 entry
->stream_id
= stream_id
;
335 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
337 MediaStreamManager::SendMessageToNativeLog(
338 "Audio input stream created successfully. Device name: " + device_name
);
339 audio_log_
->OnCreated(stream_id
, audio_params
, device_id
);
342 void AudioInputRendererHost::OnRecordStream(int stream_id
) {
343 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
345 AudioEntry
* entry
= LookupById(stream_id
);
347 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
351 entry
->controller
->Record();
352 audio_log_
->OnStarted(stream_id
);
355 void AudioInputRendererHost::OnCloseStream(int stream_id
) {
356 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
358 AudioEntry
* entry
= LookupById(stream_id
);
361 CloseAndDeleteStream(entry
);
364 void AudioInputRendererHost::OnSetVolume(int stream_id
, double volume
) {
365 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
367 AudioEntry
* entry
= LookupById(stream_id
);
369 SendErrorMessage(stream_id
, INVALID_AUDIO_ENTRY
);
373 entry
->controller
->SetVolume(volume
);
374 audio_log_
->OnSetVolume(stream_id
, volume
);
377 void AudioInputRendererHost::SendErrorMessage(
378 int stream_id
, ErrorCode error_code
) {
379 MediaStreamManager::SendMessageToNativeLog(
380 base::StringPrintf("AudioInputRendererHost error: %d", error_code
));
381 Send(new AudioInputMsg_NotifyStreamStateChanged(
382 stream_id
, media::AudioInputIPCDelegate::kError
));
385 void AudioInputRendererHost::DeleteEntries() {
386 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
388 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
389 i
!= audio_entries_
.end(); ++i
) {
390 CloseAndDeleteStream(i
->second
);
394 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
395 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
397 if (!entry
->pending_close
) {
398 entry
->controller
->Close(base::Bind(&AudioInputRendererHost::DeleteEntry
,
400 entry
->pending_close
= true;
401 audio_log_
->OnClosed(entry
->stream_id
);
405 void AudioInputRendererHost::DeleteEntry(AudioEntry
* entry
) {
406 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
408 // Delete the entry when this method goes out of scope.
409 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
411 // Erase the entry from the map.
412 audio_entries_
.erase(entry
->stream_id
);
415 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry
* entry
,
416 ErrorCode error_code
) {
417 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
419 // Sends the error message first before we close the stream because
420 // |entry| is destroyed in DeleteEntry().
421 SendErrorMessage(entry
->stream_id
, error_code
);
422 CloseAndDeleteStream(entry
);
425 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupById(
427 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
429 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
430 if (i
!= audio_entries_
.end())
435 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupByController(
436 media::AudioInputController
* controller
) {
437 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
439 // Iterate the map of entries.
440 // TODO(hclam): Implement a faster look up method.
441 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
442 i
!= audio_entries_
.end(); ++i
) {
443 if (controller
== i
->second
->controller
.get())
449 } // namespace content