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 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
106 } | 106 } |
107 | 107 |
108 // Do NOT reference count the |delegate_| to avoid cyclic reference counting. | 108 // Do NOT reference count the |delegate_| to avoid cyclic reference counting. |
109 WebRtcLocalAudioTrack* delegate_; | 109 WebRtcLocalAudioTrack* delegate_; |
110 mutable base::Lock lock_; | 110 mutable base::Lock lock_; |
111 | 111 |
112 DISALLOW_COPY_AND_ASSIGN(TrackOwner); | 112 DISALLOW_COPY_AND_ASSIGN(TrackOwner); |
113 }; | 113 }; |
114 | 114 |
115 // static | 115 // static |
116 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() { | 116 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer( |
117 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(); | 117 int render_view_id, const StreamDeviceInfo& device_info, |
118 return capturer; | 118 const blink::WebMediaConstraints& constraints, |
| 119 WebRtcAudioDeviceImpl* audio_device) { |
| 120 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer( |
| 121 render_view_id, device_info, constraints, audio_device); |
| 122 if (capturer->Initialize()) |
| 123 return capturer; |
| 124 |
| 125 return NULL; |
119 } | 126 } |
120 | 127 |
121 bool WebRtcAudioCapturer::Initialize(int render_view_id, | 128 bool WebRtcAudioCapturer::Initialize() { |
122 media::ChannelLayout channel_layout, | |
123 int sample_rate, | |
124 int buffer_size, | |
125 int session_id, | |
126 const std::string& device_id, | |
127 int paired_output_sample_rate, | |
128 int paired_output_frames_per_buffer, | |
129 int effects, | |
130 const blink::WebMediaConstraints& constraints) { | |
131 DCHECK(thread_checker_.CalledOnValidThread()); | 129 DCHECK(thread_checker_.CalledOnValidThread()); |
132 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; | 130 DVLOG(1) << "WebRtcAudioCapturer::Initialize()"; |
| 131 WebRtcLogMessage(base::StringPrintf( |
| 132 "WAC::Initialize. render_view_id=%d" |
| 133 ", channel_layout=%d, sample_rate=%d, buffer_size=%d" |
| 134 ", session_id=%d, paired_output_sample_rate=%d" |
| 135 ", paired_output_frames_per_buffer=%d, effects=%d. ", |
| 136 render_view_id_, |
| 137 device_info_.device.input.channel_layout, |
| 138 device_info_.device.input.sample_rate, |
| 139 device_info_.device.input.frames_per_buffer, |
| 140 device_info_.session_id, |
| 141 device_info_.device.matched_output.sample_rate, |
| 142 device_info_.device.matched_output.frames_per_buffer, |
| 143 device_info_.device.input.effects)); |
133 | 144 |
| 145 if (render_view_id_ == -1) { |
| 146 // Return true here to allow injecting a new source via |
| 147 // SetCapturerSourceForTesting() at a later state. |
| 148 return true; |
| 149 } |
| 150 |
| 151 media::ChannelLayout channel_layout = static_cast<media::ChannelLayout>( |
| 152 device_info_.device.input.channel_layout); |
134 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; | 153 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout; |
135 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout", | 154 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout", |
136 channel_layout, media::CHANNEL_LAYOUT_MAX); | 155 channel_layout, media::CHANNEL_LAYOUT_MAX); |
137 | 156 |
138 WebRtcLogMessage(base::StringPrintf( | |
139 "WAC::Initialize. render_view_id=%d" | |
140 ", channel_layout=%d, sample_rate=%d, buffer_size=%d" | |
141 ", session_id=%d, paired_output_sample_rate=%d" | |
142 ", paired_output_frames_per_buffer=%d", | |
143 render_view_id, | |
144 channel_layout, | |
145 sample_rate, | |
146 buffer_size, | |
147 session_id, | |
148 paired_output_sample_rate, | |
149 paired_output_frames_per_buffer)); | |
150 | |
151 render_view_id_ = render_view_id; | |
152 session_id_ = session_id; | |
153 device_id_ = device_id; | |
154 hardware_buffer_size_ = buffer_size; | |
155 output_sample_rate_ = paired_output_sample_rate; | |
156 output_frames_per_buffer_= paired_output_frames_per_buffer; | |
157 constraints_ = constraints; | |
158 | |
159 if (render_view_id == -1) { | |
160 // Return true here to allow injecting a new source via SetCapturerSource() | |
161 // at a later state. | |
162 return true; | |
163 } | |
164 | |
165 // Verify that the reported input channel configuration is supported. | 157 // Verify that the reported input channel configuration is supported. |
166 if (channel_layout != media::CHANNEL_LAYOUT_MONO && | 158 if (channel_layout != media::CHANNEL_LAYOUT_MONO && |
167 channel_layout != media::CHANNEL_LAYOUT_STEREO) { | 159 channel_layout != media::CHANNEL_LAYOUT_STEREO) { |
168 DLOG(ERROR) << channel_layout | 160 DLOG(ERROR) << channel_layout |
169 << " is not a supported input channel configuration."; | 161 << " is not a supported input channel configuration."; |
170 return false; | 162 return false; |
171 } | 163 } |
172 | 164 |
173 DVLOG(1) << "Audio input hardware sample rate: " << sample_rate; | 165 DVLOG(1) << "Audio input hardware sample rate: " |
174 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate); | 166 << device_info_.device.input.sample_rate; |
| 167 media::AudioSampleRate asr = media::AsAudioSampleRate( |
| 168 device_info_.device.input.sample_rate); |
175 if (asr != media::kUnexpectedAudioSampleRate) { | 169 if (asr != media::kUnexpectedAudioSampleRate) { |
176 UMA_HISTOGRAM_ENUMERATION( | 170 UMA_HISTOGRAM_ENUMERATION( |
177 "WebRTC.AudioInputSampleRate", asr, media::kUnexpectedAudioSampleRate); | 171 "WebRTC.AudioInputSampleRate", asr, media::kUnexpectedAudioSampleRate); |
178 } else { | 172 } else { |
179 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected", sample_rate); | 173 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected", |
| 174 device_info_.device.input.sample_rate); |
180 } | 175 } |
181 | 176 |
182 // Verify that the reported input hardware sample rate is supported | 177 // Verify that the reported input hardware sample rate is supported |
183 // on the current platform. | 178 // on the current platform. |
184 if (std::find(&kValidInputRates[0], | 179 if (std::find(&kValidInputRates[0], |
185 &kValidInputRates[0] + arraysize(kValidInputRates), | 180 &kValidInputRates[0] + arraysize(kValidInputRates), |
186 sample_rate) == | 181 device_info_.device.input.sample_rate) == |
187 &kValidInputRates[arraysize(kValidInputRates)]) { | 182 &kValidInputRates[arraysize(kValidInputRates)]) { |
188 DLOG(ERROR) << sample_rate << " is not a supported input rate."; | 183 DLOG(ERROR) << device_info_.device.input.sample_rate |
| 184 << " is not a supported input rate."; |
189 return false; | 185 return false; |
190 } | 186 } |
191 | 187 |
192 // Create and configure the default audio capturing source. The |source_| | 188 // Create and configure the default audio capturing source. |
193 // will be overwritten if an external client later calls SetCapturerSource() | 189 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id_), |
194 // providing an alternative media::AudioCapturerSource. | |
195 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id), | |
196 channel_layout, | 190 channel_layout, |
197 static_cast<float>(sample_rate), | 191 static_cast<float>(device_info_.device.input.sample_rate), |
198 effects, | 192 device_info_.device.input.effects, |
199 constraints); | 193 constraints_); |
| 194 |
| 195 // Add the capturer to the WebRtcAudioDeviceImpl since it needs some hardware |
| 196 // information from the capturer. |
| 197 if (audio_device_) |
| 198 audio_device_->AddAudioCapturer(this); |
200 | 199 |
201 return true; | 200 return true; |
202 } | 201 } |
203 | 202 |
204 WebRtcAudioCapturer::WebRtcAudioCapturer() | 203 WebRtcAudioCapturer::WebRtcAudioCapturer( |
205 : running_(false), | 204 int render_view_id, |
206 render_view_id_(-1), | 205 const StreamDeviceInfo& device_info, |
207 hardware_buffer_size_(0), | 206 const blink::WebMediaConstraints& constraints, |
208 session_id_(0), | 207 WebRtcAudioDeviceImpl* audio_device) |
| 208 : constraints_(constraints), |
| 209 running_(false), |
| 210 render_view_id_(render_view_id), |
| 211 device_info_(device_info), |
209 volume_(0), | 212 volume_(0), |
210 peer_connection_mode_(false), | 213 peer_connection_mode_(false), |
211 output_sample_rate_(0), | |
212 output_frames_per_buffer_(0), | |
213 key_pressed_(false), | 214 key_pressed_(false), |
214 need_audio_processing_(false) { | 215 need_audio_processing_(false), |
| 216 audio_device_(audio_device) { |
215 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; | 217 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()"; |
216 } | 218 } |
217 | 219 |
218 WebRtcAudioCapturer::~WebRtcAudioCapturer() { | 220 WebRtcAudioCapturer::~WebRtcAudioCapturer() { |
219 DCHECK(thread_checker_.CalledOnValidThread()); | 221 DCHECK(thread_checker_.CalledOnValidThread()); |
220 DCHECK(tracks_.IsEmpty()); | 222 DCHECK(tracks_.IsEmpty()); |
221 DCHECK(!running_); | 223 DCHECK(!running_); |
222 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; | 224 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()"; |
223 } | 225 } |
224 | 226 |
(...skipping 13 matching lines...) Expand all Loading... |
238 } | 240 } |
239 | 241 |
240 // Start the source if the first audio track is connected to the capturer. | 242 // Start the source if the first audio track is connected to the capturer. |
241 // Start() will do nothing if the capturer has already been started. | 243 // Start() will do nothing if the capturer has already been started. |
242 Start(); | 244 Start(); |
243 | 245 |
244 } | 246 } |
245 | 247 |
246 void WebRtcAudioCapturer::RemoveTrack(WebRtcLocalAudioTrack* track) { | 248 void WebRtcAudioCapturer::RemoveTrack(WebRtcLocalAudioTrack* track) { |
247 DCHECK(thread_checker_.CalledOnValidThread()); | 249 DCHECK(thread_checker_.CalledOnValidThread()); |
| 250 base::AutoLock auto_lock(lock_); |
248 | 251 |
249 bool stop_source = false; | 252 scoped_refptr<TrackOwner> removed_item = |
250 { | 253 tracks_.Remove(TrackOwner::TrackWrapper(track)); |
251 base::AutoLock auto_lock(lock_); | |
252 | 254 |
253 scoped_refptr<TrackOwner> removed_item = | 255 // Clear the delegate to ensure that no more capture callbacks will |
254 tracks_.Remove(TrackOwner::TrackWrapper(track)); | 256 // be sent to this sink. Also avoids a possible crash which can happen |
255 | 257 // if this method is called while capturing is active. |
256 // Clear the delegate to ensure that no more capture callbacks will | 258 if (removed_item.get()) |
257 // be sent to this sink. Also avoids a possible crash which can happen | 259 removed_item->Reset(); |
258 // if this method is called while capturing is active. | |
259 if (removed_item.get()) | |
260 removed_item->Reset(); | |
261 | |
262 // Stop the source if the last audio track is going away. | |
263 stop_source = tracks_.IsEmpty(); | |
264 } | |
265 | |
266 if (stop_source) | |
267 Stop(); | |
268 } | 260 } |
269 | 261 |
270 void WebRtcAudioCapturer::SetCapturerSource( | 262 void WebRtcAudioCapturer::SetCapturerSource( |
271 const scoped_refptr<media::AudioCapturerSource>& source, | 263 const scoped_refptr<media::AudioCapturerSource>& source, |
272 media::ChannelLayout channel_layout, | 264 media::ChannelLayout channel_layout, |
273 float sample_rate, | 265 float sample_rate, |
274 int effects, | 266 int effects, |
275 const blink::WebMediaConstraints& constraints) { | 267 const blink::WebMediaConstraints& constraints) { |
276 DCHECK(thread_checker_.CalledOnValidThread()); | 268 DCHECK(thread_checker_.CalledOnValidThread()); |
277 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," | 269 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << "," |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 { | 301 { |
310 base::AutoLock auto_lock(lock_); | 302 base::AutoLock auto_lock(lock_); |
311 audio_processor_ = new_audio_processor; | 303 audio_processor_ = new_audio_processor; |
312 need_audio_processing_ = NeedsAudioProcessing(constraints, effects); | 304 need_audio_processing_ = NeedsAudioProcessing(constraints, effects); |
313 | 305 |
314 // Notify all tracks about the new format. | 306 // Notify all tracks about the new format. |
315 tracks_.TagAll(); | 307 tracks_.TagAll(); |
316 } | 308 } |
317 | 309 |
318 if (source.get()) | 310 if (source.get()) |
319 source->Initialize(params, this, session_id_); | 311 source->Initialize(params, this, session_id()); |
320 | 312 |
321 if (restart_source) | 313 if (restart_source) |
322 Start(); | 314 Start(); |
323 } | 315 } |
324 | 316 |
325 void WebRtcAudioCapturer::EnablePeerConnectionMode() { | 317 void WebRtcAudioCapturer::EnablePeerConnectionMode() { |
326 DCHECK(thread_checker_.CalledOnValidThread()); | 318 DCHECK(thread_checker_.CalledOnValidThread()); |
327 DVLOG(1) << "EnablePeerConnectionMode"; | 319 DVLOG(1) << "EnablePeerConnectionMode"; |
328 // Do nothing if the peer connection mode has been enabled. | 320 // Do nothing if the peer connection mode has been enabled. |
329 if (peer_connection_mode_) | 321 if (peer_connection_mode_) |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 base::AutoLock auto_lock(lock_); | 371 base::AutoLock auto_lock(lock_); |
380 if (!running_) | 372 if (!running_) |
381 return; | 373 return; |
382 | 374 |
383 source = source_; | 375 source = source_; |
384 tracks = tracks_.Items(); | 376 tracks = tracks_.Items(); |
385 tracks_.Clear(); | 377 tracks_.Clear(); |
386 running_ = false; | 378 running_ = false; |
387 } | 379 } |
388 | 380 |
| 381 // Remove the capturer object from the WebRtcAudioDeviceImpl. |
| 382 if (audio_device_) |
| 383 audio_device_->RemoveAudioCapturer(this); |
| 384 |
389 for (TrackList::ItemList::const_iterator it = tracks.begin(); | 385 for (TrackList::ItemList::const_iterator it = tracks.begin(); |
390 it != tracks.end(); | 386 it != tracks.end(); |
391 ++it) { | 387 ++it) { |
392 (*it)->Stop(); | 388 (*it)->Stop(); |
393 } | 389 } |
394 | 390 |
395 if (source.get()) | 391 if (source.get()) |
396 source->Stop(); | 392 source->Stop(); |
397 } | 393 } |
398 | 394 |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
503 base::AutoLock auto_lock(lock_); | 499 base::AutoLock auto_lock(lock_); |
504 return audio_processor_ ? | 500 return audio_processor_ ? |
505 audio_processor_->InputFormat() : media::AudioParameters(); | 501 audio_processor_->InputFormat() : media::AudioParameters(); |
506 } | 502 } |
507 | 503 |
508 bool WebRtcAudioCapturer::GetPairedOutputParameters( | 504 bool WebRtcAudioCapturer::GetPairedOutputParameters( |
509 int* session_id, | 505 int* session_id, |
510 int* output_sample_rate, | 506 int* output_sample_rate, |
511 int* output_frames_per_buffer) const { | 507 int* output_frames_per_buffer) const { |
512 // Don't set output parameters unless all of them are valid. | 508 // Don't set output parameters unless all of them are valid. |
513 if (session_id_ <= 0 || !output_sample_rate_ || !output_frames_per_buffer_) | 509 if (device_info_.session_id <= 0 || |
| 510 !device_info_.device.matched_output.sample_rate || |
| 511 !device_info_.device.matched_output.frames_per_buffer) |
514 return false; | 512 return false; |
515 | 513 |
516 *session_id = session_id_; | 514 *session_id = device_info_.session_id; |
517 *output_sample_rate = output_sample_rate_; | 515 *output_sample_rate = device_info_.device.matched_output.sample_rate; |
518 *output_frames_per_buffer = output_frames_per_buffer_; | 516 *output_frames_per_buffer = |
| 517 device_info_.device.matched_output.frames_per_buffer; |
519 | 518 |
520 return true; | 519 return true; |
521 } | 520 } |
522 | 521 |
523 int WebRtcAudioCapturer::GetBufferSize(int sample_rate) const { | 522 int WebRtcAudioCapturer::GetBufferSize(int sample_rate) const { |
524 DCHECK(thread_checker_.CalledOnValidThread()); | 523 DCHECK(thread_checker_.CalledOnValidThread()); |
525 #if defined(OS_ANDROID) | 524 #if defined(OS_ANDROID) |
526 // TODO(henrika): Tune and adjust buffer size on Android. | 525 // TODO(henrika): Tune and adjust buffer size on Android. |
527 return (2 * sample_rate / 100); | 526 return (2 * sample_rate / 100); |
528 #endif | 527 #endif |
529 | 528 |
530 // PeerConnection is running at a buffer size of 10ms data. A multiple of | 529 // PeerConnection is running at a buffer size of 10ms data. A multiple of |
531 // 10ms as the buffer size can give the best performance to PeerConnection. | 530 // 10ms as the buffer size can give the best performance to PeerConnection. |
532 int peer_connection_buffer_size = sample_rate / 100; | 531 int peer_connection_buffer_size = sample_rate / 100; |
533 | 532 |
534 // Use the native hardware buffer size in non peer connection mode when the | 533 // Use the native hardware buffer size in non peer connection mode when the |
535 // platform is using a native buffer size smaller than the PeerConnection | 534 // platform is using a native buffer size smaller than the PeerConnection |
536 // buffer size. | 535 // buffer size. |
537 if (!peer_connection_mode_ && hardware_buffer_size_ && | 536 int hardware_buffer_size = device_info_.device.input.frames_per_buffer; |
538 hardware_buffer_size_ <= peer_connection_buffer_size) { | 537 if (!peer_connection_mode_ && hardware_buffer_size && |
539 return hardware_buffer_size_; | 538 hardware_buffer_size <= peer_connection_buffer_size) { |
| 539 return hardware_buffer_size; |
540 } | 540 } |
541 | 541 |
542 return (sample_rate / 100); | 542 return (sample_rate / 100); |
543 } | 543 } |
544 | 544 |
545 void WebRtcAudioCapturer::GetAudioProcessingParams( | 545 void WebRtcAudioCapturer::GetAudioProcessingParams( |
546 base::TimeDelta* delay, int* volume, bool* key_pressed) { | 546 base::TimeDelta* delay, int* volume, bool* key_pressed) { |
547 base::AutoLock auto_lock(lock_); | 547 base::AutoLock auto_lock(lock_); |
548 *delay = audio_delay_; | 548 *delay = audio_delay_; |
549 *volume = volume_; | 549 *volume = volume_; |
(...skipping 14 matching lines...) Expand all Loading... |
564 | 564 |
565 audio_processor = audio_processor_; | 565 audio_processor = audio_processor_; |
566 } | 566 } |
567 | 567 |
568 audio_processor->PushRenderData(render_audio, sample_rate, | 568 audio_processor->PushRenderData(render_audio, sample_rate, |
569 number_of_channels, | 569 number_of_channels, |
570 number_of_frames, | 570 number_of_frames, |
571 render_delay); | 571 render_delay); |
572 } | 572 } |
573 | 573 |
| 574 void WebRtcAudioCapturer::SetCapturerSourceForTesting( |
| 575 const scoped_refptr<media::AudioCapturerSource>& source, |
| 576 media::AudioParameters params) { |
| 577 // Create a new audio stream as source which uses the new source. |
| 578 SetCapturerSource(source, params.channel_layout(), |
| 579 static_cast<float>(params.sample_rate()), |
| 580 params.effects(), |
| 581 constraints_); |
| 582 } |
| 583 |
574 } // namespace content | 584 } // namespace content |
OLD | NEW |