Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(51)

Side by Side Diff: content/renderer/media/webrtc_audio_capturer.cc

Issue 12220063: Possible solution to synchronization problems in webrtc audio capturer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed potential update issues Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698