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_local_audio_source_provider.h"
      6 
      7 #include "base/logging.h"
      8 #include "content/renderer/render_thread_impl.h"
      9 #include "media/audio/audio_parameters.h"
     10 #include "media/base/audio_fifo.h"
     11 #include "media/base/audio_hardware_config.h"
     12 #include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h"
     13 
     14 using blink::WebVector;
     15 
     16 namespace content {
     17 
     18 static const size_t kMaxNumberOfBuffers = 10;
     19 
     20 // Size of the buffer that WebAudio processes each time, it is the same value
     21 // as AudioNode::ProcessingSizeInFrames in WebKit.
     22 // static
     23 const size_t WebRtcLocalAudioSourceProvider::kWebAudioRenderBufferSize = 128;
     24 
     25 WebRtcLocalAudioSourceProvider::WebRtcLocalAudioSourceProvider(
     26     const blink::WebMediaStreamTrack& track)
     27     : is_enabled_(false),
     28       track_(track),
     29       track_stopped_(false) {
     30   // Get the native audio output hardware sample-rate for the sink.
     31   // We need to check if RenderThreadImpl is valid here since the unittests
     32   // do not have one and they will inject their own |sink_params_| for testing.
     33   if (RenderThreadImpl::current()) {
     34     media::AudioHardwareConfig* hardware_config =
     35         RenderThreadImpl::current()->GetAudioHardwareConfig();
     36     int sample_rate = hardware_config->GetOutputSampleRate();
     37     sink_params_.Reset(
     38         media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
     39         media::CHANNEL_LAYOUT_STEREO, 2, 0, sample_rate, 16,
     40         kWebAudioRenderBufferSize);
     41   }
     42 
     43   // Connect the source provider to the track as a sink.
     44   MediaStreamAudioSink::AddToAudioTrack(this, track_);
     45 }
     46 
     47 WebRtcLocalAudioSourceProvider::~WebRtcLocalAudioSourceProvider() {
     48   if (audio_converter_.get())
     49     audio_converter_->RemoveInput(this);
     50 
     51   // If the track is still active, it is necessary to notify the track before
     52   // the source provider goes away.
     53   if (!track_stopped_)
     54     MediaStreamAudioSink::RemoveFromAudioTrack(this, track_);
     55 }
     56 
     57 void WebRtcLocalAudioSourceProvider::OnSetFormat(
     58     const media::AudioParameters& params) {
     59   // We need detach the thread here because it will be a new capture thread
     60   // calling OnSetFormat() and OnData() if the source is restarted.
     61   capture_thread_checker_.DetachFromThread();
     62   DCHECK(capture_thread_checker_.CalledOnValidThread());
     63   DCHECK(params.IsValid());
     64   DCHECK(sink_params_.IsValid());
     65 
     66   base::AutoLock auto_lock(lock_);
     67   source_params_ = params;
     68   // Create the audio converter with |disable_fifo| as false so that the
     69   // converter will request source_params.frames_per_buffer() each time.
     70   // This will not increase the complexity as there is only one client to
     71   // the converter.
     72   audio_converter_.reset(
     73       new media::AudioConverter(params, sink_params_, false));
     74   audio_converter_->AddInput(this);
     75   fifo_.reset(new media::AudioFifo(
     76       params.channels(),
     77       kMaxNumberOfBuffers * params.frames_per_buffer()));
     78   input_bus_ = media::AudioBus::Create(params.channels(),
     79                                        params.frames_per_buffer());
     80 }
     81 
     82 void WebRtcLocalAudioSourceProvider::OnReadyStateChanged(
     83       blink::WebMediaStreamSource::ReadyState state) {
     84   if (state ==  blink::WebMediaStreamSource::ReadyStateEnded)
     85     track_stopped_ = true;
     86 }
     87 
     88 void WebRtcLocalAudioSourceProvider::OnData(
     89     const int16* audio_data,
     90     int sample_rate,
     91     int number_of_channels,
     92     int number_of_frames) {
     93   DCHECK(capture_thread_checker_.CalledOnValidThread());
     94   base::AutoLock auto_lock(lock_);
     95   if (!is_enabled_)
     96     return;
     97 
     98   DCHECK(fifo_.get());
     99 
    100   // TODO(xians): A better way to handle the interleaved and deinterleaved
    101   // format switching, see issue/317710.
    102   DCHECK(input_bus_->frames() == number_of_frames);
    103   DCHECK(input_bus_->channels() == number_of_channels);
    104   input_bus_->FromInterleaved(audio_data, number_of_frames, 2);
    105 
    106   if (fifo_->frames() + number_of_frames <= fifo_->max_frames()) {
    107     fifo_->Push(input_bus_.get());
    108   } else {
    109     // This can happen if the data in FIFO is too slowly consumed or
    110     // WebAudio stops consuming data.
    111     DVLOG(3) << "Local source provicer FIFO is full" << fifo_->frames();
    112   }
    113 }
    114 
    115 void WebRtcLocalAudioSourceProvider::setClient(
    116     blink::WebAudioSourceProviderClient* client) {
    117   NOTREACHED();
    118 }
    119 
    120 void WebRtcLocalAudioSourceProvider::provideInput(
    121     const WebVector<float*>& audio_data, size_t number_of_frames) {
    122   DCHECK_EQ(number_of_frames, kWebAudioRenderBufferSize);
    123   if (!output_wrapper_ ||
    124       static_cast<size_t>(output_wrapper_->channels()) != audio_data.size()) {
    125     output_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size());
    126   }
    127 
    128   output_wrapper_->set_frames(number_of_frames);
    129   for (size_t i = 0; i < audio_data.size(); ++i)
    130     output_wrapper_->SetChannelData(i, audio_data[i]);
    131 
    132   base::AutoLock auto_lock(lock_);
    133   if (!audio_converter_)
    134     return;
    135 
    136   is_enabled_ = true;
    137   audio_converter_->Convert(output_wrapper_.get());
    138 }
    139 
    140 double WebRtcLocalAudioSourceProvider::ProvideInput(
    141     media::AudioBus* audio_bus, base::TimeDelta buffer_delay) {
    142   if (fifo_->frames() >= audio_bus->frames()) {
    143     fifo_->Consume(audio_bus, 0, audio_bus->frames());
    144   } else {
    145     audio_bus->Zero();
    146     DVLOG(1) << "WARNING: Underrun, FIFO has data " << fifo_->frames()
    147              << " samples but " << audio_bus->frames()
    148              << " samples are needed";
    149   }
    150 
    151   return 1.0;
    152 }
    153 
    154 void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting(
    155     const media::AudioParameters& sink_params) {
    156   sink_params_ = sink_params;
    157 }
    158 
    159 }  // namespace content
    160