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