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_local_audio_renderer.h"
      6 
      7 #include "base/debug/trace_event.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop/message_loop_proxy.h"
     10 #include "base/synchronization/lock.h"
     11 #include "content/renderer/media/audio_device_factory.h"
     12 #include "content/renderer/media/webrtc_audio_capturer.h"
     13 #include "content/renderer/render_thread_impl.h"
     14 #include "media/audio/audio_output_device.h"
     15 #include "media/base/audio_bus.h"
     16 #include "media/base/audio_fifo.h"
     17 #include "media/base/audio_hardware_config.h"
     18 
     19 namespace content {
     20 
     21 // media::AudioRendererSink::RenderCallback implementation
     22 int WebRtcLocalAudioRenderer::Render(
     23     media::AudioBus* audio_bus, int audio_delay_milliseconds) {
     24   base::AutoLock auto_lock(thread_lock_);
     25 
     26   if (!playing_) {
     27     audio_bus->Zero();
     28     return 0;
     29   }
     30 
     31   TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::Render");
     32 
     33   DCHECK(loopback_fifo_.get() != NULL);
     34 
     35   // Provide data by reading from the FIFO if the FIFO contains enough
     36   // to fulfill the request.
     37   if (loopback_fifo_->frames() >= audio_bus->frames()) {
     38     loopback_fifo_->Consume(audio_bus, 0, audio_bus->frames());
     39   } else {
     40     audio_bus->Zero();
     41     // This warning is perfectly safe if it happens for the first audio
     42     // frames. It should not happen in a steady-state mode.
     43     DVLOG(2) << "loopback FIFO is empty";
     44   }
     45 
     46   return audio_bus->frames();
     47 }
     48 
     49 void WebRtcLocalAudioRenderer::OnRenderError() {
     50   NOTIMPLEMENTED();
     51 }
     52 
     53 // content::WebRtcAudioCapturerSink implementation
     54 int WebRtcLocalAudioRenderer::CaptureData(const std::vector<int>& channels,
     55                                            const int16* audio_data,
     56                                            int sample_rate,
     57                                            int number_of_channels,
     58                                            int number_of_frames,
     59                                            int audio_delay_milliseconds,
     60                                            int current_volume,
     61                                            bool need_audio_processing) {
     62   TRACE_EVENT0("audio", "WebRtcLocalAudioRenderer::CaptureData");
     63   base::AutoLock auto_lock(thread_lock_);
     64 
     65   if (!playing_)
     66     return 0;
     67 
     68   // Push captured audio to FIFO so it can be read by a local sink.
     69   if (loopback_fifo_) {
     70     if (loopback_fifo_->frames() + number_of_frames <=
     71         loopback_fifo_->max_frames()) {
     72       scoped_ptr<media::AudioBus> audio_source = media::AudioBus::Create(
     73           number_of_channels, number_of_frames);
     74       audio_source->FromInterleaved(audio_data,
     75                                     audio_source->frames(),
     76                                     sizeof(audio_data[0]));
     77       loopback_fifo_->Push(audio_source.get());
     78 
     79       base::Time now = base::Time::Now();
     80       total_render_time_ += now - last_render_time_;
     81       last_render_time_ = now;
     82     } else {
     83       DVLOG(1) << "FIFO is full";
     84     }
     85   }
     86 
     87   return 0;
     88 }
     89 
     90 void WebRtcLocalAudioRenderer::SetCaptureFormat(
     91     const media::AudioParameters& params) {
     92   audio_params_ = params;
     93 }
     94 
     95 // WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer implementation.
     96 WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer(
     97     WebRtcLocalAudioTrack* audio_track,
     98     int source_render_view_id)
     99     : audio_track_(audio_track),
    100       source_render_view_id_(source_render_view_id),
    101       playing_(false) {
    102   DCHECK(audio_track);
    103   DVLOG(1) << "WebRtcLocalAudioRenderer::WebRtcLocalAudioRenderer()";
    104 }
    105 
    106 WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer() {
    107   DCHECK(thread_checker_.CalledOnValidThread());
    108   DCHECK(!sink_.get());
    109   DVLOG(1) << "WebRtcLocalAudioRenderer::~WebRtcLocalAudioRenderer()";
    110 }
    111 
    112 void WebRtcLocalAudioRenderer::Start() {
    113   DVLOG(1) << "WebRtcLocalAudioRenderer::Start()";
    114   DCHECK(thread_checker_.CalledOnValidThread());
    115   // Add this class as sink to the audio track to ensure that we receive
    116   // WebRtcAudioCapturerSink::CaptureData() callbacks for captured audio.
    117   // |audio_params_| will be updated right after the AddCapturerAudioTrack().
    118   audio_track_->AddSink(this);
    119 
    120   base::AutoLock auto_lock(thread_lock_);
    121   DCHECK(!sink_.get());
    122 
    123   // TODO(henrika): we could add a more dynamic solution here but I prefer
    124   // a fixed size combined with bad audio at overflow. The alternative is
    125   // that we start to build up latency and that can be more difficult to
    126   // detect. Tests have shown that the FIFO never contains more than 2 or 3
    127   // audio frames but I have selected a max size of ten buffers just
    128   // in case since these tests were performed on a 16 core, 64GB Win 7
    129   // machine. We could also add some sort of error notifier in this area if
    130   // the FIFO overflows.
    131   DCHECK(!loopback_fifo_);
    132   loopback_fifo_.reset(new media::AudioFifo(
    133       audio_params_.channels(), 10 * audio_params_.frames_per_buffer()));
    134 
    135 #if defined(OS_ANDROID)
    136   media::AudioHardwareConfig* hardware_config =
    137       RenderThreadImpl::current()->GetAudioHardwareConfig();
    138 #endif
    139 
    140   media::AudioParameters sink_params(audio_params_.format(),
    141                                      audio_params_.channel_layout(),
    142                                      audio_params_.sample_rate(),
    143                                      audio_params_.bits_per_sample(),
    144 #if defined(OS_ANDROID)
    145   // On Android, input and output are using same sampling rate. In order to
    146   // achieve low latency mode, we need use buffer size suggested by
    147   // AudioManager for the sink paramters which will be used to decide
    148   // buffer size for shared memory buffer.
    149                                      hardware_config->GetOutputBufferSize()
    150 #else
    151                                      2 * audio_params_.frames_per_buffer()
    152 #endif
    153                                     );
    154   sink_ = AudioDeviceFactory::NewOutputDevice(source_render_view_id_);
    155 
    156   // TODO(henrika): we could utilize the unified audio here instead and do
    157   // sink_->InitializeIO(sink_params, 2, callback_.get());
    158   // It would then be possible to avoid using the WebRtcAudioCapturer.
    159   sink_->Initialize(sink_params, this);
    160 
    161   // Start the capturer and local rendering. Note that, the capturer is owned
    162   // by the WebRTC ADM and might already bee running.
    163   sink_->Start();
    164 
    165   last_render_time_ = base::Time::Now();
    166   playing_ = false;
    167 }
    168 
    169 void WebRtcLocalAudioRenderer::Stop() {
    170   DVLOG(1) << "WebRtcLocalAudioRenderer::Stop()";
    171   DCHECK(thread_checker_.CalledOnValidThread());
    172 
    173   if (!sink_.get())
    174     return;
    175 
    176   {
    177     base::AutoLock auto_lock(thread_lock_);
    178     playing_ = false;
    179 
    180     if (loopback_fifo_.get() != NULL) {
    181       loopback_fifo_->Clear();
    182       loopback_fifo_.reset();
    183     }
    184   }
    185 
    186   // Stop the output audio stream, i.e, stop asking for data to render.
    187   sink_->Stop();
    188   sink_ = NULL;
    189 
    190   // Ensure that the capturer stops feeding us with captured audio.
    191   // Note that, we do not stop the capturer here since it may still be used by
    192   // the WebRTC ADM.
    193   audio_track_->RemoveSink(this);
    194   audio_track_ = NULL;
    195 }
    196 
    197 void WebRtcLocalAudioRenderer::Play() {
    198   DVLOG(1) << "WebRtcLocalAudioRenderer::Play()";
    199   DCHECK(thread_checker_.CalledOnValidThread());
    200   base::AutoLock auto_lock(thread_lock_);
    201 
    202   if (!sink_.get())
    203     return;
    204 
    205   // Resumes rendering by ensuring that WebRtcLocalAudioRenderer::Render()
    206   // now reads data from the local FIFO.
    207   playing_ = true;
    208   last_render_time_ = base::Time::Now();
    209 
    210   if (loopback_fifo_)
    211     loopback_fifo_->Clear();
    212 }
    213 
    214 void WebRtcLocalAudioRenderer::Pause() {
    215   DVLOG(1) << "WebRtcLocalAudioRenderer::Pause()";
    216   DCHECK(thread_checker_.CalledOnValidThread());
    217   base::AutoLock auto_lock(thread_lock_);
    218 
    219   if (!sink_.get())
    220     return;
    221 
    222   // Temporarily suspends rendering audio.
    223   // WebRtcLocalAudioRenderer::Render() will return early during this state
    224   // and only zeros will be provided to the active sink.
    225   playing_ = false;
    226 }
    227 
    228 void WebRtcLocalAudioRenderer::SetVolume(float volume) {
    229   DVLOG(1) << "WebRtcLocalAudioRenderer::SetVolume(" << volume << ")";
    230   DCHECK(thread_checker_.CalledOnValidThread());
    231   base::AutoLock auto_lock(thread_lock_);
    232   if (sink_.get())
    233     sink_->SetVolume(volume);
    234 }
    235 
    236 base::TimeDelta WebRtcLocalAudioRenderer::GetCurrentRenderTime() const {
    237   DCHECK(thread_checker_.CalledOnValidThread());
    238   base::AutoLock auto_lock(thread_lock_);
    239   if (!sink_.get())
    240     return base::TimeDelta();
    241   return total_render_time();
    242 }
    243 
    244 bool WebRtcLocalAudioRenderer::IsLocalRenderer() const {
    245   return true;
    246 }
    247 
    248 }  // namespace content
    249