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
3 // found in the LICENSE file.
4
5 #include "content/renderer/media/webrtc_audio_capturer.h"
6
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h"
12 #include "content/child/child_process.h"
13 #include "content/renderer/media/audio_device_factory.h"
14 #include "content/renderer/media/media_stream_audio_processor.h"
15 #include "content/renderer/media/media_stream_audio_processor_options.h"
16 #include "content/renderer/media/media_stream_audio_source.h"
17 #include "content/renderer/media/webrtc_audio_device_impl.h"
18 #include "content/renderer/media/webrtc_local_audio_track.h"
19 #include "content/renderer/media/webrtc_logging.h"
20 #include "media/audio/sample_rates.h"
21
22 namespace content {
23
24 namespace {
25
26 // Time constant for AudioPowerMonitor. See AudioPowerMonitor ctor comments
27 // for semantics. This value was arbitrarily chosen, but seems to work well.
28 const int kPowerMonitorTimeConstantMs = 10;
29
30 // The time between two audio power level samples.
31 const int kPowerMonitorLogIntervalSeconds = 10;
32
33 // Method to check if any of the data in |audio_source| has energy.
HasDataEnergy(const media::AudioBus & audio_source)34 bool HasDataEnergy(const media::AudioBus& audio_source) {
35 for (int ch = 0; ch < audio_source.channels(); ++ch) {
36 const float* channel_ptr = audio_source.channel(ch);
37 for (int frame = 0; frame < audio_source.frames(); ++frame) {
38 if (channel_ptr[frame] != 0)
39 return true;
40 }
41 }
42
43 // All the data is zero.
44 return false;
45 }
46
47 } // namespace
48
49 // Reference counted container of WebRtcLocalAudioTrack delegate.
50 // TODO(xians): Switch to MediaStreamAudioSinkOwner.
51 class WebRtcAudioCapturer::TrackOwner
52 : public base::RefCountedThreadSafe<WebRtcAudioCapturer::TrackOwner> {
53 public:
TrackOwner(WebRtcLocalAudioTrack * track)54 explicit TrackOwner(WebRtcLocalAudioTrack* track)
55 : delegate_(track) {}
56
Capture(const int16 * audio_data,base::TimeDelta delay,double volume,bool key_pressed,bool need_audio_processing,bool force_report_nonzero_energy)57 void Capture(const int16* audio_data,
58 base::TimeDelta delay,
59 double volume,
60 bool key_pressed,
61 bool need_audio_processing,
62 bool force_report_nonzero_energy) {
63 base::AutoLock lock(lock_);
64 if (delegate_) {
65 delegate_->Capture(audio_data,
66 delay,
67 volume,
68 key_pressed,
69 need_audio_processing,
70 force_report_nonzero_energy);
71 }
72 }
73
OnSetFormat(const media::AudioParameters & params)74 void OnSetFormat(const media::AudioParameters& params) {
75 base::AutoLock lock(lock_);
76 if (delegate_)
77 delegate_->OnSetFormat(params);
78 }
79
SetAudioProcessor(const scoped_refptr<MediaStreamAudioProcessor> & processor)80 void SetAudioProcessor(
81 const scoped_refptr<MediaStreamAudioProcessor>& processor) {
82 base::AutoLock lock(lock_);
83 if (delegate_)
84 delegate_->SetAudioProcessor(processor);
85 }
86
Reset()87 void Reset() {
88 base::AutoLock lock(lock_);
89 delegate_ = NULL;
90 }
91
Stop()92 void Stop() {
93 base::AutoLock lock(lock_);
94 DCHECK(delegate_);
95
96 // This can be reentrant so reset |delegate_| before calling out.
97 WebRtcLocalAudioTrack* temp = delegate_;
98 delegate_ = NULL;
99 temp->Stop();
100 }
101
102 // Wrapper which allows to use std::find_if() when adding and removing
103 // sinks to/from the list.
104 struct TrackWrapper {
TrackWrappercontent::WebRtcAudioCapturer::TrackOwner::TrackWrapper105 TrackWrapper(WebRtcLocalAudioTrack* track) : track_(track) {}
operator ()content::WebRtcAudioCapturer::TrackOwner::TrackWrapper106 bool operator()(
107 const scoped_refptr<WebRtcAudioCapturer::TrackOwner>& owner) const {
108 return owner->IsEqual(track_);
109 }
110 WebRtcLocalAudioTrack* track_;
111 };
112
113 protected:
~TrackOwner()114 virtual ~TrackOwner() {}
115
116 private:
117 friend class base::RefCountedThreadSafe<WebRtcAudioCapturer::TrackOwner>;
118
IsEqual(const WebRtcLocalAudioTrack * other) const119 bool IsEqual(const WebRtcLocalAudioTrack* other) const {
120 base::AutoLock lock(lock_);
121 return (other == delegate_);
122 }
123
124 // Do NOT reference count the |delegate_| to avoid cyclic reference counting.
125 WebRtcLocalAudioTrack* delegate_;
126 mutable base::Lock lock_;
127
128 DISALLOW_COPY_AND_ASSIGN(TrackOwner);
129 };
130
131 // static
CreateCapturer(int render_view_id,const StreamDeviceInfo & device_info,const blink::WebMediaConstraints & constraints,WebRtcAudioDeviceImpl * audio_device,MediaStreamAudioSource * audio_source)132 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer(
133 int render_view_id, const StreamDeviceInfo& device_info,
134 const blink::WebMediaConstraints& constraints,
135 WebRtcAudioDeviceImpl* audio_device,
136 MediaStreamAudioSource* audio_source) {
137 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer(
138 render_view_id, device_info, constraints, audio_device, audio_source);
139 if (capturer->Initialize())
140 return capturer;
141
142 return NULL;
143 }
144
Initialize()145 bool WebRtcAudioCapturer::Initialize() {
146 DCHECK(thread_checker_.CalledOnValidThread());
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));
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 MediaAudioConstraints audio_constraints(constraints_,
169 device_info_.device.input.effects);
170 if (!audio_constraints.IsValid())
171 return false;
172
173 media::ChannelLayout channel_layout = static_cast<media::ChannelLayout>(
174 device_info_.device.input.channel_layout);
175
176 // If KEYBOARD_MIC effect is set, change the layout to the corresponding
177 // layout that includes the keyboard mic.
178 if ((device_info_.device.input.effects &
179 media::AudioParameters::KEYBOARD_MIC) &&
180 MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled() &&
181 audio_constraints.GetProperty(
182 MediaAudioConstraints::kGoogExperimentalNoiseSuppression)) {
183 if (channel_layout == media::CHANNEL_LAYOUT_STEREO) {
184 channel_layout = media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC;
185 DVLOG(1) << "Changed stereo layout to stereo + keyboard mic layout due "
186 << "to KEYBOARD_MIC effect.";
187 } else {
188 DVLOG(1) << "KEYBOARD_MIC effect ignored, not compatible with layout "
189 << channel_layout;
190 }
191 }
192
193 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout;
194 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout",
195 channel_layout, media::CHANNEL_LAYOUT_MAX + 1);
196
197 // Verify that the reported input channel configuration is supported.
198 if (channel_layout != media::CHANNEL_LAYOUT_MONO &&
199 channel_layout != media::CHANNEL_LAYOUT_STEREO &&
200 channel_layout != media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
201 DLOG(ERROR) << channel_layout
202 << " is not a supported input channel configuration.";
203 return false;
204 }
205
206 DVLOG(1) << "Audio input hardware sample rate: "
207 << device_info_.device.input.sample_rate;
208 media::AudioSampleRate asr;
209 if (media::ToAudioSampleRate(device_info_.device.input.sample_rate, &asr)) {
210 UMA_HISTOGRAM_ENUMERATION(
211 "WebRTC.AudioInputSampleRate", asr, media::kAudioSampleRateMax + 1);
212 } else {
213 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected",
214 device_info_.device.input.sample_rate);
215 }
216
217 // Create and configure the default audio capturing source.
218 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id_),
219 channel_layout,
220 static_cast<float>(device_info_.device.input.sample_rate));
221
222 // Add the capturer to the WebRtcAudioDeviceImpl since it needs some hardware
223 // information from the capturer.
224 if (audio_device_)
225 audio_device_->AddAudioCapturer(this);
226
227 return true;
228 }
229
WebRtcAudioCapturer(int render_view_id,const StreamDeviceInfo & device_info,const blink::WebMediaConstraints & constraints,WebRtcAudioDeviceImpl * audio_device,MediaStreamAudioSource * audio_source)230 WebRtcAudioCapturer::WebRtcAudioCapturer(
231 int render_view_id,
232 const StreamDeviceInfo& device_info,
233 const blink::WebMediaConstraints& constraints,
234 WebRtcAudioDeviceImpl* audio_device,
235 MediaStreamAudioSource* audio_source)
236 : constraints_(constraints),
237 audio_processor_(
238 new rtc::RefCountedObject<MediaStreamAudioProcessor>(
239 constraints, device_info.device.input.effects, audio_device)),
240 running_(false),
241 render_view_id_(render_view_id),
242 device_info_(device_info),
243 volume_(0),
244 peer_connection_mode_(false),
245 key_pressed_(false),
246 need_audio_processing_(false),
247 audio_device_(audio_device),
248 audio_source_(audio_source),
249 audio_power_monitor_(
250 device_info_.device.input.sample_rate,
251 base::TimeDelta::FromMilliseconds(kPowerMonitorTimeConstantMs)) {
252 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()";
253 }
254
~WebRtcAudioCapturer()255 WebRtcAudioCapturer::~WebRtcAudioCapturer() {
256 DCHECK(thread_checker_.CalledOnValidThread());
257 DCHECK(tracks_.IsEmpty());
258 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()";
259 Stop();
260 }
261
AddTrack(WebRtcLocalAudioTrack * track)262 void WebRtcAudioCapturer::AddTrack(WebRtcLocalAudioTrack* track) {
263 DCHECK(track);
264 DVLOG(1) << "WebRtcAudioCapturer::AddTrack()";
265
266 {
267 base::AutoLock auto_lock(lock_);
268 // Verify that |track| is not already added to the list.
269 DCHECK(!tracks_.Contains(TrackOwner::TrackWrapper(track)));
270
271 // Add with a tag, so we remember to call OnSetFormat() on the new
272 // track.
273 scoped_refptr<TrackOwner> track_owner(new TrackOwner(track));
274 tracks_.AddAndTag(track_owner.get());
275 }
276 }
277
RemoveTrack(WebRtcLocalAudioTrack * track)278 void WebRtcAudioCapturer::RemoveTrack(WebRtcLocalAudioTrack* track) {
279 DCHECK(thread_checker_.CalledOnValidThread());
280 DVLOG(1) << "WebRtcAudioCapturer::RemoveTrack()";
281 bool stop_source = false;
282 {
283 base::AutoLock auto_lock(lock_);
284
285 scoped_refptr<TrackOwner> removed_item =
286 tracks_.Remove(TrackOwner::TrackWrapper(track));
287
288 // Clear the delegate to ensure that no more capture callbacks will
289 // be sent to this sink. Also avoids a possible crash which can happen
290 // if this method is called while capturing is active.
291 if (removed_item.get()) {
292 removed_item->Reset();
293 stop_source = tracks_.IsEmpty();
294 }
295 }
296 if (stop_source) {
297 // Since WebRtcAudioCapturer does not inherit MediaStreamAudioSource,
298 // and instead MediaStreamAudioSource is composed of a WebRtcAudioCapturer,
299 // we have to call StopSource on the MediaStreamSource. This will call
300 // MediaStreamAudioSource::DoStopSource which in turn call
301 // WebRtcAudioCapturerer::Stop();
302 audio_source_->StopSource();
303 }
304 }
305
SetCapturerSource(const scoped_refptr<media::AudioCapturerSource> & source,media::ChannelLayout channel_layout,float sample_rate)306 void WebRtcAudioCapturer::SetCapturerSource(
307 const scoped_refptr<media::AudioCapturerSource>& source,
308 media::ChannelLayout channel_layout,
309 float sample_rate) {
310 DCHECK(thread_checker_.CalledOnValidThread());
311 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << ","
312 << "sample_rate=" << sample_rate << ")";
313 scoped_refptr<media::AudioCapturerSource> old_source;
314 {
315 base::AutoLock auto_lock(lock_);
316 if (source_.get() == source.get())
317 return;
318
319 source_.swap(old_source);
320 source_ = source;
321
322 // Reset the flag to allow starting the new source.
323 running_ = false;
324 }
325
326 DVLOG(1) << "Switching to a new capture source.";
327 if (old_source.get())
328 old_source->Stop();
329
330 // Dispatch the new parameters both to the sink(s) and to the new source,
331 // also apply the new |constraints|.
332 // The idea is to get rid of any dependency of the microphone parameters
333 // which would normally be used by default.
334 // bits_per_sample is always 16 for now.
335 int buffer_size = GetBufferSize(sample_rate);
336 media::AudioParameters params(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
337 channel_layout, sample_rate,
338 16, buffer_size,
339 device_info_.device.input.effects);
340
341 {
342 base::AutoLock auto_lock(lock_);
343 // Notify the |audio_processor_| of the new format.
344 audio_processor_->OnCaptureFormatChanged(params);
345
346 MediaAudioConstraints audio_constraints(constraints_,
347 device_info_.device.input.effects);
348 need_audio_processing_ = audio_constraints.NeedsAudioProcessing();
349 // Notify all tracks about the new format.
350 tracks_.TagAll();
351 }
352
353 if (source.get())
354 source->Initialize(params, this, session_id());
355
356 Start();
357 }
358
EnablePeerConnectionMode()359 void WebRtcAudioCapturer::EnablePeerConnectionMode() {
360 DCHECK(thread_checker_.CalledOnValidThread());
361 DVLOG(1) << "EnablePeerConnectionMode";
362 // Do nothing if the peer connection mode has been enabled.
363 if (peer_connection_mode_)
364 return;
365
366 peer_connection_mode_ = true;
367 int render_view_id = -1;
368 media::AudioParameters input_params;
369 {
370 base::AutoLock auto_lock(lock_);
371 // Simply return if there is no existing source or the |render_view_id_| is
372 // not valid.
373 if (!source_.get() || render_view_id_== -1)
374 return;
375
376 render_view_id = render_view_id_;
377 input_params = audio_processor_->InputFormat();
378 }
379
380 // Do nothing if the current buffer size is the WebRtc native buffer size.
381 if (GetBufferSize(input_params.sample_rate()) ==
382 input_params.frames_per_buffer()) {
383 return;
384 }
385
386 // Create a new audio stream as source which will open the hardware using
387 // WebRtc native buffer size.
388 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id),
389 input_params.channel_layout(),
390 static_cast<float>(input_params.sample_rate()));
391 }
392
Start()393 void WebRtcAudioCapturer::Start() {
394 DCHECK(thread_checker_.CalledOnValidThread());
395 DVLOG(1) << "WebRtcAudioCapturer::Start()";
396 base::AutoLock auto_lock(lock_);
397 if (running_ || !source_.get())
398 return;
399
400 // Start the data source, i.e., start capturing data from the current source.
401 // We need to set the AGC control before starting the stream.
402 source_->SetAutomaticGainControl(true);
403 source_->Start();
404 running_ = true;
405 }
406
Stop()407 void WebRtcAudioCapturer::Stop() {
408 DCHECK(thread_checker_.CalledOnValidThread());
409 DVLOG(1) << "WebRtcAudioCapturer::Stop()";
410 scoped_refptr<media::AudioCapturerSource> source;
411 TrackList::ItemList tracks;
412 {
413 base::AutoLock auto_lock(lock_);
414 if (!running_)
415 return;
416
417 source = source_;
418 tracks = tracks_.Items();
419 tracks_.Clear();
420 running_ = false;
421 }
422
423 // Remove the capturer object from the WebRtcAudioDeviceImpl.
424 if (audio_device_)
425 audio_device_->RemoveAudioCapturer(this);
426
427 for (TrackList::ItemList::const_iterator it = tracks.begin();
428 it != tracks.end();
429 ++it) {
430 (*it)->Stop();
431 }
432
433 if (source.get())
434 source->Stop();
435
436 // Stop the audio processor to avoid feeding render data into the processor.
437 audio_processor_->Stop();
438 }
439
SetVolume(int volume)440 void WebRtcAudioCapturer::SetVolume(int volume) {
441 DVLOG(1) << "WebRtcAudioCapturer::SetVolume()";
442 DCHECK_LE(volume, MaxVolume());
443 double normalized_volume = static_cast<double>(volume) / MaxVolume();
444 base::AutoLock auto_lock(lock_);
445 if (source_.get())
446 source_->SetVolume(normalized_volume);
447 }
448
Volume() const449 int WebRtcAudioCapturer::Volume() const {
450 base::AutoLock auto_lock(lock_);
451 return volume_;
452 }
453
MaxVolume() const454 int WebRtcAudioCapturer::MaxVolume() const {
455 return WebRtcAudioDeviceImpl::kMaxVolumeLevel;
456 }
457
Capture(const media::AudioBus * audio_source,int audio_delay_milliseconds,double volume,bool key_pressed)458 void WebRtcAudioCapturer::Capture(const media::AudioBus* audio_source,
459 int audio_delay_milliseconds,
460 double volume,
461 bool key_pressed) {
462 // This callback is driven by AudioInputDevice::AudioThreadCallback if
463 // |source_| is AudioInputDevice, otherwise it is driven by client's
464 // CaptureCallback.
465 #if defined(OS_WIN) || defined(OS_MACOSX)
466 DCHECK_LE(volume, 1.0);
467 #elif (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_OPENBSD)
468 // We have a special situation on Linux where the microphone volume can be
469 // "higher than maximum". The input volume slider in the sound preference
470 // allows the user to set a scaling that is higher than 100%. It means that
471 // even if the reported maximum levels is N, the actual microphone level can
472 // go up to 1.5x*N and that corresponds to a normalized |volume| of 1.5x.
473 DCHECK_LE(volume, 1.6);
474 #endif
475
476 TrackList::ItemList tracks;
477 TrackList::ItemList tracks_to_notify_format;
478 int current_volume = 0;
479 base::TimeDelta audio_delay;
480 bool need_audio_processing = true;
481 {
482 base::AutoLock auto_lock(lock_);
483 if (!running_)
484 return;
485
486 // Map internal volume range of [0.0, 1.0] into [0, 255] used by AGC.
487 // The volume can be higher than 255 on Linux, and it will be cropped to
488 // 255 since AGC does not allow values out of range.
489 volume_ = static_cast<int>((volume * MaxVolume()) + 0.5);
490 current_volume = volume_ > MaxVolume() ? MaxVolume() : volume_;
491 audio_delay = base::TimeDelta::FromMilliseconds(audio_delay_milliseconds);
492 audio_delay_ = audio_delay;
493 key_pressed_ = key_pressed;
494 tracks = tracks_.Items();
495 tracks_.RetrieveAndClearTags(&tracks_to_notify_format);
496
497 // Set the flag to turn on the audio processing in PeerConnection level.
498 // Note that, we turn off the audio processing in PeerConnection if the
499 // processor has already processed the data.
500 need_audio_processing = need_audio_processing_ ?
501 !MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled() : false;
502 }
503
504 DCHECK(audio_processor_->InputFormat().IsValid());
505 DCHECK_EQ(audio_source->channels(),
506 audio_processor_->InputFormat().channels());
507 DCHECK_EQ(audio_source->frames(),
508 audio_processor_->InputFormat().frames_per_buffer());
509
510 // Notify the tracks on when the format changes. This will do nothing if
511 // |tracks_to_notify_format| is empty.
512 media::AudioParameters output_params = audio_processor_->OutputFormat();
513 for (TrackList::ItemList::const_iterator it = tracks_to_notify_format.begin();
514 it != tracks_to_notify_format.end(); ++it) {
515 (*it)->OnSetFormat(output_params);
516 (*it)->SetAudioProcessor(audio_processor_);
517 }
518
519 if ((base::TimeTicks::Now() - last_audio_level_log_time_).InSeconds() >
520 kPowerMonitorLogIntervalSeconds) {
521 audio_power_monitor_.Scan(*audio_source, audio_source->frames());
522
523 last_audio_level_log_time_ = base::TimeTicks::Now();
524
525 std::pair<float, bool> result =
526 audio_power_monitor_.ReadCurrentPowerAndClip();
527 WebRtcLogMessage(base::StringPrintf(
528 "WAC::Capture: current_audio_power=%.2fdBFS.", result.first));
529
530 audio_power_monitor_.Reset();
531 }
532
533 // Figure out if the pre-processed data has any energy or not, the
534 // information will be passed to the track to force the calculator
535 // to report energy in case the post-processed data is zeroed by the audio
536 // processing.
537 const bool force_report_nonzero_energy = HasDataEnergy(*audio_source);
538
539 // Push the data to the processor for processing.
540 audio_processor_->PushCaptureData(audio_source);
541
542 // Process and consume the data in the processor until there is not enough
543 // data in the processor.
544 int16* output = NULL;
545 int new_volume = 0;
546 while (audio_processor_->ProcessAndConsumeData(
547 audio_delay, current_volume, key_pressed, &new_volume, &output)) {
548 // Feed the post-processed data to the tracks.
549 for (TrackList::ItemList::const_iterator it = tracks.begin();
550 it != tracks.end(); ++it) {
551 (*it)->Capture(output, audio_delay, current_volume, key_pressed,
552 need_audio_processing, force_report_nonzero_energy);
553 }
554
555 if (new_volume) {
556 SetVolume(new_volume);
557
558 // Update the |current_volume| to avoid passing the old volume to AGC.
559 current_volume = new_volume;
560 }
561 }
562 }
563
OnCaptureError()564 void WebRtcAudioCapturer::OnCaptureError() {
565 NOTIMPLEMENTED();
566 }
567
source_audio_parameters() const568 media::AudioParameters WebRtcAudioCapturer::source_audio_parameters() const {
569 base::AutoLock auto_lock(lock_);
570 return audio_processor_.get() ? audio_processor_->InputFormat()
571 : media::AudioParameters();
572 }
573
GetPairedOutputParameters(int * session_id,int * output_sample_rate,int * output_frames_per_buffer) const574 bool WebRtcAudioCapturer::GetPairedOutputParameters(
575 int* session_id,
576 int* output_sample_rate,
577 int* output_frames_per_buffer) const {
578 // Don't set output parameters unless all of them are valid.
579 if (device_info_.session_id <= 0 ||
580 !device_info_.device.matched_output.sample_rate ||
581 !device_info_.device.matched_output.frames_per_buffer)
582 return false;
583
584 *session_id = device_info_.session_id;
585 *output_sample_rate = device_info_.device.matched_output.sample_rate;
586 *output_frames_per_buffer =
587 device_info_.device.matched_output.frames_per_buffer;
588
589 return true;
590 }
591
GetBufferSize(int sample_rate) const592 int WebRtcAudioCapturer::GetBufferSize(int sample_rate) const {
593 DCHECK(thread_checker_.CalledOnValidThread());
594 #if defined(OS_ANDROID)
595 // TODO(henrika): Tune and adjust buffer size on Android.
596 return (2 * sample_rate / 100);
597 #endif
598
599 // PeerConnection is running at a buffer size of 10ms data. A multiple of
600 // 10ms as the buffer size can give the best performance to PeerConnection.
601 int peer_connection_buffer_size = sample_rate / 100;
602
603 // Use the native hardware buffer size in non peer connection mode when the
604 // platform is using a native buffer size smaller than the PeerConnection
605 // buffer size.
606 int hardware_buffer_size = device_info_.device.input.frames_per_buffer;
607 if (!peer_connection_mode_ && hardware_buffer_size &&
608 hardware_buffer_size <= peer_connection_buffer_size) {
609 return hardware_buffer_size;
610 }
611
612 return (sample_rate / 100);
613 }
614
GetAudioProcessingParams(base::TimeDelta * delay,int * volume,bool * key_pressed)615 void WebRtcAudioCapturer::GetAudioProcessingParams(
616 base::TimeDelta* delay, int* volume, bool* key_pressed) {
617 base::AutoLock auto_lock(lock_);
618 *delay = audio_delay_;
619 *volume = volume_;
620 *key_pressed = key_pressed_;
621 }
622
SetCapturerSourceForTesting(const scoped_refptr<media::AudioCapturerSource> & source,media::AudioParameters params)623 void WebRtcAudioCapturer::SetCapturerSourceForTesting(
624 const scoped_refptr<media::AudioCapturerSource>& source,
625 media::AudioParameters params) {
626 // Create a new audio stream as source which uses the new source.
627 SetCapturerSource(source, params.channel_layout(),
628 static_cast<float>(params.sample_rate()));
629 }
630
631 } // namespace content
632