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
14using blink::WebVector;
15
16namespace content {
17
18static 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
23const size_t WebRtcLocalAudioSourceProvider::kWebAudioRenderBufferSize = 128;
24
25WebRtcLocalAudioSourceProvider::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
47WebRtcLocalAudioSourceProvider::~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
57void 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
82void WebRtcLocalAudioSourceProvider::OnReadyStateChanged(
83      blink::WebMediaStreamSource::ReadyState state) {
84  if (state ==  blink::WebMediaStreamSource::ReadyStateEnded)
85    track_stopped_ = true;
86}
87
88void 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
115void WebRtcLocalAudioSourceProvider::setClient(
116    blink::WebAudioSourceProviderClient* client) {
117  NOTREACHED();
118}
119
120void 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
140double 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
154void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting(
155    const media::AudioParameters& sink_params) {
156  sink_params_ = sink_params;
157}
158
159}  // namespace content
160