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/webrtc_audio_device_impl.h"
15 #include "content/renderer/media/webrtc_local_audio_track.h"
16 #include "content/renderer/media/webrtc_logging.h"
17 #include "media/audio/sample_rates.h"
18
19 namespace content {
20
21 namespace {
22
23 // Supported hardware sample rates for input and output sides.
24 #if defined(OS_WIN) || defined(OS_MACOSX)
25 // media::GetAudioInputHardwareSampleRate() asks the audio layer
26 // for its current sample rate (set by the user) on Windows and Mac OS X.
27 // The listed rates below adds restrictions and WebRtcAudioDeviceImpl::Init()
28 // will fail if the user selects any rate outside these ranges.
29 const int kValidInputRates[] = {96000, 48000, 44100, 32000, 16000, 8000};
30 #elif defined(OS_LINUX) || defined(OS_OPENBSD)
31 const int kValidInputRates[] = {48000, 44100};
32 #elif defined(OS_ANDROID)
33 const int kValidInputRates[] = {48000, 44100};
34 #else
35 const int kValidInputRates[] = {44100};
36 #endif
37
38 } // namespace
39
40 // Reference counted container of WebRtcLocalAudioTrack delegate.
41 class WebRtcAudioCapturer::TrackOwner
42 : public base::RefCountedThreadSafe<WebRtcAudioCapturer::TrackOwner> {
43 public:
TrackOwner(WebRtcLocalAudioTrack * track)44 explicit TrackOwner(WebRtcLocalAudioTrack* track)
45 : delegate_(track) {}
46
Capture(media::AudioBus * audio_source,int audio_delay_milliseconds,double volume,bool key_pressed)47 void Capture(media::AudioBus* audio_source,
48 int audio_delay_milliseconds,
49 double volume,
50 bool key_pressed) {
51 base::AutoLock lock(lock_);
52 if (delegate_) {
53 delegate_->Capture(audio_source,
54 audio_delay_milliseconds,
55 volume,
56 key_pressed);
57 }
58 }
59
OnSetFormat(const media::AudioParameters & params)60 void OnSetFormat(const media::AudioParameters& params) {
61 base::AutoLock lock(lock_);
62 if (delegate_)
63 delegate_->OnSetFormat(params);
64 }
65
Reset()66 void Reset() {
67 base::AutoLock lock(lock_);
68 delegate_ = NULL;
69 }
70
Stop()71 void Stop() {
72 base::AutoLock lock(lock_);
73 DCHECK(delegate_);
74
75 // This can be reentrant so reset |delegate_| before calling out.
76 WebRtcLocalAudioTrack* temp = delegate_;
77 delegate_ = NULL;
78 temp->Stop();
79 }
80
81 // Wrapper which allows to use std::find_if() when adding and removing
82 // sinks to/from the list.
83 struct TrackWrapper {
TrackWrappercontent::WebRtcAudioCapturer::TrackOwner::TrackWrapper84 TrackWrapper(WebRtcLocalAudioTrack* track) : track_(track) {}
operator ()content::WebRtcAudioCapturer::TrackOwner::TrackWrapper85 bool operator()(
86 const scoped_refptr<WebRtcAudioCapturer::TrackOwner>& owner) const {
87 return owner->IsEqual(track_);
88 }
89 WebRtcLocalAudioTrack* track_;
90 };
91
92 protected:
~TrackOwner()93 virtual ~TrackOwner() {}
94
95 private:
96 friend class base::RefCountedThreadSafe<WebRtcAudioCapturer::TrackOwner>;
97
IsEqual(const WebRtcLocalAudioTrack * other) const98 bool IsEqual(const WebRtcLocalAudioTrack* other) const {
99 base::AutoLock lock(lock_);
100 return (other == delegate_);
101 }
102
103 // Do NOT reference count the |delegate_| to avoid cyclic reference counting.
104 WebRtcLocalAudioTrack* delegate_;
105 mutable base::Lock lock_;
106
107 DISALLOW_COPY_AND_ASSIGN(TrackOwner);
108 };
109
110 // static
CreateCapturer()111 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() {
112 scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer();
113 return capturer;
114 }
115
Reconfigure(int sample_rate,media::ChannelLayout channel_layout,int effects)116 void WebRtcAudioCapturer::Reconfigure(int sample_rate,
117 media::ChannelLayout channel_layout,
118 int effects) {
119 DCHECK(thread_checker_.CalledOnValidThread());
120 int buffer_size = GetBufferSize(sample_rate);
121 DVLOG(1) << "Using WebRTC input buffer size: " << buffer_size;
122
123 media::AudioParameters::Format format =
124 media::AudioParameters::AUDIO_PCM_LOW_LATENCY;
125
126 // bits_per_sample is always 16 for now.
127 int bits_per_sample = 16;
128 media::AudioParameters params(format, channel_layout, 0, sample_rate,
129 bits_per_sample, buffer_size, effects);
130 {
131 base::AutoLock auto_lock(lock_);
132 params_ = params;
133
134 // Notify all tracks about the new format.
135 tracks_.TagAll();
136 }
137 }
138
Initialize(int render_view_id,media::ChannelLayout channel_layout,int sample_rate,int buffer_size,int session_id,const std::string & device_id,int paired_output_sample_rate,int paired_output_frames_per_buffer,int effects)139 bool WebRtcAudioCapturer::Initialize(int render_view_id,
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());
149 DVLOG(1) << "WebRtcAudioCapturer::Initialize()";
150
151 DVLOG(1) << "Audio input hardware channel layout: " << channel_layout;
152 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioInputChannelLayout",
153 channel_layout, media::CHANNEL_LAYOUT_MAX);
154
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.
182 if (channel_layout != media::CHANNEL_LAYOUT_MONO &&
183 channel_layout != media::CHANNEL_LAYOUT_STEREO) {
184 DLOG(ERROR) << channel_layout
185 << " is not a supported input channel configuration.";
186 return false;
187 }
188
189 DVLOG(1) << "Audio input hardware sample rate: " << sample_rate;
190 media::AudioSampleRate asr = media::AsAudioSampleRate(sample_rate);
191 if (asr != media::kUnexpectedAudioSampleRate) {
192 UMA_HISTOGRAM_ENUMERATION(
193 "WebRTC.AudioInputSampleRate", asr, media::kUnexpectedAudioSampleRate);
194 } else {
195 UMA_HISTOGRAM_COUNTS("WebRTC.AudioInputSampleRateUnexpected", sample_rate);
196 }
197
198 // Verify that the reported input hardware sample rate is supported
199 // on the current platform.
200 if (std::find(&kValidInputRates[0],
201 &kValidInputRates[0] + arraysize(kValidInputRates),
202 sample_rate) ==
203 &kValidInputRates[arraysize(kValidInputRates)]) {
204 DLOG(ERROR) << sample_rate << " is not a supported input rate.";
205 return false;
206 }
207
208 // Create and configure the default audio capturing source. The |source_|
209 // will be overwritten if an external client later calls SetCapturerSource()
210 // providing an alternative media::AudioCapturerSource.
211 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id),
212 channel_layout,
213 static_cast<float>(sample_rate),
214 effects);
215
216 return true;
217 }
218
WebRtcAudioCapturer()219 WebRtcAudioCapturer::WebRtcAudioCapturer()
220 : running_(false),
221 render_view_id_(-1),
222 hardware_buffer_size_(0),
223 session_id_(0),
224 volume_(0),
225 peer_connection_mode_(false),
226 output_sample_rate_(0),
227 output_frames_per_buffer_(0),
228 key_pressed_(false) {
229 DVLOG(1) << "WebRtcAudioCapturer::WebRtcAudioCapturer()";
230 }
231
~WebRtcAudioCapturer()232 WebRtcAudioCapturer::~WebRtcAudioCapturer() {
233 DCHECK(thread_checker_.CalledOnValidThread());
234 DCHECK(tracks_.IsEmpty());
235 DCHECK(!running_);
236 DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()";
237 }
238
AddTrack(WebRtcLocalAudioTrack * track)239 void WebRtcAudioCapturer::AddTrack(WebRtcLocalAudioTrack* track) {
240 DCHECK(track);
241 DVLOG(1) << "WebRtcAudioCapturer::AddTrack()";
242
243 {
244 base::AutoLock auto_lock(lock_);
245 // Verify that |track| is not already added to the list.
246 DCHECK(!tracks_.Contains(TrackOwner::TrackWrapper(track)));
247
248 // Add with a tag, so we remember to call OnSetFormat() on the new
249 // track.
250 scoped_refptr<TrackOwner> track_owner(new TrackOwner(track));
251 tracks_.AddAndTag(track_owner);
252 }
253
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.
256 Start();
257
258 }
259
RemoveTrack(WebRtcLocalAudioTrack * track)260 void WebRtcAudioCapturer::RemoveTrack(WebRtcLocalAudioTrack* track) {
261 DCHECK(thread_checker_.CalledOnValidThread());
262
263 bool stop_source = false;
264 {
265 base::AutoLock auto_lock(lock_);
266
267 scoped_refptr<TrackOwner> removed_item =
268 tracks_.Remove(TrackOwner::TrackWrapper(track));
269
270 // Clear the delegate to ensure that no more capture callbacks will
271 // be sent to this sink. Also avoids a possible crash which can happen
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 }
283
SetCapturerSource(const scoped_refptr<media::AudioCapturerSource> & source,media::ChannelLayout channel_layout,float sample_rate,int effects)284 void WebRtcAudioCapturer::SetCapturerSource(
285 const scoped_refptr<media::AudioCapturerSource>& source,
286 media::ChannelLayout channel_layout,
287 float sample_rate,
288 int effects) {
289 DCHECK(thread_checker_.CalledOnValidThread());
290 DVLOG(1) << "SetCapturerSource(channel_layout=" << channel_layout << ","
291 << "sample_rate=" << sample_rate << ")";
292 scoped_refptr<media::AudioCapturerSource> old_source;
293 bool restart_source = false;
294 {
295 base::AutoLock auto_lock(lock_);
296 if (source_.get() == source.get())
297 return;
298
299 source_.swap(old_source);
300 source_ = source;
301
302 // Reset the flag to allow starting the new source.
303 restart_source = running_;
304 running_ = false;
305 }
306
307 DVLOG(1) << "Switching to a new capture source.";
308 if (old_source.get())
309 old_source->Stop();
310
311 // 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
313 // which would normally be used by default.
314 Reconfigure(sample_rate, channel_layout, effects);
315
316 // Make sure to grab the new parameters in case they were reconfigured.
317 media::AudioParameters params = audio_parameters();
318 if (source.get())
319 source->Initialize(params, this, session_id_);
320
321 if (restart_source)
322 Start();
323 }
324
EnablePeerConnectionMode()325 void WebRtcAudioCapturer::EnablePeerConnectionMode() {
326 DCHECK(thread_checker_.CalledOnValidThread());
327 DVLOG(1) << "EnablePeerConnectionMode";
328 // Do nothing if the peer connection mode has been enabled.
329 if (peer_connection_mode_)
330 return;
331
332 peer_connection_mode_ = true;
333 int render_view_id = -1;
334 {
335 base::AutoLock auto_lock(lock_);
336 // Simply return if there is no existing source or the |render_view_id_| is
337 // not valid.
338 if (!source_.get() || render_view_id_== -1)
339 return;
340
341 render_view_id = render_view_id_;
342 }
343
344 // Do nothing if the current buffer size is the WebRtc native buffer size.
345 media::AudioParameters params = audio_parameters();
346 if (GetBufferSize(params.sample_rate()) == params.frames_per_buffer())
347 return;
348
349 // Create a new audio stream as source which will open the hardware using
350 // WebRtc native buffer size.
351 SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id),
352 params.channel_layout(),
353 static_cast<float>(params.sample_rate()),
354 params.effects());
355 }
356
Start()357 void WebRtcAudioCapturer::Start() {
358 DVLOG(1) << "WebRtcAudioCapturer::Start()";
359 base::AutoLock auto_lock(lock_);
360 if (running_ || !source_)
361 return;
362
363 // Start the data source, i.e., start capturing data from the current source.
364 // We need to set the AGC control before starting the stream.
365 source_->SetAutomaticGainControl(true);
366 source_->Start();
367 running_ = true;
368 }
369
Stop()370 void WebRtcAudioCapturer::Stop() {
371 DVLOG(1) << "WebRtcAudioCapturer::Stop()";
372 scoped_refptr<media::AudioCapturerSource> source;
373 TrackList::ItemList tracks;
374 {
375 base::AutoLock auto_lock(lock_);
376 if (!running_)
377 return;
378
379 source = source_;
380 tracks = tracks_.Items();
381 tracks_.Clear();
382 running_ = false;
383 }
384
385 for (TrackList::ItemList::const_iterator it = tracks.begin();
386 it != tracks.end();
387 ++it) {
388 (*it)->Stop();
389 }
390
391 if (source.get())
392 source->Stop();
393 }
394
SetVolume(int volume)395 void WebRtcAudioCapturer::SetVolume(int volume) {
396 DVLOG(1) << "WebRtcAudioCapturer::SetVolume()";
397 DCHECK_LE(volume, MaxVolume());
398 double normalized_volume = static_cast<double>(volume) / MaxVolume();
399 base::AutoLock auto_lock(lock_);
400 if (source_.get())
401 source_->SetVolume(normalized_volume);
402 }
403
Volume() const404 int WebRtcAudioCapturer::Volume() const {
405 base::AutoLock auto_lock(lock_);
406 return volume_;
407 }
408
MaxVolume() const409 int WebRtcAudioCapturer::MaxVolume() const {
410 return WebRtcAudioDeviceImpl::kMaxVolumeLevel;
411 }
412
Capture(media::AudioBus * audio_source,int audio_delay_milliseconds,double volume,bool key_pressed)413 void WebRtcAudioCapturer::Capture(media::AudioBus* audio_source,
414 int audio_delay_milliseconds,
415 double volume,
416 bool key_pressed) {
417 // This callback is driven by AudioInputDevice::AudioThreadCallback if
418 // |source_| is AudioInputDevice, otherwise it is driven by client's
419 // CaptureCallback.
420 #if defined(OS_WIN) || defined(OS_MACOSX)
421 DCHECK_LE(volume, 1.0);
422 #elif defined(OS_LINUX) || defined(OS_OPENBSD)
423 // We have a special situation on Linux where the microphone volume can be
424 // "higher than maximum". The input volume slider in the sound preference
425 // allows the user to set a scaling that is higher than 100%. It means that
426 // even if the reported maximum levels is N, the actual microphone level can
427 // go up to 1.5x*N and that corresponds to a normalized |volume| of 1.5x.
428 DCHECK_LE(volume, 1.6);
429 #endif
430
431 TrackList::ItemList tracks;
432 TrackList::ItemList tracks_to_notify_format;
433 int current_volume = 0;
434 media::AudioParameters params;
435 {
436 base::AutoLock auto_lock(lock_);
437 if (!running_)
438 return;
439
440 // Map internal volume range of [0.0, 1.0] into [0, 255] used by the
441 // webrtc::VoiceEngine. webrtc::VoiceEngine will handle the case when the
442 // volume is higher than 255.
443 volume_ = static_cast<int>((volume * MaxVolume()) + 0.5);
444 current_volume = volume_;
445 audio_delay_ = base::TimeDelta::FromMilliseconds(audio_delay_milliseconds);
446 key_pressed_ = key_pressed;
447 tracks = tracks_.Items();
448 tracks_.RetrieveAndClearTags(&tracks_to_notify_format);
449
450 CHECK(params_.IsValid());
451 CHECK_EQ(audio_source->channels(), params_.channels());
452 CHECK_EQ(audio_source->frames(), params_.frames_per_buffer());
453 params = params_;
454 }
455
456 // Notify the tracks on when the format changes. This will do nothing if
457 // |tracks_to_notify_format| is empty.
458 for (TrackList::ItemList::const_iterator it = tracks_to_notify_format.begin();
459 it != tracks_to_notify_format.end(); ++it) {
460 (*it)->OnSetFormat(params);
461 }
462
463 // Feed the data to the tracks.
464 for (TrackList::ItemList::const_iterator it = tracks.begin();
465 it != tracks.end();
466 ++it) {
467 (*it)->Capture(audio_source, audio_delay_milliseconds,
468 current_volume, key_pressed);
469 }
470 }
471
OnCaptureError()472 void WebRtcAudioCapturer::OnCaptureError() {
473 NOTIMPLEMENTED();
474 }
475
audio_parameters() const476 media::AudioParameters WebRtcAudioCapturer::audio_parameters() const {
477 base::AutoLock auto_lock(lock_);
478 return params_;
479 }
480
GetPairedOutputParameters(int * session_id,int * output_sample_rate,int * output_frames_per_buffer) const481 bool WebRtcAudioCapturer::GetPairedOutputParameters(
482 int* session_id,
483 int* output_sample_rate,
484 int* output_frames_per_buffer) const {
485 // Don't set output parameters unless all of them are valid.
486 if (session_id_ <= 0 || !output_sample_rate_ || !output_frames_per_buffer_)
487 return false;
488
489 *session_id = session_id_;
490 *output_sample_rate = output_sample_rate_;
491 *output_frames_per_buffer = output_frames_per_buffer_;
492
493 return true;
494 }
495
GetBufferSize(int sample_rate) const496 int WebRtcAudioCapturer::GetBufferSize(int sample_rate) const {
497 DCHECK(thread_checker_.CalledOnValidThread());
498 #if defined(OS_ANDROID)
499 // TODO(henrika): Tune and adjust buffer size on Android.
500 return (2 * sample_rate / 100);
501 #endif
502
503 // 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.
505 int peer_connection_buffer_size = sample_rate / 100;
506
507 // 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
509 // buffer size.
510 if (!peer_connection_mode_ && hardware_buffer_size_ &&
511 hardware_buffer_size_ <= peer_connection_buffer_size) {
512 return hardware_buffer_size_;
513 }
514
515 return (sample_rate / 100);
516 }
517
GetAudioProcessingParams(base::TimeDelta * delay,int * volume,bool * key_pressed)518 void WebRtcAudioCapturer::GetAudioProcessingParams(
519 base::TimeDelta* delay, int* volume, bool* key_pressed) {
520 base::AutoLock auto_lock(lock_);
521 *delay = audio_delay_;
522 *volume = volume_;
523 *key_pressed = key_pressed_;
524 }
525
526 } // namespace content
527