Home | History | Annotate | Download | only in media
      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:
     44   explicit TrackOwner(WebRtcLocalAudioTrack* track)
     45       : delegate_(track) {}
     46 
     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 
     60   void OnSetFormat(const media::AudioParameters& params) {
     61     base::AutoLock lock(lock_);
     62     if (delegate_)
     63       delegate_->OnSetFormat(params);
     64   }
     65 
     66   void Reset() {
     67     base::AutoLock lock(lock_);
     68     delegate_ = NULL;
     69   }
     70 
     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 {
     84     TrackWrapper(WebRtcLocalAudioTrack* track) : track_(track) {}
     85     bool operator()(
     86         const scoped_refptr<WebRtcAudioCapturer::TrackOwner>& owner) const {
     87       return owner->IsEqual(track_);
     88     }
     89     WebRtcLocalAudioTrack* track_;
     90   };
     91 
     92  protected:
     93   virtual ~TrackOwner() {}
     94 
     95  private:
     96   friend class base::RefCountedThreadSafe<WebRtcAudioCapturer::TrackOwner>;
     97 
     98   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
    111 scoped_refptr<WebRtcAudioCapturer> WebRtcAudioCapturer::CreateCapturer() {
    112   scoped_refptr<WebRtcAudioCapturer> capturer = new WebRtcAudioCapturer();
    113   return capturer;
    114 }
    115 
    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 
    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 
    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 
    232 WebRtcAudioCapturer::~WebRtcAudioCapturer() {
    233   DCHECK(thread_checker_.CalledOnValidThread());
    234   DCHECK(tracks_.IsEmpty());
    235   DCHECK(!running_);
    236   DVLOG(1) << "WebRtcAudioCapturer::~WebRtcAudioCapturer()";
    237 }
    238 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    404 int WebRtcAudioCapturer::Volume() const {
    405   base::AutoLock auto_lock(lock_);
    406   return volume_;
    407 }
    408 
    409 int WebRtcAudioCapturer::MaxVolume() const {
    410   return WebRtcAudioDeviceImpl::kMaxVolumeLevel;
    411 }
    412 
    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 
    472 void WebRtcAudioCapturer::OnCaptureError() {
    473   NOTIMPLEMENTED();
    474 }
    475 
    476 media::AudioParameters WebRtcAudioCapturer::audio_parameters() const {
    477   base::AutoLock auto_lock(lock_);
    478   return params_;
    479 }
    480 
    481 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 
    496 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 
    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