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/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 media::UserInputMonitor* user_input_monitor)
56 : audio_manager_(audio_manager),
57 media_stream_manager_(media_stream_manager),
58 audio_mirroring_manager_(audio_mirroring_manager),
59 user_input_monitor_(user_input_monitor) {}
61 AudioInputRendererHost::~AudioInputRendererHost() {
62 DCHECK(audio_entries_.empty());
65 void AudioInputRendererHost::OnChannelClosing() {
66 // Since the IPC channel is gone, close all requested audio streams.
70 void AudioInputRendererHost::OnDestruct() const {
71 BrowserThread::DeleteOnIOThread::Destruct(this);
74 void AudioInputRendererHost::OnCreated(
75 media::AudioInputController* controller) {
76 BrowserThread::PostTask(
80 &AudioInputRendererHost::DoCompleteCreation,
82 make_scoped_refptr(controller)));
85 void AudioInputRendererHost::OnRecording(
86 media::AudioInputController* controller) {
87 BrowserThread::PostTask(
91 &AudioInputRendererHost::DoSendRecordingMessage,
93 make_scoped_refptr(controller)));
96 void AudioInputRendererHost::OnError(media::AudioInputController* controller) {
97 BrowserThread::PostTask(
101 &AudioInputRendererHost::DoHandleError,
103 make_scoped_refptr(controller)));
106 void AudioInputRendererHost::OnData(media::AudioInputController* controller,
109 NOTREACHED() << "Only low-latency mode is supported.";
112 void AudioInputRendererHost::DoCompleteCreation(
113 media::AudioInputController* controller) {
114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
116 AudioEntry* entry = LookupByController(controller);
121 NOTREACHED() << "Renderer process handle is invalid.";
122 DeleteEntryOnError(entry);
126 if (!entry->controller->LowLatencyMode()) {
127 NOTREACHED() << "Only low-latency mode is supported.";
128 DeleteEntryOnError(entry);
132 // Once the audio stream is created then complete the creation process by
133 // mapping shared memory and sharing with the renderer process.
134 base::SharedMemoryHandle foreign_memory_handle;
135 if (!entry->shared_memory.ShareToProcess(PeerHandle(),
136 &foreign_memory_handle)) {
137 // If we failed to map and share the shared memory then close the audio
138 // stream and send an error message.
139 DeleteEntryOnError(entry);
143 AudioInputSyncWriter* writer =
144 static_cast<AudioInputSyncWriter*>(entry->writer.get());
147 base::SyncSocket::Handle foreign_socket_handle;
149 base::FileDescriptor foreign_socket_handle;
152 // If we failed to prepare the sync socket for the renderer then we fail
153 // the construction of audio input stream.
154 if (!writer->PrepareForeignSocketHandle(PeerHandle(),
155 &foreign_socket_handle)) {
156 DeleteEntryOnError(entry);
160 Send(new AudioInputMsg_NotifyStreamCreated(entry->stream_id,
161 foreign_memory_handle, foreign_socket_handle,
162 entry->shared_memory.requested_size(),
163 entry->shared_memory_segment_count));
166 void AudioInputRendererHost::DoSendRecordingMessage(
167 media::AudioInputController* controller) {
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
169 // TODO(henrika): See crbug.com/115262 for details on why this method
170 // should be implemented.
173 void AudioInputRendererHost::DoHandleError(
174 media::AudioInputController* controller) {
175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
177 AudioEntry* entry = LookupByController(controller);
181 DeleteEntryOnError(entry);
184 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message,
185 bool* message_was_ok) {
187 IPC_BEGIN_MESSAGE_MAP_EX(AudioInputRendererHost, message, *message_was_ok)
188 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream)
189 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream)
190 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream)
191 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume)
192 IPC_MESSAGE_UNHANDLED(handled = false)
193 IPC_END_MESSAGE_MAP_EX()
198 void AudioInputRendererHost::OnCreateStream(
202 const AudioInputHostMsg_CreateStream_Config& config) {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
205 DVLOG(1) << "AudioInputRendererHost@" << this
206 << "::OnCreateStream(stream_id=" << stream_id
207 << ", render_view_id=" << render_view_id
208 << ", session_id=" << session_id << ")";
209 DCHECK_GT(render_view_id, 0);
211 // media::AudioParameters is validated in the deserializer.
212 if (LookupById(stream_id) != NULL) {
213 SendErrorMessage(stream_id);
217 media::AudioParameters audio_params(config.params);
218 if (media_stream_manager_->audio_input_device_manager()->
219 ShouldUseFakeDevice()) {
221 media::AudioParameters::AUDIO_FAKE,
222 config.params.channel_layout(), config.params.channels(), 0,
223 config.params.sample_rate(), config.params.bits_per_sample(),
224 config.params.frames_per_buffer());
227 // Check if we have the permission to open the device and which device to use.
228 std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
229 if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
230 const StreamDeviceInfo* info = media_stream_manager_->
231 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
233 SendErrorMessage(stream_id);
234 DLOG(WARNING) << "No permission has been granted to input stream with "
235 << "session_id=" << session_id;
239 device_id = info->device.id;
242 // Create a new AudioEntry structure.
243 scoped_ptr<AudioEntry> entry(new AudioEntry());
245 const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) +
246 audio_params.GetBytesPerBuffer());
247 entry->shared_memory_segment_count = config.shared_memory_count;
249 // Create the shared memory and share it with the renderer process
250 // using a new SyncWriter object.
251 if (!entry->shared_memory.CreateAndMapAnonymous(
252 segment_size * entry->shared_memory_segment_count)) {
253 // If creation of shared memory failed then send an error message.
254 SendErrorMessage(stream_id);
258 scoped_ptr<AudioInputSyncWriter> writer(
259 new AudioInputSyncWriter(&entry->shared_memory,
260 entry->shared_memory_segment_count));
262 if (!writer->Init()) {
263 SendErrorMessage(stream_id);
267 // If we have successfully created the SyncWriter then assign it to the
268 // entry and construct an AudioInputController.
269 entry->writer.reset(writer.release());
270 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
271 entry->controller = media::AudioInputController::CreateForStream(
272 audio_manager_->GetMessageLoop(),
274 WebContentsAudioInputStream::Create(device_id,
276 audio_manager_->GetWorkerLoop(),
277 audio_mirroring_manager_),
279 user_input_monitor_);
281 // TODO(henrika): replace CreateLowLatency() with Create() as soon
282 // as satish has ensured that Speech Input also uses the default low-
283 // latency path. See crbug.com/112472 for details.
285 media::AudioInputController::CreateLowLatency(audio_manager_,
290 user_input_monitor_);
293 if (!entry->controller.get()) {
294 SendErrorMessage(stream_id);
298 // Set the initial AGC state for the audio input stream. Note that, the AGC
299 // is only supported in AUDIO_PCM_LOW_LATENCY mode.
300 if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY)
301 entry->controller->SetAutomaticGainControl(config.automatic_gain_control);
303 // Since the controller was created successfully, create an entry and add it
305 entry->stream_id = stream_id;
306 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
309 void AudioInputRendererHost::OnRecordStream(int stream_id) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
312 AudioEntry* entry = LookupById(stream_id);
314 SendErrorMessage(stream_id);
318 entry->controller->Record();
321 void AudioInputRendererHost::OnCloseStream(int stream_id) {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
324 AudioEntry* entry = LookupById(stream_id);
327 CloseAndDeleteStream(entry);
330 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
333 AudioEntry* entry = LookupById(stream_id);
335 SendErrorMessage(stream_id);
339 entry->controller->SetVolume(volume);
342 void AudioInputRendererHost::SendErrorMessage(int stream_id) {
343 Send(new AudioInputMsg_NotifyStreamStateChanged(
344 stream_id, media::AudioInputIPCDelegate::kError));
347 void AudioInputRendererHost::DeleteEntries() {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
350 for (AudioEntryMap::iterator i = audio_entries_.begin();
351 i != audio_entries_.end(); ++i) {
352 CloseAndDeleteStream(i->second);
356 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
359 if (!entry->pending_close) {
360 entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
362 entry->pending_close = true;
366 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
367 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
369 // Delete the entry when this method goes out of scope.
370 scoped_ptr<AudioEntry> entry_deleter(entry);
372 // Erase the entry from the map.
373 audio_entries_.erase(entry->stream_id);
376 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry) {
377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
379 // Sends the error message first before we close the stream because
380 // |entry| is destroyed in DeleteEntry().
381 SendErrorMessage(entry->stream_id);
382 CloseAndDeleteStream(entry);
385 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
389 AudioEntryMap::iterator i = audio_entries_.find(stream_id);
390 if (i != audio_entries_.end())
395 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
396 media::AudioInputController* controller) {
397 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
399 // Iterate the map of entries.
400 // TODO(hclam): Implement a faster look up method.
401 for (AudioEntryMap::iterator i = audio_entries_.begin();
402 i != audio_entries_.end(); ++i) {
403 if (controller == i->second->controller.get())
409 } // namespace content