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

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: Rebased 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 class CaptureBuffer : public base::RefCounted<CaptureBuffer> {
61 public:
62 CaptureBuffer(int16* buffer): buffer_(buffer) {}
63 int16* buffer() const { return buffer_.get(); }
64
65 private:
66 scoped_array<int16> buffer_;
tommi (sloooow) - chröme 2013/02/08 14:56:57 we're deprecating scoped_array and use scoped_ptr
phoglund_chromium 2013/02/08 16:10:38 Done.
67 };
tommi (sloooow) - chröme 2013/02/08 14:56:57 I don't see the audio parameters here. I think th
phoglund_chromium 2013/02/08 16:10:38 We could do that, but in that case you could argue
tommi (sloooow) - chröme 2013/02/08 16:32:04 The buffer is allocated based on specs from the pa
68
60 // static 69 // static
61 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { 70 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() {
62 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); 71 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer();
63 return capturer; 72 return capturer;
64 } 73 }
65 74
75 bool WebRtcAudioCapturer::Reconfigure(int sample_rate,
76 media::AudioParameters::Format format,
77 media::ChannelLayout channel_layout) {
tommi (sloooow) - chröme 2013/02/08 14:56:57 I was actually thinking that we'd move much of thi
phoglund_chromium 2013/02/08 16:10:38 Done. I think we can here from a different thread
78 int buffer_size = GetBufferSizeForSampleRate(sample_rate);
79 if (!buffer_size) {
80 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate;
81 return false;
82 }
83
84 // Ignored since the audio stack uses float32.
85 int bits_per_sample = 16;
86
87 // These snapshots will allow us to run callbacks outside the lock.
88 SinkList sinks;
89 media::AudioParameters params;
90 {
91 base::AutoLock auto_lock(lock_);
92
93 params_.Reset(format, channel_layout, 0, sample_rate, bits_per_sample,
94 buffer_size);
95 buffer_ = new CaptureBuffer(
96 new int16[params_.frames_per_buffer() * params_.channels()]);
97
98 sinks = sinks_;
99 params = params_;
100 }
101
102 // Tell all sinks which format we use.
103 for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it) {
104 (*it)->SetCaptureFormat(params);
105 }
106
107 return true;
108 }
109
66 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, 110 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout,
67 int sample_rate) { 111 int sample_rate) {
68 DCHECK(thread_checker_.CalledOnValidThread()); 112 DCHECK(thread_checker_.CalledOnValidThread());
69 DCHECK(!sinks_.empty()); 113 DCHECK(!sinks_.empty());
70 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; 114 DVLOG(1) << "WebRtcAudioCapturer::Initialize()";
71 115
72 media::AudioParameters::Format format = 116 media::AudioParameters::Format format =
73 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; 117 media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
74 118
75 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; 119 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 135 // Verify that the reported input hardware sample rate is supported
92 // on the current platform. 136 // on the current platform.
93 if (std::find(&kValidInputRates[0], 137 if (std::find(&kValidInputRates[0],
94 &kValidInputRates[0] + arraysize(kValidInputRates), 138 &kValidInputRates[0] + arraysize(kValidInputRates),
95 sample_rate) == 139 sample_rate) ==
96 &kValidInputRates[arraysize(kValidInputRates)]) { 140 &kValidInputRates[arraysize(kValidInputRates)]) {
97 DLOG(ERROR) << sample_rate << " is not a supported input rate."; 141 DLOG(ERROR) << sample_rate << " is not a supported input rate.";
98 return false; 142 return false;
99 } 143 }
100 144
101 int buffer_size = GetBufferSizeForSampleRate(sample_rate); 145 if (!Reconfigure(sample_rate, format, channel_layout))
102 146 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 147
114 // Create and configure the default audio capturing source. The |source_| 148 // Create and configure the default audio capturing source. The |source_|
115 // will be overwritten if an external client later calls SetCapturerSource() 149 // will be overwritten if an external client later calls SetCapturerSource()
116 // providing an alternaive media::AudioCapturerSource. 150 // providing an alternaive media::AudioCapturerSource.
117 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), 151 SetCapturerSource(AudioDeviceFactory::NewInputDevice(),
118 channel_layout, 152 channel_layout,
119 static_cast<float>(sample_rate)); 153 static_cast<float>(sample_rate));
120 154
121 return true; 155 return true;
122 } 156 }
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 } 189 }
156 } 190 }
157 191
158 void WebRtcAudioCapturer::SetCapturerSource( 192 void WebRtcAudioCapturer::SetCapturerSource(
159 const scoped_refptr<media::AudioCapturerSource>& source, 193 const scoped_refptr<media::AudioCapturerSource>& source,
160 media::ChannelLayout channel_layout, 194 media::ChannelLayout channel_layout,
161 float sample_rate) { 195 float sample_rate) {
162 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," 196 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << ","
163 << "sample_rate=" << sample_rate << ")"; 197 << "sample_rate=" << sample_rate << ")";
164 scoped_refptr<media::AudioCapturerSource> old_source; 198 scoped_refptr<media::AudioCapturerSource> old_source;
199 media::AudioParameters::Format current_format;
200 bool no_default_audio_source_exists;
165 { 201 {
166 base::AutoLock auto_lock(lock_); 202 base::AutoLock auto_lock(lock_);
167 if (source_ == source) 203 if (source_ == source)
168 return; 204 return;
169 205
170 source_.swap(old_source); 206 source_.swap(old_source);
171 source_ = source; 207 source_ = source;
208 current_format = params_.format();
209 no_default_audio_source_exists = !buffer_.get()->buffer();
tommi (sloooow) - chröme 2013/02/08 14:56:57 .get()-> should not be necessary. instead just use
phoglund_chromium 2013/02/08 16:10:38 Done.
172 } 210 }
173 211
174 const bool no_default_audio_source_exists = !buffer_.get();
175
176 // Detach the old source from normal recording or perform first-time 212 // Detach the old source from normal recording or perform first-time
177 // initialization if Initialize() has never been called. For the second 213 // initialization if Initialize() has never been called. For the second
178 // case, the caller is not "taking over an ongoing session" but instead 214 // case, the caller is not "taking over an ongoing session" but instead
179 // "taking control over a new session". 215 // "taking control over a new session".
180 if (old_source || no_default_audio_source_exists) { 216 if (old_source || no_default_audio_source_exists) {
181 DVLOG(1) << "New capture source will now be utilized."; 217 DVLOG(1) << "New capture source will now be utilized.";
182 if (old_source) 218 if (old_source)
183 old_source->Stop(); 219 old_source->Stop();
184 220
185 // Dispatch the new parameters both to the sink(s) and to the new source. 221 // 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 222 // The idea is to get rid of any dependency of the microphone parameters
187 // which would normally be used by default. 223 // which would normally be used by default.
188 224 if (!Reconfigure(sample_rate, current_format, channel_layout))
189 int buffer_size = GetBufferSizeForSampleRate(sample_rate);
190 if (!buffer_size) {
191 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate;
192 return; 225 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 } 226 }
209 227
210 if (source) 228 if (source)
211 source->Initialize(params_, this, this); 229 source->Initialize(params_, this, this);
212 } 230 }
213 231
214 void WebRtcAudioCapturer::SetStopCallback( 232 void WebRtcAudioCapturer::SetStopCallback(
215 const base::Closure& on_device_stopped_cb) { 233 const base::Closure& on_device_stopped_cb) {
216 DCHECK(thread_checker_.CalledOnValidThread()); 234 DCHECK(thread_checker_.CalledOnValidThread());
217 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; 235 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()";
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after
345 if (source_) 363 if (source_)
346 source_->SetAutomaticGainControl(enable); 364 source_->SetAutomaticGainControl(enable);
347 } 365 }
348 366
349 bool WebRtcAudioCapturer::IsInLoopbackMode() { 367 bool WebRtcAudioCapturer::IsInLoopbackMode() {
350 DCHECK(thread_checker_.CalledOnValidThread()); 368 DCHECK(thread_checker_.CalledOnValidThread());
351 base::AutoLock auto_lock(lock_); 369 base::AutoLock auto_lock(lock_);
352 return (loopback_fifo_ != NULL); 370 return (loopback_fifo_ != NULL);
353 } 371 }
354 372
373 // This callback is driven by AudioInputDevice::AudioThreadCallback if
374 // |source_| is AudioInputDevice, otherwise it is driven by client's
375 // CaptureCallback.
tommi (sloooow) - chröme 2013/02/08 14:56:57 We generally keep comments like this inside the im
phoglund_chromium 2013/02/08 16:10:38 Done.
355 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, 376 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source,
356 int audio_delay_milliseconds, 377 int audio_delay_milliseconds,
357 double volume) { 378 double volume) {
358 // This callback is driven by AudioInputDevice::AudioThreadCallback if
359 // |source_| is AudioInputDevice, otherwise it is driven by client's
360 // CaptureCallback.
361 SinkList sinks; 379 SinkList sinks;
380 scoped_refptr<CaptureBuffer> buffer_ref_while_calling;
381 int bytes_per_sample;
362 { 382 {
363 base::AutoLock auto_lock(lock_); 383 base::AutoLock auto_lock(lock_);
364 if (!running_) 384 if (!running_)
365 return; 385 return;
366 386
367 // Copy the sink list to a local variable. 387 // Copy the stuff we will need to local variables. In particular, we grab
388 // a reference to the buffer so we can ensure it stays alive even if the
389 // buffer is reconfigured while we are calling back.
390 bytes_per_sample = params_.bits_per_sample() / 8;
tommi (sloooow) - chröme 2013/02/08 14:56:57 if params_ is owned by the CaptureBuffer, then you
phoglund_chromium 2013/02/08 16:10:38 Done.
391 buffer_ref_while_calling = buffer_;
368 sinks = sinks_; 392 sinks = sinks_;
369 393
370 // Push captured audio to FIFO so it can be read by a local sink. 394 // 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. 395 // Buffering is only enabled if we are rendering a local media stream.
372 if (loopback_fifo_ && buffering_) { 396 if (loopback_fifo_ && buffering_) {
tommi (sloooow) - chröme 2013/02/08 14:56:57 should this block of code be moved out of the lock
phoglund_chromium 2013/02/08 16:10:38 I have no idea, so I'll just leave it for now. It
373 if (loopback_fifo_->frames() + audio_source->frames() <= 397 if (loopback_fifo_->frames() + audio_source->frames() <=
374 loopback_fifo_->max_frames()) { 398 loopback_fifo_->max_frames()) {
375 loopback_fifo_->Push(audio_source); 399 loopback_fifo_->Push(audio_source);
376 } else { 400 } else {
377 DVLOG(1) << "FIFO is full"; 401 DVLOG(1) << "FIFO is full";
378 } 402 }
379 } 403 }
380 } 404 }
381 405
382 // Interleave, scale, and clip input to int and store result in 406 // Interleave, scale, and clip input to int and store result in
383 // a local byte buffer. 407 // a local byte buffer.
384 audio_source->ToInterleaved(audio_source->frames(), 408 audio_source->ToInterleaved(audio_source->frames(), bytes_per_sample,
385 params_.bits_per_sample() / 8, 409 buffer_ref_while_calling->buffer());
386 buffer_.get());
387 410
388 // Feed the data to the sinks. 411 // Feed the data to the sinks.
389 for (SinkList::const_iterator it = sinks.begin(); 412 for (SinkList::const_iterator it = sinks.begin();
390 it != sinks.end(); 413 it != sinks.end();
391 ++it) { 414 ++it) {
392 (*it)->CaptureData(reinterpret_cast<const int16*>(buffer_.get()), 415 (*it)->CaptureData(buffer_ref_while_calling->buffer(),
393 audio_source->channels(), audio_source->frames(), 416 audio_source->channels(), audio_source->frames(),
394 audio_delay_milliseconds, volume); 417 audio_delay_milliseconds, volume);
395 } 418 }
396 } 419 }
397 420
398 void WebRtcAudioCapturer::OnCaptureError() { 421 void WebRtcAudioCapturer::OnCaptureError() {
399 NOTIMPLEMENTED(); 422 NOTIMPLEMENTED();
400 } 423 }
401 424
402 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { 425 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) {
(...skipping 15 matching lines...) Expand all
418 // Inform the local renderer about the stopped device. 441 // Inform the local renderer about the stopped device.
419 // The renderer can then save resources by not asking for more data from 442 // 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 443 // 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 444 // be posted on the message loop of the main render thread thanks to
422 // usage of BindToLoop() when the callback was initialized. 445 // usage of BindToLoop() when the callback was initialized.
423 if (!on_device_stopped_cb_.is_null()) 446 if (!on_device_stopped_cb_.is_null())
424 on_device_stopped_cb_.Run(); 447 on_device_stopped_cb_.Run();
425 } 448 }
426 449
427 } // namespace content 450 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698