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 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 50 // found that the best combination is 20ms on the input side and 10ms on the | 50 // found that the best combination is 20ms on the input side and 10ms on the |
| 51 // output side. | 51 // output side. |
| 52 // TODO(henrika): It might be possible to reduce the input buffer | 52 // TODO(henrika): It might be possible to reduce the input buffer |
| 53 // size and reduce the delay even more. | 53 // size and reduce the delay even more. |
| 54 buffer_size = 2 * sample_rate / 100; | 54 buffer_size = 2 * sample_rate / 100; |
| 55 #endif | 55 #endif |
| 56 | 56 |
| 57 return buffer_size; | 57 return buffer_size; |
| 58 } | 58 } |
| 59 | 59 |
| 60 class CaptureBuffer : public base::RefCounted<CaptureBuffer> { | |
| 61 public: | |
| 62 CaptureBuffer(int16* buffer): buffer_(buffer) {} | |
| 63 int16* buffer() const { return buffer_.get(); } | |
| 64 | |
| 65 private: | |
| 66 scoped_array<int16> buffer_; | |
|
tommi (sloooow) - chröme
2013/02/08 14:56:57
we're deprecating scoped_array and use scoped_ptr
phoglund_chromium
2013/02/08 16:10:38
Done.
| |
| 67 }; | |
|
tommi (sloooow) - chröme
2013/02/08 14:56:57
I don't see the audio parameters here. I think th
phoglund_chromium
2013/02/08 16:10:38
We could do that, but in that case you could argue
tommi (sloooow) - chröme
2013/02/08 16:32:04
The buffer is allocated based on specs from the pa
| |
| 68 | |
| 60 // static | 69 // static |
| 61 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { | 70 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { |
| 62 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); | 71 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); |
| 63 return capturer; | 72 return capturer; |
| 64 } | 73 } |
| 65 | 74 |
| 75 bool WebRtcAudioCapturer::Reconfigure(int sample_rate, | |
| 76 media::AudioParameters::Format format, | |
| 77 media::ChannelLayout channel_layout) { | |
|
tommi (sloooow) - chröme
2013/02/08 14:56:57
I was actually thinking that we'd move much of thi
phoglund_chromium
2013/02/08 16:10:38
Done. I think we can here from a different thread
| |
| 78 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | |
| 79 if (!buffer_size) { | |
| 80 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate; | |
| 81 return false; | |
| 82 } | |
| 83 | |
| 84 // Ignored since the audio stack uses float32. | |
| 85 int bits_per_sample = 16; | |
| 86 | |
| 87 // These snapshots will allow us to run callbacks outside the lock. | |
| 88 SinkList sinks; | |
| 89 media::AudioParameters params; | |
| 90 { | |
| 91 base::AutoLock auto_lock(lock_); | |
| 92 | |
| 93 params_.Reset(format, channel_layout, 0, sample_rate, bits_per_sample, | |
| 94 buffer_size); | |
| 95 buffer_ = new CaptureBuffer( | |
| 96 new int16[params_.frames_per_buffer() * params_.channels()]); | |
| 97 | |
| 98 sinks = sinks_; | |
| 99 params = params_; | |
| 100 } | |
| 101 | |
| 102 // Tell all sinks which format we use. | |
| 103 for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it) { | |
| 104 (*it)->SetCaptureFormat(params); | |
| 105 } | |
| 106 | |
| 107 return true; | |
| 108 } | |
| 109 | |
| 66 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, | 110 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, |
| 67 int sample_rate) { | 111 int sample_rate) { |
| 68 DCHECK(thread_checker_.CalledOnValidThread()); | 112 DCHECK(thread_checker_.CalledOnValidThread()); |
| 69 DCHECK(!sinks_.empty()); | 113 DCHECK(!sinks_.empty()); |
| 70 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; | 114 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; |
| 71 | 115 |
| 72 media::AudioParameters::Format format = | 116 media::AudioParameters::Format format = |
| 73 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; | 117 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; |
| 74 | 118 |
| 75 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; | 119 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 91 // Verify that the reported input hardware sample rate is supported | 135 // Verify that the reported input hardware sample rate is supported |
| 92 // on the current platform. | 136 // on the current platform. |
| 93 if (std::find(&kValidInputRates[0], | 137 if (std::find(&kValidInputRates[0], |
| 94 &kValidInputRates[0] + arraysize(kValidInputRates), | 138 &kValidInputRates[0] + arraysize(kValidInputRates), |
| 95 sample_rate) == | 139 sample_rate) == |
| 96 &kValidInputRates[arraysize(kValidInputRates)]) { | 140 &kValidInputRates[arraysize(kValidInputRates)]) { |
| 97 DLOG(ERROR) << sample_rate << " is not a supported input rate."; | 141 DLOG(ERROR) << sample_rate << " is not a supported input rate."; |
| 98 return false; | 142 return false; |
| 99 } | 143 } |
| 100 | 144 |
| 101 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | 145 if (!Reconfigure(sample_rate, format, channel_layout)) |
| 102 | 146 return false; |
| 103 // Configure audio parameters for the default source. | |
| 104 params_.Reset(format, channel_layout, 0, 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 | 147 |
| 114 // Create and configure the default audio capturing source. The |source_| | 148 // Create and configure the default audio capturing source. The |source_| |
| 115 // will be overwritten if an external client later calls SetCapturerSource() | 149 // will be overwritten if an external client later calls SetCapturerSource() |
| 116 // providing an alternaive media::AudioCapturerSource. | 150 // providing an alternaive media::AudioCapturerSource. |
| 117 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), | 151 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), |
| 118 channel_layout, | 152 channel_layout, |
| 119 static_cast<float>(sample_rate)); | 153 static_cast<float>(sample_rate)); |
| 120 | 154 |
| 121 return true; | 155 return true; |
| 122 } | 156 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 155 } | 189 } |
| 156 } | 190 } |
| 157 | 191 |
| 158 void WebRtcAudioCapturer::SetCapturerSource( | 192 void WebRtcAudioCapturer::SetCapturerSource( |
| 159 const scoped_refptr<media::AudioCapturerSource>& source, | 193 const scoped_refptr<media::AudioCapturerSource>& source, |
| 160 media::ChannelLayout channel_layout, | 194 media::ChannelLayout channel_layout, |
| 161 float sample_rate) { | 195 float sample_rate) { |
| 162 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," | 196 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," |
| 163 << "sample_rate=" << sample_rate << ")"; | 197 << "sample_rate=" << sample_rate << ")"; |
| 164 scoped_refptr<media::AudioCapturerSource> old_source; | 198 scoped_refptr<media::AudioCapturerSource> old_source; |
| 199 media::AudioParameters::Format current_format; | |
| 200 bool no_default_audio_source_exists; | |
| 165 { | 201 { |
| 166 base::AutoLock auto_lock(lock_); | 202 base::AutoLock auto_lock(lock_); |
| 167 if (source_ == source) | 203 if (source_ == source) |
| 168 return; | 204 return; |
| 169 | 205 |
| 170 source_.swap(old_source); | 206 source_.swap(old_source); |
| 171 source_ = source; | 207 source_ = source; |
| 208 current_format = params_.format(); | |
| 209 no_default_audio_source_exists = !buffer_.get()->buffer(); | |
|
tommi (sloooow) - chröme
2013/02/08 14:56:57
.get()-> should not be necessary. instead just use
phoglund_chromium
2013/02/08 16:10:38
Done.
| |
| 172 } | 210 } |
| 173 | 211 |
| 174 const bool no_default_audio_source_exists = !buffer_.get(); | |
| 175 | |
| 176 // Detach the old source from normal recording or perform first-time | 212 // Detach the old source from normal recording or perform first-time |
| 177 // initialization if Initialize() has never been called. For the second | 213 // initialization if Initialize() has never been called. For the second |
| 178 // case, the caller is not "taking over an ongoing session" but instead | 214 // case, the caller is not "taking over an ongoing session" but instead |
| 179 // "taking control over a new session". | 215 // "taking control over a new session". |
| 180 if (old_source || no_default_audio_source_exists) { | 216 if (old_source || no_default_audio_source_exists) { |
| 181 DVLOG(1) << "New capture source will now be utilized."; | 217 DVLOG(1) << "New capture source will now be utilized."; |
| 182 if (old_source) | 218 if (old_source) |
| 183 old_source->Stop(); | 219 old_source->Stop(); |
| 184 | 220 |
| 185 // Dispatch the new parameters both to the sink(s) and to the new source. | 221 // 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 | 222 // The idea is to get rid of any dependency of the microphone parameters |
| 187 // which would normally be used by default. | 223 // which would normally be used by default. |
| 188 | 224 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; | 225 return; |
| 193 } | |
| 194 | |
| 195 params_.Reset(params_.format(), | |
| 196 channel_layout, | |
| 197 0, | |
| 198 sample_rate, | |
| 199 16, // ignored since the audio stack uses float32. | |
| 200 buffer_size); | |
| 201 | |
| 202 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]); | |
| 203 | |
| 204 for (SinkList::const_iterator it = sinks_.begin(); | |
| 205 it != sinks_.end(); ++it) { | |
| 206 (*it)->SetCaptureFormat(params_); | |
| 207 } | |
| 208 } | 226 } |
| 209 | 227 |
| 210 if (source) | 228 if (source) |
| 211 source->Initialize(params_, this, this); | 229 source->Initialize(params_, this, this); |
| 212 } | 230 } |
| 213 | 231 |
| 214 void WebRtcAudioCapturer::SetStopCallback( | 232 void WebRtcAudioCapturer::SetStopCallback( |
| 215 const base::Closure& on_device_stopped_cb) { | 233 const base::Closure& on_device_stopped_cb) { |
| 216 DCHECK(thread_checker_.CalledOnValidThread()); | 234 DCHECK(thread_checker_.CalledOnValidThread()); |
| 217 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; | 235 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 345 if (source_) | 363 if (source_) |
| 346 source_->SetAutomaticGainControl(enable); | 364 source_->SetAutomaticGainControl(enable); |
| 347 } | 365 } |
| 348 | 366 |
| 349 bool WebRtcAudioCapturer::IsInLoopbackMode() { | 367 bool WebRtcAudioCapturer::IsInLoopbackMode() { |
| 350 DCHECK(thread_checker_.CalledOnValidThread()); | 368 DCHECK(thread_checker_.CalledOnValidThread()); |
| 351 base::AutoLock auto_lock(lock_); | 369 base::AutoLock auto_lock(lock_); |
| 352 return (loopback_fifo_ != NULL); | 370 return (loopback_fifo_ != NULL); |
| 353 } | 371 } |
| 354 | 372 |
| 373 // This callback is driven by AudioInputDevice::AudioThreadCallback if | |
| 374 // |source_| is AudioInputDevice, otherwise it is driven by client's | |
| 375 // CaptureCallback. | |
|
tommi (sloooow) - chröme
2013/02/08 14:56:57
We generally keep comments like this inside the im
phoglund_chromium
2013/02/08 16:10:38
Done.
| |
| 355 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, | 376 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, |
| 356 int audio_delay_milliseconds, | 377 int audio_delay_milliseconds, |
| 357 double volume) { | 378 double volume) { |
| 358 // This callback is driven by AudioInputDevice::AudioThreadCallback if | |
| 359 // |source_| is AudioInputDevice, otherwise it is driven by client's | |
| 360 // CaptureCallback. | |
| 361 SinkList sinks; | 379 SinkList sinks; |
| 380 scoped_refptr<CaptureBuffer> buffer_ref_while_calling; | |
| 381 int bytes_per_sample; | |
| 362 { | 382 { |
| 363 base::AutoLock auto_lock(lock_); | 383 base::AutoLock auto_lock(lock_); |
| 364 if (!running_) | 384 if (!running_) |
| 365 return; | 385 return; |
| 366 | 386 |
| 367 // Copy the sink list to a local variable. | 387 // Copy the stuff we will need to local variables. In particular, we grab |
| 388 // a reference to the buffer so we can ensure it stays alive even if the | |
| 389 // buffer is reconfigured while we are calling back. | |
| 390 bytes_per_sample = params_.bits_per_sample() / 8; | |
|
tommi (sloooow) - chröme
2013/02/08 14:56:57
if params_ is owned by the CaptureBuffer, then you
phoglund_chromium
2013/02/08 16:10:38
Done.
| |
| 391 buffer_ref_while_calling = buffer_; | |
| 368 sinks = sinks_; | 392 sinks = sinks_; |
| 369 | 393 |
| 370 // Push captured audio to FIFO so it can be read by a local sink. | 394 // Push captured audio to FIFO so it can be read by a local sink. |
| 371 // Buffering is only enabled if we are rendering a local media stream. | 395 // Buffering is only enabled if we are rendering a local media stream. |
| 372 if (loopback_fifo_ && buffering_) { | 396 if (loopback_fifo_ && buffering_) { |
|
tommi (sloooow) - chröme
2013/02/08 14:56:57
should this block of code be moved out of the lock
phoglund_chromium
2013/02/08 16:10:38
I have no idea, so I'll just leave it for now. It
| |
| 373 if (loopback_fifo_->frames() + audio_source->frames() <= | 397 if (loopback_fifo_->frames() + audio_source->frames() <= |
| 374 loopback_fifo_->max_frames()) { | 398 loopback_fifo_->max_frames()) { |
| 375 loopback_fifo_->Push(audio_source); | 399 loopback_fifo_->Push(audio_source); |
| 376 } else { | 400 } else { |
| 377 DVLOG(1) << "FIFO is full"; | 401 DVLOG(1) << "FIFO is full"; |
| 378 } | 402 } |
| 379 } | 403 } |
| 380 } | 404 } |
| 381 | 405 |
| 382 // Interleave, scale, and clip input to int and store result in | 406 // Interleave, scale, and clip input to int and store result in |
| 383 // a local byte buffer. | 407 // a local byte buffer. |
| 384 audio_source->ToInterleaved(audio_source->frames(), | 408 audio_source->ToInterleaved(audio_source->frames(), bytes_per_sample, |
| 385 params_.bits_per_sample() / 8, | 409 buffer_ref_while_calling->buffer()); |
| 386 buffer_.get()); | |
| 387 | 410 |
| 388 // Feed the data to the sinks. | 411 // Feed the data to the sinks. |
| 389 for (SinkList::const_iterator it = sinks.begin(); | 412 for (SinkList::const_iterator it = sinks.begin(); |
| 390 it != sinks.end(); | 413 it != sinks.end(); |
| 391 ++it) { | 414 ++it) { |
| 392 (*it)->CaptureData(reinterpret_cast<const int16*>(buffer_.get()), | 415 (*it)->CaptureData(buffer_ref_while_calling->buffer(), |
| 393 audio_source->channels(), audio_source->frames(), | 416 audio_source->channels(), audio_source->frames(), |
| 394 audio_delay_milliseconds, volume); | 417 audio_delay_milliseconds, volume); |
| 395 } | 418 } |
| 396 } | 419 } |
| 397 | 420 |
| 398 void WebRtcAudioCapturer::OnCaptureError() { | 421 void WebRtcAudioCapturer::OnCaptureError() { |
| 399 NOTIMPLEMENTED(); | 422 NOTIMPLEMENTED(); |
| 400 } | 423 } |
| 401 | 424 |
| 402 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { | 425 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 418 // Inform the local renderer about the stopped device. | 441 // Inform the local renderer about the stopped device. |
| 419 // The renderer can then save resources by not asking for more data from | 442 // The renderer can then save resources by not asking for more data from |
| 420 // the stopped source. We are on the IO thread but the callback task will | 443 // the stopped source. We are on the IO thread but the callback task will |
| 421 // be posted on the message loop of the main render thread thanks to | 444 // be posted on the message loop of the main render thread thanks to |
| 422 // usage of BindToLoop() when the callback was initialized. | 445 // usage of BindToLoop() when the callback was initialized. |
| 423 if (!on_device_stopped_cb_.is_null()) | 446 if (!on_device_stopped_cb_.is_null()) |
| 424 on_device_stopped_cb_.Run(); | 447 on_device_stopped_cb_.Run(); |
| 425 } | 448 } |
| 426 | 449 |
| 427 } // namespace content | 450 } // namespace content |
| OLD | NEW |