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 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |