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