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

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 comment 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 ~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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698