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

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: Had to restore usage of UTF16ToUTF8() in MediaStreamImpl::OnCreateNativeSourcesComplete to build on… 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 // Tell all sinks which format we use.
100 for (SinkList::const_iterator it = sinks_.begin();
101 it != sinks_.end(); ++it) {
102 (*it)->SetCaptureFormat(params_);
103 }
104
105 buffer_.reset(new int16[params_.frames_per_buffer() * params_.channels()]);
106
107 // Create and configure the default audio capturing source. The |source_|
108 // will be overwritten if an external client later calls SetCapturerSource()
109 // providing an alternaive media::AudioCapturerSource.
110 SetCapturerSource(AudioDeviceFactory::NewInputDevice());
111
112 return true;
68 } 113 }
69 114
70 WebRtcAudioCapturer::WebRtcAudioCapturer() 115 WebRtcAudioCapturer::WebRtcAudioCapturer()
71 : source_(NULL), 116 : source_(NULL),
72 running_(false), 117 running_(false),
73 buffering_(false) { 118 buffering_(false) {
119 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()";
74 } 120 }
75 121
76 WebRtcAudioCapturer::~WebRtcAudioCapturer() { 122 WebRtcAudioCapturer::~WebRtcAudioCapturer() {
123 DCHECK(thread_checker_.CalledOnValidThread());
77 DCHECK(sinks_.empty()); 124 DCHECK(sinks_.empty());
78 DCHECK(!loopback_fifo_); 125 DCHECK(!loopback_fifo_);
126 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()";
79 } 127 }
80 128
81 void WebRtcAudioCapturer::AddCapturerSink(WebRtcAudioCapturerSink* sink) { 129 void WebRtcAudioCapturer::AddCapturerSink(WebRtcAudioCapturerSink* sink) {
82 { 130 DCHECK(thread_checker_.CalledOnValidThread());
83 base::AutoLock auto_lock(lock_); 131 DVLOG(1) << "WebRtcAudioCapturer::AddCapturerSink()";
84 DCHECK(std::find(sinks_.begin(), sinks_.end(), sink) == sinks_.end()); 132 base::AutoLock auto_lock(lock_);
85 sinks_.push_back(sink); 133 DCHECK(std::find(sinks_.begin(), sinks_.end(), sink) == sinks_.end());
86 } 134 sinks_.push_back(sink);
87
88 // Tell the |sink| which format we use.
89 sink->SetCaptureFormat(params_);
90 } 135 }
91 136
92 void WebRtcAudioCapturer::RemoveCapturerSink(WebRtcAudioCapturerSink* sink) { 137 void WebRtcAudioCapturer::RemoveCapturerSink(WebRtcAudioCapturerSink* sink) {
138 DCHECK(thread_checker_.CalledOnValidThread());
139 DVLOG(1) << "WebRtcAudioCapturer::RemoveCapturerSink()";
93 base::AutoLock auto_lock(lock_); 140 base::AutoLock auto_lock(lock_);
94 for (SinkList::iterator it = sinks_.begin(); it != sinks_.end(); ++it) { 141 for (SinkList::iterator it = sinks_.begin(); it != sinks_.end(); ++it) {
95 if (sink == *it) { 142 if (sink == *it) {
96 sinks_.erase(it); 143 sinks_.erase(it);
97 break; 144 break;
98 } 145 }
99 } 146 }
100 } 147 }
101 148
102 void WebRtcAudioCapturer::SetCapturerSource( 149 void WebRtcAudioCapturer::SetCapturerSource(
103 const scoped_refptr<media::AudioCapturerSource>& source) { 150 const scoped_refptr<media::AudioCapturerSource>& source) {
151 DCHECK(thread_checker_.CalledOnValidThread());
104 DVLOG(1) << "SetCapturerSource()"; 152 DVLOG(1) << "SetCapturerSource()";
105 scoped_refptr<media::AudioCapturerSource> old_source; 153 scoped_refptr<media::AudioCapturerSource> old_source;
106 { 154 {
107 base::AutoLock auto_lock(lock_); 155 base::AutoLock auto_lock(lock_);
108 if (source_ == source) 156 if (source_ == source)
109 return; 157 return;
110 158
111 source_.swap(old_source); 159 source_.swap(old_source);
112 source_ = source; 160 source_ = source;
113 } 161 }
114 162
115 // Detach the old source from normal recording. 163 // Detach the old source from normal recording.
116 if (old_source) 164 if (old_source)
117 old_source->Stop(); 165 old_source->Stop();
118 166
119 if (source) 167 if (source)
120 source->Initialize(params_, this, this); 168 source->Initialize(params_, this, this);
121 } 169 }
122 170
123 void WebRtcAudioCapturer::SetStopCallback( 171 void WebRtcAudioCapturer::SetStopCallback(
124 const base::Closure& on_device_stopped_cb) { 172 const base::Closure& on_device_stopped_cb) {
173 DCHECK(thread_checker_.CalledOnValidThread());
125 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()"; 174 DVLOG(1) << "WebRtcAudioCapturer::SetStopCallback()";
126 base::AutoLock auto_lock(lock_); 175 base::AutoLock auto_lock(lock_);
127 on_device_stopped_cb_ = on_device_stopped_cb; 176 on_device_stopped_cb_ = on_device_stopped_cb;
128 } 177 }
129 178
130 void WebRtcAudioCapturer::PrepareLoopback() { 179 void WebRtcAudioCapturer::PrepareLoopback() {
180 DCHECK(thread_checker_.CalledOnValidThread());
131 DVLOG(1) << "WebRtcAudioCapturer::PrepareLoopback()"; 181 DVLOG(1) << "WebRtcAudioCapturer::PrepareLoopback()";
132 base::AutoLock auto_lock(lock_); 182 base::AutoLock auto_lock(lock_);
133 DCHECK(!loopback_fifo_); 183 DCHECK(!loopback_fifo_);
134 184
135 // TODO(henrika): we could add a more dynamic solution here but I prefer 185 // 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 186 // 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 187 // 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 188 // 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 189 // 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 190 // 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 191 // machine. We could also add some sort of error notifier in this area if
142 // the FIFO overflows. 192 // the FIFO overflows.
143 loopback_fifo_.reset(new media::AudioFifo(params_.channels(), 193 loopback_fifo_.reset(new media::AudioFifo(params_.channels(),
144 10 * params_.frames_per_buffer())); 194 10 * params_.frames_per_buffer()));
145 buffering_ = true; 195 buffering_ = true;
146 } 196 }
147 197
148 void WebRtcAudioCapturer::CancelLoopback() { 198 void WebRtcAudioCapturer::CancelLoopback() {
199 DCHECK(thread_checker_.CalledOnValidThread());
149 DVLOG(1) << "WebRtcAudioCapturer::CancelLoopback()"; 200 DVLOG(1) << "WebRtcAudioCapturer::CancelLoopback()";
150 base::AutoLock auto_lock(lock_); 201 base::AutoLock auto_lock(lock_);
151 buffering_ = false; 202 buffering_ = false;
152 if (loopback_fifo_.get() != NULL) { 203 if (loopback_fifo_.get() != NULL) {
153 loopback_fifo_->Clear(); 204 loopback_fifo_->Clear();
154 loopback_fifo_.reset(); 205 loopback_fifo_.reset();
155 } 206 }
156 } 207 }
157 208
158 void WebRtcAudioCapturer::PauseBuffering() { 209 void WebRtcAudioCapturer::PauseBuffering() {
210 DCHECK(thread_checker_.CalledOnValidThread());
159 DVLOG(1) << "WebRtcAudioCapturer::PauseBuffering()"; 211 DVLOG(1) << "WebRtcAudioCapturer::PauseBuffering()";
160 base::AutoLock auto_lock(lock_); 212 base::AutoLock auto_lock(lock_);
161 buffering_ = false; 213 buffering_ = false;
162 } 214 }
163 215
164 void WebRtcAudioCapturer::ResumeBuffering() { 216 void WebRtcAudioCapturer::ResumeBuffering() {
217 DCHECK(thread_checker_.CalledOnValidThread());
165 DVLOG(1) << "WebRtcAudioCapturer::ResumeBuffering()"; 218 DVLOG(1) << "WebRtcAudioCapturer::ResumeBuffering()";
166 base::AutoLock auto_lock(lock_); 219 base::AutoLock auto_lock(lock_);
167 if (buffering_) 220 if (buffering_)
168 return; 221 return;
169 if (loopback_fifo_.get() != NULL) 222 if (loopback_fifo_.get() != NULL)
170 loopback_fifo_->Clear(); 223 loopback_fifo_->Clear();
171 buffering_ = true; 224 buffering_ = true;
172 } 225 }
173 226
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) { 227 void WebRtcAudioCapturer::ProvideInput(media::AudioBus* dest) {
224 base::AutoLock auto_lock(lock_); 228 base::AutoLock auto_lock(lock_);
225 DCHECK(loopback_fifo_.get() != NULL); 229 DCHECK(loopback_fifo_.get() != NULL);
226 230
227 if (!running_) { 231 if (!running_) {
228 dest->Zero(); 232 dest->Zero();
229 return; 233 return;
230 } 234 }
231 235
232 // Provide data by reading from the FIFO if the FIFO contains enough 236 // Provide data by reading from the FIFO if the FIFO contains enough
233 // to fulfill the request. 237 // to fulfill the request.
234 if (loopback_fifo_->frames() >= dest->frames()) { 238 if (loopback_fifo_->frames() >= dest->frames()) {
235 loopback_fifo_->Consume(dest, 0, dest->frames()); 239 loopback_fifo_->Consume(dest, 0, dest->frames());
236 } else { 240 } else {
237 dest->Zero(); 241 dest->Zero();
238 // This warning is perfectly safe if it happens for the first audio 242 // This warning is perfectly safe if it happens for the first audio
239 // frames. It should not happen in a steady-state mode. 243 // frames. It should not happen in a steady-state mode.
240 DLOG(WARNING) << "WARNING: loopback FIFO is empty."; 244 DVLOG(2) << "WARNING: loopback FIFO is empty.";
241 } 245 }
242 } 246 }
243 247
244 void WebRtcAudioCapturer::Start() { 248 void WebRtcAudioCapturer::Start() {
245 DVLOG(1) << "WebRtcAudioCapturer::Start()"; 249 DVLOG(1) << "WebRtcAudioCapturer::Start()";
246 base::AutoLock auto_lock(lock_); 250 base::AutoLock auto_lock(lock_);
247 if (running_) 251 if (running_)
248 return; 252 return;
249 253
250 // What Start() does is supposed to be very light, for example, posting a 254 // What Start() does is supposed to be very light, for example, posting a
(...skipping 23 matching lines...) Expand all
274 running_ = false; 278 running_ = false;
275 } 279 }
276 280
277 if (source) 281 if (source)
278 source->Stop(); 282 source->Stop();
279 } 283 }
280 284
281 void WebRtcAudioCapturer::SetVolume(double volume) { 285 void WebRtcAudioCapturer::SetVolume(double volume) {
282 DVLOG(1) << "WebRtcAudioCapturer::SetVolume()"; 286 DVLOG(1) << "WebRtcAudioCapturer::SetVolume()";
283 base::AutoLock auto_lock(lock_); 287 base::AutoLock auto_lock(lock_);
284
285 if (source_) 288 if (source_)
286 source_->SetVolume(volume); 289 source_->SetVolume(volume);
287 } 290 }
288 291
289 void WebRtcAudioCapturer::SetDevice(int session_id) { 292 void WebRtcAudioCapturer::SetDevice(int session_id) {
293 DCHECK(thread_checker_.CalledOnValidThread());
290 DVLOG(1) << "WebRtcAudioCapturer::SetDevice(" << session_id << ")"; 294 DVLOG(1) << "WebRtcAudioCapturer::SetDevice(" << session_id << ")";
291 base::AutoLock auto_lock(lock_); 295 base::AutoLock auto_lock(lock_);
292 if (source_) 296 if (source_)
293 source_->SetDevice(session_id); 297 source_->SetDevice(session_id);
294 } 298 }
295 299
296 void WebRtcAudioCapturer::SetAutomaticGainControl(bool enable) { 300 void WebRtcAudioCapturer::SetAutomaticGainControl(bool enable) {
297 base::AutoLock auto_lock(lock_); 301 base::AutoLock auto_lock(lock_);
298 if (source_) 302 if (source_)
299 source_->SetAutomaticGainControl(enable); 303 source_->SetAutomaticGainControl(enable);
300 } 304 }
301 305
302 bool WebRtcAudioCapturer::IsInLoopbackMode() { 306 bool WebRtcAudioCapturer::IsInLoopbackMode() {
307 DCHECK(thread_checker_.CalledOnValidThread());
303 base::AutoLock auto_lock(lock_); 308 base::AutoLock auto_lock(lock_);
304 return (loopback_fifo_ != NULL); 309 return (loopback_fifo_ != NULL);
305 } 310 }
306 311
307 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source, 312 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source,
308 int audio_delay_milliseconds, 313 int audio_delay_milliseconds,
309 double volume) { 314 double volume) {
310 // This callback is driven by AudioInputDevice::AudioThreadCallback if 315 // This callback is driven by AudioInputDevice::AudioThreadCallback if
311 // |source_| is AudioInputDevice, otherwise it is driven by client's 316 // |source_| is AudioInputDevice, otherwise it is driven by client's
312 // CaptureCallback. 317 // CaptureCallback.
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 // Inform the local renderer about the stopped device. 375 // Inform the local renderer about the stopped device.
371 // The renderer can then save resources by not asking for more data from 376 // 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 377 // 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 378 // be posted on the message loop of the main render thread thanks to
374 // usage of BindToLoop() when the callback was initialized. 379 // usage of BindToLoop() when the callback was initialized.
375 if (!on_device_stopped_cb_.is_null()) 380 if (!on_device_stopped_cb_.is_null())
376 on_device_stopped_cb_.Run(); 381 on_device_stopped_cb_.Run();
377 } 382 }
378 383
379 } // namespace content 384 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698