OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |