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

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: 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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
55 #endif 55 #endif
56 56
57 return buffer_size; 57 return buffer_size;
58 } 58 }
59 59
60 // static 60 // static
61 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { 61 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() {
62 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); 62 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer();
63 return capturer; 63 return capturer;
64 } 64 }
65 65
phoglund_chromium 2013/02/07 14:03:18 I found two places in the code which was doing rou
66 bool WebRtcAudioCapturer::Reconfigure(int sample_rate,
67 media::AudioParameters::Format format,
68 media::ChannelLayout channel_layout) {
69 int buffer_size = GetBufferSizeForSampleRate(sample_rate);
70 if (!buffer_size) {
71 DLOG(ERROR) << "Unsupported sample-rate: " << sample_rate;
72 return false;
73 }
74
75 // Ignored since the audio stack uses float32.
tommi (sloooow) - chröme 2013/02/07 14:57:47 s/Ignored/bits_per_sample is always 16
76 int bits_per_sample = 16;
77
78 // These snapshots will allow us to run callbacks outside the lock.
79 SinkList sinks;
80 media::AudioParameters params;
81 {
82 base::AutoLock auto_lock(lock_);
83
phoglund_chromium 2013/02/07 14:03:18 So along with copying parameters, this is the addi
84 while (buffer_in_use_)
85 buffer_in_use_cv_.Wait();
86
phoglund_chromium 2013/02/07 14:03:18 Once the buffer is not used, we can move ahead wit
87 params_.Reset(format, channel_layout, sample_rate, bits_per_sample,
88 buffer_size);
89 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]);
90
91 sinks = sinks_;
92 params = params_;
93 }
94
95 // Tell all sinks which format we use.
96 for (SinkList::const_iterator it = sinks.begin(); it != sinks.end(); ++it) {
97 (*it)->SetCaptureFormat(params);
98 }
99
100 return true;
101 }
102
66 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout, 103 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout,
67 int sample_rate) { 104 int sample_rate) {
68 DCHECK(thread_checker_.CalledOnValidThread()); 105 DCHECK(thread_checker_.CalledOnValidThread());
69 DCHECK(!sinks_.empty()); 106 DCHECK(!sinks_.empty());
70 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; 107 DVLOG(1) << "WebRtcAudioCapturer::Initialize()";
71 108
72 media::AudioParameters::Format format = 109 media::AudioParameters::Format format =
73 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; 110 media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
74 111
75 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; 112 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout;
(...skipping 14 matching lines...) Expand all
90 127
91 // Verify that the reported input hardware sample rate is supported 128 // Verify that the reported input hardware sample rate is supported
92 // on the current platform. 129 // on the current platform.
93 if (std::find(&kValidInputRates[0], 130 if (std::find(&kValidInputRates[0],
94 &kValidInputRates[0] + arraysize(kValidInputRates), 131 &kValidInputRates[0] + arraysize(kValidInputRates),
95 sample_rate) == 132 sample_rate) ==
96 &kValidInputRates[arraysize(kValidInputRates)]) { 133 &kValidInputRates[arraysize(kValidInputRates)]) {
97 DLOG(ERROR) << sample_rate << " is not a supported input rate."; 134 DLOG(ERROR) << sample_rate << " is not a supported input rate.";
98 return false; 135 return false;
99 } 136 }
100 137
phoglund_chromium 2013/02/07 14:03:18 This code didn't check if GetBufferSize... returne
101 int buffer_size = GetBufferSizeForSampleRate(sample_rate); 138 if (!Reconfigure(sample_rate, format, channel_layout))
102 139 return false;
103 // Configure audio parameters for the default source.
104 params_.Reset(format, channel_layout, 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 140
114 // Create and configure the default audio capturing source. The |source_| 141 // Create and configure the default audio capturing source. The |source_|
115 // will be overwritten if an external client later calls SetCapturerSource() 142 // will be overwritten if an external client later calls SetCapturerSource()
116 // providing an alternaive media::AudioCapturerSource. 143 // providing an alternaive media::AudioCapturerSource.
117 SetCapturerSource(AudioDeviceFactory::NewInputDevice(), 144 SetCapturerSource(AudioDeviceFactory::NewInputDevice(),
118 channel_layout, 145 channel_layout,
119 static_cast<float>(sample_rate)); 146 static_cast<float>(sample_rate));
120 147
121 return true; 148 return true;
122 } 149 }
123 150
124 WebRtcAudioCapturer::WebRtcAudioCapturer() 151 WebRtcAudioCapturer::WebRtcAudioCapturer()
125 : source_(NULL), 152 : buffer_in_use_cv_(&lock_),
153 buffer_in_use_(false),
154 source_(NULL),
126 running_(false), 155 running_(false),
127 buffering_(false) { 156 buffering_(false) {
128 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; 157 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()";
129 } 158 }
130 159
131 WebRtcAudioCapturer::~WebRtcAudioCapturer() { 160 WebRtcAudioCapturer::~WebRtcAudioCapturer() {
132 DCHECK(thread_checker_.CalledOnValidThread()); 161 DCHECK(thread_checker_.CalledOnValidThread());
133 DCHECK(sinks_.empty()); 162 DCHECK(sinks_.empty());
134 DCHECK(!loopback_fifo_); 163 DCHECK(!loopback_fifo_);
135 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; 164 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()";
(...skipping 19 matching lines...) Expand all
155 } 184 }
156 } 185 }
157 186
158 void WebRtcAudioCapturer::SetCapturerSource( 187 void WebRtcAudioCapturer::SetCapturerSource(
159 const scoped_refptr<media::AudioCapturerSource>& source, 188 const scoped_refptr<media::AudioCapturerSource>& source,
160 media::ChannelLayout channel_layout, 189 media::ChannelLayout channel_layout,
161 float sample_rate) { 190 float sample_rate) {
162 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," 191 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << ","
163 << "sample_rate=" << sample_rate << ")"; 192 << "sample_rate=" << sample_rate << ")";
164 scoped_refptr<media::AudioCapturerSource> old_source; 193 scoped_refptr<media::AudioCapturerSource> old_source;
194 media::AudioParameters::Format current_format;
165 { 195 {
166 base::AutoLock auto_lock(lock_); 196 base::AutoLock auto_lock(lock_);
167 if (source_ == source) 197 if (source_ == source)
168 return; 198 return;
169 199
170 source_.swap(old_source); 200 source_.swap(old_source);
171 source_ = source; 201 source_ = source;
phoglund_chromium 2013/02/07 14:03:18 We copy this here to avoid grabbing the lock again
202 current_format = params_.format();
172 } 203 }
173 204
174 const bool no_default_audio_source_exists = !buffer_.get(); 205 const bool no_default_audio_source_exists = !buffer_.get();
175 206
176 // Detach the old source from normal recording or perform first-time 207 // Detach the old source from normal recording or perform first-time
177 // initialization if Initialize() has never been called. For the second 208 // initialization if Initialize() has never been called. For the second
178 // case, the caller is not "taking over an ongoing session" but instead 209 // case, the caller is not "taking over an ongoing session" but instead
179 // "taking control over a new session". 210 // "taking control over a new session".
180 if (old_source || no_default_audio_source_exists) { 211 if (old_source || no_default_audio_source_exists) {
181 DVLOG(1) << "New capture source will now be utilized."; 212 DVLOG(1) << "New capture source will now be utilized.";
182 if (old_source) 213 if (old_source)
183 old_source->Stop(); 214 old_source->Stop();
184 215
185 // Dispatch the new parameters both to the sink(s) and to the new source. 216 // 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 217 // The idea is to get rid of any dependency of the microphone parameters
187 // which would normally be used by default. 218 // which would normally be used by default.
188 219 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; 220 return;
193 }
194
195 params_.Reset(params_.format(),
196 channel_layout,
197 sample_rate,
198 16, // ignored since the audio stack uses float32.
199 buffer_size);
200
201 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]);
202
203 for (SinkList::const_iterator it = sinks_.begin();
204 it != sinks_.end(); ++it) {
205 (*it)->SetCaptureFormat(params_);
206 }
207 } 221 }
208 222
209 if (source) 223 if (source)
210 source->Initialize(params_, this, this); 224 source->Initialize(params_, this, this);
211 } 225 }
212 226
213 void WebRtcAudioCapturer::SetStopCallback( 227 void WebRtcAudioCapturer::SetStopCallback(
214 const base::Closure& on_device_stopped_cb) { 228 const base::Closure& on_device_stopped_cb) {
215 DCHECK(thread_checker_.CalledOnValidThread()); 229 DCHECK(thread_checker_.CalledOnValidThread());
216 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; 230 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()";
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
351 return (loopback_fifo_ != NULL); 365 return (loopback_fifo_ != NULL);
352 } 366 }
353 367
354 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, 368 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source,
355 int audio_delay_milliseconds, 369 int audio_delay_milliseconds,
356 double volume) { 370 double volume) {
357 // This callback is driven by AudioInputDevice::AudioThreadCallback if 371 // This callback is driven by AudioInputDevice::AudioThreadCallback if
358 // |source_| is AudioInputDevice, otherwise it is driven by client's 372 // |source_| is AudioInputDevice, otherwise it is driven by client's
359 // CaptureCallback. 373 // CaptureCallback.
360 SinkList sinks; 374 SinkList sinks;
375 int16* buffer;
376 int bytes_per_sample;
361 { 377 {
362 base::AutoLock auto_lock(lock_); 378 base::AutoLock auto_lock(lock_);
363 if (!running_) 379 if (!running_)
364 return; 380 return;
365 381
phoglund_chromium 2013/02/07 14:03:18 Here we use buffer_in_use variable to protect agai
tommi (sloooow) - chröme 2013/02/07 14:57:47 There's no thread check that ensures that the meth
366 // Copy the sink list to a local variable. 382 // We're assuming here that this method is called only once at a time.
383 buffer_in_use_ = true;
384 buffer_in_use_cv_.Broadcast();
385
386 // Copy the stuff we will need to local variables.
367 sinks = sinks_; 387 sinks = sinks_;
388 buffer = buffer_.get();
389 bytes_per_sample = params_.bits_per_sample() / 8;
368 390
369 // Push captured audio to FIFO so it can be read by a local sink. 391 // Push captured audio to FIFO so it can be read by a local sink.
370 // Buffering is only enabled if we are rendering a local media stream. 392 // Buffering is only enabled if we are rendering a local media stream.
371 if (loopback_fifo_ && buffering_) { 393 if (loopback_fifo_ && buffering_) {
372 if (loopback_fifo_->frames() + audio_source->frames() <= 394 if (loopback_fifo_->frames() + audio_source->frames() <=
373 loopback_fifo_->max_frames()) { 395 loopback_fifo_->max_frames()) {
374 loopback_fifo_->Push(audio_source); 396 loopback_fifo_->Push(audio_source);
375 } else { 397 } else {
376 DLOG(WARNING) << "FIFO is full"; 398 DLOG(WARNING) << "FIFO is full";
377 } 399 }
378 } 400 }
379 } 401 }
380 402
381 // Interleave, scale, and clip input to int and store result in 403 // Interleave, scale, and clip input to int and store result in
382 // a local byte buffer. 404 // a local byte buffer.
383 audio_source->ToInterleaved(audio_source->frames(), 405 audio_source->ToInterleaved(audio_source->frames(), bytes_per_sample, buffer);
384 params_.bits_per_sample() / 8,
385 buffer_.get());
386 406
387 // Feed the data to the sinks. 407 // Feed the data to the sinks.
388 for (SinkList::const_iterator it = sinks.begin(); 408 for (SinkList::const_iterator it = sinks.begin();
389 it != sinks.end(); 409 it != sinks.end();
390 ++it) { 410 ++it) {
391 (*it)->CaptureData(reinterpret_cast<const int16*>(buffer_.get()), 411 (*it)->CaptureData(reinterpret_cast<const int16*>(buffer),
392 audio_source->channels(), audio_source->frames(), 412 audio_source->channels(), audio_source->frames(),
393 audio_delay_milliseconds, volume); 413 audio_delay_milliseconds, volume);
394 } 414 }
415
phoglund_chromium 2013/02/07 14:03:18 If this note isn't true, we need WebRTC to somehow
416 // Note: This assumes that the callbacks are guaranteed to be done with the
417 // data after returning!
418 {
419 base::AutoLock auto_lock(lock_);
420 buffer_in_use_ = false;
421 buffer_in_use_cv_.Broadcast();
422 }
395 } 423 }
396 424
397 void WebRtcAudioCapturer::OnCaptureError() { 425 void WebRtcAudioCapturer::OnCaptureError() {
398 NOTIMPLEMENTED(); 426 NOTIMPLEMENTED();
399 } 427 }
400 428
401 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) { 429 void WebRtcAudioCapturer::OnDeviceStarted(const std::string& device_id) {
402 device_id_ = device_id; 430 device_id_ = device_id;
403 } 431 }
404 432
(...skipping 12 matching lines...) Expand all
417 // Inform the local renderer about the stopped device. 445 // Inform the local renderer about the stopped device.
418 // The renderer can then save resources by not asking for more data from 446 // The renderer can then save resources by not asking for more data from
419 // the stopped source. We are on the IO thread but the callback task will 447 // the stopped source. We are on the IO thread but the callback task will
420 // be posted on the message loop of the main render thread thanks to 448 // be posted on the message loop of the main render thread thanks to
421 // usage of BindToLoop() when the callback was initialized. 449 // usage of BindToLoop() when the callback was initialized.
422 if (!on_device_stopped_cb_.is_null()) 450 if (!on_device_stopped_cb_.is_null())
423 on_device_stopped_cb_.Run(); 451 on_device_stopped_cb_.Run();
424 } 452 }
425 453
426 } // namespace content 454 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698