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/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" 21 22namespace { 23 24void 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(); 32} 33 34} // namespace 35 36namespace content { 37 38struct AudioInputRendererHost::AudioEntry { 39 AudioEntry(); 40 ~AudioEntry(); 41 42 // The AudioInputController that manages the audio input stream. 43 scoped_refptr<media::AudioInputController> controller; 44 45 // The audio input stream ID in the render view. 46 int stream_id; 47 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; 52 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; 56 57 // Set to true after we called Close() for the controller. 58 bool pending_close; 59 60 // If this entry's layout has a keyboard mic channel. 61 bool has_keyboard_mic_; 62}; 63 64AudioInputRendererHost::AudioEntry::AudioEntry() 65 : stream_id(0), 66 shared_memory_segment_count(0), 67 pending_close(false), 68 has_keyboard_mic_(false) { 69} 70 71AudioInputRendererHost::AudioEntry::~AudioEntry() {} 72 73AudioInputRendererHost::AudioInputRendererHost( 74 media::AudioManager* audio_manager, 75 MediaStreamManager* media_stream_manager, 76 AudioMirroringManager* audio_mirroring_manager, 77 media::UserInputMonitor* user_input_monitor) 78 : BrowserMessageFilter(AudioMsgStart), 79 audio_manager_(audio_manager), 80 media_stream_manager_(media_stream_manager), 81 audio_mirroring_manager_(audio_mirroring_manager), 82 user_input_monitor_(user_input_monitor), 83 audio_log_(MediaInternals::GetInstance()->CreateAudioLog( 84 media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)) {} 85 86AudioInputRendererHost::~AudioInputRendererHost() { 87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 88 DCHECK(audio_entries_.empty()); 89} 90 91void AudioInputRendererHost::OnChannelClosing() { 92 // Since the IPC sender is gone, close all requested audio streams. 93 DeleteEntries(); 94} 95 96void AudioInputRendererHost::OnDestruct() const { 97 BrowserThread::DeleteOnIOThread::Destruct(this); 98} 99 100void AudioInputRendererHost::OnCreated( 101 media::AudioInputController* controller) { 102 BrowserThread::PostTask( 103 BrowserThread::IO, 104 FROM_HERE, 105 base::Bind( 106 &AudioInputRendererHost::DoCompleteCreation, 107 this, 108 make_scoped_refptr(controller))); 109} 110 111void AudioInputRendererHost::OnRecording( 112 media::AudioInputController* controller) { 113 BrowserThread::PostTask( 114 BrowserThread::IO, 115 FROM_HERE, 116 base::Bind( 117 &AudioInputRendererHost::DoSendRecordingMessage, 118 this, 119 make_scoped_refptr(controller))); 120} 121 122void AudioInputRendererHost::OnError(media::AudioInputController* controller, 123 media::AudioInputController::ErrorCode error_code) { 124 BrowserThread::PostTask( 125 BrowserThread::IO, 126 FROM_HERE, 127 base::Bind( 128 &AudioInputRendererHost::DoHandleError, 129 this, 130 make_scoped_refptr(controller), 131 error_code)); 132} 133 134void AudioInputRendererHost::OnData(media::AudioInputController* controller, 135 const media::AudioBus* data) { 136 NOTREACHED() << "Only low-latency mode is supported."; 137} 138 139void AudioInputRendererHost::OnLog(media::AudioInputController* controller, 140 const std::string& message) { 141 BrowserThread::PostTask(BrowserThread::IO, 142 FROM_HERE, 143 base::Bind(&AudioInputRendererHost::DoLog, 144 this, 145 make_scoped_refptr(controller), 146 message)); 147} 148 149void AudioInputRendererHost::DoCompleteCreation( 150 media::AudioInputController* controller) { 151 DCHECK_CURRENTLY_ON(BrowserThread::IO); 152 153 AudioEntry* entry = LookupByController(controller); 154 if (!entry) { 155 NOTREACHED() << "AudioInputController is invalid."; 156 return; 157 } 158 159 if (!PeerHandle()) { 160 NOTREACHED() << "Renderer process handle is invalid."; 161 DeleteEntryOnError(entry, INVALID_PEER_HANDLE); 162 return; 163 } 164 165 if (!entry->controller->SharedMemoryAndSyncSocketMode()) { 166 NOTREACHED() << "Only shared-memory/sync-socket mode is supported."; 167 DeleteEntryOnError(entry, INVALID_LATENCY_MODE); 168 return; 169 } 170 171 // Once the audio stream is created then complete the creation process by 172 // mapping shared memory and sharing with the renderer process. 173 base::SharedMemoryHandle foreign_memory_handle; 174 if (!entry->shared_memory.ShareToProcess(PeerHandle(), 175 &foreign_memory_handle)) { 176 // If we failed to map and share the shared memory then close the audio 177 // stream and send an error message. 178 DeleteEntryOnError(entry, MEMORY_SHARING_FAILED); 179 return; 180 } 181 182 AudioInputSyncWriter* writer = 183 static_cast<AudioInputSyncWriter*>(entry->writer.get()); 184 185 base::SyncSocket::TransitDescriptor socket_transit_descriptor; 186 187 // If we failed to prepare the sync socket for the renderer then we fail 188 // the construction of audio input stream. 189 if (!writer->PrepareForeignSocket(PeerHandle(), &socket_transit_descriptor)) { 190 DeleteEntryOnError(entry, SYNC_SOCKET_ERROR); 191 return; 192 } 193 194 LogMessage(entry->stream_id, 195 "DoCompleteCreation: IPC channel and stream are now open", 196 true); 197 198 Send(new AudioInputMsg_NotifyStreamCreated( 199 entry->stream_id, foreign_memory_handle, socket_transit_descriptor, 200 entry->shared_memory.requested_size(), 201 entry->shared_memory_segment_count)); 202} 203 204void AudioInputRendererHost::DoSendRecordingMessage( 205 media::AudioInputController* controller) { 206 DCHECK_CURRENTLY_ON(BrowserThread::IO); 207 // TODO(henrika): See crbug.com/115262 for details on why this method 208 // should be implemented. 209 AudioEntry* entry = LookupByController(controller); 210 if (!entry) { 211 NOTREACHED() << "AudioInputController is invalid."; 212 return; 213 } 214 LogMessage( 215 entry->stream_id, "DoSendRecordingMessage: stream is now started", true); 216} 217 218void AudioInputRendererHost::DoHandleError( 219 media::AudioInputController* controller, 220 media::AudioInputController::ErrorCode error_code) { 221 DCHECK_CURRENTLY_ON(BrowserThread::IO); 222 AudioEntry* entry = LookupByController(controller); 223 if (!entry) { 224 NOTREACHED() << "AudioInputController is invalid."; 225 return; 226 } 227 228 // This is a fix for crbug.com/357501. The error can be triggered when closing 229 // the lid on Macs, which causes more problems than it fixes. 230 // Also, in crbug.com/357569, the goal is to remove usage of the error since 231 // it was added to solve a crash on Windows that no longer can be reproduced. 232 if (error_code == media::AudioInputController::NO_DATA_ERROR) { 233 // TODO(henrika): it might be possible to do something other than just 234 // logging when we detect many NO_DATA_ERROR calls for a stream. 235 LogMessage(entry->stream_id, "AIC::DoCheckForNoData: NO_DATA_ERROR", false); 236 return; 237 } 238 239 std::ostringstream oss; 240 oss << "AIC reports error_code=" << error_code; 241 LogMessage(entry->stream_id, oss.str(), false); 242 243 audio_log_->OnError(entry->stream_id); 244 DeleteEntryOnError(entry, AUDIO_INPUT_CONTROLLER_ERROR); 245} 246 247void AudioInputRendererHost::DoLog(media::AudioInputController* controller, 248 const std::string& message) { 249 DCHECK_CURRENTLY_ON(BrowserThread::IO); 250 AudioEntry* entry = LookupByController(controller); 251 if (!entry) { 252 NOTREACHED() << "AudioInputController is invalid."; 253 return; 254 } 255 256 // Add stream ID and current audio level reported by AIC to native log. 257 LogMessage(entry->stream_id, message, false); 258} 259 260bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message) { 261 bool handled = true; 262 IPC_BEGIN_MESSAGE_MAP(AudioInputRendererHost, message) 263 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CreateStream, OnCreateStream) 264 IPC_MESSAGE_HANDLER(AudioInputHostMsg_RecordStream, OnRecordStream) 265 IPC_MESSAGE_HANDLER(AudioInputHostMsg_CloseStream, OnCloseStream) 266 IPC_MESSAGE_HANDLER(AudioInputHostMsg_SetVolume, OnSetVolume) 267 IPC_MESSAGE_UNHANDLED(handled = false) 268 IPC_END_MESSAGE_MAP() 269 270 return handled; 271} 272 273void AudioInputRendererHost::OnCreateStream( 274 int stream_id, 275 int render_view_id, 276 int session_id, 277 const AudioInputHostMsg_CreateStream_Config& config) { 278 DCHECK_CURRENTLY_ON(BrowserThread::IO); 279 280#if defined(OS_CHROMEOS) 281 if (config.params.channel_layout() == 282 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) { 283 media_stream_manager_->audio_input_device_manager() 284 ->RegisterKeyboardMicStream( 285 base::Bind(&AudioInputRendererHost::DoCreateStream, 286 this, 287 stream_id, 288 render_view_id, 289 session_id, 290 config)); 291 } else { 292 DoCreateStream(stream_id, render_view_id, session_id, config); 293 } 294#else 295 DoCreateStream(stream_id, render_view_id, session_id, config); 296#endif 297} 298 299void AudioInputRendererHost::DoCreateStream( 300 int stream_id, 301 int render_view_id, 302 int session_id, 303 const AudioInputHostMsg_CreateStream_Config& config) { 304 DCHECK_CURRENTLY_ON(BrowserThread::IO); 305 306 std::ostringstream oss; 307 oss << "[stream_id=" << stream_id << "] " 308 << "AIRH::OnCreateStream(render_view_id=" << render_view_id 309 << ", session_id=" << session_id << ")"; 310 DCHECK_GT(render_view_id, 0); 311 312 // media::AudioParameters is validated in the deserializer. 313 if (LookupById(stream_id) != NULL) { 314 SendErrorMessage(stream_id, STREAM_ALREADY_EXISTS); 315 MaybeUnregisterKeyboardMicStream(config); 316 return; 317 } 318 319 media::AudioParameters audio_params(config.params); 320 if (media_stream_manager_->audio_input_device_manager()-> 321 ShouldUseFakeDevice()) { 322 audio_params.Reset( 323 media::AudioParameters::AUDIO_FAKE, 324 config.params.channel_layout(), config.params.channels(), 325 config.params.sample_rate(), config.params.bits_per_sample(), 326 config.params.frames_per_buffer()); 327 } 328 329 // Check if we have the permission to open the device and which device to use. 330 std::string device_name; 331 std::string device_id = media::AudioManagerBase::kDefaultDeviceId; 332 if (audio_params.format() != media::AudioParameters::AUDIO_FAKE) { 333 const StreamDeviceInfo* info = media_stream_manager_-> 334 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id); 335 if (!info) { 336 SendErrorMessage(stream_id, PERMISSION_DENIED); 337 DLOG(WARNING) << "No permission has been granted to input stream with " 338 << "session_id=" << session_id; 339 MaybeUnregisterKeyboardMicStream(config); 340 return; 341 } 342 343 device_id = info->device.id; 344 device_name = info->device.name; 345 oss << ": device_name=" << device_name; 346 } 347 348 // Create a new AudioEntry structure. 349 scoped_ptr<AudioEntry> entry(new AudioEntry()); 350 351 const uint32 segment_size = 352 (sizeof(media::AudioInputBufferParameters) + 353 media::AudioBus::CalculateMemorySize(audio_params)); 354 entry->shared_memory_segment_count = config.shared_memory_count; 355 356 // Create the shared memory and share it with the renderer process 357 // using a new SyncWriter object. 358 base::CheckedNumeric<uint32> size = segment_size; 359 size *= entry->shared_memory_segment_count; 360 if (!size.IsValid() || 361 !entry->shared_memory.CreateAndMapAnonymous(size.ValueOrDie())) { 362 // If creation of shared memory failed then send an error message. 363 SendErrorMessage(stream_id, SHARED_MEMORY_CREATE_FAILED); 364 MaybeUnregisterKeyboardMicStream(config); 365 return; 366 } 367 368 scoped_ptr<AudioInputSyncWriter> writer(new AudioInputSyncWriter( 369 &entry->shared_memory, entry->shared_memory_segment_count, audio_params)); 370 371 if (!writer->Init()) { 372 SendErrorMessage(stream_id, SYNC_WRITER_INIT_FAILED); 373 MaybeUnregisterKeyboardMicStream(config); 374 return; 375 } 376 377 // If we have successfully created the SyncWriter then assign it to the 378 // entry and construct an AudioInputController. 379 entry->writer.reset(writer.release()); 380 if (WebContentsCaptureUtil::IsWebContentsDeviceId(device_id)) { 381 entry->controller = media::AudioInputController::CreateForStream( 382 audio_manager_->GetTaskRunner(), 383 this, 384 WebContentsAudioInputStream::Create( 385 device_id, 386 audio_params, 387 audio_manager_->GetWorkerTaskRunner(), 388 audio_mirroring_manager_), 389 entry->writer.get(), 390 user_input_monitor_); 391 } else { 392 // TODO(henrika): replace CreateLowLatency() with Create() as soon 393 // as satish has ensured that Speech Input also uses the default low- 394 // latency path. See crbug.com/112472 for details. 395 entry->controller = 396 media::AudioInputController::CreateLowLatency(audio_manager_, 397 this, 398 audio_params, 399 device_id, 400 entry->writer.get(), 401 user_input_monitor_); 402 } 403 404 if (!entry->controller.get()) { 405 SendErrorMessage(stream_id, STREAM_CREATE_ERROR); 406 MaybeUnregisterKeyboardMicStream(config); 407 return; 408 } 409 410 // Set the initial AGC state for the audio input stream. Note that, the AGC 411 // is only supported in AUDIO_PCM_LOW_LATENCY mode. 412 if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) { 413 entry->controller->SetAutomaticGainControl(config.automatic_gain_control); 414 oss << ", AGC=" << config.automatic_gain_control; 415 } 416 417#if defined(OS_CHROMEOS) 418 if (config.params.channel_layout() == 419 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) { 420 entry->has_keyboard_mic_ = true; 421 } 422#endif 423 424 MediaStreamManager::SendMessageToNativeLog(oss.str()); 425 DVLOG(1) << oss.str(); 426 427 // Since the controller was created successfully, create an entry and add it 428 // to the map. 429 entry->stream_id = stream_id; 430 audio_entries_.insert(std::make_pair(stream_id, entry.release())); 431 audio_log_->OnCreated(stream_id, audio_params, device_id); 432} 433 434void AudioInputRendererHost::OnRecordStream(int stream_id) { 435 DCHECK_CURRENTLY_ON(BrowserThread::IO); 436 LogMessage(stream_id, "OnRecordStream", true); 437 438 AudioEntry* entry = LookupById(stream_id); 439 if (!entry) { 440 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY); 441 return; 442 } 443 444 entry->controller->Record(); 445 audio_log_->OnStarted(stream_id); 446} 447 448void AudioInputRendererHost::OnCloseStream(int stream_id) { 449 DCHECK_CURRENTLY_ON(BrowserThread::IO); 450 LogMessage(stream_id, "OnCloseStream", true); 451 452 AudioEntry* entry = LookupById(stream_id); 453 454 if (entry) 455 CloseAndDeleteStream(entry); 456} 457 458void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) { 459 DCHECK_CURRENTLY_ON(BrowserThread::IO); 460 461 AudioEntry* entry = LookupById(stream_id); 462 if (!entry) { 463 SendErrorMessage(stream_id, INVALID_AUDIO_ENTRY); 464 return; 465 } 466 467 entry->controller->SetVolume(volume); 468 audio_log_->OnSetVolume(stream_id, volume); 469} 470 471void AudioInputRendererHost::SendErrorMessage( 472 int stream_id, ErrorCode error_code) { 473 std::string err_msg = 474 base::StringPrintf("SendErrorMessage(error_code=%d)", error_code); 475 LogMessage(stream_id, err_msg, true); 476 477 Send(new AudioInputMsg_NotifyStreamStateChanged( 478 stream_id, media::AudioInputIPCDelegate::kError)); 479} 480 481void AudioInputRendererHost::DeleteEntries() { 482 DCHECK_CURRENTLY_ON(BrowserThread::IO); 483 484 for (AudioEntryMap::iterator i = audio_entries_.begin(); 485 i != audio_entries_.end(); ++i) { 486 CloseAndDeleteStream(i->second); 487 } 488} 489 490void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) { 491 DCHECK_CURRENTLY_ON(BrowserThread::IO); 492 493 if (!entry->pending_close) { 494 LogMessage(entry->stream_id, "CloseAndDeleteStream", true); 495 entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry, 496 this, entry)); 497 entry->pending_close = true; 498 audio_log_->OnClosed(entry->stream_id); 499 } 500} 501 502void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) { 503 DCHECK_CURRENTLY_ON(BrowserThread::IO); 504 LogMessage(entry->stream_id, "DeleteEntry: stream is now closed", true); 505 506#if defined(OS_CHROMEOS) 507 if (entry->has_keyboard_mic_) { 508 media_stream_manager_->audio_input_device_manager() 509 ->UnregisterKeyboardMicStream(); 510 } 511#endif 512 513 // Delete the entry when this method goes out of scope. 514 scoped_ptr<AudioEntry> entry_deleter(entry); 515 516 // Erase the entry from the map. 517 audio_entries_.erase(entry->stream_id); 518} 519 520void AudioInputRendererHost::DeleteEntryOnError(AudioEntry* entry, 521 ErrorCode error_code) { 522 DCHECK_CURRENTLY_ON(BrowserThread::IO); 523 524 // Sends the error message first before we close the stream because 525 // |entry| is destroyed in DeleteEntry(). 526 SendErrorMessage(entry->stream_id, error_code); 527 CloseAndDeleteStream(entry); 528} 529 530AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupById( 531 int stream_id) { 532 DCHECK_CURRENTLY_ON(BrowserThread::IO); 533 534 AudioEntryMap::iterator i = audio_entries_.find(stream_id); 535 if (i != audio_entries_.end()) 536 return i->second; 537 return NULL; 538} 539 540AudioInputRendererHost::AudioEntry* AudioInputRendererHost::LookupByController( 541 media::AudioInputController* controller) { 542 DCHECK_CURRENTLY_ON(BrowserThread::IO); 543 544 // Iterate the map of entries. 545 // TODO(hclam): Implement a faster look up method. 546 for (AudioEntryMap::iterator i = audio_entries_.begin(); 547 i != audio_entries_.end(); ++i) { 548 if (controller == i->second->controller.get()) 549 return i->second; 550 } 551 return NULL; 552} 553 554void AudioInputRendererHost::MaybeUnregisterKeyboardMicStream( 555 const AudioInputHostMsg_CreateStream_Config& config) { 556#if defined(OS_CHROMEOS) 557 if (config.params.channel_layout() == 558 media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) { 559 media_stream_manager_->audio_input_device_manager() 560 ->UnregisterKeyboardMicStream(); 561 } 562#endif 563} 564 565} // namespace content 566