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/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"
21 struct AudioInputRendererHost::AudioEntry
{
25 // The AudioInputController that manages the audio input stream.
26 scoped_refptr
<media::AudioInputController
> controller
;
28 // The audio input stream ID in the render view.
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
;
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
;
40 // Set to true after we called Close() for the controller.
44 AudioInputRendererHost::AudioEntry::AudioEntry()
46 shared_memory_segment_count(0),
47 pending_close(false) {
50 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
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
)) {}
64 AudioInputRendererHost::~AudioInputRendererHost() {
65 DCHECK(audio_entries_
.empty());
68 void AudioInputRendererHost::OnChannelClosing() {
69 // Since the IPC channel is gone, close all requested audio streams.
73 void AudioInputRendererHost::OnDestruct() const {
74 BrowserThread::DeleteOnIOThread::Destruct(this);
77 void AudioInputRendererHost::OnCreated(
78 media::AudioInputController
* controller
) {
79 BrowserThread::PostTask(
83 &AudioInputRendererHost::DoCompleteCreation
,
85 make_scoped_refptr(controller
)));
88 void AudioInputRendererHost::OnRecording(
89 media::AudioInputController
* controller
) {
90 BrowserThread::PostTask(
94 &AudioInputRendererHost::DoSendRecordingMessage
,
96 make_scoped_refptr(controller
)));
99 void AudioInputRendererHost::OnError(media::AudioInputController
* controller
) {
100 BrowserThread::PostTask(
104 &AudioInputRendererHost::DoHandleError
,
106 make_scoped_refptr(controller
)));
109 void AudioInputRendererHost::OnData(media::AudioInputController
* controller
,
112 NOTREACHED() << "Only low-latency mode is supported.";
115 void AudioInputRendererHost::DoCompleteCreation(
116 media::AudioInputController
* controller
) {
117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
119 AudioEntry
* entry
= LookupByController(controller
);
124 NOTREACHED() << "Renderer process handle is invalid.";
125 DeleteEntryOnError(entry
);
129 if (!entry
->controller
->LowLatencyMode()) {
130 NOTREACHED() << "Only low-latency mode is supported.";
131 DeleteEntryOnError(entry
);
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
);
146 AudioInputSyncWriter
* writer
=
147 static_cast<AudioInputSyncWriter
*>(entry
->writer
.get());
150 base::SyncSocket::Handle foreign_socket_handle
;
152 base::FileDescriptor foreign_socket_handle
;
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
);
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
));
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.
176 void AudioInputRendererHost::DoHandleError(
177 media::AudioInputController
* controller
) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
180 AudioEntry
* entry
= LookupByController(controller
);
184 audio_log_
->OnError(entry
->stream_id
);
185 DeleteEntryOnError(entry
);
188 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message
& message
,
189 bool* message_was_ok
) {
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()
202 void AudioInputRendererHost::OnCreateStream(
206 const AudioInputHostMsg_CreateStream_Config
& config
) {
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
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);
215 // media::AudioParameters is validated in the deserializer.
216 if (LookupById(stream_id
) != NULL
) {
217 SendErrorMessage(stream_id
);
221 media::AudioParameters
audio_params(config
.params
);
222 if (media_stream_manager_
->audio_input_device_manager()->
223 ShouldUseFakeDevice()) {
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());
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
);
237 SendErrorMessage(stream_id
);
238 DLOG(WARNING
) << "No permission has been granted to input stream with "
239 << "session_id=" << session_id
;
243 device_id
= info
->device
.id
;
246 // Create a new AudioEntry structure.
247 scoped_ptr
<AudioEntry
> entry(new AudioEntry());
249 const uint32 segment_size
= (sizeof(media::AudioInputBufferParameters
) +
250 audio_params
.GetBytesPerBuffer());
251 entry
->shared_memory_segment_count
= config
.shared_memory_count
;
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
);
262 scoped_ptr
<AudioInputSyncWriter
> writer(
263 new AudioInputSyncWriter(&entry
->shared_memory
,
264 entry
->shared_memory_segment_count
));
266 if (!writer
->Init()) {
267 SendErrorMessage(stream_id
);
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(),
278 WebContentsAudioInputStream::Create(device_id
,
280 audio_manager_
->GetWorkerLoop(),
281 audio_mirroring_manager_
),
283 user_input_monitor_
);
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.
289 media::AudioInputController::CreateLowLatency(audio_manager_
,
294 user_input_monitor_
);
297 if (!entry
->controller
.get()) {
298 SendErrorMessage(stream_id
);
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
);
307 // Since the controller was created successfully, create an entry and add it
309 entry
->stream_id
= stream_id
;
310 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
312 audio_log_
->OnCreated(stream_id
, audio_params
, device_id
, std::string());
315 void AudioInputRendererHost::OnRecordStream(int stream_id
) {
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
318 AudioEntry
* entry
= LookupById(stream_id
);
320 SendErrorMessage(stream_id
);
324 entry
->controller
->Record();
325 audio_log_
->OnStarted(stream_id
);
328 void AudioInputRendererHost::OnCloseStream(int stream_id
) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
331 AudioEntry
* entry
= LookupById(stream_id
);
334 CloseAndDeleteStream(entry
);
337 void AudioInputRendererHost::OnSetVolume(int stream_id
, double volume
) {
338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
340 AudioEntry
* entry
= LookupById(stream_id
);
342 SendErrorMessage(stream_id
);
346 entry
->controller
->SetVolume(volume
);
347 audio_log_
->OnSetVolume(stream_id
, volume
);
350 void AudioInputRendererHost::SendErrorMessage(int stream_id
) {
351 Send(new AudioInputMsg_NotifyStreamStateChanged(
352 stream_id
, media::AudioInputIPCDelegate::kError
));
355 void AudioInputRendererHost::DeleteEntries() {
356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
358 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
359 i
!= audio_entries_
.end(); ++i
) {
360 CloseAndDeleteStream(i
->second
);
364 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
367 if (!entry
->pending_close
) {
368 entry
->controller
->Close(base::Bind(&AudioInputRendererHost::DeleteEntry
,
370 entry
->pending_close
= true;
371 audio_log_
->OnClosed(entry
->stream_id
);
375 void AudioInputRendererHost::DeleteEntry(AudioEntry
* entry
) {
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
378 // Delete the entry when this method goes out of scope.
379 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
381 // Erase the entry from the map.
382 audio_entries_
.erase(entry
->stream_id
);
385 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry
* entry
) {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
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
);
394 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupById(
396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
398 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
399 if (i
!= audio_entries_
.end())
404 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupByController(
405 media::AudioInputController
* controller
) {
406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
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())
418 } // namespace content