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
WebRtcLocalAudioSourceProvider()25 WebRtcLocalAudioSourceProvider::WebRtcLocalAudioSourceProvider()
26 : is_enabled_(false) {
27 // Get the native audio output hardware sample-rate for the sink.
28 // We need to check if RenderThreadImpl is valid here since the unittests
29 // do not have one and they will inject their own |sink_params_| for testing.
30 if (RenderThreadImpl::current()) {
31 media::AudioHardwareConfig* hardware_config =
32 RenderThreadImpl::current()->GetAudioHardwareConfig();
33 int sample_rate = hardware_config->GetOutputSampleRate();
34 sink_params_.Reset(
35 media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
36 media::CHANNEL_LAYOUT_STEREO, 2, 0, sample_rate, 16,
37 kWebAudioRenderBufferSize);
38 }
39 }
40
~WebRtcLocalAudioSourceProvider()41 WebRtcLocalAudioSourceProvider::~WebRtcLocalAudioSourceProvider() {
42 if (audio_converter_.get())
43 audio_converter_->RemoveInput(this);
44 }
45
OnSetFormat(const media::AudioParameters & params)46 void WebRtcLocalAudioSourceProvider::OnSetFormat(
47 const media::AudioParameters& params) {
48 // We need detach the thread here because it will be a new capture thread
49 // calling OnSetFormat() and OnData() if the source is restarted.
50 capture_thread_checker_.DetachFromThread();
51 DCHECK(capture_thread_checker_.CalledOnValidThread());
52 DCHECK(params.IsValid());
53 DCHECK(sink_params_.IsValid());
54
55 base::AutoLock auto_lock(lock_);
56 source_params_ = params;
57 // Create the audio converter with |disable_fifo| as false so that the
58 // converter will request source_params.frames_per_buffer() each time.
59 // This will not increase the complexity as there is only one client to
60 // the converter.
61 audio_converter_.reset(
62 new media::AudioConverter(params, sink_params_, false));
63 audio_converter_->AddInput(this);
64 fifo_.reset(new media::AudioFifo(
65 params.channels(),
66 kMaxNumberOfBuffers * params.frames_per_buffer()));
67 input_bus_ = media::AudioBus::Create(params.channels(),
68 params.frames_per_buffer());
69 }
70
OnData(const int16 * audio_data,int sample_rate,int number_of_channels,int number_of_frames)71 void WebRtcLocalAudioSourceProvider::OnData(
72 const int16* audio_data,
73 int sample_rate,
74 int number_of_channels,
75 int number_of_frames) {
76 DCHECK(capture_thread_checker_.CalledOnValidThread());
77 base::AutoLock auto_lock(lock_);
78 if (!is_enabled_)
79 return;
80
81 DCHECK(fifo_.get());
82
83 // TODO(xians): A better way to handle the interleaved and deinterleaved
84 // format switching, see issue/317710.
85 DCHECK(input_bus_->frames() == number_of_frames);
86 DCHECK(input_bus_->channels() == number_of_channels);
87 input_bus_->FromInterleaved(audio_data, number_of_frames, 2);
88
89 if (fifo_->frames() + number_of_frames <= fifo_->max_frames()) {
90 fifo_->Push(input_bus_.get());
91 } else {
92 // This can happen if the data in FIFO is too slowed to be consumed or
93 // WebAudio stops consuming data.
94 DLOG(WARNING) << "Local source provicer FIFO is full" << fifo_->frames();
95 }
96 }
97
setClient(blink::WebAudioSourceProviderClient * client)98 void WebRtcLocalAudioSourceProvider::setClient(
99 blink::WebAudioSourceProviderClient* client) {
100 NOTREACHED();
101 }
102
provideInput(const WebVector<float * > & audio_data,size_t number_of_frames)103 void WebRtcLocalAudioSourceProvider::provideInput(
104 const WebVector<float*>& audio_data, size_t number_of_frames) {
105 DCHECK_EQ(number_of_frames, kWebAudioRenderBufferSize);
106 if (!output_wrapper_ ||
107 static_cast<size_t>(output_wrapper_->channels()) != audio_data.size()) {
108 output_wrapper_ = media::AudioBus::CreateWrapper(audio_data.size());
109 }
110
111 output_wrapper_->set_frames(number_of_frames);
112 for (size_t i = 0; i < audio_data.size(); ++i)
113 output_wrapper_->SetChannelData(i, audio_data[i]);
114
115 base::AutoLock auto_lock(lock_);
116 if (!audio_converter_)
117 return;
118
119 is_enabled_ = true;
120 audio_converter_->Convert(output_wrapper_.get());
121 }
122
ProvideInput(media::AudioBus * audio_bus,base::TimeDelta buffer_delay)123 double WebRtcLocalAudioSourceProvider::ProvideInput(
124 media::AudioBus* audio_bus, base::TimeDelta buffer_delay) {
125 if (fifo_->frames() >= audio_bus->frames()) {
126 fifo_->Consume(audio_bus, 0, audio_bus->frames());
127 } else {
128 audio_bus->Zero();
129 DVLOG(1) << "WARNING: Underrun, FIFO has data " << fifo_->frames()
130 << " samples but " << audio_bus->frames()
131 << " samples are needed";
132 }
133
134 return 1.0;
135 }
136
SetSinkParamsForTesting(const media::AudioParameters & sink_params)137 void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting(
138 const media::AudioParameters& sink_params) {
139 sink_params_ = sink_params;
140 }
141
142 } // namespace content
143