Home | History | Annotate | Download | only in media
      1 // Copyright 2013 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_device_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/metrics/histogram.h"
      9 #include "base/strings/string_util.h"
     10 #include "base/win/windows_version.h"
     11 #include "content/renderer/media/webrtc_audio_capturer.h"
     12 #include "content/renderer/media/webrtc_audio_renderer.h"
     13 #include "content/renderer/render_thread_impl.h"
     14 #include "media/audio/audio_parameters.h"
     15 #include "media/audio/audio_util.h"
     16 #include "media/audio/sample_rates.h"
     17 
     18 using media::AudioParameters;
     19 using media::ChannelLayout;
     20 
     21 namespace content {
     22 
     23 WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()
     24     : ref_count_(0),
     25       audio_transport_callback_(NULL),
     26       input_delay_ms_(0),
     27       output_delay_ms_(0),
     28       initialized_(false),
     29       playing_(false),
     30       recording_(false),
     31       agc_is_enabled_(false),
     32       microphone_volume_(0) {
     33   DVLOG(1) << "WebRtcAudioDeviceImpl::WebRtcAudioDeviceImpl()";
     34 }
     35 
     36 WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl() {
     37   DVLOG(1) << "WebRtcAudioDeviceImpl::~WebRtcAudioDeviceImpl()";
     38   DCHECK(thread_checker_.CalledOnValidThread());
     39   Terminate();
     40 }
     41 
     42 int32_t WebRtcAudioDeviceImpl::AddRef() {
     43   DCHECK(thread_checker_.CalledOnValidThread());
     44   return base::subtle::Barrier_AtomicIncrement(&ref_count_, 1);
     45 }
     46 
     47 int32_t WebRtcAudioDeviceImpl::Release() {
     48   DCHECK(thread_checker_.CalledOnValidThread());
     49   int ret = base::subtle::Barrier_AtomicIncrement(&ref_count_, -1);
     50   if (ret == 0) {
     51     delete this;
     52   }
     53   return ret;
     54 }
     55 int WebRtcAudioDeviceImpl::CaptureData(const std::vector<int>& channels,
     56                                        const int16* audio_data,
     57                                        int sample_rate,
     58                                        int number_of_channels,
     59                                        int number_of_frames,
     60                                        int audio_delay_milliseconds,
     61                                        int current_volume,
     62                                        bool need_audio_processing) {
     63   int total_delay_ms = 0;
     64   {
     65     base::AutoLock auto_lock(lock_);
     66     // Return immediately when not recording or |channels| is empty.
     67     // See crbug.com/274017: renderer crash dereferencing invalid channels[0].
     68     if (!recording_ || channels.empty())
     69       return 0;
     70 
     71     // Store the reported audio delay locally.
     72     input_delay_ms_ = audio_delay_milliseconds;
     73     total_delay_ms = input_delay_ms_ + output_delay_ms_;
     74     DVLOG(2) << "total delay: " << input_delay_ms_ + output_delay_ms_;
     75   }
     76 
     77   // Write audio samples in blocks of 10 milliseconds to the registered
     78   // webrtc::AudioTransport sink. Keep writing until our internal byte
     79   // buffer is empty.
     80   // TODO(niklase): Wire up the key press detection.
     81   const int16* audio_buffer = audio_data;
     82   const int samples_per_10_msec = (sample_rate / 100);
     83   int accumulated_audio_samples = 0;
     84   bool key_pressed = false;
     85   uint32_t new_volume = 0;
     86   while (accumulated_audio_samples < number_of_frames) {
     87     // Deliver 10ms of recorded 16-bit linear PCM audio.
     88     int new_mic_level = audio_transport_callback_->OnDataAvailable(
     89         &channels[0],
     90         channels.size(),
     91         audio_buffer,
     92         sample_rate,
     93         number_of_channels,
     94         samples_per_10_msec,
     95         total_delay_ms,
     96         current_volume,
     97         key_pressed,
     98         need_audio_processing);
     99 
    100     accumulated_audio_samples += samples_per_10_msec;
    101     audio_buffer += samples_per_10_msec * number_of_channels;
    102 
    103     // The latest non-zero new microphone level will be returned.
    104     if (new_mic_level)
    105       new_volume = new_mic_level;
    106   }
    107 
    108   return new_volume;
    109 }
    110 
    111 void WebRtcAudioDeviceImpl::SetCaptureFormat(
    112     const media::AudioParameters& params) {
    113   DVLOG(1) << "WebRtcAudioDeviceImpl::SetCaptureFormat()";
    114   DCHECK(thread_checker_.CalledOnValidThread());
    115 }
    116 
    117 void WebRtcAudioDeviceImpl::RenderData(uint8* audio_data,
    118                                        int number_of_channels,
    119                                        int number_of_frames,
    120                                        int audio_delay_milliseconds) {
    121   DCHECK_LE(number_of_frames, output_buffer_size());
    122   {
    123     base::AutoLock auto_lock(lock_);
    124     // Store the reported audio delay locally.
    125     output_delay_ms_ = audio_delay_milliseconds;
    126   }
    127 
    128   const int channels = number_of_channels;
    129   DCHECK_LE(channels, output_channels());
    130 
    131   int samples_per_sec = output_sample_rate();
    132   int samples_per_10_msec = (samples_per_sec / 100);
    133   int bytes_per_sample = output_audio_parameters_.bits_per_sample() / 8;
    134   const int bytes_per_10_msec =
    135       channels * samples_per_10_msec * bytes_per_sample;
    136 
    137   uint32_t num_audio_samples = 0;
    138   int accumulated_audio_samples = 0;
    139 
    140   // Get audio samples in blocks of 10 milliseconds from the registered
    141   // webrtc::AudioTransport source. Keep reading until our internal buffer
    142   // is full.
    143   while (accumulated_audio_samples < number_of_frames) {
    144     // Get 10ms and append output to temporary byte buffer.
    145     audio_transport_callback_->NeedMorePlayData(samples_per_10_msec,
    146                                                 bytes_per_sample,
    147                                                 channels,
    148                                                 samples_per_sec,
    149                                                 audio_data,
    150                                                 num_audio_samples);
    151     accumulated_audio_samples += num_audio_samples;
    152     audio_data += bytes_per_10_msec;
    153   }
    154 }
    155 
    156 void WebRtcAudioDeviceImpl::SetRenderFormat(const AudioParameters& params) {
    157   DCHECK(thread_checker_.CalledOnValidThread());
    158   output_audio_parameters_ = params;
    159 }
    160 
    161 void WebRtcAudioDeviceImpl::RemoveAudioRenderer(WebRtcAudioRenderer* renderer) {
    162   DCHECK(thread_checker_.CalledOnValidThread());
    163   DCHECK_EQ(renderer, renderer_);
    164   base::AutoLock auto_lock(lock_);
    165   renderer_ = NULL;
    166   playing_ = false;
    167 }
    168 
    169 int32_t WebRtcAudioDeviceImpl::RegisterAudioCallback(
    170     webrtc::AudioTransport* audio_callback) {
    171   DVLOG(1) << "WebRtcAudioDeviceImpl::RegisterAudioCallback()";
    172   DCHECK(thread_checker_.CalledOnValidThread());
    173   DCHECK_EQ(audio_transport_callback_ == NULL, audio_callback != NULL);
    174   audio_transport_callback_ = audio_callback;
    175   return 0;
    176 }
    177 
    178 int32_t WebRtcAudioDeviceImpl::Init() {
    179   DVLOG(1) << "WebRtcAudioDeviceImpl::Init()";
    180   DCHECK(thread_checker_.CalledOnValidThread());
    181 
    182   // We need to return a success to continue the initialization of WebRtc VoE
    183   // because failure on the capturer_ initialization should not prevent WebRTC
    184   // from working. See issue http://crbug.com/144421 for details.
    185   initialized_ = true;
    186 
    187   return 0;
    188 }
    189 
    190 int32_t WebRtcAudioDeviceImpl::Terminate() {
    191   DVLOG(1) << "WebRtcAudioDeviceImpl::Terminate()";
    192   DCHECK(thread_checker_.CalledOnValidThread());
    193 
    194   // Calling Terminate() multiple times in a row is OK.
    195   if (!initialized_)
    196     return 0;
    197 
    198   StopRecording();
    199   StopPlayout();
    200 
    201   // It is necessary to stop the |renderer_| before going away.
    202   if (renderer_.get()) {
    203     // Grab a local reference while we call Stop(), which will trigger a call to
    204     // RemoveAudioRenderer that clears our reference to the audio renderer.
    205     scoped_refptr<WebRtcAudioRenderer> local_renderer(renderer_);
    206     local_renderer->Stop();
    207     DCHECK(!renderer_.get());
    208   }
    209 
    210   capturers_.clear();
    211 
    212   initialized_ = false;
    213   return 0;
    214 }
    215 
    216 bool WebRtcAudioDeviceImpl::Initialized() const {
    217   return initialized_;
    218 }
    219 
    220 int32_t WebRtcAudioDeviceImpl::PlayoutIsAvailable(bool* available) {
    221   *available = initialized_;
    222   return 0;
    223 }
    224 
    225 bool WebRtcAudioDeviceImpl::PlayoutIsInitialized() const {
    226   return initialized_;
    227 }
    228 
    229 int32_t WebRtcAudioDeviceImpl::RecordingIsAvailable(bool* available) {
    230   *available = (!capturers_.empty());
    231   return 0;
    232 }
    233 
    234 bool WebRtcAudioDeviceImpl::RecordingIsInitialized() const {
    235   DVLOG(1) << "WebRtcAudioDeviceImpl::RecordingIsInitialized()";
    236   DCHECK(thread_checker_.CalledOnValidThread());
    237   return (!capturers_.empty());
    238 }
    239 
    240 int32_t WebRtcAudioDeviceImpl::StartPlayout() {
    241   DVLOG(1) << "WebRtcAudioDeviceImpl::StartPlayout()";
    242   LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing";
    243   {
    244     base::AutoLock auto_lock(lock_);
    245     if (!audio_transport_callback_)
    246       return 0;
    247   }
    248 
    249   if (playing_) {
    250     // webrtc::VoiceEngine assumes that it is OK to call Start() twice and
    251     // that the call is ignored the second time.
    252     return 0;
    253   }
    254 
    255   playing_ = true;
    256   start_render_time_ = base::Time::Now();
    257   return 0;
    258 }
    259 
    260 int32_t WebRtcAudioDeviceImpl::StopPlayout() {
    261   DVLOG(1) << "WebRtcAudioDeviceImpl::StopPlayout()";
    262   if (!playing_) {
    263     // webrtc::VoiceEngine assumes that it is OK to call Stop() just in case.
    264     return 0;
    265   }
    266 
    267   // Add histogram data to be uploaded as part of an UMA logging event.
    268   // This histogram keeps track of total playout times.
    269   if (!start_render_time_.is_null()) {
    270     base::TimeDelta render_time = base::Time::Now() - start_render_time_;
    271     UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioRenderTime", render_time);
    272   }
    273 
    274   playing_ = false;
    275   return 0;
    276 }
    277 
    278 bool WebRtcAudioDeviceImpl::Playing() const {
    279   return playing_;
    280 }
    281 
    282 int32_t WebRtcAudioDeviceImpl::StartRecording() {
    283   DVLOG(1) << "WebRtcAudioDeviceImpl::StartRecording()";
    284   DCHECK(initialized_);
    285   LOG_IF(ERROR, !audio_transport_callback_) << "Audio transport is missing";
    286   if (!audio_transport_callback_) {
    287     return -1;
    288   }
    289 
    290   {
    291     base::AutoLock auto_lock(lock_);
    292     if (recording_)
    293       return 0;
    294 
    295     recording_ = true;
    296   }
    297 
    298   start_capture_time_ = base::Time::Now();
    299 
    300   return 0;
    301 }
    302 
    303 int32_t WebRtcAudioDeviceImpl::StopRecording() {
    304   DVLOG(1) << "WebRtcAudioDeviceImpl::StopRecording()";
    305   {
    306     base::AutoLock auto_lock(lock_);
    307     if (!recording_)
    308       return 0;
    309 
    310     recording_ = false;
    311   }
    312 
    313   // Add histogram data to be uploaded as part of an UMA logging event.
    314   // This histogram keeps track of total recording times.
    315   if (!start_capture_time_.is_null()) {
    316     base::TimeDelta capture_time = base::Time::Now() - start_capture_time_;
    317     UMA_HISTOGRAM_LONG_TIMES("WebRTC.AudioCaptureTime", capture_time);
    318   }
    319 
    320   return 0;
    321 }
    322 
    323 bool WebRtcAudioDeviceImpl::Recording() const {
    324   base::AutoLock auto_lock(lock_);
    325   return recording_;
    326 }
    327 
    328 int32_t WebRtcAudioDeviceImpl::SetAGC(bool enable) {
    329   DVLOG(1) <<  "WebRtcAudioDeviceImpl::SetAGC(enable=" << enable << ")";
    330   DCHECK(initialized_);
    331 
    332   // Return early if we are not changing the AGC state.
    333   if (enable == agc_is_enabled_)
    334     return 0;
    335 
    336   // Set the AGC on all the capturers. It depends on the source of the
    337   // capturer whether AGC is supported or not.
    338   // The current implementation does not support changing the AGC state while
    339   // recording. Using this approach simplifies the design and it is also
    340   // inline with the latest WebRTC standard.
    341   for (CapturerList::const_iterator iter = capturers_.begin();
    342        iter != capturers_.end(); ++iter) {
    343     if (!(*iter)->is_recording())
    344       (*iter)->SetAutomaticGainControl(enable);
    345   }
    346 
    347   agc_is_enabled_ = enable;
    348   return 0;
    349 }
    350 
    351 bool WebRtcAudioDeviceImpl::AGC() const {
    352   DVLOG(1) << "WebRtcAudioDeviceImpl::AGC()";
    353   DCHECK(thread_checker_.CalledOnValidThread());
    354   // To reduce the usage of IPC messages, an internal AGC state is used.
    355   // TODO(henrika): investigate if there is a need for a "deeper" getter.
    356   return agc_is_enabled_;
    357 }
    358 
    359 int32_t WebRtcAudioDeviceImpl::SetMicrophoneVolume(uint32_t volume) {
    360   DVLOG(1) << "WebRtcAudioDeviceImpl::SetMicrophoneVolume(" << volume << ")";
    361   DCHECK(initialized_);
    362 
    363   // Only one microphone is supported at the moment, which is represented by
    364   // the default capturer.
    365   scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer());
    366   if (!capturer.get())
    367     return -1;
    368 
    369   capturer->SetVolume(volume);
    370   return 0;
    371 }
    372 
    373 // TODO(henrika): sort out calling thread once we start using this API.
    374 int32_t WebRtcAudioDeviceImpl::MicrophoneVolume(uint32_t* volume) const {
    375   DVLOG(1) << "WebRtcAudioDeviceImpl::MicrophoneVolume()";
    376   // We only support one microphone now, which is accessed via the default
    377   // capturer.
    378   DCHECK(initialized_);
    379   scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer());
    380   if (!capturer.get())
    381     return -1;
    382 
    383   *volume = static_cast<uint32_t>(capturer->Volume());
    384   return 0;
    385 }
    386 
    387 int32_t WebRtcAudioDeviceImpl::MaxMicrophoneVolume(uint32_t* max_volume) const {
    388   DCHECK(initialized_);
    389   *max_volume = kMaxVolumeLevel;
    390   return 0;
    391 }
    392 
    393 int32_t WebRtcAudioDeviceImpl::MinMicrophoneVolume(uint32_t* min_volume) const {
    394   *min_volume = 0;
    395   return 0;
    396 }
    397 
    398 int32_t WebRtcAudioDeviceImpl::StereoPlayoutIsAvailable(bool* available) const {
    399   DCHECK(initialized_);
    400   *available = (output_channels() == 2);
    401   return 0;
    402 }
    403 
    404 int32_t WebRtcAudioDeviceImpl::StereoRecordingIsAvailable(
    405     bool* available) const {
    406   DCHECK(initialized_);
    407   // TODO(xians): These kind of hardware methods do not make much sense since we
    408   // support multiple sources. Remove or figure out new APIs for such methods.
    409   scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer());
    410   if (!capturer.get())
    411     return -1;
    412 
    413   *available = (capturer->audio_parameters().channels() == 2);
    414   return 0;
    415 }
    416 
    417 int32_t WebRtcAudioDeviceImpl::PlayoutDelay(uint16_t* delay_ms) const {
    418   base::AutoLock auto_lock(lock_);
    419   *delay_ms = static_cast<uint16_t>(output_delay_ms_);
    420   return 0;
    421 }
    422 
    423 int32_t WebRtcAudioDeviceImpl::RecordingDelay(uint16_t* delay_ms) const {
    424   base::AutoLock auto_lock(lock_);
    425   *delay_ms = static_cast<uint16_t>(input_delay_ms_);
    426   return 0;
    427 }
    428 
    429 int32_t WebRtcAudioDeviceImpl::RecordingSampleRate(
    430     uint32_t* samples_per_sec) const {
    431   // We use the default capturer as the recording sample rate.
    432   scoped_refptr<WebRtcAudioCapturer> capturer(GetDefaultCapturer());
    433   if (!capturer.get())
    434     return -1;
    435 
    436   *samples_per_sec = static_cast<uint32_t>(
    437       capturer->audio_parameters().sample_rate());
    438   return 0;
    439 }
    440 
    441 int32_t WebRtcAudioDeviceImpl::PlayoutSampleRate(
    442     uint32_t* samples_per_sec) const {
    443   *samples_per_sec = static_cast<uint32_t>(output_sample_rate());
    444   return 0;
    445 }
    446 
    447 bool WebRtcAudioDeviceImpl::SetAudioRenderer(WebRtcAudioRenderer* renderer) {
    448   DCHECK(thread_checker_.CalledOnValidThread());
    449   DCHECK(renderer);
    450 
    451   base::AutoLock auto_lock(lock_);
    452   if (renderer_.get())
    453     return false;
    454 
    455   if (!renderer->Initialize(this))
    456     return false;
    457 
    458   renderer_ = renderer;
    459   return true;
    460 }
    461 
    462 void WebRtcAudioDeviceImpl::AddAudioCapturer(
    463     const scoped_refptr<WebRtcAudioCapturer>& capturer) {
    464   DVLOG(1) << "WebRtcAudioDeviceImpl::AddAudioCapturer()";
    465   DCHECK(thread_checker_.CalledOnValidThread());
    466   DCHECK(capturer.get());
    467 
    468   // Enable/disable the AGC on the new capture.
    469   DCHECK(!capturer->is_recording());
    470   capturer->SetAutomaticGainControl(agc_is_enabled_);
    471 
    472   // We only support one microphone today, which means the list can contain
    473   // only one capturer with a valid device id.
    474   DCHECK(capturer->device_id().empty() || !GetDefaultCapturer());
    475   base::AutoLock auto_lock(lock_);
    476   capturers_.push_back(capturer);
    477 }
    478 
    479 scoped_refptr<WebRtcAudioCapturer>
    480 WebRtcAudioDeviceImpl::GetDefaultCapturer() const {
    481   base::AutoLock auto_lock(lock_);
    482   for (CapturerList::const_iterator iter = capturers_.begin();
    483        iter != capturers_.end(); ++iter) {
    484     if (!(*iter)->device_id().empty())
    485       return *iter;
    486   }
    487 
    488   return NULL;
    489 }
    490 
    491 }  // namespace content
    492