Chromium Code Reviews| 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 26 matching lines...) Expand all Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |