Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / media / audio_input_renderer_host.cc
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
44 AudioInputRendererHost::AudioEntry::AudioEntry()
45     : stream_id(0),
46       shared_memory_segment_count(0),
47       pending_close(false) {
48 }
49
50 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
51
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
64 AudioInputRendererHost::~AudioInputRendererHost() {
65   DCHECK(audio_entries_.empty());
66 }
67
68 void AudioInputRendererHost::OnChannelClosing() {
69   // Since the IPC channel is gone, close all requested audio streams.
70   DeleteEntries();
71 }
72
73 void AudioInputRendererHost::OnDestruct() const {
74   BrowserThread::DeleteOnIOThread::Destruct(this);
75 }
76
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
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
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
109 void AudioInputRendererHost::OnData(media::AudioInputController* controller,
110                                     const uint8* data,
111                                     uint32 size) {
112   NOTREACHED() << "Only low-latency mode is supported.";
113 }
114
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
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
176 void AudioInputRendererHost::DoHandleError(
177     media::AudioInputController* controller) {
178   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
179   MediaStreamManager::SendMessageToNativeLog(
180       "The AudioInputController signalled an error.");
181
182   AudioEntry* entry = LookupByController(controller);
183   if (!entry)
184     return;
185
186   audio_log_->OnError(entry->stream_id);
187   DeleteEntryOnError(entry);
188 }
189
190 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message,
191                                                bool* message_was_ok) {
192   bool handled = true;
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()
200
201   return handled;
202 }
203
204 void AudioInputRendererHost::OnCreateStream(
205     int stream_id,
206     int render_view_id,
207     int session_id,
208     const AudioInputHostMsg_CreateStream_Config& config) {
209   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
210
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);
216
217   // media::AudioParameters is validated in the deserializer.
218   if (LookupById(stream_id) != NULL) {
219     SendErrorMessage(stream_id);
220     return;
221   }
222
223   media::AudioParameters audio_params(config.params);
224   if (media_stream_manager_->audio_input_device_manager()->
225       ShouldUseFakeDevice()) {
226     audio_params.Reset(
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());
231   }
232
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);
238     if (!info) {
239       SendErrorMessage(stream_id);
240       DLOG(WARNING) << "No permission has been granted to input stream with "
241                     << "session_id=" << session_id;
242       return;
243     }
244
245     device_id = info->device.id;
246   }
247
248   // Create a new AudioEntry structure.
249   scoped_ptr<AudioEntry> entry(new AudioEntry());
250
251   const uint32 segment_size = (sizeof(media::AudioInputBufferParameters) +
252                                audio_params.GetBytesPerBuffer());
253   entry->shared_memory_segment_count = config.shared_memory_count;
254
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);
261     return;
262   }
263
264   scoped_ptr<AudioInputSyncWriter> writer(
265       new AudioInputSyncWriter(&entry->shared_memory,
266                                entry->shared_memory_segment_count));
267
268   if (!writer->Init()) {
269     SendErrorMessage(stream_id);
270     return;
271   }
272
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(),
279         this,
280         WebContentsAudioInputStream::Create(
281             device_id,
282             audio_params,
283             audio_manager_->GetWorkerTaskRunner(),
284             audio_mirroring_manager_),
285         entry->writer.get(),
286         user_input_monitor_);
287   } else {
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.
291     entry->controller =
292         media::AudioInputController::CreateLowLatency(audio_manager_,
293                                                       this,
294                                                       audio_params,
295                                                       device_id,
296                                                       entry->writer.get(),
297                                                       user_input_monitor_);
298   }
299
300   if (!entry->controller.get()) {
301     SendErrorMessage(stream_id);
302     return;
303   }
304
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);
309
310   // Since the controller was created successfully, create an entry and add it
311   // to the map.
312   entry->stream_id = stream_id;
313   audio_entries_.insert(std::make_pair(stream_id, entry.release()));
314
315   MediaStreamManager::SendMessageToNativeLog(
316       "Audio input stream created successfully.");
317   audio_log_->OnCreated(stream_id, audio_params, device_id);
318 }
319
320 void AudioInputRendererHost::OnRecordStream(int stream_id) {
321   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
322
323   AudioEntry* entry = LookupById(stream_id);
324   if (!entry) {
325     SendErrorMessage(stream_id);
326     return;
327   }
328
329   entry->controller->Record();
330   audio_log_->OnStarted(stream_id);
331 }
332
333 void AudioInputRendererHost::OnCloseStream(int stream_id) {
334   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
335
336   AudioEntry* entry = LookupById(stream_id);
337
338   if (entry)
339     CloseAndDeleteStream(entry);
340 }
341
342 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
344
345   AudioEntry* entry = LookupById(stream_id);
346   if (!entry) {
347     SendErrorMessage(stream_id);
348     return;
349   }
350
351   entry->controller->SetVolume(volume);
352   audio_log_->OnSetVolume(stream_id, volume);
353 }
354
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));
360 }
361
362 void AudioInputRendererHost::DeleteEntries() {
363   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
364
365   for (AudioEntryMap::iterator i = audio_entries_.begin();
366        i != audio_entries_.end(); ++i) {
367     CloseAndDeleteStream(i->second);
368   }
369 }
370
371 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
372   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
373
374   if (!entry->pending_close) {
375     entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
376                                         this, entry));
377     entry->pending_close = true;
378     audio_log_->OnClosed(entry->stream_id);
379   }
380 }
381
382 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
383   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
384
385   // Delete the entry when this method goes out of scope.
386   scoped_ptr<AudioEntry> entry_deleter(entry);
387
388   // Erase the entry from the map.
389   audio_entries_.erase(entry->stream_id);
390 }
391
392 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry) {
393   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
394
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);
399 }
400
401 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
402     int stream_id) {
403   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
404
405   AudioEntryMap::iterator i = audio_entries_.find(stream_id);
406   if (i != audio_entries_.end())
407     return i->second;
408   return NULL;
409 }
410
411 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
412     media::AudioInputController* controller) {
413   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
414
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())
420       return i->second;
421   }
422   return NULL;
423 }
424
425 }  // namespace content