• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "content/browser/renderer_host/media/audio_input_renderer_host.h"
6 
7 #include "base/bind.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"
18 
19 namespace content {
20 
21 struct AudioInputRendererHost::AudioEntry {
22   AudioEntry();
23   ~AudioEntry();
24 
25   // The AudioInputController that manages the audio input stream.
26   scoped_refptr<media::AudioInputController> controller;
27 
28   // The audio input stream ID in the render view.
29   int stream_id;
30 
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;
35 
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;
39 
40   // Set to true after we called Close() for the controller.
41   bool pending_close;
42 };
43 
AudioEntry()44 AudioInputRendererHost::AudioEntry::AudioEntry()
45     : stream_id(0),
46       shared_memory_segment_count(0),
47       pending_close(false) {
48 }
49 
~AudioEntry()50 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
51 
AudioInputRendererHost(media::AudioManager * audio_manager,MediaStreamManager * media_stream_manager,AudioMirroringManager * audio_mirroring_manager,media::UserInputMonitor * user_input_monitor)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)) {}
63 
~AudioInputRendererHost()64 AudioInputRendererHost::~AudioInputRendererHost() {
65   DCHECK(audio_entries_.empty());
66 }
67 
OnChannelClosing()68 void AudioInputRendererHost::OnChannelClosing() {
69   // Since the IPC channel is gone, close all requested audio streams.
70   DeleteEntries();
71 }
72 
OnDestruct() const73 void AudioInputRendererHost::OnDestruct() const {
74   BrowserThread::DeleteOnIOThread::Destruct(this);
75 }
76 
OnCreated(media::AudioInputController * controller)77 void AudioInputRendererHost::OnCreated(
78     media::AudioInputController* controller) {
79   BrowserThread::PostTask(
80       BrowserThread::IO,
81       FROM_HERE,
82       base::Bind(
83           &AudioInputRendererHost::DoCompleteCreation,
84           this,
85           make_scoped_refptr(controller)));
86 }
87 
OnRecording(media::AudioInputController * controller)88 void AudioInputRendererHost::OnRecording(
89     media::AudioInputController* controller) {
90   BrowserThread::PostTask(
91       BrowserThread::IO,
92       FROM_HERE,
93       base::Bind(
94           &AudioInputRendererHost::DoSendRecordingMessage,
95           this,
96           make_scoped_refptr(controller)));
97 }
98 
OnError(media::AudioInputController * controller)99 void AudioInputRendererHost::OnError(media::AudioInputController* controller) {
100   BrowserThread::PostTask(
101       BrowserThread::IO,
102       FROM_HERE,
103       base::Bind(
104           &AudioInputRendererHost::DoHandleError,
105           this,
106           make_scoped_refptr(controller)));
107 }
108 
OnData(media::AudioInputController * controller,const uint8 * data,uint32 size)109 void AudioInputRendererHost::OnData(media::AudioInputController* controller,
110                                     const uint8* data,
111                                     uint32 size) {
112   NOTREACHED() << "Only low-latency mode is supported.";
113 }
114 
DoCompleteCreation(media::AudioInputController * controller)115 void AudioInputRendererHost::DoCompleteCreation(
116     media::AudioInputController* controller) {
117   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
118 
119   AudioEntry* entry = LookupByController(controller);
120   if (!entry)
121     return;
122 
123   if (!PeerHandle()) {
124     NOTREACHED() << "Renderer process handle is invalid.";
125     DeleteEntryOnError(entry);
126     return;
127   }
128 
129   if (!entry->controller->LowLatencyMode()) {
130     NOTREACHED() << "Only low-latency mode is supported.";
131     DeleteEntryOnError(entry);
132     return;
133   }
134 
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);
143     return;
144   }
145 
146   AudioInputSyncWriter* writer =
147       static_cast<AudioInputSyncWriter*>(entry->writer.get());
148 
149 #if defined(OS_WIN)
150   base::SyncSocket::Handle foreign_socket_handle;
151 #else
152   base::FileDescriptor foreign_socket_handle;
153 #endif
154 
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);
160     return;
161   }
162 
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));
167 }
168 
DoSendRecordingMessage(media::AudioInputController * controller)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.
174 }
175 
DoHandleError(media::AudioInputController * controller)176 void AudioInputRendererHost::DoHandleError(
177     media::AudioInputController* controller) {
178   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
179 
180   AudioEntry* entry = LookupByController(controller);
181   if (!entry)
182     return;
183 
184   audio_log_->OnError(entry->stream_id);
185   DeleteEntryOnError(entry);
186 }
187 
OnMessageReceived(const IPC::Message & message,bool * message_was_ok)188 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message,
189                                                bool* message_was_ok) {
190   bool handled = true;
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()
198 
199   return handled;
200 }
201 
OnCreateStream(int stream_id,int render_view_id,int session_id,const AudioInputHostMsg_CreateStream_Config & config)202 void AudioInputRendererHost::OnCreateStream(
203     int stream_id,
204     int render_view_id,
205     int session_id,
206     const AudioInputHostMsg_CreateStream_Config& config) {
207   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
208 
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);
214 
215   // media::AudioParameters is validated in the deserializer.
216   if (LookupById(stream_id) != NULL) {
217     SendErrorMessage(stream_id);
218     return;
219   }
220 
221   media::AudioParameters audio_params(config.params);
222   if (media_stream_manager_->audio_input_device_manager()->
223       ShouldUseFakeDevice()) {
224     audio_params.Reset(
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());
229   }
230 
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);
236     if (!info) {
237       SendErrorMessage(stream_id);
238       DLOG(WARNING) << "No permission has been granted to input stream with "
239                     << "session_id=" << session_id;
240       return;
241     }
242 
243     device_id = info->device.id;
244   }
245 
246   // Create a new AudioEntry structure.
247   scoped_ptr<AudioEntry> entry(new AudioEntry());
248 
249   const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) +
250                                audio_params.GetBytesPerBuffer());
251   entry->shared_memory_segment_count = config.shared_memory_count;
252 
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);
259     return;
260   }
261 
262   scoped_ptr<AudioInputSyncWriter> writer(
263       new AudioInputSyncWriter(&entry->shared_memory,
264                                entry->shared_memory_segment_count));
265 
266   if (!writer->Init()) {
267     SendErrorMessage(stream_id);
268     return;
269   }
270 
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(),
277         this,
278         WebContentsAudioInputStream::Create(device_id,
279                                             audio_params,
280                                             audio_manager_->GetWorkerLoop(),
281                                             audio_mirroring_manager_),
282         entry->writer.get(),
283         user_input_monitor_);
284   } else {
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.
288     entry->controller =
289         media::AudioInputController::CreateLowLatency(audio_manager_,
290                                                       this,
291                                                       audio_params,
292                                                       device_id,
293                                                       entry->writer.get(),
294                                                       user_input_monitor_);
295   }
296 
297   if (!entry->controller.get()) {
298     SendErrorMessage(stream_id);
299     return;
300   }
301 
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);
306 
307   // Since the controller was created successfully, create an entry and add it
308   // to the map.
309   entry->stream_id = stream_id;
310   audio_entries_.insert(std::make_pair(stream_id, entry.release()));
311 
312   audio_log_->OnCreated(stream_id, audio_params, device_id, std::string());
313 }
314 
OnRecordStream(int stream_id)315 void AudioInputRendererHost::OnRecordStream(int stream_id) {
316   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
317 
318   AudioEntry* entry = LookupById(stream_id);
319   if (!entry) {
320     SendErrorMessage(stream_id);
321     return;
322   }
323 
324   entry->controller->Record();
325   audio_log_->OnStarted(stream_id);
326 }
327 
OnCloseStream(int stream_id)328 void AudioInputRendererHost::OnCloseStream(int stream_id) {
329   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
330 
331   AudioEntry* entry = LookupById(stream_id);
332 
333   if (entry)
334     CloseAndDeleteStream(entry);
335 }
336 
OnSetVolume(int stream_id,double volume)337 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
338   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
339 
340   AudioEntry* entry = LookupById(stream_id);
341   if (!entry) {
342     SendErrorMessage(stream_id);
343     return;
344   }
345 
346   entry->controller->SetVolume(volume);
347   audio_log_->OnSetVolume(stream_id, volume);
348 }
349 
SendErrorMessage(int stream_id)350 void AudioInputRendererHost::SendErrorMessage(int stream_id) {
351   Send(new AudioInputMsg_NotifyStreamStateChanged(
352       stream_id, media::AudioInputIPCDelegate::kError));
353 }
354 
DeleteEntries()355 void AudioInputRendererHost::DeleteEntries() {
356   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
357 
358   for (AudioEntryMap::iterator i = audio_entries_.begin();
359        i != audio_entries_.end(); ++i) {
360     CloseAndDeleteStream(i->second);
361   }
362 }
363 
CloseAndDeleteStream(AudioEntry * entry)364 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
365   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
366 
367   if (!entry->pending_close) {
368     entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
369                                         this, entry));
370     entry->pending_close = true;
371     audio_log_->OnClosed(entry->stream_id);
372   }
373 }
374 
DeleteEntry(AudioEntry * entry)375 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
376   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
377 
378   // Delete the entry when this method goes out of scope.
379   scoped_ptr<AudioEntry> entry_deleter(entry);
380 
381   // Erase the entry from the map.
382   audio_entries_.erase(entry->stream_id);
383 }
384 
DeleteEntryOnError(AudioEntry * entry)385 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry) {
386   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
387 
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);
392 }
393 
LookupById(int stream_id)394 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
395     int stream_id) {
396   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
397 
398   AudioEntryMap::iterator i = audio_entries_.find(stream_id);
399   if (i != audio_entries_.end())
400     return i->second;
401   return NULL;
402 }
403 
LookupByController(media::AudioInputController * controller)404 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
405     media::AudioInputController* controller) {
406   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
407 
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())
413       return i->second;
414   }
415   return NULL;
416 }
417 
418 }  // namespace content
419