• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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