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/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
101 } | 101 } |
102 | 102 |
103 // Do NOT reference count the |delegate_| to avoid cyclic reference counting. | 103 // Do NOT reference count the |delegate_| to avoid cyclic reference counting. |
104 WebRtcLocalAudioTrack* delegate_; | 104 WebRtcLocalAudioTrack* delegate_; |
105 mutable base::Lock lock_; | 105 mutable base::Lock lock_; |
106 | 106 |
107 DISALLOW_COPY_AND_ASSIGN(TrackOwner); | 107 DISALLOW_COPY_AND_ASSIGN(TrackOwner); |
108 }; | 108 }; |
109 | 109 |
110 // static | 110 // static |
111 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { | 111 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer( |
112 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); | 112 int render_view_id, const StreamDeviceInfo& device_info, |
113 return capturer; | 113 WebRtcAudioDeviceImpl* audio_device) { |
114 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer( | |
115 render_view_id, device_info, audio_device); | |
116 if (capturer->Initialize()) | |
117 return capturer; | |
118 | |
119 return NULL; | |
114 } | 120 } |
115 | 121 |
116 void WebRtcAudioCapturer::Reconfigure(int sample_rate, | 122 void WebRtcAudioCapturer::Reconfigure(int sample_rate, |
117 media::ChannelLayout channel_layout, | 123 media::ChannelLayout channel_layout, |
118 int effects) { | 124 int effects) { |
119 DCHECK(thread_checker_.CalledOnValidThread()); | 125 DCHECK(thread_checker_.CalledOnValidThread()); |
120 int buffer_size = GetBufferSize(sample_rate); | 126 int buffer_size = GetBufferSize(sample_rate); |
121 DVLOG(1) << "Using WebRTC input buffer size: " << buffer_size; | 127 DVLOG(1) << "Using WebRTC input buffer size: " << buffer_size; |
122 | 128 |
123 media::AudioParameters::Format format = | 129 media::AudioParameters::Format format = |
124 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; | 130 media::AudioParameters::AUDIO_PCM_LOW_LATENCY; |
125 | 131 |
126 // bits_per_sample is always 16 for now. | 132 // bits_per_sample is always 16 for now. |
127 int bits_per_sample = 16; | 133 int bits_per_sample = 16; |
128 media::AudioParameters params(format, channel_layout, 0, sample_rate, | 134 media::AudioParameters params(format, channel_layout, 0, sample_rate, |
129 bits_per_sample, buffer_size, effects); | 135 bits_per_sample, buffer_size, effects); |
130 { | 136 { |
131 base::AutoLock auto_lock(lock_); | 137 base::AutoLock auto_lock(lock_); |
132 params_ = params; | 138 params_ = params; |
133 | 139 |
134 // Notify all tracks about the new format. | 140 // Notify all tracks about the new format. |
135 tracks_.TagAll(); | 141 tracks_.TagAll(); |
136 } | 142 } |
137 } | 143 } |
138 | 144 |
139 bool WebRtcAudioCapturer::Initialize(int render_view_id, | 145 bool WebRtcAudioCapturer::Initialize() { |
140 media::ChannelLayout channel_layout, | |
141 int sample_rate, | |
142 int buffer_size, | |
143 int session_id, | |
144 const std::string& device_id, | |
145 int paired_output_sample_rate, | |
146 int paired_output_frames_per_buffer, | |
147 int effects) { | |
148 DCHECK(thread_checker_.CalledOnValidThread()); | 146 DCHECK(thread_checker_.CalledOnValidThread()); |
149 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; | 147 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; |
148 WebRtcLogMessage(base::StringPrintf( | |
149 "WAC::Initialize. render_view_id=%d" | |
150 ", channel_layout=%d, sample_rate=%d, buffer_size=%d" | |
151 ", session_id=%d, paired_output_sample_rate=%d" | |
152 ", paired_output_frames_per_buffer=%d, effects=%d. ", | |
153 render_view_id_, | |
154 device_info_.device.input.channel_layout, | |
155 device_info_.device.input.sample_rate, | |
156 device_info_.device.input.frames_per_buffer, | |
157 device_info_.session_id, | |
158 device_info_.device.matched_output.sample_rate, | |
159 device_info_.device.matched_output.frames_per_buffer, | |
160 device_info_.device.input.effects)); | |
150 | 161 |
162 if (render_view_id_ == -1) { | |
163 // Return true here to allow injecting a new source via | |
164 // SetCapturerSourceForTesting() at a later state. | |
165 return true; | |
166 } | |
167 | |
168 media::ChannelLayout channel_layout = static_cast<media::ChannelLayout>( | |
169 device_info_.device.input.channel_layout); | |
151 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; | 170 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; |
152 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout", | 171 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout", |
153 channel_layout, media::CHANNEL_LAYOUT_MAX); | 172 channel_layout, media::CHANNEL_LAYOUT_MAX); |
154 | 173 |
155 WebRtcLogMessage(base::StringPrintf( | |
156 "WAC::Initialize. render_view_id=%d" | |
157 ", channel_layout=%d, sample_rate=%d, buffer_size=%d" | |
158 ", session_id=%d, paired_output_sample_rate=%d" | |
159 ", paired_output_frames_per_buffer=%d", | |
160 render_view_id, | |
161 channel_layout, | |
162 sample_rate, | |
163 buffer_size, | |
164 session_id, | |
165 paired_output_sample_rate, | |
166 paired_output_frames_per_buffer)); | |
167 | |
168 render_view_id_ = render_view_id; | |
169 session_id_ = session_id; | |
170 device_id_ = device_id; | |
171 hardware_buffer_size_ = buffer_size; | |
172 output_sample_rate_ = paired_output_sample_rate; | |
173 output_frames_per_buffer_= paired_output_frames_per_buffer; | |
174 | |
175 if (render_view_id == -1) { | |
176 // Return true here to allow injecting a new source via SetCapturerSource() | |
177 // at a later state. | |
178 return true; | |
179 } | |
180 | |
181 // Verify that the reported input channel configuration is supported. | 174 // Verify that the reported input channel configuration is supported. |
182 if (channel_layout != media::CHANNEL_LAYOUT_MONO && | 175 if (channel_layout != media::CHANNEL_LAYOUT_MONO && |
183 channel_layout != media::CHANNEL_LAYOUT_STEREO) { | 176 channel_layout != media::CHANNEL_LAYOUT_STEREO) { |
184 DLOG(ERROR) << channel_layout | 177 DLOG(ERROR) << channel_layout |
185 << " is not a supported input channel configuration."; | 178 << " is not a supported input channel configuration."; |
186 return false; | 179 return false; |
187 } | 180 } |
188 | 181 |
189 DVLOG(1) << "Audio input hardware sample rate: " << sample_rate; | 182 DVLOG(1) << "Audio input hardware sample rate: " |
190 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); | 183 << device_info_.device.input.sample_rate; |
184 media::AudioSampleRate asr = media::AsAudioSampleRate( | |
185 device_info_.device.input.sample_rate); | |
191 if (asr != media::kUnexpectedAudioSampleRate) { | 186 if (asr != media::kUnexpectedAudioSampleRate) { |
192 UMA_HISTOGRAM_ENUMERATION( | 187 UMA_HISTOGRAM_ENUMERATION( |
193 "WebRTC.AudioInputSampleRate", asr, media::kUnexpectedAudioSampleRate); | 188 "WebRTC.AudioInputSampleRate", asr, media::kUnexpectedAudioSampleRate); |
194 } else { | 189 } else { |
195 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected", sample_rate); | 190 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected", |
191 device_info_.device.input.sample_rate); | |
196 } | 192 } |
197 | 193 |
198 // Verify that the reported input hardware sample rate is supported | 194 // Verify that the reported input hardware sample rate is supported |
199 // on the current platform. | 195 // on the current platform. |
200 if (std::find(&kValidInputRates[0], | 196 if (std::find(&kValidInputRates[0], |
201 &kValidInputRates[0] + arraysize(kValidInputRates), | 197 &kValidInputRates[0] + arraysize(kValidInputRates), |
202 sample_rate) == | 198 device_info_.device.input.sample_rate) == |
203 &kValidInputRates[arraysize(kValidInputRates)]) { | 199 &kValidInputRates[arraysize(kValidInputRates)]) { |
204 DLOG(ERROR) << sample_rate << " is not a supported input rate."; | 200 DLOG(ERROR) << device_info_.device.input.sample_rate |
201 << " is not a supported input rate."; | |
205 return false; | 202 return false; |
206 } | 203 } |
207 | 204 |
208 // Create and configure the default audio capturing source. The |source_| | 205 // Create and configure the default audio capturing source. |
209 // will be overwritten if an external client later calls SetCapturerSource() | 206 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id_), |
210 // providing an alternative media::AudioCapturerSource. | |
211 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id), | |
212 channel_layout, | 207 channel_layout, |
213 static_cast<float>(sample_rate), | 208 static_cast<float>(device_info_.device.input.sample_rate), |
214 effects); | 209 device_info_.device.input.effects); |
210 | |
211 // Add the capturer to the WebRtcAudioDeviceImpl since it needs some hardware | |
212 // information from the capturer. | |
213 if (audio_device_) | |
214 audio_device_->AddAudioCapturer(this); | |
215 | 215 |
216 return true; | 216 return true; |
217 } | 217 } |
218 | 218 |
219 WebRtcAudioCapturer::WebRtcAudioCapturer() | 219 WebRtcAudioCapturer::WebRtcAudioCapturer(int render_view_id, |
220 const StreamDeviceInfo& device_info, | |
221 WebRtcAudioDeviceImpl* audio_device) | |
220 : running_(false), | 222 : running_(false), |
221 render_view_id_(-1), | 223 render_view_id_(render_view_id), |
222 hardware_buffer_size_(0), | 224 device_info_(device_info), |
223 session_id_(0), | |
224 volume_(0), | 225 volume_(0), |
225 peer_connection_mode_(false), | 226 peer_connection_mode_(false), |
226 output_sample_rate_(0), | 227 key_pressed_(false), |
227 output_frames_per_buffer_(0), | 228 audio_device_(audio_device) { |
228 key_pressed_(false) { | |
229 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; | 229 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; |
230 } | 230 } |
231 | 231 |
232 WebRtcAudioCapturer::~WebRtcAudioCapturer() { | 232 WebRtcAudioCapturer::~WebRtcAudioCapturer() { |
233 DCHECK(thread_checker_.CalledOnValidThread()); | 233 DCHECK(thread_checker_.CalledOnValidThread()); |
234 DCHECK(tracks_.IsEmpty()); | 234 DCHECK(tracks_.IsEmpty()); |
235 DCHECK(!running_); | 235 DCHECK(!running_); |
236 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; | 236 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; |
237 } | 237 } |
238 | 238 |
(...skipping 13 matching lines...) Expand all Loading... | |
252 } | 252 } |
253 | 253 |
254 // Start the source if the first audio track is connected to the capturer. | 254 // Start the source if the first audio track is connected to the capturer. |
255 // Start() will do nothing if the capturer has already been started. | 255 // Start() will do nothing if the capturer has already been started. |
256 Start(); | 256 Start(); |
257 | 257 |
258 } | 258 } |
259 | 259 |
260 void WebRtcAudioCapturer::RemoveTrack(WebRtcLocalAudioTrack* track) { | 260 void WebRtcAudioCapturer::RemoveTrack(WebRtcLocalAudioTrack* track) { |
261 DCHECK(thread_checker_.CalledOnValidThread()); | 261 DCHECK(thread_checker_.CalledOnValidThread()); |
262 base::AutoLock auto_lock(lock_); | |
262 | 263 |
263 bool stop_source = false; | 264 scoped_refptr<TrackOwner> removed_item = |
264 { | 265 tracks_.Remove(TrackOwner::TrackWrapper(track)); |
265 base::AutoLock auto_lock(lock_); | |
266 | 266 |
267 scoped_refptr<TrackOwner> removed_item = | 267 // Clear the delegate to ensure that no more capture callbacks will |
268 tracks_.Remove(TrackOwner::TrackWrapper(track)); | 268 // be sent to this sink. Also avoids a possible crash which can happen |
269 | 269 // if this method is called while capturing is active. |
270 // Clear the delegate to ensure that no more capture callbacks will | 270 if (removed_item.get()) |
271 // be sent to this sink. Also avoids a possible crash which can happen | 271 removed_item->Reset(); |
272 // if this method is called while capturing is active. | |
273 if (removed_item.get()) | |
274 removed_item->Reset(); | |
275 | |
276 // Stop the source if the last audio track is going away. | |
277 stop_source = tracks_.IsEmpty(); | |
278 } | |
279 | |
280 if (stop_source) | |
281 Stop(); | |
282 } | 272 } |
283 | 273 |
284 void WebRtcAudioCapturer::SetCapturerSource( | 274 void WebRtcAudioCapturer::SetCapturerSource( |
285 const scoped_refptr<media::AudioCapturerSource>& source, | 275 const scoped_refptr<media::AudioCapturerSource>& source, |
286 media::ChannelLayout channel_layout, | 276 media::ChannelLayout channel_layout, |
287 float sample_rate, | 277 float sample_rate, |
288 int effects) { | 278 int effects) { |
289 DCHECK(thread_checker_.CalledOnValidThread()); | 279 DCHECK(thread_checker_.CalledOnValidThread()); |
290 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," | 280 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," |
291 << "sample_rate=" << sample_rate << ")"; | 281 << "sample_rate=" << sample_rate << ")"; |
(...skipping 17 matching lines...) Expand all Loading... | |
309 old_source->Stop(); | 299 old_source->Stop(); |
310 | 300 |
311 // Dispatch the new parameters both to the sink(s) and to the new source. | 301 // Dispatch the new parameters both to the sink(s) and to the new source. |
312 // The idea is to get rid of any dependency of the microphone parameters | 302 // The idea is to get rid of any dependency of the microphone parameters |
313 // which would normally be used by default. | 303 // which would normally be used by default. |
314 Reconfigure(sample_rate, channel_layout, effects); | 304 Reconfigure(sample_rate, channel_layout, effects); |
315 | 305 |
316 // Make sure to grab the new parameters in case they were reconfigured. | 306 // Make sure to grab the new parameters in case they were reconfigured. |
317 media::AudioParameters params = audio_parameters(); | 307 media::AudioParameters params = audio_parameters(); |
318 if (source.get()) | 308 if (source.get()) |
319 source->Initialize(params, this, session_id_); | 309 source->Initialize(params, this, session_id()); |
320 | 310 |
321 if (restart_source) | 311 if (restart_source) |
322 Start(); | 312 Start(); |
323 } | 313 } |
324 | 314 |
325 void WebRtcAudioCapturer::EnablePeerConnectionMode() { | 315 void WebRtcAudioCapturer::EnablePeerConnectionMode() { |
326 DCHECK(thread_checker_.CalledOnValidThread()); | 316 DCHECK(thread_checker_.CalledOnValidThread()); |
327 DVLOG(1) << "EnablePeerConnectionMode"; | 317 DVLOG(1) << "EnablePeerConnectionMode"; |
328 // Do nothing if the peer connection mode has been enabled. | 318 // Do nothing if the peer connection mode has been enabled. |
329 if (peer_connection_mode_) | 319 if (peer_connection_mode_) |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
375 base::AutoLock auto_lock(lock_); | 365 base::AutoLock auto_lock(lock_); |
376 if (!running_) | 366 if (!running_) |
377 return; | 367 return; |
378 | 368 |
379 source = source_; | 369 source = source_; |
380 tracks = tracks_.Items(); | 370 tracks = tracks_.Items(); |
381 tracks_.Clear(); | 371 tracks_.Clear(); |
382 running_ = false; | 372 running_ = false; |
383 } | 373 } |
384 | 374 |
375 // Remove the capturer object from the WebRtcAudioDeviceImpl. | |
376 if (audio_device_) | |
perkj_chrome
2014/01/14 12:53:55
Can audio_device_ be NULL? Otherwise skip the chec
no longer working on chromium
2014/01/14 14:10:56
Yes, for example, for most of the unittests they a
| |
377 audio_device_->RemoveAudioCapturer(this); | |
378 | |
385 for (TrackList::ItemList::const_iterator it = tracks.begin(); | 379 for (TrackList::ItemList::const_iterator it = tracks.begin(); |
386 it != tracks.end(); | 380 it != tracks.end(); |
387 ++it) { | 381 ++it) { |
388 (*it)->Stop(); | 382 (*it)->Stop(); |
389 } | 383 } |
390 | 384 |
391 if (source.get()) | 385 if (source.get()) |
392 source->Stop(); | 386 source->Stop(); |
393 } | 387 } |
394 | 388 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
476 media::AudioParameters WebRtcAudioCapturer::audio_parameters() const { | 470 media::AudioParameters WebRtcAudioCapturer::audio_parameters() const { |
477 base::AutoLock auto_lock(lock_); | 471 base::AutoLock auto_lock(lock_); |
478 return params_; | 472 return params_; |
479 } | 473 } |
480 | 474 |
481 bool WebRtcAudioCapturer::GetPairedOutputParameters( | 475 bool WebRtcAudioCapturer::GetPairedOutputParameters( |
482 int* session_id, | 476 int* session_id, |
483 int* output_sample_rate, | 477 int* output_sample_rate, |
484 int* output_frames_per_buffer) const { | 478 int* output_frames_per_buffer) const { |
485 // Don't set output parameters unless all of them are valid. | 479 // Don't set output parameters unless all of them are valid. |
486 if (session_id_ <= 0 || !output_sample_rate_ || !output_frames_per_buffer_) | 480 if (device_info_.session_id <= 0 || |
481 !device_info_.device.matched_output.sample_rate || | |
482 !device_info_.device.matched_output.frames_per_buffer) | |
487 return false; | 483 return false; |
488 | 484 |
489 *session_id = session_id_; | 485 *session_id = device_info_.session_id; |
490 *output_sample_rate = output_sample_rate_; | 486 *output_sample_rate = device_info_.device.matched_output.sample_rate; |
491 *output_frames_per_buffer = output_frames_per_buffer_; | 487 *output_frames_per_buffer = |
488 device_info_.device.matched_output.frames_per_buffer; | |
492 | 489 |
493 return true; | 490 return true; |
494 } | 491 } |
495 | 492 |
496 int WebRtcAudioCapturer::GetBufferSize(int sample_rate) const { | 493 int WebRtcAudioCapturer::GetBufferSize(int sample_rate) const { |
497 DCHECK(thread_checker_.CalledOnValidThread()); | 494 DCHECK(thread_checker_.CalledOnValidThread()); |
498 #if defined(OS_ANDROID) | 495 #if defined(OS_ANDROID) |
499 // TODO(henrika): Tune and adjust buffer size on Android. | 496 // TODO(henrika): Tune and adjust buffer size on Android. |
500 return (2 * sample_rate / 100); | 497 return (2 * sample_rate / 100); |
501 #endif | 498 #endif |
502 | 499 |
503 // PeerConnection is running at a buffer size of 10ms data. A multiple of | 500 // PeerConnection is running at a buffer size of 10ms data. A multiple of |
504 // 10ms as the buffer size can give the best performance to PeerConnection. | 501 // 10ms as the buffer size can give the best performance to PeerConnection. |
505 int peer_connection_buffer_size = sample_rate / 100; | 502 int peer_connection_buffer_size = sample_rate / 100; |
506 | 503 |
507 // Use the native hardware buffer size in non peer connection mode when the | 504 // Use the native hardware buffer size in non peer connection mode when the |
508 // platform is using a native buffer size smaller than the PeerConnection | 505 // platform is using a native buffer size smaller than the PeerConnection |
509 // buffer size. | 506 // buffer size. |
510 if (!peer_connection_mode_ && hardware_buffer_size_ && | 507 int hardware_buffer_size = device_info_.device.input.frames_per_buffer; |
511 hardware_buffer_size_ <= peer_connection_buffer_size) { | 508 if (!peer_connection_mode_ && hardware_buffer_size && |
512 return hardware_buffer_size_; | 509 hardware_buffer_size <= peer_connection_buffer_size) { |
510 return hardware_buffer_size; | |
513 } | 511 } |
514 | 512 |
515 return (sample_rate / 100); | 513 return (sample_rate / 100); |
516 } | 514 } |
517 | 515 |
518 void WebRtcAudioCapturer::GetAudioProcessingParams( | 516 void WebRtcAudioCapturer::GetAudioProcessingParams( |
519 base::TimeDelta* delay, int* volume, bool* key_pressed) { | 517 base::TimeDelta* delay, int* volume, bool* key_pressed) { |
520 base::AutoLock auto_lock(lock_); | 518 base::AutoLock auto_lock(lock_); |
521 *delay = audio_delay_; | 519 *delay = audio_delay_; |
522 *volume = volume_; | 520 *volume = volume_; |
523 *key_pressed = key_pressed_; | 521 *key_pressed = key_pressed_; |
524 } | 522 } |
525 | 523 |
524 void WebRtcAudioCapturer::SetCapturerSourceForTesting( | |
525 const scoped_refptr<media::AudioCapturerSource>& source, | |
526 media::AudioParameters params) { | |
527 // Create a new audio stream as source which uses the new source. | |
528 SetCapturerSource(source, params.channel_layout(), | |
529 static_cast<float>(params.sample_rate()), | |
530 params.effects()); | |
531 } | |
532 | |
526 } // namespace content | 533 } // namespace content |
OLD | NEW |