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

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

Issue 12261003: Possible solution to synchronization problems in webrtc audio capturer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
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
OLDNEW
« no previous file with comments | « content/renderer/media/webrtc_audio_capturer.h ('k') | content/renderer/media/webrtc_local_audio_renderer.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698