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/renderer/media/webrtc_audio_capturer.h" 6 7 #include "base/bind.h" 8 #include "base/logging.h" 9 #include "base/metrics/histogram.h" 10 #include "base/strings/string_util.h" 11 #include "base/strings/stringprintf.h" 12 #include "content/child/child_process.h" 13 #include "content/renderer/media/audio_device_factory.h" 14 #include "content/renderer/media/webrtc_audio_device_impl.h" 15 #include "content/renderer/media/webrtc_local_audio_track.h" 16 #include "content/renderer/media/webrtc_logging.h" 17 #include "media/audio/sample_rates.h" 18 19 namespace content { 20 21 namespace { 22 23 // Supported hardware sample rates for input and output sides. 24 #if defined(OS_WIN) || defined(OS_MACOSX) 25 // media::GetAudioInputHardwareSampleRate() asks the audio layer 26 // for its current sample rate (set by the user) on Windows and Mac OS X. 27 // The listed rates below adds restrictions and WebRtcAudioDeviceImpl::Init() 28 // will fail if the user selects any rate outside these ranges. 29 const int kValidInputRates[] = {96000, 48000, 44100, 32000, 16000, 8000}; 30 #elif defined(OS_LINUX) || defined(OS_OPENBSD) 31 const int kValidInputRates[] = {48000, 44100}; 32 #elif defined(OS_ANDROID) 33 const int kValidInputRates[] = {48000, 44100}; 34 #else 35 const int kValidInputRates[] = {44100}; 36 #endif 37 38 } // namespace 39 40 // Reference counted container of WebRtcLocalAudioTrack delegate. 41 class WebRtcAudioCapturer::TrackOwner 42 : public base::RefCountedThreadSafe<WebRtcAudioCapturer::TrackOwner> { 43 public: 44 explicit TrackOwner(WebRtcLocalAudioTrack* track) 45 : delegate_(track) {} 46 47 void Capture(media::AudioBus* audio_source, 48 int audio_delay_milliseconds, 49 double volume, 50 bool key_pressed) { 51 base::AutoLock lock(lock_); 52 if (delegate_) { 53 delegate_->Capture(audio_source, 54 audio_delay_milliseconds, 55 volume, 56 key_pressed); 57 } 58 } 59 60 void OnSetFormat(const media::AudioParameters& params) { 61 base::AutoLock lock(lock_); 62 if (delegate_) 63 delegate_->OnSetFormat(params); 64 } 65 66 void Reset() { 67 base::AutoLock lock(lock_); 68 delegate_ = NULL; 69 } 70 71 void Stop() { 72 base::AutoLock lock(lock_); 73 DCHECK(delegate_); 74 75 // This can be reentrant so reset |delegate_| before calling out. 76 WebRtcLocalAudioTrack* temp = delegate_; 77 delegate_ = NULL; 78 temp->Stop(); 79 } 80 81 // Wrapper which allows to use std::find_if() when adding and removing 82 // sinks to/from the list. 83 struct TrackWrapper { 84 TrackWrapper(WebRtcLocalAudioTrack* track) : track_(track) {} 85 bool operator()( 86 const scoped_refptr<WebRtcAudioCapturer::TrackOwner>& owner) const { 87 return owner->IsEqual(track_); 88 } 89 WebRtcLocalAudioTrack* track_; 90 }; 91 92 protected: 93 virtual ~TrackOwner() {} 94 95 private: 96 friend class base::RefCountedThreadSafe<WebRtcAudioCapturer::TrackOwner>; 97 98 bool IsEqual(const WebRtcLocalAudioTrack* other) const { 99 base::AutoLock lock(lock_); 100 return (other == delegate_); 101 } 102 103 // Do NOT reference count the |delegate_| to avoid cyclic reference counting. 104 WebRtcLocalAudioTrack* delegate_; 105 mutable base::Lock lock_; 106 107 DISALLOW_COPY_AND_ASSIGN(TrackOwner); 108 }; 109 110 // static 111 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { 112 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); 113 return capturer; 114 } 115 116 void WebRtcAudioCapturer::Reconfigure(int sample_rate, 117 media::ChannelLayout channel_layout, 118 int effects) { 119 DCHECK(thread_checker_.CalledOnValidThread()); 120 int buffer_size = GetBufferSize(sample_rate); 121 DVLOG(1) << "Using WebRTC input buffer size: " << buffer_size; 122 123 media::AudioParameters::Format format = 124 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; 125 126 // bits_per_sample is always 16 for now. 127 int bits_per_sample = 16; 128 media::AudioParameters params(format, channel_layout, 0, sample_rate, 129 bits_per_sample, buffer_size, effects); 130 { 131 base::AutoLock auto_lock(lock_); 132 params_ = params; 133 134 // Notify all tracks about the new format. 135 tracks_.TagAll(); 136 } 137 } 138 139 bool WebRtcAudioCapturer::Initialize(int render_view_id, 140 media::ChannelLayout channel_layout, 141 int sample_rate, 142 int buffer_size, 143 int session_id, 144 const std::string& device_id, 145 int paired_output_sample_rate, 146 int paired_output_frames_per_buffer, 147 int effects) { 148 DCHECK(thread_checker_.CalledOnValidThread()); 149 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; 150 151 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; 152 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout", 153 channel_layout, media::CHANNEL_LAYOUT_MAX); 154 155 WebRtcLogMessage(base::StringPrintf( 156 "WAC::Initialize. render_view_id=%d" 157 ", channel_layout=%d, sample_rate=%d, buffer_size=%d" 158 ", session_id=%d, paired_output_sample_rate=%d" 159 ", paired_output_frames_per_buffer=%d", 160 render_view_id, 161 channel_layout, 162 sample_rate, 163 buffer_size, 164 session_id, 165 paired_output_sample_rate, 166 paired_output_frames_per_buffer)); 167 168 render_view_id_ = render_view_id; 169 session_id_ = session_id; 170 device_id_ = device_id; 171 hardware_buffer_size_ = buffer_size; 172 output_sample_rate_ = paired_output_sample_rate; 173 output_frames_per_buffer_= paired_output_frames_per_buffer; 174 175 if (render_view_id == -1) { 176 // Return true here to allow injecting a new source via SetCapturerSource() 177 // at a later state. 178 return true; 179 } 180 181 // Verify that the reported input channel configuration is supported. 182 if (channel_layout != media::CHANNEL_LAYOUT_MONO && 183 channel_layout != media::CHANNEL_LAYOUT_STEREO) { 184 DLOG(ERROR) << channel_layout 185 << " is not a supported input channel configuration."; 186 return false; 187 } 188 189 DVLOG(1) << "Audio input hardware sample rate: " << sample_rate; 190 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); 191 if (asr != media::kUnexpectedAudioSampleRate) { 192 UMA_HISTOGRAM_ENUMERATION( 193 "WebRTC.AudioInputSampleRate", asr, media::kUnexpectedAudioSampleRate); 194 } else { 195 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected", sample_rate); 196 } 197 198 // Verify that the reported input hardware sample rate is supported 199 // on the current platform. 200 if (std::find(&kValidInputRates[0], 201 &kValidInputRates[0] + arraysize(kValidInputRates), 202 sample_rate) == 203 &kValidInputRates[arraysize(kValidInputRates)]) { 204 DLOG(ERROR) << sample_rate << " is not a supported input rate."; 205 return false; 206 } 207 208 // Create and configure the default audio capturing source. The |source_| 209 // will be overwritten if an external client later calls SetCapturerSource() 210 // providing an alternative media::AudioCapturerSource. 211 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id), 212 channel_layout, 213 static_cast<float>(sample_rate), 214 effects); 215 216 return true; 217 } 218 219 WebRtcAudioCapturer::WebRtcAudioCapturer() 220 : running_(false), 221 render_view_id_(-1), 222 hardware_buffer_size_(0), 223 session_id_(0), 224 volume_(0), 225 peer_connection_mode_(false), 226 output_sample_rate_(0), 227 output_frames_per_buffer_(0), 228 key_pressed_(false) { 229 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; 230 } 231 232 WebRtcAudioCapturer::~WebRtcAudioCapturer() { 233 DCHECK(thread_checker_.CalledOnValidThread()); 234 DCHECK(tracks_.IsEmpty()); 235 DCHECK(!running_); 236 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; 237 } 238 239 void WebRtcAudioCapturer::AddTrack(WebRtcLocalAudioTrack* track) { 240 DCHECK(track); 241 DVLOG(1) << "WebRtcAudioCapturer::AddTrack()"; 242 243 { 244 base::AutoLock auto_lock(lock_); 245 // Verify that |track| is not already added to the list. 246 DCHECK(!tracks_.Contains(TrackOwner::TrackWrapper(track))); 247 248 // Add with a tag, so we remember to call OnSetFormat() on the new 249 // track. 250 scoped_refptr<TrackOwner> track_owner(new TrackOwner(track)); 251 tracks_.AddAndTag(track_owner); 252 } 253 254 // Start the source if the first audio track is connected to the capturer. 255 // Start() will do nothing if the capturer has already been started. 256 Start(); 257 258 } 259 260 void WebRtcAudioCapturer::RemoveTrack(WebRtcLocalAudioTrack* track) { 261 DCHECK(thread_checker_.CalledOnValidThread()); 262 263 bool stop_source = false; 264 { 265 base::AutoLock auto_lock(lock_); 266 267 scoped_refptr<TrackOwner> removed_item = 268 tracks_.Remove(TrackOwner::TrackWrapper(track)); 269 270 // Clear the delegate to ensure that no more capture callbacks will 271 // be sent to this sink. Also avoids a possible crash which can happen 272 // if this method is called while capturing is active. 273 if (removed_item.get()) 274 removed_item->Reset(); 275 276 // Stop the source if the last audio track is going away. 277 stop_source = tracks_.IsEmpty(); 278 } 279 280 if (stop_source) 281 Stop(); 282 } 283 284 void WebRtcAudioCapturer::SetCapturerSource( 285 const scoped_refptr<media::AudioCapturerSource>& source, 286 media::ChannelLayout channel_layout, 287 float sample_rate, 288 int effects) { 289 DCHECK(thread_checker_.CalledOnValidThread()); 290 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," 291 << "sample_rate=" << sample_rate << ")"; 292 scoped_refptr<media::AudioCapturerSource> old_source; 293 bool restart_source = false; 294 { 295 base::AutoLock auto_lock(lock_); 296 if (source_.get() == source.get()) 297 return; 298 299 source_.swap(old_source); 300 source_ = source; 301 302 // Reset the flag to allow starting the new source. 303 restart_source = running_; 304 running_ = false; 305 } 306 307 DVLOG(1) << "Switching to a new capture source."; 308 if (old_source.get()) 309 old_source->Stop(); 310 311 // Dispatch the new parameters both to the sink(s) and to the new source. 312 // The idea is to get rid of any dependency of the microphone parameters 313 // which would normally be used by default. 314 Reconfigure(sample_rate, channel_layout, effects); 315 316 // Make sure to grab the new parameters in case they were reconfigured. 317 media::AudioParameters params = audio_parameters(); 318 if (source.get()) 319 source->Initialize(params, this, session_id_); 320 321 if (restart_source) 322 Start(); 323 } 324 325 void WebRtcAudioCapturer::EnablePeerConnectionMode() { 326 DCHECK(thread_checker_.CalledOnValidThread()); 327 DVLOG(1) << "EnablePeerConnectionMode"; 328 // Do nothing if the peer connection mode has been enabled. 329 if (peer_connection_mode_) 330 return; 331 332 peer_connection_mode_ = true; 333 int render_view_id = -1; 334 { 335 base::AutoLock auto_lock(lock_); 336 // Simply return if there is no existing source or the |render_view_id_| is 337 // not valid. 338 if (!source_.get() || render_view_id_== -1) 339 return; 340 341 render_view_id = render_view_id_; 342 } 343 344 // Do nothing if the current buffer size is the WebRtc native buffer size. 345 media::AudioParameters params = audio_parameters(); 346 if (GetBufferSize(params.sample_rate()) == params.frames_per_buffer()) 347 return; 348 349 // Create a new audio stream as source which will open the hardware using 350 // WebRtc native buffer size. 351 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id), 352 params.channel_layout(), 353 static_cast<float>(params.sample_rate()), 354 params.effects()); 355 } 356 357 void WebRtcAudioCapturer::Start() { 358 DVLOG(1) << "WebRtcAudioCapturer::Start()"; 359 base::AutoLock auto_lock(lock_); 360 if (running_ || !source_) 361 return; 362 363 // Start the data source, i.e., start capturing data from the current source. 364 // We need to set the AGC control before starting the stream. 365 source_->SetAutomaticGainControl(true); 366 source_->Start(); 367 running_ = true; 368 } 369 370 void WebRtcAudioCapturer::Stop() { 371 DVLOG(1) << "WebRtcAudioCapturer::Stop()"; 372 scoped_refptr<media::AudioCapturerSource> source; 373 TrackList::ItemList tracks; 374 { 375 base::AutoLock auto_lock(lock_); 376 if (!running_) 377 return; 378 379 source = source_; 380 tracks = tracks_.Items(); 381 tracks_.Clear(); 382 running_ = false; 383 } 384 385 for (TrackList::ItemList::const_iterator it = tracks.begin(); 386 it != tracks.end(); 387 ++it) { 388 (*it)->Stop(); 389 } 390 391 if (source.get()) 392 source->Stop(); 393 } 394 395 void WebRtcAudioCapturer::SetVolume(int volume) { 396 DVLOG(1) << "WebRtcAudioCapturer::SetVolume()"; 397 DCHECK_LE(volume, MaxVolume()); 398 double normalized_volume = static_cast<double>(volume) / MaxVolume(); 399 base::AutoLock auto_lock(lock_); 400 if (source_.get()) 401 source_->SetVolume(normalized_volume); 402 } 403 404 int WebRtcAudioCapturer::Volume() const { 405 base::AutoLock auto_lock(lock_); 406 return volume_; 407 } 408 409 int WebRtcAudioCapturer::MaxVolume() const { 410 return WebRtcAudioDeviceImpl::kMaxVolumeLevel; 411 } 412 413 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, 414 int audio_delay_milliseconds, 415 double volume, 416 bool key_pressed) { 417 // This callback is driven by AudioInputDevice::AudioThreadCallback if 418 // |source_| is AudioInputDevice, otherwise it is driven by client's 419 // CaptureCallback. 420 #if defined(OS_WIN) || defined(OS_MACOSX) 421 DCHECK_LE(volume, 1.0); 422 #elif defined(OS_LINUX) || defined(OS_OPENBSD) 423 // We have a special situation on Linux where the microphone volume can be 424 // "higher than maximum". The input volume slider in the sound preference 425 // allows the user to set a scaling that is higher than 100%. It means that 426 // even if the reported maximum levels is N, the actual microphone level can 427 // go up to 1.5x*N and that corresponds to a normalized |volume| of 1.5x. 428 DCHECK_LE(volume, 1.6); 429 #endif 430 431 TrackList::ItemList tracks; 432 TrackList::ItemList tracks_to_notify_format; 433 int current_volume = 0; 434 media::AudioParameters params; 435 { 436 base::AutoLock auto_lock(lock_); 437 if (!running_) 438 return; 439 440 // Map internal volume range of [0.0, 1.0] into [0, 255] used by the 441 // webrtc::VoiceEngine. webrtc::VoiceEngine will handle the case when the 442 // volume is higher than 255. 443 volume_ = static_cast<int>((volume * MaxVolume()) + 0.5); 444 current_volume = volume_; 445 audio_delay_ = base::TimeDelta::FromMilliseconds(audio_delay_milliseconds); 446 key_pressed_ = key_pressed; 447 tracks = tracks_.Items(); 448 tracks_.RetrieveAndClearTags(&tracks_to_notify_format); 449 450 CHECK(params_.IsValid()); 451 CHECK_EQ(audio_source->channels(), params_.channels()); 452 CHECK_EQ(audio_source->frames(), params_.frames_per_buffer()); 453 params = params_; 454 } 455 456 // Notify the tracks on when the format changes. This will do nothing if 457 // |tracks_to_notify_format| is empty. 458 for (TrackList::ItemList::const_iterator it = tracks_to_notify_format.begin(); 459 it != tracks_to_notify_format.end(); ++it) { 460 (*it)->OnSetFormat(params); 461 } 462 463 // Feed the data to the tracks. 464 for (TrackList::ItemList::const_iterator it = tracks.begin(); 465 it != tracks.end(); 466 ++it) { 467 (*it)->Capture(audio_source, audio_delay_milliseconds, 468 current_volume, key_pressed); 469 } 470 } 471 472 void WebRtcAudioCapturer::OnCaptureError() { 473 NOTIMPLEMENTED(); 474 } 475 476 media::AudioParameters WebRtcAudioCapturer::audio_parameters() const { 477 base::AutoLock auto_lock(lock_); 478 return params_; 479 } 480 481 bool WebRtcAudioCapturer::GetPairedOutputParameters( 482 int* session_id, 483 int* output_sample_rate, 484 int* output_frames_per_buffer) const { 485 // Don't set output parameters unless all of them are valid. 486 if (session_id_ <= 0 || !output_sample_rate_ || !output_frames_per_buffer_) 487 return false; 488 489 *session_id = session_id_; 490 *output_sample_rate = output_sample_rate_; 491 *output_frames_per_buffer = output_frames_per_buffer_; 492 493 return true; 494 } 495 496 int WebRtcAudioCapturer::GetBufferSize(int sample_rate) const { 497 DCHECK(thread_checker_.CalledOnValidThread()); 498 #if defined(OS_ANDROID) 499 // TODO(henrika): Tune and adjust buffer size on Android. 500 return (2 * sample_rate / 100); 501 #endif 502 503 // PeerConnection is running at a buffer size of 10ms data. A multiple of 504 // 10ms as the buffer size can give the best performance to PeerConnection. 505 int peer_connection_buffer_size = sample_rate / 100; 506 507 // Use the native hardware buffer size in non peer connection mode when the 508 // platform is using a native buffer size smaller than the PeerConnection 509 // buffer size. 510 if (!peer_connection_mode_ && hardware_buffer_size_ && 511 hardware_buffer_size_ <= peer_connection_buffer_size) { 512 return hardware_buffer_size_; 513 } 514 515 return (sample_rate / 100); 516 } 517 518 void WebRtcAudioCapturer::GetAudioProcessingParams( 519 base::TimeDelta* delay, int* volume, bool* key_pressed) { 520 base::AutoLock auto_lock(lock_); 521 *delay = audio_delay_; 522 *volume = volume_; 523 *key_pressed = key_pressed_; 524 } 525 526 } // namespace content 527