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));
179 MediaStreamManager::SendMessageToNativeLog(
180 "The AudioInputController signalled an error.");
182 AudioEntry* entry = LookupByController(controller);
186 audio_log_->OnError(entry->stream_id);
187 DeleteEntryOnError(entry);
190 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message,
191 bool* message_was_ok) {
193 IPC_BEGIN_MESSAGE_MAP_EX(AudioInputRendererHost, message, *message_was_ok)
194 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream)
195 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream)
196 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream)
197 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume)
198 IPC_MESSAGE_UNHANDLED(handled = false)
199 IPC_END_MESSAGE_MAP_EX()
204 void AudioInputRendererHost::OnCreateStream(
208 const AudioInputHostMsg_CreateStream_Config& config) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
211 DVLOG(1) << "AudioInputRendererHost@" << this
212 << "::OnCreateStream(stream_id=" << stream_id
213 << ", render_view_id=" << render_view_id
214 << ", session_id=" << session_id << ")";
215 DCHECK_GT(render_view_id, 0);
217 // media::AudioParameters is validated in the deserializer.
218 if (LookupById(stream_id) != NULL) {
219 SendErrorMessage(stream_id);
223 media::AudioParameters audio_params(config.params);
224 if (media_stream_manager_->audio_input_device_manager()->
225 ShouldUseFakeDevice()) {
227 media::AudioParameters::AUDIO_FAKE,
228 config.params.channel_layout(), config.params.channels(), 0,
229 config.params.sample_rate(), config.params.bits_per_sample(),
230 config.params.frames_per_buffer());
233 // Check if we have the permission to open the device and which device to use.
234 std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
235 if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
236 const StreamDeviceInfo* info = media_stream_manager_->
237 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
239 SendErrorMessage(stream_id);
240 DLOG(WARNING) << "No permission has been granted to input stream with "
241 << "session_id=" << session_id;
245 device_id = info->device.id;
248 // Create a new AudioEntry structure.
249 scoped_ptr<AudioEntry> entry(new AudioEntry());
251 const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) +
252 audio_params.GetBytesPerBuffer());
253 entry->shared_memory_segment_count = config.shared_memory_count;
255 // Create the shared memory and share it with the renderer process
256 // using a new SyncWriter object.
257 if (!entry->shared_memory.CreateAndMapAnonymous(
258 segment_size * entry->shared_memory_segment_count)) {
259 // If creation of shared memory failed then send an error message.
260 SendErrorMessage(stream_id);
264 scoped_ptr<AudioInputSyncWriter> writer(
265 new AudioInputSyncWriter(&entry->shared_memory,
266 entry->shared_memory_segment_count));
268 if (!writer->Init()) {
269 SendErrorMessage(stream_id);
273 // If we have successfully created the SyncWriter then assign it to the
274 // entry and construct an AudioInputController.
275 entry->writer.reset(writer.release());
276 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
277 entry->controller = media::AudioInputController::CreateForStream(
278 audio_manager_->GetTaskRunner(),
280 WebContentsAudioInputStream::Create(
283 audio_manager_->GetWorkerTaskRunner(),
284 audio_mirroring_manager_),
286 user_input_monitor_);
288 // TODO(henrika): replace CreateLowLatency() with Create() as soon
289 // as satish has ensured that Speech Input also uses the default low-
290 // latency path. See crbug.com/112472 for details.
292 media::AudioInputController::CreateLowLatency(audio_manager_,
297 user_input_monitor_);
300 if (!entry->controller.get()) {
301 SendErrorMessage(stream_id);
305 // Set the initial AGC state for the audio input stream. Note that, the AGC
306 // is only supported in AUDIO_PCM_LOW_LATENCY mode.
307 if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY)
308 entry->controller->SetAutomaticGainControl(config.automatic_gain_control);
310 // Since the controller was created successfully, create an entry and add it
312 entry->stream_id = stream_id;
313 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
315 MediaStreamManager::SendMessageToNativeLog(
316 "Audio input stream created successfully.");
317 audio_log_->OnCreated(stream_id, audio_params, device_id);
320 void AudioInputRendererHost::OnRecordStream(int stream_id) {
321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
323 AudioEntry* entry = LookupById(stream_id);
325 SendErrorMessage(stream_id);
329 entry->controller->Record();
330 audio_log_->OnStarted(stream_id);
333 void AudioInputRendererHost::OnCloseStream(int stream_id) {
334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
336 AudioEntry* entry = LookupById(stream_id);
339 CloseAndDeleteStream(entry);
342 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
345 AudioEntry* entry = LookupById(stream_id);
347 SendErrorMessage(stream_id);
351 entry->controller->SetVolume(volume);
352 audio_log_->OnSetVolume(stream_id, volume);
355 void AudioInputRendererHost::SendErrorMessage(int stream_id) {
356 MediaStreamManager::SendMessageToNativeLog(
357 "An error occurred in AudioInputRendererHost.");
358 Send(new AudioInputMsg_NotifyStreamStateChanged(
359 stream_id, media::AudioInputIPCDelegate::kError));
362 void AudioInputRendererHost::DeleteEntries() {
363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
365 for (AudioEntryMap::iterator i = audio_entries_.begin();
366 i != audio_entries_.end(); ++i) {
367 CloseAndDeleteStream(i->second);
371 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
374 if (!entry->pending_close) {
375 entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
377 entry->pending_close = true;
378 audio_log_->OnClosed(entry->stream_id);
382 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
385 // Delete the entry when this method goes out of scope.
386 scoped_ptr<AudioEntry> entry_deleter(entry);
388 // Erase the entry from the map.
389 audio_entries_.erase(entry->stream_id);
392 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry) {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
395 // Sends the error message first before we close the stream because
396 // |entry| is destroyed in DeleteEntry().
397 SendErrorMessage(entry->stream_id);
398 CloseAndDeleteStream(entry);
401 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
403 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
405 AudioEntryMap::iterator i = audio_entries_.find(stream_id);
406 if (i != audio_entries_.end())
411 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
412 media::AudioInputController* controller) {
413 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
415 // Iterate the map of entries.
416 // TODO(hclam): Implement a faster look up method.
417 for (AudioEntryMap::iterator i = audio_entries_.begin();
418 i != audio_entries_.end(); ++i) {
419 if (controller == i->second->controller.get())
425 } // namespace content