root/content/renderer/media/webrtc_local_audio_source_provider.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. track_stopped_
  2. OnSetFormat
  3. OnReadyStateChanged
  4. OnData
  5. setClient
  6. provideInput
  7. ProvideInput
  8. SetSinkParamsForTesting

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/webrtc_local_audio_source_provider.h"

#include "base/logging.h"
#include "content/renderer/render_thread_impl.h"
#include "media/audio/audio_parameters.h"
#include "media/base/audio_fifo.h"
#include "media/base/audio_hardware_config.h"
#include "third_party/WebKit/public/platform/WebAudioSourceProviderClient.h"

using blink::WebVector;

namespace content {

static const size_t kMaxNumberOfBuffers = 10;

// Size of the buffer that WebAudio processes each time, it is the same value
// as AudioNode::ProcessingSizeInFrames in WebKit.
// static
const size_t WebRtcLocalAudioSourceProvider::kWebAudioRenderBufferSize = 128;

WebRtcLocalAudioSourceProvider::WebRtcLocalAudioSourceProvider(
    const blink::WebMediaStreamTrack& track)
    : is_enabled_(false),
      track_(track),
      track_stopped_(false) {
  // Get the native audio output hardware sample-rate for the sink.
  // We need to check if RenderThreadImpl is valid here since the unittests
  // do not have one and they will inject their own |sink_params_| for testing.
  if (RenderThreadImpl::current()) {
    media::AudioHardwareConfig* hardware_config =
        RenderThreadImpl::current()->GetAudioHardwareConfig();
    int sample_rate = hardware_config->GetOutputSampleRate();
    sink_params_.Reset(
        media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
        media::CHANNEL_LAYOUT_STEREO, 2, 0, sample_rate, 16,
        kWebAudioRenderBufferSize);
  }

  // Connect the source provider to the track as a sink.
  MediaStreamAudioSink::AddToAudioTrack(this, track_);
}

WebRtcLocalAudioSourceProvider::~WebRtcLocalAudioSourceProvider() {
  if (audio_converter_.get())
    audio_converter_->RemoveInput(this);

  // If the track is still active, it is necessary to notify the track before
  // the source provider goes away.
  if (!track_stopped_)
    MediaStreamAudioSink::RemoveFromAudioTrack(this, track_);
}

void WebRtcLocalAudioSourceProvider::OnSetFormat(
    const media::AudioParameters& params) {
  // We need detach the thread here because it will be a new capture thread
  // calling OnSetFormat() and OnData() if the source is restarted.
  capture_thread_checker_.DetachFromThread();
  DCHECK(capture_thread_checker_.CalledOnValidThread());
  DCHECK(params.IsValid());
  DCHECK(sink_params_.IsValid());

  base::AutoLock auto_lock(lock_);
  source_params_ = params;
  // Create the audio converter with |disable_fifo| as false so that the
  // converter will request source_params.frames_per_buffer() each time.
  // This will not increase the complexity as there is only one client to
  // the converter.
  audio_converter_.reset(
      new media::AudioConverter(params, sink_params_, false));
  audio_converter_->AddInput(this);
  fifo_.reset(new media::AudioFifo(
      params.channels(),
      kMaxNumberOfBuffers * params.frames_per_buffer()));
  input_bus_ = media::AudioBus::Create(params.channels(),
                                       params.frames_per_buffer());
}

void WebRtcLocalAudioSourceProvider::OnReadyStateChanged(
      blink::WebMediaStreamSource::ReadyState state) {
  if (state ==  blink::WebMediaStreamSource::ReadyStateEnded)
    track_stopped_ = true;
}

void WebRtcLocalAudioSourceProvider::OnData(
    const int16* audio_data,
    int sample_rate,
    int number_of_channels,
    int number_of_frames) {
  DCHECK(capture_thread_checker_.CalledOnValidThread());
  base::AutoLock auto_lock(lock_);
  if (!is_enabled_)
    return;

  DCHECK(fifo_.get());

  // TODO(xians): A better way to handle the interleaved and deinterleaved
  // format switching, see issue/317710.
  DCHECK(input_bus_->frames() == number_of_frames);
  DCHECK(input_bus_->channels() == number_of_channels);
  input_bus_->FromInterleaved(audio_data, number_of_frames, 2);

  if (fifo_->frames() + number_of_frames <= fifo_->max_frames()) {
    fifo_->Push(input_bus_.get());
  } else {
    // This can happen if the data in FIFO is too slowly consumed or
    // WebAudio stops consuming data.
    DVLOG(3) << "Local source provicer FIFO is full" << fifo_->frames();
  }
}

void WebRtcLocalAudioSourceProvider::setClient(
    blink::WebAudioSourceProviderClient* client) {
  NOTREACHED();
}

void WebRtcLocalAudioSourceProvider::provideInput(
    const WebVector<float*>& audio_data, size_t number_of_frames) {
  DCHECK_EQ(number_of_frames, kWebAudioRenderBufferSize);
  if (!output_wrapper_ ||
      static_cast<size_t>(output_wrapper_->channels()) != audio_data.size()) {
    output_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size());
  }

  output_wrapper_->set_frames(number_of_frames);
  for (size_t i = 0; i < audio_data.size(); ++i)
    output_wrapper_->SetChannelData(i, audio_data[i]);

  base::AutoLock auto_lock(lock_);
  if (!audio_converter_)
    return;

  is_enabled_ = true;
  audio_converter_->Convert(output_wrapper_.get());
}

double WebRtcLocalAudioSourceProvider::ProvideInput(
    media::AudioBus* audio_bus, base::TimeDelta buffer_delay) {
  if (fifo_->frames() >= audio_bus->frames()) {
    fifo_->Consume(audio_bus, 0, audio_bus->frames());
  } else {
    audio_bus->Zero();
    DVLOG(1) << "WARNING: Underrun, FIFO has data " << fifo_->frames()
             << " samples but " << audio_bus->frames()
             << " samples are needed";
  }

  return 1.0;
}

void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting(
    const media::AudioParameters& sink_params) {
  sink_params_ = sink_params;
}

}  // namespace content

/* [<][>][^][v][top][bottom][index][help] */