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 // This is a temporary audio buffer with parameters used to send data to | |
61 // callbacks. | |
62 class WebRtcAudioCapturer::ConfiguredBuffer : | |
63 public base::RefCounted<WebRtcAudioCapturer::ConfiguredBuffer> { | |
64 public: | |
65 ConfiguredBuffer() {} | |
66 | |
67 bool Initialize(int sample_rate, | |
68 media::AudioParameters::Format format, | |
69 media::ChannelLayout channel_layout) { | |
70 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | |
71 if (!buffer_size) { | |
72 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate; | |
73 return false; | |
74 } | |
75 | |
76 // bits_per_sample is always 16 for now. | |
77 int bits_per_sample = 16; | |
78 | |
79 params_.Reset(format, channel_layout, 0, sample_rate, bits_per_sample, | |
80 buffer_size); | |
81 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]); | |
82 | |
83 return true; | |
84 } | |
85 | |
86 int16* buffer() const { return buffer_.get(); } | |
87 const media::AudioParameters& params() const { return params_; } | |
88 | |
89 private: | |
90 scoped_ptr<int16[]> buffer_; | |
tommi (sloooow) - chröme
2013/02/08 16:32:04
hmm... this doesn't look right. should just be sc
phoglund_chromium
2013/02/08 17:06:00
The deprecation comment for scoped_array says "rep
| |
91 | |
92 // Cached values of utilized audio parameters. Platform dependent. | |
tommi (sloooow) - chröme
2013/02/08 16:32:04
nit: skip the "platform dependent" part.
phoglund_chromium
2013/02/08 17:06:00
Done.
| |
93 media::AudioParameters params_; | |
94 }; | |
95 | |
60 // static | 96 // static |
61 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { | 97 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { |
62 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); | 98 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); |
63 return capturer; | 99 return capturer; |
64 } | 100 } |
65 | 101 |
102 bool WebRtcAudioCapturer::Reconfigure(int sample_rate, | |
103 media::AudioParameters::Format format, | |
104 media::ChannelLayout channel_layout) { | |
105 scoped_refptr<ConfiguredBuffer> new_buffer(new ConfiguredBuffer()); | |
106 if (!new_buffer->Initialize(sample_rate, format, channel_layout)) | |
107 return false; | |
108 | |
109 SinkList sinks; | |
110 { | |
111 base::AutoLock auto_lock(lock_); | |
112 | |
113 buffer_ = new_buffer; | |
114 sinks = sinks_; | |
115 } | |
116 | |
117 // Tell all sinks which format we use. | |
118 for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it) | |
119 (*it)->SetCaptureFormat(new_buffer->params()); | |
120 | |
121 return true; | |
122 } | |
123 | |
66 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, | 124 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, |
67 int sample_rate) { | 125 int sample_rate) { |
68 DCHECK(thread_checker_.CalledOnValidThread()); | 126 DCHECK(thread_checker_.CalledOnValidThread()); |
69 DCHECK(!sinks_.empty()); | 127 DCHECK(!sinks_.empty()); |
70 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; | 128 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; |
71 | 129 |
72 media::AudioParameters::Format format = | 130 media::AudioParameters::Format format = |
73 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; | 131 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; |
74 | 132 |
75 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; | 133 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 | 149 // Verify that the reported input hardware sample rate is supported |
92 // on the current platform. | 150 // on the current platform. |
93 if (std::find(&kValidInputRates[0], | 151 if (std::find(&kValidInputRates[0], |
94 &kValidInputRates[0] + arraysize(kValidInputRates), | 152 &kValidInputRates[0] + arraysize(kValidInputRates), |
95 sample_rate) == | 153 sample_rate) == |
96 &kValidInputRates[arraysize(kValidInputRates)]) { | 154 &kValidInputRates[arraysize(kValidInputRates)]) { |
97 DLOG(ERROR) << sample_rate << " is not a supported input rate."; | 155 DLOG(ERROR) << sample_rate << " is not a supported input rate."; |
98 return false; | 156 return false; |
99 } | 157 } |
100 | 158 |
101 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | 159 if (!Reconfigure(sample_rate, format, channel_layout)) |
102 | 160 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 | 161 |
114 // Create and configure the default audio capturing source. The |source_| | 162 // Create and configure the default audio capturing source. The |source_| |
115 // will be overwritten if an external client later calls SetCapturerSource() | 163 // will be overwritten if an external client later calls SetCapturerSource() |
116 // providing an alternaive media::AudioCapturerSource. | 164 // providing an alternaive media::AudioCapturerSource. |
117 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), | 165 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), |
118 channel_layout, | 166 channel_layout, |
119 static_cast<float>(sample_rate)); | 167 static_cast<float>(sample_rate)); |
120 | 168 |
121 return true; | 169 return true; |
122 } | 170 } |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
155 } | 203 } |
156 } | 204 } |
157 | 205 |
158 void WebRtcAudioCapturer::SetCapturerSource( | 206 void WebRtcAudioCapturer::SetCapturerSource( |
159 const scoped_refptr<media::AudioCapturerSource>& source, | 207 const scoped_refptr<media::AudioCapturerSource>& source, |
160 media::ChannelLayout channel_layout, | 208 media::ChannelLayout channel_layout, |
161 float sample_rate) { | 209 float sample_rate) { |
162 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," | 210 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," |
163 << "sample_rate=" << sample_rate << ")"; | 211 << "sample_rate=" << sample_rate << ")"; |
164 scoped_refptr<media::AudioCapturerSource> old_source; | 212 scoped_refptr<media::AudioCapturerSource> old_source; |
213 scoped_refptr<ConfiguredBuffer> current_buffer; | |
165 { | 214 { |
166 base::AutoLock auto_lock(lock_); | 215 base::AutoLock auto_lock(lock_); |
167 if (source_ == source) | 216 if (source_ == source) |
168 return; | 217 return; |
169 | 218 |
170 source_.swap(old_source); | 219 source_.swap(old_source); |
171 source_ = source; | 220 source_ = source; |
221 current_buffer = buffer_; | |
172 } | 222 } |
173 | 223 |
174 const bool no_default_audio_source_exists = !buffer_.get(); | 224 const bool no_default_audio_source_exists = !current_buffer->buffer(); |
175 | 225 |
176 // Detach the old source from normal recording or perform first-time | 226 // Detach the old source from normal recording or perform first-time |
177 // initialization if Initialize() has never been called. For the second | 227 // initialization if Initialize() has never been called. For the second |
178 // case, the caller is not "taking over an ongoing session" but instead | 228 // case, the caller is not "taking over an ongoing session" but instead |
179 // "taking control over a new session". | 229 // "taking control over a new session". |
180 if (old_source || no_default_audio_source_exists) { | 230 if (old_source || no_default_audio_source_exists) { |
181 DVLOG(1) << "New capture source will now be utilized."; | 231 DVLOG(1) << "New capture source will now be utilized."; |
182 if (old_source) | 232 if (old_source) |
183 old_source->Stop(); | 233 old_source->Stop(); |
184 | 234 |
185 // Dispatch the new parameters both to the sink(s) and to the new source. | 235 // 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 | 236 // The idea is to get rid of any dependency of the microphone parameters |
187 // which would normally be used by default. | 237 // which would normally be used by default. |
188 | 238 if (!Reconfigure(sample_rate, current_buffer->params().format(), |
189 int buffer_size = GetBufferSizeForSampleRate(sample_rate); | 239 channel_layout)) |
tommi (sloooow) - chröme
2013/02/08 16:32:04
use {}
phoglund_chromium
2013/02/08 17:06:00
Done.
| |
190 if (!buffer_size) { | |
191 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate; | |
192 return; | 240 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 } | 241 } |
209 | 242 |
210 if (source) | 243 if (source) { |
211 source->Initialize(params_, this, this); | 244 // Make sure to grab the new parameters in case they were reconfigured. |
phoglund_chromium
2013/02/08 16:10:38
This here was tricky. I initially passed in curren
tommi (sloooow) - chröme
2013/02/08 16:32:04
audio_parameters() returns a reference to the para
phoglund_chromium
2013/02/08 17:06:00
Done.
| |
245 source->Initialize(audio_parameter(), this, this); | |
246 } | |
212 } | 247 } |
213 | 248 |
214 void WebRtcAudioCapturer::SetStopCallback( | 249 void WebRtcAudioCapturer::SetStopCallback( |
215 const base::Closure& on_device_stopped_cb) { | 250 const base::Closure& on_device_stopped_cb) { |
216 DCHECK(thread_checker_.CalledOnValidThread()); | 251 DCHECK(thread_checker_.CalledOnValidThread()); |
217 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; | 252 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; |
218 base::AutoLock auto_lock(lock_); | 253 base::AutoLock auto_lock(lock_); |
219 on_device_stopped_cb_ = on_device_stopped_cb; | 254 on_device_stopped_cb_ = on_device_stopped_cb; |
220 } | 255 } |
221 | 256 |
222 void WebRtcAudioCapturer::PrepareLoopback() { | 257 void WebRtcAudioCapturer::PrepareLoopback() { |
223 DCHECK(thread_checker_.CalledOnValidThread()); | 258 DCHECK(thread_checker_.CalledOnValidThread()); |
224 DVLOG(1) << "WebRtcAudioCapturer::PrepareLoopback()"; | 259 DVLOG(1) << "WebRtcAudioCapturer::PrepareLoopback()"; |
225 base::AutoLock auto_lock(lock_); | 260 base::AutoLock auto_lock(lock_); |
226 DCHECK(!loopback_fifo_); | 261 DCHECK(!loopback_fifo_); |
227 | 262 |
228 // TODO(henrika): we could add a more dynamic solution here but I prefer | 263 // TODO(henrika): we could add a more dynamic solution here but I prefer |
229 // a fixed size combined with bad audio at overflow. The alternative is | 264 // a fixed size combined with bad audio at overflow. The alternative is |
230 // that we start to build up latency and that can be more difficult to | 265 // that we start to build up latency and that can be more difficult to |
231 // detect. Tests have shown that the FIFO never contains more than 2 or 3 | 266 // detect. Tests have shown that the FIFO never contains more than 2 or 3 |
232 // audio frames but I have selected a max size of ten buffers just | 267 // audio frames but I have selected a max size of ten buffers just |
233 // in case since these tests were performed on a 16 core, 64GB Win 7 | 268 // in case since these tests were performed on a 16 core, 64GB Win 7 |
234 // machine. We could also add some sort of error notifier in this area if | 269 // machine. We could also add some sort of error notifier in this area if |
235 // the FIFO overflows. | 270 // the FIFO overflows. |
236 loopback_fifo_.reset(new media::AudioFifo(params_.channels(), | 271 loopback_fifo_.reset(new media::AudioFifo( |
237 10 * params_.frames_per_buffer())); | 272 buffer_->params().channels(), |
273 10 * buffer_->params().frames_per_buffer())); | |
238 buffering_ = true; | 274 buffering_ = true; |
239 } | 275 } |
240 | 276 |
241 void WebRtcAudioCapturer::CancelLoopback() { | 277 void WebRtcAudioCapturer::CancelLoopback() { |
242 DCHECK(thread_checker_.CalledOnValidThread()); | 278 DCHECK(thread_checker_.CalledOnValidThread()); |
243 DVLOG(1) << "WebRtcAudioCapturer::CancelLoopback()"; | 279 DVLOG(1) << "WebRtcAudioCapturer::CancelLoopback()"; |
244 base::AutoLock auto_lock(lock_); | 280 base::AutoLock auto_lock(lock_); |
245 buffering_ = false; | 281 buffering_ = false; |
246 if (loopback_fifo_.get() != NULL) { | 282 if (loopback_fifo_.get() != NULL) { |
247 loopback_fifo_->Clear(); | 283 loopback_fifo_->Clear(); |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
352 return (loopback_fifo_ != NULL); | 388 return (loopback_fifo_ != NULL); |
353 } | 389 } |
354 | 390 |
355 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, | 391 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, |
356 int audio_delay_milliseconds, | 392 int audio_delay_milliseconds, |
357 double volume) { | 393 double volume) { |
358 // This callback is driven by AudioInputDevice::AudioThreadCallback if | 394 // This callback is driven by AudioInputDevice::AudioThreadCallback if |
359 // |source_| is AudioInputDevice, otherwise it is driven by client's | 395 // |source_| is AudioInputDevice, otherwise it is driven by client's |
360 // CaptureCallback. | 396 // CaptureCallback. |
361 SinkList sinks; | 397 SinkList sinks; |
398 scoped_refptr<ConfiguredBuffer> buffer_ref_while_calling; | |
362 { | 399 { |
363 base::AutoLock auto_lock(lock_); | 400 base::AutoLock auto_lock(lock_); |
364 if (!running_) | 401 if (!running_) |
365 return; | 402 return; |
366 | 403 |
367 // Copy the sink list to a local variable. | 404 // Copy the stuff we will need to local variables. In particular, we grab |
405 // a reference to the buffer so we can ensure it stays alive even if the | |
406 // buffer is reconfigured while we are calling back. | |
407 buffer_ref_while_calling = buffer_; | |
368 sinks = sinks_; | 408 sinks = sinks_; |
369 | 409 |
370 // Push captured audio to FIFO so it can be read by a local sink. | 410 // 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. | 411 // Buffering is only enabled if we are rendering a local media stream. |
372 if (loopback_fifo_ && buffering_) { | 412 if (loopback_fifo_ && buffering_) { |
373 if (loopback_fifo_->frames() + audio_source->frames() <= | 413 if (loopback_fifo_->frames() + audio_source->frames() <= |
374 loopback_fifo_->max_frames()) { | 414 loopback_fifo_->max_frames()) { |
375 loopback_fifo_->Push(audio_source); | 415 loopback_fifo_->Push(audio_source); |
376 } else { | 416 } else { |
377 DVLOG(1) << "FIFO is full"; | 417 DVLOG(1) << "FIFO is full"; |
378 } | 418 } |
379 } | 419 } |
380 } | 420 } |
381 | 421 |
422 int bytes_per_sample = | |
423 buffer_ref_while_calling->params().bits_per_sample() / 8; | |
424 | |
382 // Interleave, scale, and clip input to int and store result in | 425 // Interleave, scale, and clip input to int and store result in |
383 // a local byte buffer. | 426 // a local byte buffer. |
384 audio_source->ToInterleaved(audio_source->frames(), | 427 audio_source->ToInterleaved(audio_source->frames(), bytes_per_sample, |
385 params_.bits_per_sample() / 8, | 428 buffer_ref_while_calling->buffer()); |
386 buffer_.get()); | |
387 | 429 |
388 // Feed the data to the sinks. | 430 // Feed the data to the sinks. |
389 for (SinkList::const_iterator it = sinks.begin(); | 431 for (SinkList::const_iterator it = sinks.begin(); |
390 it != sinks.end(); | 432 it != sinks.end(); |
391 ++it) { | 433 ++it) { |
392 (*it)->CaptureData(reinterpret_cast<const int16*>(buffer_.get()), | 434 (*it)->CaptureData(buffer_ref_while_calling->buffer(), |
393 audio_source->channels(), audio_source->frames(), | 435 audio_source->channels(), audio_source->frames(), |
394 audio_delay_milliseconds, volume); | 436 audio_delay_milliseconds, volume); |
395 } | 437 } |
396 } | 438 } |
397 | 439 |
398 void WebRtcAudioCapturer::OnCaptureError() { | 440 void WebRtcAudioCapturer::OnCaptureError() { |
399 NOTIMPLEMENTED(); | 441 NOTIMPLEMENTED(); |
400 } | 442 } |
401 | 443 |
402 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { | 444 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { |
(...skipping 14 matching lines...) Expand all Loading... | |
417 | 459 |
418 // Inform the local renderer about the stopped device. | 460 // Inform the local renderer about the stopped device. |
419 // The renderer can then save resources by not asking for more data from | 461 // 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 | 462 // 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 | 463 // be posted on the message loop of the main render thread thanks to |
422 // usage of BindToLoop() when the callback was initialized. | 464 // usage of BindToLoop() when the callback was initialized. |
423 if (!on_device_stopped_cb_.is_null()) | 465 if (!on_device_stopped_cb_.is_null()) |
424 on_device_stopped_cb_.Run(); | 466 on_device_stopped_cb_.Run(); |
425 } | 467 } |
426 | 468 |
469 const media::AudioParameters& WebRtcAudioCapturer::audio_parameter() { | |
470 base::AutoLock auto_lock(lock_); | |
471 DCHECK(buffer_.get() != NULL); | |
472 return buffer_->params(); | |
473 } | |
474 | |
427 } // namespace content | 475 } // namespace content |
OLD | NEW |