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.
5 #include "content/renderer/media/webrtc_local_audio_source_provider.h"
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"
14 using blink::WebVector
;
18 static const size_t kMaxNumberOfBuffers
= 10;
20 // Size of the buffer that WebAudio processes each time, it is the same value
21 // as AudioNode::ProcessingSizeInFrames in WebKit.
23 const size_t WebRtcLocalAudioSourceProvider::kWebAudioRenderBufferSize
= 128;
25 WebRtcLocalAudioSourceProvider::WebRtcLocalAudioSourceProvider(
26 const blink::WebMediaStreamTrack
& 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();
38 media::AudioParameters::AUDIO_PCM_LOW_LATENCY
,
39 media::CHANNEL_LAYOUT_STEREO
, 2, 0, sample_rate
, 16,
40 kWebAudioRenderBufferSize
);
43 // Connect the source provider to the track as a sink.
44 MediaStreamAudioSink::AddToAudioTrack(this, track_
);
47 WebRtcLocalAudioSourceProvider::~WebRtcLocalAudioSourceProvider() {
48 if (audio_converter_
.get())
49 audio_converter_
->RemoveInput(this);
51 // If the track is still active, it is necessary to notify the track before
52 // the source provider goes away.
54 MediaStreamAudioSink::RemoveFromAudioTrack(this, track_
);
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());
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
72 audio_converter_
.reset(
73 new media::AudioConverter(params
, sink_params_
, false));
74 audio_converter_
->AddInput(this);
75 fifo_
.reset(new media::AudioFifo(
77 kMaxNumberOfBuffers
* params
.frames_per_buffer()));
78 input_bus_
= media::AudioBus::Create(params
.channels(),
79 params
.frames_per_buffer());
82 void WebRtcLocalAudioSourceProvider::OnReadyStateChanged(
83 blink::WebMediaStreamSource::ReadyState state
) {
84 if (state
== blink::WebMediaStreamSource::ReadyStateEnded
)
85 track_stopped_
= true;
88 void WebRtcLocalAudioSourceProvider::OnData(
89 const int16
* audio_data
,
91 int number_of_channels
,
92 int number_of_frames
) {
93 DCHECK(capture_thread_checker_
.CalledOnValidThread());
94 base::AutoLock
auto_lock(lock_
);
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);
106 if (fifo_
->frames() + number_of_frames
<= fifo_
->max_frames()) {
107 fifo_
->Push(input_bus_
.get());
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();
115 void WebRtcLocalAudioSourceProvider::setClient(
116 blink::WebAudioSourceProviderClient
* client
) {
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());
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
]);
132 base::AutoLock
auto_lock(lock_
);
133 if (!audio_converter_
)
137 audio_converter_
->Convert(output_wrapper_
.get());
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());
146 DVLOG(1) << "WARNING: Underrun, FIFO has data " << fifo_
->frames()
147 << " samples but " << audio_bus
->frames()
148 << " samples are needed";
154 void WebRtcLocalAudioSourceProvider::SetSinkParamsForTesting(
155 const media::AudioParameters
& sink_params
) {
156 sink_params_
= sink_params
;
159 } // namespace content