Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "content/renderer/media/webrtc_audio_capturer.h" | 5 #include "content/renderer/media/webrtc_audio_capturer.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 55 #endif | 55 #endif |
| 56 | 56 |
| 57 return buffer_size; | 57 return buffer_size; |
| 58 } | 58 } |
| 59 | 59 |
| 60 // static | 60 // static |
| 61 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { | 61 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { |
| 62 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); | 62 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); |
| 63 return capturer; | 63 return capturer; |
| 64 } | 64 } |
| 65 | 65 |
|
phoglund_chromium
2013/02/07 14:03:18
I found two places in the code which was doing rou
| |
| 66 bool WebRtcAudioCapturer::Reconfigure(int sample_rate, | |
| 67 media::AudioParameters::Format format, | |
| 68 media::ChannelLayout channel_layout) { | |
| 69 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | |
| 70 if (!buffer_size) { | |
| 71 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate; | |
| 72 return false; | |
| 73 } | |
| 74 | |
| 75 // Ignored since the audio stack uses float32. | |
|
tommi (sloooow) - chröme
2013/02/07 14:57:47
s/Ignored/bits_per_sample is always 16
| |
| 76 int bits_per_sample = 16; | |
| 77 | |
| 78 // These snapshots will allow us to run callbacks outside the lock. | |
| 79 SinkList sinks; | |
| 80 media::AudioParameters params; | |
| 81 { | |
| 82 base::AutoLock auto_lock(lock_); | |
| 83 | |
|
phoglund_chromium
2013/02/07 14:03:18
So along with copying parameters, this is the addi
| |
| 84 while (buffer_in_use_) | |
| 85 buffer_in_use_cv_.Wait(); | |
| 86 | |
|
phoglund_chromium
2013/02/07 14:03:18
Once the buffer is not used, we can move ahead wit
| |
| 87 params_.Reset(format, channel_layout, sample_rate, bits_per_sample, | |
| 88 buffer_size); | |
| 89 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]); | |
| 90 | |
| 91 sinks = sinks_; | |
| 92 params = params_; | |
| 93 } | |
| 94 | |
| 95 // Tell all sinks which format we use. | |
| 96 for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it) { | |
| 97 (*it)->SetCaptureFormat(params); | |
| 98 } | |
| 99 | |
| 100 return true; | |
| 101 } | |
| 102 | |
| 66 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, | 103 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, |
| 67 int sample_rate) { | 104 int sample_rate) { |
| 68 DCHECK(thread_checker_.CalledOnValidThread()); | 105 DCHECK(thread_checker_.CalledOnValidThread()); |
| 69 DCHECK(!sinks_.empty()); | 106 DCHECK(!sinks_.empty()); |
| 70 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; | 107 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; |
| 71 | 108 |
| 72 media::AudioParameters::Format format = | 109 media::AudioParameters::Format format = |
| 73 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; | 110 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; |
| 74 | 111 |
| 75 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; | 112 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 90 | 127 |
| 91 // Verify that the reported input hardware sample rate is supported | 128 // Verify that the reported input hardware sample rate is supported |
| 92 // on the current platform. | 129 // on the current platform. |
| 93 if (std::find(&kValidInputRates[0], | 130 if (std::find(&kValidInputRates[0], |
| 94 &kValidInputRates[0] + arraysize(kValidInputRates), | 131 &kValidInputRates[0] + arraysize(kValidInputRates), |
| 95 sample_rate) == | 132 sample_rate) == |
| 96 &kValidInputRates[arraysize(kValidInputRates)]) { | 133 &kValidInputRates[arraysize(kValidInputRates)]) { |
| 97 DLOG(ERROR) << sample_rate << " is not a supported input rate."; | 134 DLOG(ERROR) << sample_rate << " is not a supported input rate."; |
| 98 return false; | 135 return false; |
| 99 } | 136 } |
| 100 | 137 |
|
phoglund_chromium
2013/02/07 14:03:18
This code didn't check if GetBufferSize... returne
| |
| 101 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | 138 if (!Reconfigure(sample_rate, format, channel_layout)) |
| 102 | 139 return false; |
| 103 // Configure audio parameters for the default source. | |
| 104 params_.Reset(format, channel_layout, sample_rate, 16, buffer_size); | |
| 105 | |
| 106 // Tell all sinks which format we use. | |
| 107 for (SinkList::const_iterator it = sinks_.begin(); | |
| 108 it != sinks_.end(); ++it) { | |
| 109 (*it)->SetCaptureFormat(params_); | |
| 110 } | |
| 111 | |
| 112 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]); | |
| 113 | 140 |
| 114 // Create and configure the default audio capturing source. The |source_| | 141 // Create and configure the default audio capturing source. The |source_| |
| 115 // will be overwritten if an external client later calls SetCapturerSource() | 142 // will be overwritten if an external client later calls SetCapturerSource() |
| 116 // providing an alternaive media::AudioCapturerSource. | 143 // providing an alternaive media::AudioCapturerSource. |
| 117 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), | 144 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), |
| 118 channel_layout, | 145 channel_layout, |
| 119 static_cast<float>(sample_rate)); | 146 static_cast<float>(sample_rate)); |
| 120 | 147 |
| 121 return true; | 148 return true; |
| 122 } | 149 } |
| 123 | 150 |
| 124 WebRtcAudioCapturer::WebRtcAudioCapturer() | 151 WebRtcAudioCapturer::WebRtcAudioCapturer() |
| 125 : source_(NULL), | 152 : buffer_in_use_cv_(&lock_), |
| 153 buffer_in_use_(false), | |
| 154 source_(NULL), | |
| 126 running_(false), | 155 running_(false), |
| 127 buffering_(false) { | 156 buffering_(false) { |
| 128 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; | 157 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; |
| 129 } | 158 } |
| 130 | 159 |
| 131 WebRtcAudioCapturer::~WebRtcAudioCapturer() { | 160 WebRtcAudioCapturer::~WebRtcAudioCapturer() { |
| 132 DCHECK(thread_checker_.CalledOnValidThread()); | 161 DCHECK(thread_checker_.CalledOnValidThread()); |
| 133 DCHECK(sinks_.empty()); | 162 DCHECK(sinks_.empty()); |
| 134 DCHECK(!loopback_fifo_); | 163 DCHECK(!loopback_fifo_); |
| 135 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; | 164 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 155 } | 184 } |
| 156 } | 185 } |
| 157 | 186 |
| 158 void WebRtcAudioCapturer::SetCapturerSource( | 187 void WebRtcAudioCapturer::SetCapturerSource( |
| 159 const scoped_refptr<media::AudioCapturerSource>& source, | 188 const scoped_refptr<media::AudioCapturerSource>& source, |
| 160 media::ChannelLayout channel_layout, | 189 media::ChannelLayout channel_layout, |
| 161 float sample_rate) { | 190 float sample_rate) { |
| 162 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," | 191 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," |
| 163 << "sample_rate=" << sample_rate << ")"; | 192 << "sample_rate=" << sample_rate << ")"; |
| 164 scoped_refptr<media::AudioCapturerSource> old_source; | 193 scoped_refptr<media::AudioCapturerSource> old_source; |
| 194 media::AudioParameters::Format current_format; | |
| 165 { | 195 { |
| 166 base::AutoLock auto_lock(lock_); | 196 base::AutoLock auto_lock(lock_); |
| 167 if (source_ == source) | 197 if (source_ == source) |
| 168 return; | 198 return; |
| 169 | 199 |
| 170 source_.swap(old_source); | 200 source_.swap(old_source); |
| 171 source_ = source; | 201 source_ = source; |
|
phoglund_chromium
2013/02/07 14:03:18
We copy this here to avoid grabbing the lock again
| |
| 202 current_format = params_.format(); | |
| 172 } | 203 } |
| 173 | 204 |
| 174 const bool no_default_audio_source_exists = !buffer_.get(); | 205 const bool no_default_audio_source_exists = !buffer_.get(); |
| 175 | 206 |
| 176 // Detach the old source from normal recording or perform first-time | 207 // Detach the old source from normal recording or perform first-time |
| 177 // initialization if Initialize() has never been called. For the second | 208 // initialization if Initialize() has never been called. For the second |
| 178 // case, the caller is not "taking over an ongoing session" but instead | 209 // case, the caller is not "taking over an ongoing session" but instead |
| 179 // "taking control over a new session". | 210 // "taking control over a new session". |
| 180 if (old_source || no_default_audio_source_exists) { | 211 if (old_source || no_default_audio_source_exists) { |
| 181 DVLOG(1) << "New capture source will now be utilized."; | 212 DVLOG(1) << "New capture source will now be utilized."; |
| 182 if (old_source) | 213 if (old_source) |
| 183 old_source->Stop(); | 214 old_source->Stop(); |
| 184 | 215 |
| 185 // Dispatch the new parameters both to the sink(s) and to the new source. | 216 // Dispatch the new parameters both to the sink(s) and to the new source. |
| 186 // The idea is to get rid of any dependency of the microphone parameters | 217 // The idea is to get rid of any dependency of the microphone parameters |
| 187 // which would normally be used by default. | 218 // which would normally be used by default. |
| 188 | 219 if (!Reconfigure(sample_rate, current_format, channel_layout)) |
| 189 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | |
| 190 if (!buffer_size) { | |
| 191 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate; | |
| 192 return; | 220 return; |
| 193 } | |
| 194 | |
| 195 params_.Reset(params_.format(), | |
| 196 channel_layout, | |
| 197 sample_rate, | |
| 198 16, // ignored since the audio stack uses float32. | |
| 199 buffer_size); | |
| 200 | |
| 201 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]); | |
| 202 | |
| 203 for (SinkList::const_iterator it = sinks_.begin(); | |
| 204 it != sinks_.end(); ++it) { | |
| 205 (*it)->SetCaptureFormat(params_); | |
| 206 } | |
| 207 } | 221 } |
| 208 | 222 |
| 209 if (source) | 223 if (source) |
| 210 source->Initialize(params_, this, this); | 224 source->Initialize(params_, this, this); |
| 211 } | 225 } |
| 212 | 226 |
| 213 void WebRtcAudioCapturer::SetStopCallback( | 227 void WebRtcAudioCapturer::SetStopCallback( |
| 214 const base::Closure& on_device_stopped_cb) { | 228 const base::Closure& on_device_stopped_cb) { |
| 215 DCHECK(thread_checker_.CalledOnValidThread()); | 229 DCHECK(thread_checker_.CalledOnValidThread()); |
| 216 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; | 230 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 351 return (loopback_fifo_ != NULL); | 365 return (loopback_fifo_ != NULL); |
| 352 } | 366 } |
| 353 | 367 |
| 354 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, | 368 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, |
| 355 int audio_delay_milliseconds, | 369 int audio_delay_milliseconds, |
| 356 double volume) { | 370 double volume) { |
| 357 // This callback is driven by AudioInputDevice::AudioThreadCallback if | 371 // This callback is driven by AudioInputDevice::AudioThreadCallback if |
| 358 // |source_| is AudioInputDevice, otherwise it is driven by client's | 372 // |source_| is AudioInputDevice, otherwise it is driven by client's |
| 359 // CaptureCallback. | 373 // CaptureCallback. |
| 360 SinkList sinks; | 374 SinkList sinks; |
| 375 int16* buffer; | |
| 376 int bytes_per_sample; | |
| 361 { | 377 { |
| 362 base::AutoLock auto_lock(lock_); | 378 base::AutoLock auto_lock(lock_); |
| 363 if (!running_) | 379 if (!running_) |
| 364 return; | 380 return; |
| 365 | 381 |
|
phoglund_chromium
2013/02/07 14:03:18
Here we use buffer_in_use variable to protect agai
tommi (sloooow) - chröme
2013/02/07 14:57:47
There's no thread check that ensures that the meth
| |
| 366 // Copy the sink list to a local variable. | 382 // We're assuming here that this method is called only once at a time. |
| 383 buffer_in_use_ = true; | |
| 384 buffer_in_use_cv_.Broadcast(); | |
| 385 | |
| 386 // Copy the stuff we will need to local variables. | |
| 367 sinks = sinks_; | 387 sinks = sinks_; |
| 388 buffer = buffer_.get(); | |
| 389 bytes_per_sample = params_.bits_per_sample() / 8; | |
| 368 | 390 |
| 369 // Push captured audio to FIFO so it can be read by a local sink. | 391 // Push captured audio to FIFO so it can be read by a local sink. |
| 370 // Buffering is only enabled if we are rendering a local media stream. | 392 // Buffering is only enabled if we are rendering a local media stream. |
| 371 if (loopback_fifo_ && buffering_) { | 393 if (loopback_fifo_ && buffering_) { |
| 372 if (loopback_fifo_->frames() + audio_source->frames() <= | 394 if (loopback_fifo_->frames() + audio_source->frames() <= |
| 373 loopback_fifo_->max_frames()) { | 395 loopback_fifo_->max_frames()) { |
| 374 loopback_fifo_->Push(audio_source); | 396 loopback_fifo_->Push(audio_source); |
| 375 } else { | 397 } else { |
| 376 DLOG(WARNING) << "FIFO is full"; | 398 DLOG(WARNING) << "FIFO is full"; |
| 377 } | 399 } |
| 378 } | 400 } |
| 379 } | 401 } |
| 380 | 402 |
| 381 // Interleave, scale, and clip input to int and store result in | 403 // Interleave, scale, and clip input to int and store result in |
| 382 // a local byte buffer. | 404 // a local byte buffer. |
| 383 audio_source->ToInterleaved(audio_source->frames(), | 405 audio_source->ToInterleaved(audio_source->frames(), bytes_per_sample, buffer); |
| 384 params_.bits_per_sample() / 8, | |
| 385 buffer_.get()); | |
| 386 | 406 |
| 387 // Feed the data to the sinks. | 407 // Feed the data to the sinks. |
| 388 for (SinkList::const_iterator it = sinks.begin(); | 408 for (SinkList::const_iterator it = sinks.begin(); |
| 389 it != sinks.end(); | 409 it != sinks.end(); |
| 390 ++it) { | 410 ++it) { |
| 391 (*it)->CaptureData(reinterpret_cast<const int16*>(buffer_.get()), | 411 (*it)->CaptureData(reinterpret_cast<const int16*>(buffer), |
| 392 audio_source->channels(), audio_source->frames(), | 412 audio_source->channels(), audio_source->frames(), |
| 393 audio_delay_milliseconds, volume); | 413 audio_delay_milliseconds, volume); |
| 394 } | 414 } |
| 415 | |
|
phoglund_chromium
2013/02/07 14:03:18
If this note isn't true, we need WebRTC to somehow
| |
| 416 // Note: This assumes that the callbacks are guaranteed to be done with the | |
| 417 // data after returning! | |
| 418 { | |
| 419 base::AutoLock auto_lock(lock_); | |
| 420 buffer_in_use_ = false; | |
| 421 buffer_in_use_cv_.Broadcast(); | |
| 422 } | |
| 395 } | 423 } |
| 396 | 424 |
| 397 void WebRtcAudioCapturer::OnCaptureError() { | 425 void WebRtcAudioCapturer::OnCaptureError() { |
| 398 NOTIMPLEMENTED(); | 426 NOTIMPLEMENTED(); |
| 399 } | 427 } |
| 400 | 428 |
| 401 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { | 429 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { |
| 402 device_id_ = device_id; | 430 device_id_ = device_id; |
| 403 } | 431 } |
| 404 | 432 |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 417 // Inform the local renderer about the stopped device. | 445 // Inform the local renderer about the stopped device. |
| 418 // The renderer can then save resources by not asking for more data from | 446 // The renderer can then save resources by not asking for more data from |
| 419 // the stopped source. We are on the IO thread but the callback task will | 447 // the stopped source. We are on the IO thread but the callback task will |
| 420 // be posted on the message loop of the main render thread thanks to | 448 // be posted on the message loop of the main render thread thanks to |
| 421 // usage of BindToLoop() when the callback was initialized. | 449 // usage of BindToLoop() when the callback was initialized. |
| 422 if (!on_device_stopped_cb_.is_null()) | 450 if (!on_device_stopped_cb_.is_null()) |
| 423 on_device_stopped_cb_.Run(); | 451 on_device_stopped_cb_.Run(); |
| 424 } | 452 } |
| 425 | 453 |
| 426 } // namespace content | 454 } // namespace content |
| OLD | NEW |