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