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

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

Issue 11783059: Ensures that WebRTC works for device selection using a different sample rate than default (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed content_unittests Created 7 years, 11 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 26 matching lines...) Expand all
37 int buffer_size = 0; 37 int buffer_size = 0;
38 #if defined(OS_WIN) || defined(OS_MACOSX) 38 #if defined(OS_WIN) || defined(OS_MACOSX)
39 // Use different buffer sizes depending on the current hardware sample rate. 39 // Use different buffer sizes depending on the current hardware sample rate.
40 if (sample_rate == 44100) { 40 if (sample_rate == 44100) {
41 // We do run at 44.1kHz at the actual audio layer, but ask for frames 41 // We do run at 44.1kHz at the actual audio layer, but ask for frames
42 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine. 42 // at 44.0kHz to ensure that we can feed them to the webrtc::VoiceEngine.
43 buffer_size = 440; 43 buffer_size = 440;
44 } else { 44 } else {
45 buffer_size = (sample_rate / 100); 45 buffer_size = (sample_rate / 100);
46 DCHECK_EQ(buffer_size * 100, sample_rate) << 46 DCHECK_EQ(buffer_size * 100, sample_rate) <<
47 "Sample rate not supported. Should have been caught in Init()."; 47 "Sample rate not supported";
48 } 48 }
49 #elif defined(OS_LINUX) || defined(OS_OPENBSD) 49 #elif defined(OS_LINUX) || defined(OS_OPENBSD)
50 // Based on tests using the current ALSA implementation in Chrome, we have 50 // Based on tests using the current ALSA implementation in Chrome, we have
51 // found that the best combination is 20ms on the input side and 10ms on the 51 // found that the best combination is 20ms on the input side and 10ms on the
52 // output side. 52 // output side.
53 // TODO(henrika): It might be possible to reduce the input buffer 53 // TODO(henrika): It might be possible to reduce the input buffer
54 // size and reduce the delay even more. 54 // size and reduce the delay even more.
55 buffer_size = 2 * sample_rate / 100; 55 buffer_size = 2 * sample_rate / 100;
56 #endif 56 #endif
57 57
58 return buffer_size; 58 return buffer_size;
59 } 59 }
60 60
61 // static 61 // static
62 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { 62 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() {
63 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); 63 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer();
64 if (capturer->Initialize()) 64 return capturer;
65 return capturer; 65 }
66 66
67 return NULL; 67 bool WebRtcAudioCapturer::Initialize(media::ChannelLayout channel_layout,
68 int sample_rate) {
69 DCHECK(thread_checker_.CalledOnValidThread());
70 DCHECK(!sinks_.empty());
71 DVLOG(1) << "WebRtcAudioCapturer::Initialize()";
72
73 media::AudioParameters::Format format =
74 media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
75
76 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout;
77 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout",
78 channel_layout, media::CHANNEL_LAYOUT_MAX);
79
80 DVLOG(1) << "Audio input hardware sample rate: " << sample_rate;
81 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputSampleRate",
82 sample_rate, media::kUnexpectedAudioSampleRate);
83
84 // Verify that the reported input hardware sample rate is supported
85 // on the current platform.
86 if (std::find(&kValidInputRates[0],
87 &kValidInputRates[0] + arraysize(kValidInputRates),
88 sample_rate) ==
89 &kValidInputRates[arraysize(kValidInputRates)]) {
90 DLOG(ERROR) << sample_rate << " is not a supported input rate.";
91 return false;
92 }
93
94 int buffer_size = GetBufferSizeForSampleRate(sample_rate);
95
96 // Configure audio parameters for the default source.
97 params_.Reset(format, channel_layout, sample_rate, 16, buffer_size);
98
99 {
100 // Tell all sinks which format we use.
101 base::AutoLock auto_lock(lock_);
102 for (SinkList::const_iterator it = sinks_.begin();
tommi (sloooow) - chröme 2013/01/15 17:43:49 is it possible that other methods can be called on
henrika (OOO until Aug 14) 2013/01/16 16:37:17 Really good point, thanks. Will make it more clear
103 it != sinks_.end(); ++it) {
tommi (sloooow) - chröme 2013/01/15 17:43:49 fix indent
henrika (OOO until Aug 14) 2013/01/16 16:37:17 Done.
104 (*it)->SetCaptureFormat(params_);
105 }
106 }
107
108 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]);
109
110 // Create and configure the default audio capturing source. The |source_|
111 // will be overwritten if an external client later calls SetCapturerSource()
112 // providing an alternaive media::AudioCapturerSource.
113 SetCapturerSource(AudioDeviceFactory::NewInputDevice());
114
115 return true;
68 } 116 }
69 117
70 WebRtcAudioCapturer::WebRtcAudioCapturer() 118 WebRtcAudioCapturer::WebRtcAudioCapturer()
71 : source_(NULL), 119 : source_(NULL),
72 running_(false), 120 running_(false),
73 buffering_(false) { 121 buffering_(false) {
122 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()";
74 } 123 }
75 124
76 WebRtcAudioCapturer::~WebRtcAudioCapturer() { 125 WebRtcAudioCapturer::~WebRtcAudioCapturer() {
126 DCHECK(thread_checker_.CalledOnValidThread());
77 DCHECK(sinks_.empty()); 127 DCHECK(sinks_.empty());
78 DCHECK(!loopback_fifo_); 128 DCHECK(!loopback_fifo_);
129 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()";
79 } 130 }
80 131
81 void WebRtcAudioCapturer::AddCapturerSink(WebRtcAudioCapturerSink* sink) { 132 void WebRtcAudioCapturer::AddCapturerSink(WebRtcAudioCapturerSink* sink) {
82 { 133 DCHECK(thread_checker_.CalledOnValidThread());
83 base::AutoLock auto_lock(lock_); 134 DVLOG(1) << "WebRtcAudioCapturer::AddCapturerSink()";
84 DCHECK(std::find(sinks_.begin(), sinks_.end(), sink) == sinks_.end()); 135 DCHECK(std::find(sinks_.begin(), sinks_.end(), sink) == sinks_.end());
tommi (sloooow) - chröme 2013/01/15 17:43:49 move back under lock
henrika (OOO until Aug 14) 2013/01/16 16:37:17 Feels much better. Done.
85 sinks_.push_back(sink); 136 base::AutoLock auto_lock(lock_);
86 } 137 sinks_.push_back(sink);
87
88 // Tell the |sink| which format we use.
89 sink->SetCaptureFormat(params_);
90 } 138 }
91 139
92 void WebRtcAudioCapturer::RemoveCapturerSink(WebRtcAudioCapturerSink* sink) { 140 void WebRtcAudioCapturer::RemoveCapturerSink(WebRtcAudioCapturerSink* sink) {
141 DCHECK(thread_checker_.CalledOnValidThread());
142 DVLOG(1) << "WebRtcAudioCapturer::RemoveCapturerSink()";
93 base::AutoLock auto_lock(lock_); 143 base::AutoLock auto_lock(lock_);
94 for (SinkList::iterator it = sinks_.begin(); it != sinks_.end(); ++it) { 144 for (SinkList::iterator it = sinks_.begin(); it != sinks_.end(); ++it) {
95 if (sink == *it) { 145 if (sink == *it) {
96 sinks_.erase(it); 146 sinks_.erase(it);
97 break; 147 break;
98 } 148 }
99 } 149 }
100 } 150 }
101 151
102 void WebRtcAudioCapturer::SetCapturerSource( 152 void WebRtcAudioCapturer::SetCapturerSource(
103 const scoped_refptr<media::AudioCapturerSource>& source) { 153 const scoped_refptr<media::AudioCapturerSource>& source) {
154 DCHECK(thread_checker_.CalledOnValidThread());
104 DVLOG(1) << "SetCapturerSource()"; 155 DVLOG(1) << "SetCapturerSource()";
105 scoped_refptr<media::AudioCapturerSource> old_source; 156 scoped_refptr<media::AudioCapturerSource> old_source;
106 { 157 {
107 base::AutoLock auto_lock(lock_); 158 base::AutoLock auto_lock(lock_);
108 if (source_ == source) 159 if (source_ == source)
109 return; 160 return;
110 161
111 source_.swap(old_source); 162 source_.swap(old_source);
112 source_ = source; 163 source_ = source;
113 } 164 }
114 165
115 // Detach the old source from normal recording. 166 // Detach the old source from normal recording.
116 if (old_source) 167 if (old_source)
117 old_source->Stop(); 168 old_source->Stop();
118 169
119 if (source) 170 if (source)
120 source->Initialize(params_, this, this); 171 source->Initialize(params_, this, this);
121 } 172 }
122 173
123 void WebRtcAudioCapturer::SetStopCallback( 174 void WebRtcAudioCapturer::SetStopCallback(
124 const base::Closure& on_device_stopped_cb) { 175 const base::Closure& on_device_stopped_cb) {
176 DCHECK(thread_checker_.CalledOnValidThread());
125 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; 177 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()";
126 base::AutoLock auto_lock(lock_); 178 base::AutoLock auto_lock(lock_);
127 on_device_stopped_cb_ = on_device_stopped_cb; 179 on_device_stopped_cb_ = on_device_stopped_cb;
128 } 180 }
129 181
130 void WebRtcAudioCapturer::PrepareLoopback() { 182 void WebRtcAudioCapturer::PrepareLoopback() {
183 DCHECK(thread_checker_.CalledOnValidThread());
131 DVLOG(1) << "WebRtcAudioCapturer::PrepareLoopback()"; 184 DVLOG(1) << "WebRtcAudioCapturer::PrepareLoopback()";
132 base::AutoLock auto_lock(lock_); 185 base::AutoLock auto_lock(lock_);
133 DCHECK(!loopback_fifo_); 186 DCHECK(!loopback_fifo_);
134 187
135 // TODO(henrika): we could add a more dynamic solution here but I prefer 188 // TODO(henrika): we could add a more dynamic solution here but I prefer
136 // a fixed size combined with bad audio at overflow. The alternative is 189 // a fixed size combined with bad audio at overflow. The alternative is
137 // that we start to build up latency and that can be more difficult to 190 // that we start to build up latency and that can be more difficult to
138 // detect. Tests have shown that the FIFO never contains more than 2 or 3 191 // detect. Tests have shown that the FIFO never contains more than 2 or 3
139 // audio frames but I have selected a max size of ten buffers just 192 // audio frames but I have selected a max size of ten buffers just
140 // in case since these tests were performed on a 16 core, 64GB Win 7 193 // in case since these tests were performed on a 16 core, 64GB Win 7
141 // machine. We could also add some sort of error notifier in this area if 194 // machine. We could also add some sort of error notifier in this area if
142 // the FIFO overflows. 195 // the FIFO overflows.
143 loopback_fifo_.reset(new media::AudioFifo(params_.channels(), 196 loopback_fifo_.reset(new media::AudioFifo(params_.channels(),
144 10 * params_.frames_per_buffer())); 197 10 * params_.frames_per_buffer()));
145 buffering_ = true; 198 buffering_ = true;
146 } 199 }
147 200
148 void WebRtcAudioCapturer::CancelLoopback() { 201 void WebRtcAudioCapturer::CancelLoopback() {
202 DCHECK(thread_checker_.CalledOnValidThread());
149 DVLOG(1) << "WebRtcAudioCapturer::CancelLoopback()"; 203 DVLOG(1) << "WebRtcAudioCapturer::CancelLoopback()";
150 base::AutoLock auto_lock(lock_); 204 base::AutoLock auto_lock(lock_);
151 buffering_ = false; 205 buffering_ = false;
152 if (loopback_fifo_.get() != NULL) { 206 if (loopback_fifo_.get() != NULL) {
153 loopback_fifo_->Clear(); 207 loopback_fifo_->Clear();
154 loopback_fifo_.reset(); 208 loopback_fifo_.reset();
155 } 209 }
156 } 210 }
157 211
158 void WebRtcAudioCapturer::PauseBuffering() { 212 void WebRtcAudioCapturer::PauseBuffering() {
213 DCHECK(thread_checker_.CalledOnValidThread());
159 DVLOG(1) << "WebRtcAudioCapturer::PauseBuffering()"; 214 DVLOG(1) << "WebRtcAudioCapturer::PauseBuffering()";
160 base::AutoLock auto_lock(lock_); 215 base::AutoLock auto_lock(lock_);
161 buffering_ = false; 216 buffering_ = false;
162 } 217 }
163 218
164 void WebRtcAudioCapturer::ResumeBuffering() { 219 void WebRtcAudioCapturer::ResumeBuffering() {
220 DCHECK(thread_checker_.CalledOnValidThread());
tommi (sloooow) - chröme 2013/01/15 17:43:49 thanks for adding all these thread dchecks!
henrika (OOO until Aug 14) 2013/01/16 16:37:17 Thanks. I have done lots of manual testing here wi
165 DVLOG(1) << "WebRtcAudioCapturer::ResumeBuffering()"; 221 DVLOG(1) << "WebRtcAudioCapturer::ResumeBuffering()";
166 base::AutoLock auto_lock(lock_); 222 base::AutoLock auto_lock(lock_);
167 if (buffering_) 223 if (buffering_)
168 return; 224 return;
169 if (loopback_fifo_.get() != NULL) 225 if (loopback_fifo_.get() != NULL)
170 loopback_fifo_->Clear(); 226 loopback_fifo_->Clear();
171 buffering_ = true; 227 buffering_ = true;
172 } 228 }
173 229
174 bool WebRtcAudioCapturer::Initialize() {
175 DVLOG(1) << "WebRtcAudioCapturer::Initialize()";
176 // Ask the browser for the default audio input hardware sample-rate.
177 // This request is based on a synchronous IPC message.
178 // TODO(xians): we should ask for the native sample rate of a specific device.
179 int sample_rate = GetAudioInputSampleRate();
180 DVLOG(1) << "Audio input hardware sample rate: " << sample_rate;
181 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputSampleRate",
182 sample_rate, media::kUnexpectedAudioSampleRate);
183
184 // Verify that the reported input hardware sample rate is supported
185 // on the current platform.
186 if (std::find(&kValidInputRates[0],
187 &kValidInputRates[0] + arraysize(kValidInputRates),
188 sample_rate) ==
189 &kValidInputRates[arraysize(kValidInputRates)]) {
190 DLOG(ERROR) << sample_rate << " is not a supported input rate.";
191 return false;
192 }
193
194 // Ask the browser for the default number of audio input channels.
195 // This request is based on a synchronous IPC message.
196 // TODO(xians): we should ask for the layout of a specific device.
197 media::ChannelLayout channel_layout = GetAudioInputChannelLayout();
198 DVLOG(1) << "Audio input hardware channels: " << channel_layout;
199
200 media::AudioParameters::Format format =
201 media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
202 int buffer_size = GetBufferSizeForSampleRate(sample_rate);
203 if (!buffer_size) {
204 DLOG(ERROR) << "Unsupported platform";
205 return false;
206 }
207
208 params_.Reset(format, channel_layout, sample_rate, 16, buffer_size);
209
210 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]);
211
212 // Create and configure the default audio capturing source. The |source_|
213 // will be overwritten if the client call the source calls
214 // SetCapturerSource().
215 SetCapturerSource(AudioDeviceFactory::NewInputDevice());
216
217 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout",
218 channel_layout, media::CHANNEL_LAYOUT_MAX);
219
220 return true;
221 }
222
223 void WebRtcAudioCapturer::ProvideInput(media::AudioBus* dest) { 230 void WebRtcAudioCapturer::ProvideInput(media::AudioBus* dest) {
224 base::AutoLock auto_lock(lock_); 231 base::AutoLock auto_lock(lock_);
225 DCHECK(loopback_fifo_.get() != NULL); 232 DCHECK(loopback_fifo_.get() != NULL);
226 233
227 if (!running_) { 234 if (!running_) {
228 dest->Zero(); 235 dest->Zero();
229 return; 236 return;
230 } 237 }
231 238
232 // Provide data by reading from the FIFO if the FIFO contains enough 239 // Provide data by reading from the FIFO if the FIFO contains enough
233 // to fulfill the request. 240 // to fulfill the request.
234 if (loopback_fifo_->frames() >= dest->frames()) { 241 if (loopback_fifo_->frames() >= dest->frames()) {
235 loopback_fifo_->Consume(dest, 0, dest->frames()); 242 loopback_fifo_->Consume(dest, 0, dest->frames());
236 } else { 243 } else {
237 dest->Zero(); 244 dest->Zero();
238 // This warning is perfectly safe if it happens for the first audio 245 // This warning is perfectly safe if it happens for the first audio
239 // frames. It should not happen in a steady-state mode. 246 // frames. It should not happen in a steady-state mode.
240 DLOG(WARNING) << "WARNING: loopback FIFO is empty."; 247 DVLOG(2) << "WARNING: loopback FIFO is empty.";
241 } 248 }
242 } 249 }
243 250
244 void WebRtcAudioCapturer::Start() { 251 void WebRtcAudioCapturer::Start() {
245 DVLOG(1) << "WebRtcAudioCapturer::Start()"; 252 DVLOG(1) << "WebRtcAudioCapturer::Start()";
246 base::AutoLock auto_lock(lock_); 253 base::AutoLock auto_lock(lock_);
247 if (running_) 254 if (running_)
248 return; 255 return;
249 256
250 // What Start() does is supposed to be very light, for example, posting a 257 // What Start() does is supposed to be very light, for example, posting a
(...skipping 20 matching lines...) Expand all
271 } 278 }
272 279
273 source = source_; 280 source = source_;
274 running_ = false; 281 running_ = false;
275 } 282 }
276 283
277 if (source) 284 if (source)
278 source->Stop(); 285 source->Stop();
279 } 286 }
280 287
281 void WebRtcAudioCapturer::SetVolume(double volume) { 288 void WebRtcAudioCapturer::SetVolume(double volume) {
tommi (sloooow) - chröme 2013/01/15 17:43:49 can SetVolume be called from multiple threads but
henrika (OOO until Aug 14) 2013/01/16 16:37:17 Correct. This one is special since it is called by
282 DVLOG(1) << "WebRtcAudioCapturer::SetVolume()"; 289 DVLOG(1) << "WebRtcAudioCapturer::SetVolume()";
283 base::AutoLock auto_lock(lock_); 290 base::AutoLock auto_lock(lock_);
284
285 if (source_) 291 if (source_)
286 source_->SetVolume(volume); 292 source_->SetVolume(volume);
287 } 293 }
288 294
289 void WebRtcAudioCapturer::SetDevice(int session_id) { 295 void WebRtcAudioCapturer::SetDevice(int session_id) {
296 DCHECK(thread_checker_.CalledOnValidThread());
290 DVLOG(1) << "WebRtcAudioCapturer::SetDevice(" << session_id << ")"; 297 DVLOG(1) << "WebRtcAudioCapturer::SetDevice(" << session_id << ")";
291 base::AutoLock auto_lock(lock_); 298 base::AutoLock auto_lock(lock_);
292 if (source_) 299 if (source_)
293 source_->SetDevice(session_id); 300 source_->SetDevice(session_id);
294 } 301 }
295 302
296 void WebRtcAudioCapturer::SetAutomaticGainControl(bool enable) { 303 void WebRtcAudioCapturer::SetAutomaticGainControl(bool enable) {
297 base::AutoLock auto_lock(lock_); 304 base::AutoLock auto_lock(lock_);
298 if (source_) 305 if (source_)
299 source_->SetAutomaticGainControl(enable); 306 source_->SetAutomaticGainControl(enable);
300 } 307 }
301 308
302 bool WebRtcAudioCapturer::IsInLoopbackMode() { 309 bool WebRtcAudioCapturer::IsInLoopbackMode() {
310 DCHECK(thread_checker_.CalledOnValidThread());
303 base::AutoLock auto_lock(lock_); 311 base::AutoLock auto_lock(lock_);
304 return (loopback_fifo_ != NULL); 312 return (loopback_fifo_ != NULL);
305 } 313 }
306 314
307 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, 315 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source,
308 int audio_delay_milliseconds, 316 int audio_delay_milliseconds,
309 double volume) { 317 double volume) {
310 // This callback is driven by AudioInputDevice::AudioThreadCallback if 318 // This callback is driven by AudioInputDevice::AudioThreadCallback if
311 // |source_| is AudioInputDevice, otherwise it is driven by client's 319 // |source_| is AudioInputDevice, otherwise it is driven by client's
312 // CaptureCallback. 320 // CaptureCallback.
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 // Inform the local renderer about the stopped device. 378 // Inform the local renderer about the stopped device.
371 // The renderer can then save resources by not asking for more data from 379 // The renderer can then save resources by not asking for more data from
372 // the stopped source. We are on the IO thread but the callback task will 380 // the stopped source. We are on the IO thread but the callback task will
373 // be posted on the message loop of the main render thread thanks to 381 // be posted on the message loop of the main render thread thanks to
374 // usage of BindToLoop() when the callback was initialized. 382 // usage of BindToLoop() when the callback was initialized.
375 if (!on_device_stopped_cb_.is_null()) 383 if (!on_device_stopped_cb_.is_null())
376 on_device_stopped_cb_.Run(); 384 on_device_stopped_cb_.Run();
377 } 385 }
378 386
379 } // namespace content 387 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698