1 // Copyright 2013 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/renderer/media/webrtc_audio_device_impl.h" 6 7 #include "base/bind.h" 8 #include "base/metrics/histogram.h" 9 #include "base/strings/string_util.h" 10 #include "base/win/windows_version.h" 11 #include "content/renderer/media/webrtc_audio_capturer.h" 12 #include "content/renderer/media/webrtc_audio_renderer.h" 13 #include "content/renderer/render_thread_impl.h" 14 #include "media/audio/audio_parameters.h" 15 #include "media/audio/audio_util.h" 16 #include "media/audio/sample_rates.h" 17 18 using media::AudioParameters; 19 using media::ChannelLayout; 20 21 namespace content { 22 23 WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl() 24 : ref_count_(0), 25 audio_transport_callback_(NULL), 26 input_delay_ms_(0), 27 output_delay_ms_(0), 28 initialized_(false), 29 playing_(false), 30 recording_(false), 31 agc_is_enabled_(false), 32 microphone_volume_(0) { 33 DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()"; 34 } 35 36 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() { 37 DVLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()"; 38 DCHECK(thread_checker_.CalledOnValidThread()); 39 Terminate(); 40 } 41 42 int32_t WebRtcAudioDeviceImpl::AddRef() { 43 DCHECK(thread_checker_.CalledOnValidThread()); 44 return base::subtle::Barrier_AtomicIncrement(&ref_count_, 1); 45 } 46 47 int32_t WebRtcAudioDeviceImpl::Release() { 48 DCHECK(thread_checker_.CalledOnValidThread()); 49 int ret = base::subtle::Barrier_AtomicIncrement(&ref_count_, -1); 50 if (ret == 0) { 51 delete this; 52 } 53 return ret; 54 } 55 int WebRtcAudioDeviceImpl::CaptureData(const std::vector<int>& channels, 56 const int16* audio_data, 57 int sample_rate, 58 int number_of_channels, 59 int number_of_frames, 60 int audio_delay_milliseconds, 61 int current_volume, 62 bool need_audio_processing) { 63 int total_delay_ms = 0; 64 { 65 base::AutoLock auto_lock(lock_); 66 // Return immediately when not recording or |channels| is empty. 67 // See crbug.com/274017: renderer crash dereferencing invalid channels[0]. 68 if (!recording_ || channels.empty()) 69 return 0; 70 71 // Store the reported audio delay locally. 72 input_delay_ms_ = audio_delay_milliseconds; 73 total_delay_ms = input_delay_ms_ + output_delay_ms_; 74 DVLOG(2) << "total delay: " << input_delay_ms_ + output_delay_ms_; 75 } 76 77 // Write audio samples in blocks of 10 milliseconds to the registered 78 // webrtc::AudioTransport sink. Keep writing until our internal byte 79 // buffer is empty. 80 // TODO(niklase): Wire up the key press detection. 81 const int16* audio_buffer = audio_data; 82 const int samples_per_10_msec = (sample_rate / 100); 83 int accumulated_audio_samples = 0; 84 bool key_pressed = false; 85 uint32_t new_volume = 0; 86 while (accumulated_audio_samples < number_of_frames) { 87 // Deliver 10ms of recorded 16-bit linear PCM audio. 88 int new_mic_level = audio_transport_callback_->OnDataAvailable( 89 &channels[0], 90 channels.size(), 91 audio_buffer, 92 sample_rate, 93 number_of_channels, 94 samples_per_10_msec, 95 total_delay_ms, 96 current_volume, 97 key_pressed, 98 need_audio_processing); 99 100 accumulated_audio_samples += samples_per_10_msec; 101 audio_buffer += samples_per_10_msec * number_of_channels; 102 103 // The latest non-zero new microphone level will be returned. 104 if (new_mic_level) 105 new_volume = new_mic_level; 106 } 107 108 return new_volume; 109 } 110 111 void WebRtcAudioDeviceImpl::SetCaptureFormat( 112 const media::AudioParameters& params) { 113 DVLOG(1) << "WebRtcAudioDeviceImpl::SetCaptureFormat()"; 114 DCHECK(thread_checker_.CalledOnValidThread()); 115 } 116 117 void WebRtcAudioDeviceImpl::RenderData(uint8* audio_data, 118 int number_of_channels, 119 int number_of_frames, 120 int audio_delay_milliseconds) { 121 DCHECK_LE(number_of_frames, output_buffer_size()); 122 { 123 base::AutoLock auto_lock(lock_); 124 // Store the reported audio delay locally. 125 output_delay_ms_ = audio_delay_milliseconds; 126 } 127 128 const int channels = number_of_channels; 129 DCHECK_LE(channels, output_channels()); 130 131 int samples_per_sec = output_sample_rate(); 132 int samples_per_10_msec = (samples_per_sec / 100); 133 int bytes_per_sample = output_audio_parameters_.bits_per_sample() / 8; 134 const int bytes_per_10_msec = 135 channels * samples_per_10_msec * bytes_per_sample; 136 137 uint32_t num_audio_samples = 0; 138 int accumulated_audio_samples = 0; 139 140 // Get audio samples in blocks of 10 milliseconds from the registered 141 // webrtc::AudioTransport source. Keep reading until our internal buffer 142 // is full. 143 while (accumulated_audio_samples < number_of_frames) { 144 // Get 10ms and append output to temporary byte buffer. 145 audio_transport_callback_->NeedMorePlayData(samples_per_10_msec, 146 bytes_per_sample, 147 channels, 148 samples_per_sec, 149 audio_data, 150 num_audio_samples); 151 accumulated_audio_samples += num_audio_samples; 152 audio_data += bytes_per_10_msec; 153 } 154 } 155 156 void WebRtcAudioDeviceImpl::SetRenderFormat(const AudioParameters& params) { 157 DCHECK(thread_checker_.CalledOnValidThread()); 158 output_audio_parameters_ = params; 159 } 160 161 void WebRtcAudioDeviceImpl::RemoveAudioRenderer(WebRtcAudioRenderer* renderer) { 162 DCHECK(thread_checker_.CalledOnValidThread()); 163 DCHECK_EQ(renderer, renderer_); 164 base::AutoLock auto_lock(lock_); 165 renderer_ = NULL; 166 playing_ = false; 167 } 168 169 int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback( 170 webrtc::AudioTransport* audio_callback) { 171 DVLOG(1) << "WebRtcAudioDeviceImpl::RegisterAudioCallback()"; 172 DCHECK(thread_checker_.CalledOnValidThread()); 173 DCHECK_EQ(audio_transport_callback_ == NULL, audio_callback != NULL); 174 audio_transport_callback_ = audio_callback; 175 return 0; 176 } 177 178 int32_t WebRtcAudioDeviceImpl::Init() { 179 DVLOG(1) << "WebRtcAudioDeviceImpl::Init()"; 180 DCHECK(thread_checker_.CalledOnValidThread()); 181 182 // We need to return a success to continue the initialization of WebRtc VoE 183 // because failure on the capturer_ initialization should not prevent WebRTC 184 // from working. See issue http://crbug.com/144421 for details. 185 initialized_ = true; 186 187 return 0; 188 } 189 190 int32_t WebRtcAudioDeviceImpl::Terminate() { 191 DVLOG(1) << "WebRtcAudioDeviceImpl::Terminate()"; 192 DCHECK(thread_checker_.CalledOnValidThread()); 193 194 // Calling Terminate() multiple times in a row is OK. 195 if (!initialized_) 196 return 0; 197 198 StopRecording(); 199 StopPlayout(); 200 201 // It is necessary to stop the |renderer_| before going away. 202 if (renderer_.get()) { 203 // Grab a local reference while we call Stop(), which will trigger a call to 204 // RemoveAudioRenderer that clears our reference to the audio renderer. 205 scoped_refptr<WebRtcAudioRenderer> local_renderer(renderer_); 206 local_renderer->Stop(); 207 DCHECK(!renderer_.get()); 208 } 209 210 capturers_.clear(); 211 212 initialized_ = false; 213 return 0; 214 } 215 216 bool WebRtcAudioDeviceImpl::Initialized() const { 217 return initialized_; 218 } 219 220 int32_t WebRtcAudioDeviceImpl::PlayoutIsAvailable(bool* available) { 221 *available = initialized_; 222 return 0; 223 } 224 225 bool WebRtcAudioDeviceImpl::PlayoutIsInitialized() const { 226 return initialized_; 227 } 228 229 int32_t WebRtcAudioDeviceImpl::RecordingIsAvailable(bool* available) { 230 *available = (!capturers_.empty()); 231 return 0; 232 } 233 234 bool WebRtcAudioDeviceImpl::RecordingIsInitialized() const { 235 DVLOG(1) << "WebRtcAudioDeviceImpl::RecordingIsInitialized()"; 236 DCHECK(thread_checker_.CalledOnValidThread()); 237 return (!capturers_.empty()); 238 } 239 240 int32_t WebRtcAudioDeviceImpl::StartPlayout() { 241 DVLOG(1) << "WebRtcAudioDeviceImpl::StartPlayout()"; 242 LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing"; 243 { 244 base::AutoLock auto_lock(lock_); 245 if (!audio_transport_callback_) 246 return 0; 247 } 248 249 if (playing_) { 250 // webrtc::VoiceEngine assumes that it is OK to call Start() twice and 251 // that the call is ignored the second time. 252 return 0; 253 } 254 255 playing_ = true; 256 start_render_time_ = base::Time::Now(); 257 return 0; 258 } 259 260 int32_t WebRtcAudioDeviceImpl::StopPlayout() { 261 DVLOG(1) << "WebRtcAudioDeviceImpl::StopPlayout()"; 262 if (!playing_) { 263 // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case. 264 return 0; 265 } 266 267 // Add histogram data to be uploaded as part of an UMA logging event. 268 // This histogram keeps track of total playout times. 269 if (!start_render_time_.is_null()) { 270 base::TimeDelta render_time = base::Time::Now() - start_render_time_; 271 UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioRenderTime", render_time); 272 } 273 274 playing_ = false; 275 return 0; 276 } 277 278 bool WebRtcAudioDeviceImpl::Playing() const { 279 return playing_; 280 } 281 282 int32_t WebRtcAudioDeviceImpl::StartRecording() { 283 DVLOG(1) << "WebRtcAudioDeviceImpl::StartRecording()"; 284 DCHECK(initialized_); 285 LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing"; 286 if (!audio_transport_callback_) { 287 return -1; 288 } 289 290 { 291 base::AutoLock auto_lock(lock_); 292 if (recording_) 293 return 0; 294 295 recording_ = true; 296 } 297 298 start_capture_time_ = base::Time::Now(); 299 300 return 0; 301 } 302 303 int32_t WebRtcAudioDeviceImpl::StopRecording() { 304 DVLOG(1) << "WebRtcAudioDeviceImpl::StopRecording()"; 305 { 306 base::AutoLock auto_lock(lock_); 307 if (!recording_) 308 return 0; 309 310 recording_ = false; 311 } 312 313 // Add histogram data to be uploaded as part of an UMA logging event. 314 // This histogram keeps track of total recording times. 315 if (!start_capture_time_.is_null()) { 316 base::TimeDelta capture_time = base::Time::Now() - start_capture_time_; 317 UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioCaptureTime", capture_time); 318 } 319 320 return 0; 321 } 322 323 bool WebRtcAudioDeviceImpl::Recording() const { 324 base::AutoLock auto_lock(lock_); 325 return recording_; 326 } 327 328 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) { 329 DVLOG(1) << "WebRtcAudioDeviceImpl::SetAGC(enable=" << enable << ")"; 330 DCHECK(initialized_); 331 332 // Return early if we are not changing the AGC state. 333 if (enable == agc_is_enabled_) 334 return 0; 335 336 // Set the AGC on all the capturers. It depends on the source of the 337 // capturer whether AGC is supported or not. 338 // The current implementation does not support changing the AGC state while 339 // recording. Using this approach simplifies the design and it is also 340 // inline with the latest WebRTC standard. 341 for (CapturerList::const_iterator iter = capturers_.begin(); 342 iter != capturers_.end(); ++iter) { 343 if (!(*iter)->is_recording()) 344 (*iter)->SetAutomaticGainControl(enable); 345 } 346 347 agc_is_enabled_ = enable; 348 return 0; 349 } 350 351 bool WebRtcAudioDeviceImpl::AGC() const { 352 DVLOG(1) << "WebRtcAudioDeviceImpl::AGC()"; 353 DCHECK(thread_checker_.CalledOnValidThread()); 354 // To reduce the usage of IPC messages, an internal AGC state is used. 355 // TODO(henrika): investigate if there is a need for a "deeper" getter. 356 return agc_is_enabled_; 357 } 358 359 int32_t WebRtcAudioDeviceImpl::SetMicrophoneVolume(uint32_t volume) { 360 DVLOG(1) << "WebRtcAudioDeviceImpl::SetMicrophoneVolume(" << volume << ")"; 361 DCHECK(initialized_); 362 363 // Only one microphone is supported at the moment, which is represented by 364 // the default capturer. 365 scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer()); 366 if (!capturer.get()) 367 return -1; 368 369 capturer->SetVolume(volume); 370 return 0; 371 } 372 373 // TODO(henrika): sort out calling thread once we start using this API. 374 int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume) const { 375 DVLOG(1) << "WebRtcAudioDeviceImpl::MicrophoneVolume()"; 376 // We only support one microphone now, which is accessed via the default 377 // capturer. 378 DCHECK(initialized_); 379 scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer()); 380 if (!capturer.get()) 381 return -1; 382 383 *volume = static_cast<uint32_t>(capturer->Volume()); 384 return 0; 385 } 386 387 int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume(uint32_t* max_volume) const { 388 DCHECK(initialized_); 389 *max_volume = kMaxVolumeLevel; 390 return 0; 391 } 392 393 int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume(uint32_t* min_volume) const { 394 *min_volume = 0; 395 return 0; 396 } 397 398 int32_t WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable(bool* available) const { 399 DCHECK(initialized_); 400 *available = (output_channels() == 2); 401 return 0; 402 } 403 404 int32_t WebRtcAudioDeviceImpl::StereoRecordingIsAvailable( 405 bool* available) const { 406 DCHECK(initialized_); 407 // TODO(xians): These kind of hardware methods do not make much sense since we 408 // support multiple sources. Remove or figure out new APIs for such methods. 409 scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer()); 410 if (!capturer.get()) 411 return -1; 412 413 *available = (capturer->audio_parameters().channels() == 2); 414 return 0; 415 } 416 417 int32_t WebRtcAudioDeviceImpl::PlayoutDelay(uint16_t* delay_ms) const { 418 base::AutoLock auto_lock(lock_); 419 *delay_ms = static_cast<uint16_t>(output_delay_ms_); 420 return 0; 421 } 422 423 int32_t WebRtcAudioDeviceImpl::RecordingDelay(uint16_t* delay_ms) const { 424 base::AutoLock auto_lock(lock_); 425 *delay_ms = static_cast<uint16_t>(input_delay_ms_); 426 return 0; 427 } 428 429 int32_t WebRtcAudioDeviceImpl::RecordingSampleRate( 430 uint32_t* samples_per_sec) const { 431 // We use the default capturer as the recording sample rate. 432 scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer()); 433 if (!capturer.get()) 434 return -1; 435 436 *samples_per_sec = static_cast<uint32_t>( 437 capturer->audio_parameters().sample_rate()); 438 return 0; 439 } 440 441 int32_t WebRtcAudioDeviceImpl::PlayoutSampleRate( 442 uint32_t* samples_per_sec) const { 443 *samples_per_sec = static_cast<uint32_t>(output_sample_rate()); 444 return 0; 445 } 446 447 bool WebRtcAudioDeviceImpl::SetAudioRenderer(WebRtcAudioRenderer* renderer) { 448 DCHECK(thread_checker_.CalledOnValidThread()); 449 DCHECK(renderer); 450 451 base::AutoLock auto_lock(lock_); 452 if (renderer_.get()) 453 return false; 454 455 if (!renderer->Initialize(this)) 456 return false; 457 458 renderer_ = renderer; 459 return true; 460 } 461 462 void WebRtcAudioDeviceImpl::AddAudioCapturer( 463 const scoped_refptr<WebRtcAudioCapturer>& capturer) { 464 DVLOG(1) << "WebRtcAudioDeviceImpl::AddAudioCapturer()"; 465 DCHECK(thread_checker_.CalledOnValidThread()); 466 DCHECK(capturer.get()); 467 468 // Enable/disable the AGC on the new capture. 469 DCHECK(!capturer->is_recording()); 470 capturer->SetAutomaticGainControl(agc_is_enabled_); 471 472 // We only support one microphone today, which means the list can contain 473 // only one capturer with a valid device id. 474 DCHECK(capturer->device_id().empty() || !GetDefaultCapturer()); 475 base::AutoLock auto_lock(lock_); 476 capturers_.push_back(capturer); 477 } 478 479 scoped_refptr<WebRtcAudioCapturer> 480 WebRtcAudioDeviceImpl::GetDefaultCapturer() const { 481 base::AutoLock auto_lock(lock_); 482 for (CapturerList::const_iterator iter = capturers_.begin(); 483 iter != capturers_.end(); ++iter) { 484 if (!(*iter)->device_id().empty()) 485 return *iter; 486 } 487 488 return NULL; 489 } 490 491 } // namespace content 492