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/metrics/histogram.h"
9 #include "base/process.h"
10 #include "base/shared_memory.h"
11 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
12 #include "content/browser/renderer_host/media/audio_input_sync_writer.h"
13 #include "content/browser/renderer_host/media/media_stream_manager.h"
14 #include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
15 #include "content/browser/renderer_host/media/web_contents_capture_util.h"
16 #include "media/audio/audio_manager_base.h"
20 struct AudioInputRendererHost::AudioEntry
{
24 // The AudioInputController that manages the audio input stream.
25 scoped_refptr
<media::AudioInputController
> controller
;
27 // The audio input stream ID in the render view.
30 // Shared memory for transmission of the audio data. It has
31 // |shared_memory_segment_count| equal lengthed segments.
32 base::SharedMemory shared_memory
;
33 int shared_memory_segment_count
;
35 // The synchronous writer to be used by the controller. We have the
36 // ownership of the writer.
37 scoped_ptr
<media::AudioInputController::SyncWriter
> writer
;
39 // Set to true after we called Close() for the controller.
43 AudioInputRendererHost::AudioEntry::AudioEntry()
45 shared_memory_segment_count(0),
46 pending_close(false) {
49 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
51 AudioInputRendererHost::AudioInputRendererHost(
52 media::AudioManager
* audio_manager
,
53 MediaStreamManager
* media_stream_manager
,
54 AudioMirroringManager
* audio_mirroring_manager
)
55 : audio_manager_(audio_manager
),
56 media_stream_manager_(media_stream_manager
),
57 audio_mirroring_manager_(audio_mirroring_manager
) {
60 AudioInputRendererHost::~AudioInputRendererHost() {
61 DCHECK(audio_entries_
.empty());
64 void AudioInputRendererHost::OnChannelClosing() {
65 BrowserMessageFilter::OnChannelClosing();
67 // Since the IPC channel is gone, close all requested audio streams.
71 void AudioInputRendererHost::OnDestruct() const {
72 BrowserThread::DeleteOnIOThread::Destruct(this);
75 void AudioInputRendererHost::OnCreated(
76 media::AudioInputController
* controller
) {
77 BrowserThread::PostTask(
81 &AudioInputRendererHost::DoCompleteCreation
,
83 make_scoped_refptr(controller
)));
86 void AudioInputRendererHost::OnRecording(
87 media::AudioInputController
* controller
) {
88 BrowserThread::PostTask(
92 &AudioInputRendererHost::DoSendRecordingMessage
,
94 make_scoped_refptr(controller
)));
97 void AudioInputRendererHost::OnError(media::AudioInputController
* controller
) {
98 BrowserThread::PostTask(
102 &AudioInputRendererHost::DoHandleError
,
104 make_scoped_refptr(controller
)));
107 void AudioInputRendererHost::OnData(media::AudioInputController
* controller
,
110 NOTREACHED() << "Only low-latency mode is supported.";
113 void AudioInputRendererHost::DoCompleteCreation(
114 media::AudioInputController
* controller
) {
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
117 AudioEntry
* entry
= LookupByController(controller
);
121 if (!peer_handle()) {
122 NOTREACHED() << "Renderer process handle is invalid.";
123 DeleteEntryOnError(entry
);
127 if (!entry
->controller
->LowLatencyMode()) {
128 NOTREACHED() << "Only low-latency mode is supported.";
129 DeleteEntryOnError(entry
);
133 // Once the audio stream is created then complete the creation process by
134 // mapping shared memory and sharing with the renderer process.
135 base::SharedMemoryHandle foreign_memory_handle
;
136 if (!entry
->shared_memory
.ShareToProcess(peer_handle(),
137 &foreign_memory_handle
)) {
138 // If we failed to map and share the shared memory then close the audio
139 // stream and send an error message.
140 DeleteEntryOnError(entry
);
144 AudioInputSyncWriter
* writer
=
145 static_cast<AudioInputSyncWriter
*>(entry
->writer
.get());
148 base::SyncSocket::Handle foreign_socket_handle
;
150 base::FileDescriptor foreign_socket_handle
;
153 // If we failed to prepare the sync socket for the renderer then we fail
154 // the construction of audio input stream.
155 if (!writer
->PrepareForeignSocketHandle(peer_handle(),
156 &foreign_socket_handle
)) {
157 DeleteEntryOnError(entry
);
161 Send(new AudioInputMsg_NotifyStreamCreated(entry
->stream_id
,
162 foreign_memory_handle
, foreign_socket_handle
,
163 entry
->shared_memory
.requested_size(),
164 entry
->shared_memory_segment_count
));
167 void AudioInputRendererHost::DoSendRecordingMessage(
168 media::AudioInputController
* controller
) {
169 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
170 // TODO(henrika): See crbug.com/115262 for details on why this method
171 // should be implemented.
174 void AudioInputRendererHost::DoHandleError(
175 media::AudioInputController
* controller
) {
176 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
178 AudioEntry
* entry
= LookupByController(controller
);
182 DeleteEntryOnError(entry
);
185 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message
& message
,
186 bool* message_was_ok
) {
188 IPC_BEGIN_MESSAGE_MAP_EX(AudioInputRendererHost
, message
, *message_was_ok
)
189 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream
, OnCreateStream
)
190 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream
, OnRecordStream
)
191 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream
, OnCloseStream
)
192 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume
, OnSetVolume
)
193 IPC_MESSAGE_UNHANDLED(handled
= false)
194 IPC_END_MESSAGE_MAP_EX()
199 void AudioInputRendererHost::OnCreateStream(
203 const AudioInputHostMsg_CreateStream_Config
& config
) {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
206 DVLOG(1) << "AudioInputRendererHost@" << this
207 << "::OnCreateStream(stream_id=" << stream_id
208 << ", render_view_id=" << render_view_id
209 << ", session_id=" << session_id
<< ")";
210 DCHECK_GT(render_view_id
, 0);
212 // media::AudioParameters is validated in the deserializer.
213 if (LookupById(stream_id
) != NULL
) {
214 SendErrorMessage(stream_id
);
218 media::AudioParameters
audio_params(config
.params
);
219 if (media_stream_manager_
->audio_input_device_manager()->
220 ShouldUseFakeDevice()) {
222 media::AudioParameters::AUDIO_FAKE
,
223 config
.params
.channel_layout(), config
.params
.channels(), 0,
224 config
.params
.sample_rate(), config
.params
.bits_per_sample(),
225 config
.params
.frames_per_buffer());
228 // Check if we have the permission to open the device and which device to use.
229 std::string device_id
= media::AudioManagerBase::kDefaultDeviceId
;
230 if (audio_params
.format() != media::AudioParameters::AUDIO_FAKE
) {
231 const StreamDeviceInfo
* info
= media_stream_manager_
->
232 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id
);
234 SendErrorMessage(stream_id
);
235 DLOG(WARNING
) << "No permission has been granted to input stream with "
236 << "session_id=" << session_id
;
240 device_id
= info
->device
.id
;
243 // Create a new AudioEntry structure.
244 scoped_ptr
<AudioEntry
> entry(new AudioEntry());
246 const uint32 segment_size
= (sizeof(media::AudioInputBufferParameters
) +
247 audio_params
.GetBytesPerBuffer());
248 entry
->shared_memory_segment_count
= config
.shared_memory_count
;
250 // Create the shared memory and share it with the renderer process
251 // using a new SyncWriter object.
252 if (!entry
->shared_memory
.CreateAndMapAnonymous(
253 segment_size
* entry
->shared_memory_segment_count
)) {
254 // If creation of shared memory failed then send an error message.
255 SendErrorMessage(stream_id
);
259 scoped_ptr
<AudioInputSyncWriter
> writer(
260 new AudioInputSyncWriter(&entry
->shared_memory
,
261 entry
->shared_memory_segment_count
));
263 if (!writer
->Init()) {
264 SendErrorMessage(stream_id
);
268 // If we have successfully created the SyncWriter then assign it to the
269 // entry and construct an AudioInputController.
270 entry
->writer
.reset(writer
.release());
271 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id
)) {
272 entry
->controller
= media::AudioInputController::CreateForStream(
273 audio_manager_
->GetMessageLoop(),
275 WebContentsAudioInputStream::Create(
276 device_id
, audio_params
, audio_manager_
->GetWorkerLoop(),
277 audio_mirroring_manager_
),
278 entry
->writer
.get());
280 // TODO(henrika): replace CreateLowLatency() with Create() as soon
281 // as satish has ensured that Speech Input also uses the default low-
282 // latency path. See crbug.com/112472 for details.
283 entry
->controller
= media::AudioInputController::CreateLowLatency(
288 entry
->writer
.get());
291 if (!entry
->controller
.get()) {
292 SendErrorMessage(stream_id
);
296 // Set the initial AGC state for the audio input stream. Note that, the AGC
297 // is only supported in AUDIO_PCM_LOW_LATENCY mode.
298 if (config
.params
.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY
)
299 entry
->controller
->SetAutomaticGainControl(config
.automatic_gain_control
);
301 // Since the controller was created successfully, create an entry and add it
303 entry
->stream_id
= stream_id
;
304 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
307 void AudioInputRendererHost::OnRecordStream(int stream_id
) {
308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
310 AudioEntry
* entry
= LookupById(stream_id
);
312 SendErrorMessage(stream_id
);
316 entry
->controller
->Record();
319 void AudioInputRendererHost::OnCloseStream(int stream_id
) {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
322 AudioEntry
* entry
= LookupById(stream_id
);
325 CloseAndDeleteStream(entry
);
328 void AudioInputRendererHost::OnSetVolume(int stream_id
, double volume
) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
331 AudioEntry
* entry
= LookupById(stream_id
);
333 SendErrorMessage(stream_id
);
337 entry
->controller
->SetVolume(volume
);
340 void AudioInputRendererHost::SendErrorMessage(int stream_id
) {
341 Send(new AudioInputMsg_NotifyStreamStateChanged(
342 stream_id
, media::AudioInputIPCDelegate::kError
));
345 void AudioInputRendererHost::DeleteEntries() {
346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
348 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
349 i
!= audio_entries_
.end(); ++i
) {
350 CloseAndDeleteStream(i
->second
);
354 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry
* entry
) {
355 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
357 if (!entry
->pending_close
) {
358 entry
->controller
->Close(base::Bind(&AudioInputRendererHost::DeleteEntry
,
360 entry
->pending_close
= true;
364 void AudioInputRendererHost::DeleteEntry(AudioEntry
* entry
) {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
367 // Delete the entry when this method goes out of scope.
368 scoped_ptr
<AudioEntry
> entry_deleter(entry
);
370 // Erase the entry from the map.
371 audio_entries_
.erase(entry
->stream_id
);
374 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry
* entry
) {
375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
377 // Sends the error message first before we close the stream because
378 // |entry| is destroyed in DeleteEntry().
379 SendErrorMessage(entry
->stream_id
);
380 CloseAndDeleteStream(entry
);
383 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupById(
385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
387 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
388 if (i
!= audio_entries_
.end())
393 AudioInputRendererHost::AudioEntry
* AudioInputRendererHost::LookupByController(
394 media::AudioInputController
* controller
) {
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
397 // Iterate the map of entries.
398 // TODO(hclam): Implement a faster look up method.
399 for (AudioEntryMap::iterator i
= audio_entries_
.begin();
400 i
!= audio_entries_
.end(); ++i
) {
401 if (controller
== i
->second
->controller
.get())
407 } // namespace content