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