Add ICU message format support
[chromium-blink-merge.git] / content / browser / renderer_host / media / audio_input_renderer_host.cc
blob2692f74dc99895a725c6c585be4987fd98cfe7f5
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"
7 #include "base/bind.h"
8 #include "base/memory/shared_memory.h"
9 #include "base/metrics/histogram.h"
10 #include "base/numerics/safe_math.h"
11 #include "base/process/process.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/browser/media/capture/web_contents_audio_input_stream.h"
14 #include "content/browser/media/capture/web_contents_capture_util.h"
15 #include "content/browser/media/media_internals.h"
16 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
17 #include "content/browser/renderer_host/media/audio_input_sync_writer.h"
18 #include "content/browser/renderer_host/media/media_stream_manager.h"
19 #include "media/audio/audio_manager_base.h"
20 #include "media/base/audio_bus.h"
22 namespace {
24 void LogMessage(int stream_id, const std::string& msg, bool add_prefix) {
25 std::ostringstream oss;
26 oss << "[stream_id=" << stream_id << "] ";
27 if (add_prefix)
28 oss << "AIRH::";
29 oss << msg;
30 content::MediaStreamManager::SendMessageToNativeLog(oss.str());
31 DVLOG(1) << oss.str();
34 } // namespace
36 namespace content {
38 struct AudioInputRendererHost::AudioEntry {
39 AudioEntry();
40 ~AudioEntry();
42 // The AudioInputController that manages the audio input stream.
43 scoped_refptr<media::AudioInputController> controller;
45 // The audio input stream ID in the render view.
46 int stream_id;
48 // Shared memory for transmission of the audio data. It has
49 // |shared_memory_segment_count| equal lengthed segments.
50 base::SharedMemory shared_memory;
51 int shared_memory_segment_count;
53 // The synchronous writer to be used by the controller. We have the
54 // ownership of the writer.
55 scoped_ptr<media::AudioInputController::SyncWriter> writer;
57 // Set to true after we called Close() for the controller.
58 bool pending_close;
60 // If this entry's layout has a keyboard mic channel.
61 bool has_keyboard_mic_;
64 AudioInputRendererHost::AudioEntry::AudioEntry()
65 : stream_id(0),
66 shared_memory_segment_count(0),
67 pending_close(false),
68 has_keyboard_mic_(false) {
71 AudioInputRendererHost::AudioEntry::~AudioEntry() {}
73 AudioInputRendererHost::AudioInputRendererHost(
74 int render_process_id,
75 media::AudioManager* audio_manager,
76 MediaStreamManager* media_stream_manager,
77 AudioMirroringManager* audio_mirroring_manager,
78 media::UserInputMonitor* user_input_monitor)
79 : BrowserMessageFilter(AudioMsgStart),
80 render_process_id_(render_process_id),
81 audio_manager_(audio_manager),
82 media_stream_manager_(media_stream_manager),
83 audio_mirroring_manager_(audio_mirroring_manager),
84 user_input_monitor_(user_input_monitor),
85 audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
86 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)) {}
88 AudioInputRendererHost::~AudioInputRendererHost() {
89 DCHECK_CURRENTLY_ON(BrowserThread::IO);
90 DCHECK(audio_entries_.empty());
93 void AudioInputRendererHost::OnChannelClosing() {
94 // Since the IPC sender is gone, close all requested audio streams.
95 DeleteEntries();
98 void AudioInputRendererHost::OnDestruct() const {
99 BrowserThread::DeleteOnIOThread::Destruct(this);
102 void AudioInputRendererHost::OnCreated(
103 media::AudioInputController* controller) {
104 BrowserThread::PostTask(
105 BrowserThread::IO,
106 FROM_HERE,
107 base::Bind(
108 &AudioInputRendererHost::DoCompleteCreation,
109 this,
110 make_scoped_refptr(controller)));
113 void AudioInputRendererHost::OnRecording(
114 media::AudioInputController* controller) {
115 BrowserThread::PostTask(
116 BrowserThread::IO,
117 FROM_HERE,
118 base::Bind(
119 &AudioInputRendererHost::DoSendRecordingMessage,
120 this,
121 make_scoped_refptr(controller)));
124 void AudioInputRendererHost::OnError(media::AudioInputController* controller,
125 media::AudioInputController::ErrorCode error_code) {
126 BrowserThread::PostTask(
127 BrowserThread::IO,
128 FROM_HERE,
129 base::Bind(
130 &AudioInputRendererHost::DoHandleError,
131 this,
132 make_scoped_refptr(controller),
133 error_code));
136 void AudioInputRendererHost::OnData(media::AudioInputController* controller,
137 const media::AudioBus* data) {
138 NOTREACHED() << "Only low-latency mode is supported.";
141 void AudioInputRendererHost::OnLog(media::AudioInputController* controller,
142 const std::string& message) {
143 BrowserThread::PostTask(BrowserThread::IO,
144 FROM_HERE,
145 base::Bind(&AudioInputRendererHost::DoLog,
146 this,
147 make_scoped_refptr(controller),
148 message));
151 void AudioInputRendererHost::DoCompleteCreation(
152 media::AudioInputController* controller) {
153 DCHECK_CURRENTLY_ON(BrowserThread::IO);
155 AudioEntry* entry = LookupByController(controller);
156 if (!entry) {
157 NOTREACHED() << "AudioInputController is invalid.";
158 return;
161 if (!PeerHandle()) {
162 NOTREACHED() << "Renderer process handle is invalid.";
163 DeleteEntryOnError(entry, INVALID_PEER_HANDLE);
164 return;
167 if (!entry->controller->SharedMemoryAndSyncSocketMode()) {
168 NOTREACHED() << "Only shared-memory/sync-socket mode is supported.";
169 DeleteEntryOnError(entry, INVALID_LATENCY_MODE);
170 return;
173 // Once the audio stream is created then complete the creation process by
174 // mapping shared memory and sharing with the renderer process.
175 base::SharedMemoryHandle foreign_memory_handle;
176 if (!entry->shared_memory.ShareToProcess(PeerHandle(),
177 &foreign_memory_handle)) {
178 // If we failed to map and share the shared memory then close the audio
179 // stream and send an error message.
180 DeleteEntryOnError(entry, MEMORY_SHARING_FAILED);
181 return;
184 AudioInputSyncWriter* writer =
185 static_cast<AudioInputSyncWriter*>(entry->writer.get());
187 base::SyncSocket::TransitDescriptor socket_transit_descriptor;
189 // If we failed to prepare the sync socket for the renderer then we fail
190 // the construction of audio input stream.
191 if (!writer->PrepareForeignSocket(PeerHandle(), &socket_transit_descriptor)) {
192 DeleteEntryOnError(entry, SYNC_SOCKET_ERROR);
193 return;
196 LogMessage(entry->stream_id,
197 "DoCompleteCreation: IPC channel and stream are now open",
198 true);
200 Send(new AudioInputMsg_NotifyStreamCreated(
201 entry->stream_id, foreign_memory_handle, socket_transit_descriptor,
202 entry->shared_memory.requested_size(),
203 entry->shared_memory_segment_count));
206 void AudioInputRendererHost::DoSendRecordingMessage(
207 media::AudioInputController* controller) {
208 DCHECK_CURRENTLY_ON(BrowserThread::IO);
209 // TODO(henrika): See crbug.com/115262 for details on why this method
210 // should be implemented.
211 AudioEntry* entry = LookupByController(controller);
212 if (!entry) {
213 NOTREACHED() << "AudioInputController is invalid.";
214 return;
216 LogMessage(
217 entry->stream_id, "DoSendRecordingMessage: stream is now started", true);
220 void AudioInputRendererHost::DoHandleError(
221 media::AudioInputController* controller,
222 media::AudioInputController::ErrorCode error_code) {
223 DCHECK_CURRENTLY_ON(BrowserThread::IO);
224 AudioEntry* entry = LookupByController(controller);
225 if (!entry) {
226 NOTREACHED() << "AudioInputController is invalid.";
227 return;
230 // This is a fix for crbug.com/357501. The error can be triggered when closing
231 // the lid on Macs, which causes more problems than it fixes.
232 // Also, in crbug.com/357569, the goal is to remove usage of the error since
233 // it was added to solve a crash on Windows that no longer can be reproduced.
234 if (error_code == media::AudioInputController::NO_DATA_ERROR) {
235 // TODO(henrika): it might be possible to do something other than just
236 // logging when we detect many NO_DATA_ERROR calls for a stream.
237 LogMessage(entry->stream_id, "AIC::DoCheckForNoData: NO_DATA_ERROR", false);
238 return;
241 std::ostringstream oss;
242 oss << "AIC reports error_code=" << error_code;
243 LogMessage(entry->stream_id, oss.str(), false);
245 audio_log_->OnError(entry->stream_id);
246 DeleteEntryOnError(entry, AUDIO_INPUT_CONTROLLER_ERROR);
249 void AudioInputRendererHost::DoLog(media::AudioInputController* controller,
250 const std::string& message) {
251 DCHECK_CURRENTLY_ON(BrowserThread::IO);
252 AudioEntry* entry = LookupByController(controller);
253 if (!entry) {
254 NOTREACHED() << "AudioInputController is invalid.";
255 return;
258 // Add stream ID and current audio level reported by AIC to native log.
259 LogMessage(entry->stream_id, message, false);
262 bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message) {
263 bool handled = true;
264 IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost, message)
265 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream)
266 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream)
267 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream)
268 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume)
269 IPC_MESSAGE_UNHANDLED(handled = false)
270 IPC_END_MESSAGE_MAP()
272 return handled;
275 void AudioInputRendererHost::OnCreateStream(
276 int stream_id,
277 int render_frame_id,
278 int session_id,
279 const AudioInputHostMsg_CreateStream_Config& config) {
280 DCHECK_CURRENTLY_ON(BrowserThread::IO);
282 #if defined(OS_CHROMEOS)
283 if (config.params.channel_layout() ==
284 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
285 media_stream_manager_->audio_input_device_manager()
286 ->RegisterKeyboardMicStream(
287 base::Bind(&AudioInputRendererHost::DoCreateStream, this, stream_id,
288 render_frame_id, session_id, config));
289 } else {
290 DoCreateStream(stream_id, render_frame_id, session_id, config);
292 #else
293 DoCreateStream(stream_id, render_frame_id, session_id, config);
294 #endif
297 void AudioInputRendererHost::DoCreateStream(
298 int stream_id,
299 int render_frame_id,
300 int session_id,
301 const AudioInputHostMsg_CreateStream_Config& config) {
302 DCHECK_CURRENTLY_ON(BrowserThread::IO);
304 std::ostringstream oss;
305 oss << "[stream_id=" << stream_id << "] "
306 << "AIRH::OnCreateStream(render_frame_id=" << render_frame_id
307 << ", session_id=" << session_id << ")";
308 DCHECK_GT(render_frame_id, 0);
310 // media::AudioParameters is validated in the deserializer.
311 if (LookupById(stream_id) != NULL) {
312 SendErrorMessage(stream_id, STREAM_ALREADY_EXISTS);
313 MaybeUnregisterKeyboardMicStream(config);
314 return;
317 media::AudioParameters audio_params(config.params);
318 if (media_stream_manager_->audio_input_device_manager()->
319 ShouldUseFakeDevice()) {
320 audio_params.Reset(
321 media::AudioParameters::AUDIO_FAKE,
322 config.params.channel_layout(), config.params.channels(),
323 config.params.sample_rate(), config.params.bits_per_sample(),
324 config.params.frames_per_buffer());
327 // Check if we have the permission to open the device and which device to use.
328 std::string device_name;
329 std::string device_id = media::AudioManagerBase::kDefaultDeviceId;
330 if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) {
331 const StreamDeviceInfo* info = media_stream_manager_->
332 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id);
333 if (!info) {
334 SendErrorMessage(stream_id, PERMISSION_DENIED);
335 DLOG(WARNING) << "No permission has been granted to input stream with "
336 << "session_id=" << session_id;
337 MaybeUnregisterKeyboardMicStream(config);
338 return;
341 device_id = info->device.id;
342 device_name = info->device.name;
343 oss << ": device_name=" << device_name;
346 // Create a new AudioEntry structure.
347 scoped_ptr<AudioEntry> entry(new AudioEntry());
349 const uint32 segment_size =
350 (sizeof(media::AudioInputBufferParameters) +
351 media::AudioBus::CalculateMemorySize(audio_params));
352 entry->shared_memory_segment_count = config.shared_memory_count;
354 // Create the shared memory and share it with the renderer process
355 // using a new SyncWriter object.
356 base::CheckedNumeric<uint32> size = segment_size;
357 size *= entry->shared_memory_segment_count;
358 if (!size.IsValid() ||
359 !entry->shared_memory.CreateAndMapAnonymous(size.ValueOrDie())) {
360 // If creation of shared memory failed then send an error message.
361 SendErrorMessage(stream_id, SHARED_MEMORY_CREATE_FAILED);
362 MaybeUnregisterKeyboardMicStream(config);
363 return;
366 scoped_ptr<AudioInputSyncWriter> writer(new AudioInputSyncWriter(
367 &entry->shared_memory, entry->shared_memory_segment_count, audio_params));
369 if (!writer->Init()) {
370 SendErrorMessage(stream_id, SYNC_WRITER_INIT_FAILED);
371 MaybeUnregisterKeyboardMicStream(config);
372 return;
375 // If we have successfully created the SyncWriter then assign it to the
376 // entry and construct an AudioInputController.
377 entry->writer.reset(writer.release());
378 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) {
379 entry->controller = media::AudioInputController::CreateForStream(
380 audio_manager_->GetTaskRunner(),
381 this,
382 WebContentsAudioInputStream::Create(
383 device_id,
384 audio_params,
385 audio_manager_->GetWorkerTaskRunner(),
386 audio_mirroring_manager_),
387 entry->writer.get(),
388 user_input_monitor_);
389 } else {
390 DCHECK_EQ(config.params.format(),
391 media::AudioParameters::AUDIO_PCM_LOW_LATENCY);
392 entry->controller = media::AudioInputController::CreateLowLatency(
393 audio_manager_,
394 this,
395 audio_params,
396 device_id,
397 entry->writer.get(),
398 user_input_monitor_,
399 config.automatic_gain_control);
400 oss << ", AGC=" << config.automatic_gain_control;
403 if (!entry->controller.get()) {
404 SendErrorMessage(stream_id, STREAM_CREATE_ERROR);
405 MaybeUnregisterKeyboardMicStream(config);
406 return;
409 #if defined(OS_CHROMEOS)
410 if (config.params.channel_layout() ==
411 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
412 entry->has_keyboard_mic_ = true;
414 #endif
416 MediaStreamManager::SendMessageToNativeLog(oss.str());
417 DVLOG(1) << oss.str();
419 // Since the controller was created successfully, create an entry and add it
420 // to the map.
421 entry->stream_id = stream_id;
422 audio_entries_.insert(std::make_pair(stream_id, entry.release()));
423 audio_log_->OnCreated(stream_id, audio_params, device_id);
424 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry(
425 stream_id, render_process_id_, render_frame_id, audio_log_.get());
428 void AudioInputRendererHost::OnRecordStream(int stream_id) {
429 DCHECK_CURRENTLY_ON(BrowserThread::IO);
430 LogMessage(stream_id, "OnRecordStream", true);
432 AudioEntry* entry = LookupById(stream_id);
433 if (!entry) {
434 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
435 return;
438 entry->controller->Record();
439 audio_log_->OnStarted(stream_id);
442 void AudioInputRendererHost::OnCloseStream(int stream_id) {
443 DCHECK_CURRENTLY_ON(BrowserThread::IO);
444 LogMessage(stream_id, "OnCloseStream", true);
446 AudioEntry* entry = LookupById(stream_id);
448 if (entry)
449 CloseAndDeleteStream(entry);
452 void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) {
453 DCHECK_CURRENTLY_ON(BrowserThread::IO);
455 AudioEntry* entry = LookupById(stream_id);
456 if (!entry) {
457 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY);
458 return;
461 entry->controller->SetVolume(volume);
462 audio_log_->OnSetVolume(stream_id, volume);
465 void AudioInputRendererHost::SendErrorMessage(
466 int stream_id, ErrorCode error_code) {
467 std::string err_msg =
468 base::StringPrintf("SendErrorMessage(error_code=%d)", error_code);
469 LogMessage(stream_id, err_msg, true);
471 Send(new AudioInputMsg_NotifyStreamStateChanged(
472 stream_id, media::AUDIO_INPUT_IPC_DELEGATE_STATE_ERROR));
475 void AudioInputRendererHost::DeleteEntries() {
476 DCHECK_CURRENTLY_ON(BrowserThread::IO);
478 for (AudioEntryMap::iterator i = audio_entries_.begin();
479 i != audio_entries_.end(); ++i) {
480 CloseAndDeleteStream(i->second);
484 void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) {
485 DCHECK_CURRENTLY_ON(BrowserThread::IO);
487 if (!entry->pending_close) {
488 LogMessage(entry->stream_id, "CloseAndDeleteStream", true);
489 entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry,
490 this, entry));
491 entry->pending_close = true;
492 audio_log_->OnClosed(entry->stream_id);
496 void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) {
497 DCHECK_CURRENTLY_ON(BrowserThread::IO);
498 LogMessage(entry->stream_id, "DeleteEntry: stream is now closed", true);
500 #if defined(OS_CHROMEOS)
501 if (entry->has_keyboard_mic_) {
502 media_stream_manager_->audio_input_device_manager()
503 ->UnregisterKeyboardMicStream();
505 #endif
507 // Delete the entry when this method goes out of scope.
508 scoped_ptr<AudioEntry> entry_deleter(entry);
510 // Erase the entry from the map.
511 audio_entries_.erase(entry->stream_id);
514 void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry,
515 ErrorCode error_code) {
516 DCHECK_CURRENTLY_ON(BrowserThread::IO);
518 // Sends the error message first before we close the stream because
519 // |entry| is destroyed in DeleteEntry().
520 SendErrorMessage(entry->stream_id, error_code);
521 CloseAndDeleteStream(entry);
524 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById(
525 int stream_id) {
526 DCHECK_CURRENTLY_ON(BrowserThread::IO);
528 AudioEntryMap::iterator i = audio_entries_.find(stream_id);
529 if (i != audio_entries_.end())
530 return i->second;
531 return NULL;
534 AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController(
535 media::AudioInputController* controller) {
536 DCHECK_CURRENTLY_ON(BrowserThread::IO);
538 // Iterate the map of entries.
539 // TODO(hclam): Implement a faster look up method.
540 for (AudioEntryMap::iterator i = audio_entries_.begin();
541 i != audio_entries_.end(); ++i) {
542 if (controller == i->second->controller.get())
543 return i->second;
545 return NULL;
548 void AudioInputRendererHost::MaybeUnregisterKeyboardMicStream(
549 const AudioInputHostMsg_CreateStream_Config& config) {
550 #if defined(OS_CHROMEOS)
551 if (config.params.channel_layout() ==
552 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
553 media_stream_manager_->audio_input_device_manager()
554 ->UnregisterKeyboardMicStream();
556 #endif
559 } // namespace content