1 // Copyright (c) 2012 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_audio_renderer.h"
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9 #include "base/string_util.h"
10 #include "content/renderer/media/audio_device_factory.h"
11 #include "content/renderer/media/renderer_audio_output_device.h"
12 #include "content/renderer/media/webrtc_audio_device_impl.h"
13 #include "content/renderer/render_thread_impl.h"
14 #include "media/audio/audio_parameters.h"
15 #include "media/audio/sample_rates.h"
16 #include "media/base/audio_hardware_config.h"
19 #include "base/win/windows_version.h"
20 #include "media/audio/win/core_audio_util_win.h"
27 // Supported hardware sample rates for output sides.
28 #if defined(OS_WIN) || defined(OS_MACOSX)
29 // AudioHardwareConfig::GetOutputSampleRate() asks the audio layer for its
30 // current sample rate (set by the user) on Windows and Mac OS X. The listed
31 // rates below adds restrictions and Initialize() will fail if the user selects
32 // any rate outside these ranges.
33 const int kValidOutputRates
[] = {96000, 48000, 44100, 32000, 16000};
34 #elif defined(OS_LINUX) || defined(OS_OPENBSD)
35 const int kValidOutputRates
[] = {48000, 44100};
36 #elif defined(OS_ANDROID)
37 // TODO(leozwang): We want to use native sampling rate on Android to achieve
38 // low latency, currently 16000 is used to work around audio problem on some
40 const int kValidOutputRates
[] = {48000, 44100, 16000};
42 const int kValidOutputRates
[] = {44100};
45 // TODO(xians): Merge the following code to WebRtcAudioCapturer, or remove.
46 enum AudioFramesPerBuffer
{
49 k440
, // WebRTC works internally with 440 audio frames at 44.1kHz.
56 kUnexpectedAudioBufferSize
// Must always be last!
59 // Helper method to convert integral values to their respective enum values
60 // above, or kUnexpectedAudioBufferSize if no match exists.
61 AudioFramesPerBuffer
AsAudioFramesPerBuffer(int frames_per_buffer
) {
62 switch (frames_per_buffer
) {
63 case 160: return k160
;
64 case 320: return k320
;
65 case 440: return k440
;
66 case 480: return k480
;
67 case 640: return k640
;
68 case 880: return k880
;
69 case 960: return k960
;
70 case 1440: return k1440
;
71 case 1920: return k1920
;
73 return kUnexpectedAudioBufferSize
;
76 void AddHistogramFramesPerBuffer(int param
) {
77 AudioFramesPerBuffer afpb
= AsAudioFramesPerBuffer(param
);
78 if (afpb
!= kUnexpectedAudioBufferSize
) {
79 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer",
80 afpb
, kUnexpectedAudioBufferSize
);
82 // Report unexpected sample rates using a unique histogram name.
83 UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputFramesPerBufferUnexpected", param
);
89 WebRtcAudioRenderer::WebRtcAudioRenderer(int source_render_view_id
)
90 : state_(UNINITIALIZED
),
91 source_render_view_id_(source_render_view_id
),
94 audio_delay_milliseconds_(0),
95 frame_duration_milliseconds_(0),
99 WebRtcAudioRenderer::~WebRtcAudioRenderer() {
100 DCHECK(thread_checker_
.CalledOnValidThread());
101 DCHECK_EQ(state_
, UNINITIALIZED
);
105 bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource
* source
) {
106 DVLOG(1) << "WebRtcAudioRenderer::Initialize()";
107 DCHECK(thread_checker_
.CalledOnValidThread());
108 base::AutoLock
auto_lock(lock_
);
109 DCHECK_EQ(state_
, UNINITIALIZED
);
114 sink_
= AudioDeviceFactory::NewOutputDevice();
117 // Use mono on all platforms but Windows for now.
118 // TODO(henrika): Tracking at http://crbug.com/166771.
119 media::ChannelLayout channel_layout
= media::CHANNEL_LAYOUT_MONO
;
121 channel_layout
= media::CHANNEL_LAYOUT_STEREO
;
124 // Ask the renderer for the default audio output hardware sample-rate.
125 media::AudioHardwareConfig
* hardware_config
=
126 RenderThreadImpl::current()->GetAudioHardwareConfig();
127 int sample_rate
= hardware_config
->GetOutputSampleRate();
128 DVLOG(1) << "Audio output hardware sample rate: " << sample_rate
;
129 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputSampleRate",
130 sample_rate
, media::kUnexpectedAudioSampleRate
);
132 // Verify that the reported output hardware sample rate is supported
133 // on the current platform.
134 if (std::find(&kValidOutputRates
[0],
135 &kValidOutputRates
[0] + arraysize(kValidOutputRates
),
137 &kValidOutputRates
[arraysize(kValidOutputRates
)]) {
138 DLOG(ERROR
) << sample_rate
<< " is not a supported output rate.";
142 // Set up audio parameters for the source, i.e., the WebRTC client.
143 // The WebRTC client only supports multiples of 10ms as buffer size where
144 // 10ms is preferred for lowest possible delay.
146 media::AudioParameters source_params
;
149 if (sample_rate
% 8000 == 0) {
150 buffer_size
= (sample_rate
/ 100);
151 } else if (sample_rate
== 44100) {
152 // The resampler in WebRTC does not support 441 as input. We hard code
153 // the size to 440 (~0.9977ms) instead and rely on the internal jitter
154 // buffer in WebRTC to deal with the resulting drift.
155 // TODO(henrika): ensure that WebRTC supports 44100Hz and use 441 instead.
161 int channels
= ChannelLayoutToChannelCount(channel_layout
);
162 source_params
.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY
,
163 channel_layout
, channels
, 0,
164 sample_rate
, 16, buffer_size
);
166 // Set up audio parameters for the sink, i.e., the native audio output stream.
167 // We strive to open up using native parameters to achieve best possible
168 // performance and to ensure that no FIFO is needed on the browser side to
169 // match the client request. Any mismatch between the source and the sink is
170 // taken care of in this class instead using a pull FIFO.
172 media::AudioParameters sink_params
;
174 buffer_size
= hardware_config
->GetOutputBufferSize();
175 sink_params
.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY
,
176 channel_layout
, channels
, 0, sample_rate
, 16, buffer_size
);
178 // Create a FIFO if re-buffering is required to match the source input with
179 // the sink request. The source acts as provider here and the sink as
181 if (source_params
.frames_per_buffer() != sink_params
.frames_per_buffer()) {
182 DVLOG(1) << "Rebuffering from " << source_params
.frames_per_buffer()
183 << " to " << sink_params
.frames_per_buffer();
184 audio_fifo_
.reset(new media::AudioPullFifo(
185 source_params
.channels(),
186 source_params
.frames_per_buffer(),
188 &WebRtcAudioRenderer::SourceCallback
,
189 base::Unretained(this))));
191 // The I/O ratio is used in delay calculations where one scheme is used
192 // for |fifo_io_ratio_| > 1 and another scheme for < 1.0.
193 fifo_io_ratio_
= static_cast<double>(source_params
.frames_per_buffer()) /
194 sink_params
.frames_per_buffer();
197 frame_duration_milliseconds_
= base::Time::kMillisecondsPerSecond
/
198 static_cast<double>(source_params
.sample_rate());
200 // Allocate local audio buffers based on the parameters above.
201 // It is assumed that each audio sample contains 16 bits and each
202 // audio frame contains one or two audio samples depending on the
203 // number of channels.
205 new int16
[source_params
.frames_per_buffer() * source_params
.channels()]);
208 source
->SetRenderFormat(source_params
);
210 // Configure the audio rendering client and start rendering.
211 sink_
->Initialize(sink_params
, this);
212 sink_
->SetSourceRenderView(source_render_view_id_
);
215 // User must call Play() before any audio can be heard.
218 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputChannelLayout",
219 source_params
.channel_layout(),
220 media::CHANNEL_LAYOUT_MAX
);
221 UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer",
222 source_params
.frames_per_buffer(),
223 kUnexpectedAudioBufferSize
);
224 AddHistogramFramesPerBuffer(source_params
.frames_per_buffer());
229 void WebRtcAudioRenderer::Start() {
230 // TODO(xians): refactor to make usage of Start/Stop more symmetric.
234 void WebRtcAudioRenderer::Play() {
235 DVLOG(1) << "WebRtcAudioRenderer::Play()";
236 DCHECK(thread_checker_
.CalledOnValidThread());
237 base::AutoLock
auto_lock(lock_
);
238 if (state_
== UNINITIALIZED
)
241 DCHECK(play_ref_count_
== 0 || state_
== PLAYING
);
246 audio_delay_milliseconds_
= 0;
247 audio_fifo_
->Clear();
251 void WebRtcAudioRenderer::Pause() {
252 DVLOG(1) << "WebRtcAudioRenderer::Pause()";
253 DCHECK(thread_checker_
.CalledOnValidThread());
254 base::AutoLock
auto_lock(lock_
);
255 if (state_
== UNINITIALIZED
)
258 DCHECK_EQ(state_
, PLAYING
);
259 DCHECK_GT(play_ref_count_
, 0);
260 if (!--play_ref_count_
)
264 void WebRtcAudioRenderer::Stop() {
265 DVLOG(1) << "WebRtcAudioRenderer::Stop()";
266 DCHECK(thread_checker_
.CalledOnValidThread());
267 base::AutoLock
auto_lock(lock_
);
268 if (state_
== UNINITIALIZED
)
271 source_
->RemoveAudioRenderer(this);
274 state_
= UNINITIALIZED
;
277 void WebRtcAudioRenderer::SetVolume(float volume
) {
278 DCHECK(thread_checker_
.CalledOnValidThread());
279 base::AutoLock
auto_lock(lock_
);
280 if (state_
== UNINITIALIZED
)
283 sink_
->SetVolume(volume
);
286 base::TimeDelta
WebRtcAudioRenderer::GetCurrentRenderTime() const {
287 return base::TimeDelta();
290 bool WebRtcAudioRenderer::IsLocalRenderer() const {
294 int WebRtcAudioRenderer::Render(media::AudioBus
* audio_bus
,
295 int audio_delay_milliseconds
) {
296 base::AutoLock
auto_lock(lock_
);
300 DVLOG(2) << "WebRtcAudioRenderer::Render()";
301 DVLOG(2) << "audio_delay_milliseconds: " << audio_delay_milliseconds
;
303 if (fifo_io_ratio_
> 1.0)
304 audio_delay_milliseconds_
+= audio_delay_milliseconds
;
306 audio_delay_milliseconds_
= audio_delay_milliseconds
;
309 audio_fifo_
->Consume(audio_bus
, audio_bus
->frames());
311 SourceCallback(0, audio_bus
);
313 return (state_
== PLAYING
) ? audio_bus
->frames() : 0;
316 void WebRtcAudioRenderer::OnRenderError() {
318 LOG(ERROR
) << "OnRenderError()";
321 // Called by AudioPullFifo when more data is necessary.
322 void WebRtcAudioRenderer::SourceCallback(
323 int fifo_frame_delay
, media::AudioBus
* audio_bus
) {
324 DVLOG(2) << "WebRtcAudioRenderer::SourceCallback("
325 << fifo_frame_delay
<< ", "
326 << audio_bus
->frames() << ")";
328 int output_delay_milliseconds
= audio_delay_milliseconds_
;
329 output_delay_milliseconds
+= frame_duration_milliseconds_
* fifo_frame_delay
;
330 DVLOG(2) << "output_delay_milliseconds: " << output_delay_milliseconds
;
332 // We need to keep render data for the |source_| regardless of |state_|,
333 // otherwise the data will be buffered up inside |source_|.
334 source_
->RenderData(reinterpret_cast<uint8
*>(buffer_
.get()),
335 audio_bus
->channels(), audio_bus
->frames(),
336 output_delay_milliseconds
);
338 if (fifo_io_ratio_
> 1.0)
339 audio_delay_milliseconds_
= 0;
341 // Avoid filling up the audio bus if we are not playing; instead
342 // return here and ensure that the returned value in Render() is 0.
343 if (state_
!= PLAYING
) {
348 // De-interleave each channel and convert to 32-bit floating-point
349 // with nominal range -1.0 -> +1.0 to match the callback format.
350 audio_bus
->FromInterleaved(buffer_
.get(),
355 } // namespace content