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 "media/audio/audio_output_resampler.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/metrics/histogram.h"
11 #include "base/numerics/safe_conversions.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/trace_event/trace_event.h"
14 #include "build/build_config.h"
15 #include "media/audio/audio_output_proxy.h"
16 #include "media/audio/sample_rates.h"
17 #include "media/base/audio_converter.h"
18 #include "media/base/limits.h"
22 class OnMoreDataConverter
23 : public AudioOutputStream::AudioSourceCallback
,
24 public AudioConverter::InputCallback
{
26 OnMoreDataConverter(const AudioParameters
& input_params
,
27 const AudioParameters
& output_params
);
28 ~OnMoreDataConverter() override
;
30 // AudioSourceCallback interface.
31 int OnMoreData(AudioBus
* dest
, uint32 total_bytes_delay
) override
;
32 void OnError(AudioOutputStream
* stream
) override
;
34 // Sets |source_callback_|. If this is not a new object, then Stop() must be
35 // called before Start().
36 void Start(AudioOutputStream::AudioSourceCallback
* callback
);
38 // Clears |source_callback_| and flushes the resampler.
41 bool started() { return source_callback_
!= nullptr; }
44 // AudioConverter::InputCallback implementation.
45 double ProvideInput(AudioBus
* audio_bus
,
46 base::TimeDelta buffer_delay
) override
;
48 // Ratio of input bytes to output bytes used to correct playback delay with
49 // regard to buffering and resampling.
50 const double io_ratio_
;
53 AudioOutputStream::AudioSourceCallback
* source_callback_
;
55 // Last |total_bytes_delay| received via OnMoreData(), used to correct
56 // playback delay by ProvideInput() and passed on to |source_callback_|.
57 uint32 current_total_bytes_delay_
;
59 const int input_bytes_per_second_
;
61 // Handles resampling, buffering, and channel mixing between input and output
63 AudioConverter audio_converter_
;
65 DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter
);
68 // Record UMA statistics for hardware output configuration.
69 static void RecordStats(const AudioParameters
& output_params
) {
70 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
71 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
72 // to report a discrete value.
73 UMA_HISTOGRAM_ENUMERATION(
74 "Media.HardwareAudioBitsPerChannel",
75 output_params
.bits_per_sample(),
76 limits::kMaxBitsPerSample
); // PRESUBMIT_IGNORE_UMA_MAX
77 UMA_HISTOGRAM_ENUMERATION(
78 "Media.HardwareAudioChannelLayout", output_params
.channel_layout(),
79 CHANNEL_LAYOUT_MAX
+ 1);
80 UMA_HISTOGRAM_ENUMERATION(
81 "Media.HardwareAudioChannelCount", output_params
.channels(),
82 limits::kMaxChannels
); // PRESUBMIT_IGNORE_UMA_MAX
85 if (ToAudioSampleRate(output_params
.sample_rate(), &asr
)) {
86 UMA_HISTOGRAM_ENUMERATION(
87 "Media.HardwareAudioSamplesPerSecond", asr
, kAudioSampleRateMax
+ 1);
90 "Media.HardwareAudioSamplesPerSecondUnexpected",
91 output_params
.sample_rate());
95 // Record UMA statistics for hardware output configuration after fallback.
96 static void RecordFallbackStats(const AudioParameters
& output_params
) {
97 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
98 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
99 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
100 // to report a discrete value.
101 UMA_HISTOGRAM_ENUMERATION(
102 "Media.FallbackHardwareAudioBitsPerChannel",
103 output_params
.bits_per_sample(),
104 limits::kMaxBitsPerSample
); // PRESUBMIT_IGNORE_UMA_MAX
105 UMA_HISTOGRAM_ENUMERATION(
106 "Media.FallbackHardwareAudioChannelLayout",
107 output_params
.channel_layout(), CHANNEL_LAYOUT_MAX
+ 1);
108 UMA_HISTOGRAM_ENUMERATION(
109 "Media.FallbackHardwareAudioChannelCount", output_params
.channels(),
110 limits::kMaxChannels
); // PRESUBMIT_IGNORE_UMA_MAX
113 if (ToAudioSampleRate(output_params
.sample_rate(), &asr
)) {
114 UMA_HISTOGRAM_ENUMERATION(
115 "Media.FallbackHardwareAudioSamplesPerSecond",
116 asr
, kAudioSampleRateMax
+ 1);
118 UMA_HISTOGRAM_COUNTS(
119 "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
120 output_params
.sample_rate());
124 // Converts low latency based |output_params| into high latency appropriate
125 // output parameters in error situations.
126 void AudioOutputResampler::SetupFallbackParams() {
127 // Only Windows has a high latency output driver that is not the same as the low
130 // Choose AudioParameters appropriate for opening the device in high latency
131 // mode. |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
132 // MAXIMUM frame size for low latency.
133 static const int kMinLowLatencyFrameSize
= 2048;
134 const int frames_per_buffer
=
135 std::max(params_
.frames_per_buffer(), kMinLowLatencyFrameSize
);
137 output_params_
= AudioParameters(
138 AudioParameters::AUDIO_PCM_LINEAR
, params_
.channel_layout(),
139 params_
.sample_rate(), params_
.bits_per_sample(),
146 AudioOutputResampler::AudioOutputResampler(AudioManager
* audio_manager
,
147 const AudioParameters
& input_params
,
148 const AudioParameters
& output_params
,
149 const std::string
& output_device_id
,
150 const base::TimeDelta
& close_delay
)
151 : AudioOutputDispatcher(audio_manager
, input_params
, output_device_id
),
152 close_delay_(close_delay
),
153 output_params_(output_params
),
154 original_output_params_(output_params
),
155 streams_opened_(false),
156 reinitialize_timer_(FROM_HERE
,
158 base::Bind(&AudioOutputResampler::Reinitialize
,
159 base::Unretained(this)),
161 DCHECK(input_params
.IsValid());
162 DCHECK(output_params
.IsValid());
163 DCHECK_EQ(output_params_
.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY
);
165 // Record UMA statistics for the hardware configuration.
166 RecordStats(output_params
);
171 AudioOutputResampler::~AudioOutputResampler() {
172 DCHECK(callbacks_
.empty());
175 void AudioOutputResampler::Reinitialize() {
176 DCHECK(task_runner_
->BelongsToCurrentThread());
177 DCHECK(streams_opened_
);
179 // We can only reinitialize the dispatcher if it has no active proxies. Check
180 // if one has been created since the reinitialization timer was started.
181 if (dispatcher_
->HasOutputProxies())
184 // Log a trace event so we can get feedback in the field when this happens.
185 TRACE_EVENT0("audio", "AudioOutputResampler::Reinitialize");
187 dispatcher_
->Shutdown();
188 output_params_
= original_output_params_
;
189 streams_opened_
= false;
193 void AudioOutputResampler::Initialize() {
194 DCHECK(!streams_opened_
);
195 DCHECK(callbacks_
.empty());
196 dispatcher_
= new AudioOutputDispatcherImpl(
197 audio_manager_
, output_params_
, device_id_
, close_delay_
);
200 bool AudioOutputResampler::OpenStream() {
201 DCHECK(task_runner_
->BelongsToCurrentThread());
203 if (dispatcher_
->OpenStream()) {
204 // Only record the UMA statistic if we didn't fallback during construction
205 // and only for the first stream we open.
206 if (!streams_opened_
&&
207 output_params_
.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY
) {
208 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
210 streams_opened_
= true;
214 // If we've already tried to open the stream in high latency mode or we've
215 // successfully opened a stream previously, there's nothing more to be done.
216 if (output_params_
.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY
||
217 streams_opened_
|| !callbacks_
.empty()) {
221 DCHECK_EQ(output_params_
.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY
);
223 // Record UMA statistics about the hardware which triggered the failure so
224 // we can debug and triage later.
225 RecordFallbackStats(output_params_
);
227 // Only Windows has a high latency output driver that is not the same as the
230 DLOG(ERROR
) << "Unable to open audio device in low latency mode. Falling "
231 << "back to high latency audio output.";
233 SetupFallbackParams();
234 if (dispatcher_
->OpenStream()) {
235 streams_opened_
= true;
240 DLOG(ERROR
) << "Unable to open audio device in high latency mode. Falling "
241 << "back to fake audio output.";
243 // Finally fall back to a fake audio output device.
244 output_params_
.Reset(
245 AudioParameters::AUDIO_FAKE
, params_
.channel_layout(),
246 params_
.channels(), params_
.sample_rate(),
247 params_
.bits_per_sample(), params_
.frames_per_buffer());
249 if (dispatcher_
->OpenStream()) {
250 streams_opened_
= true;
257 bool AudioOutputResampler::StartStream(
258 AudioOutputStream::AudioSourceCallback
* callback
,
259 AudioOutputProxy
* stream_proxy
) {
260 DCHECK(task_runner_
->BelongsToCurrentThread());
262 OnMoreDataConverter
* resampler_callback
= nullptr;
263 CallbackMap::iterator it
= callbacks_
.find(stream_proxy
);
264 if (it
== callbacks_
.end()) {
265 resampler_callback
= new OnMoreDataConverter(params_
, output_params_
);
266 callbacks_
[stream_proxy
] = resampler_callback
;
268 resampler_callback
= it
->second
;
271 resampler_callback
->Start(callback
);
272 bool result
= dispatcher_
->StartStream(resampler_callback
, stream_proxy
);
274 resampler_callback
->Stop();
278 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy
* stream_proxy
,
280 DCHECK(task_runner_
->BelongsToCurrentThread());
281 dispatcher_
->StreamVolumeSet(stream_proxy
, volume
);
284 void AudioOutputResampler::StopStream(AudioOutputProxy
* stream_proxy
) {
285 DCHECK(task_runner_
->BelongsToCurrentThread());
286 dispatcher_
->StopStream(stream_proxy
);
288 // Now that StopStream() has completed the underlying physical stream should
289 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
290 // OnMoreDataConverter.
291 CallbackMap::iterator it
= callbacks_
.find(stream_proxy
);
292 if (it
!= callbacks_
.end())
296 void AudioOutputResampler::CloseStream(AudioOutputProxy
* stream_proxy
) {
297 DCHECK(task_runner_
->BelongsToCurrentThread());
298 dispatcher_
->CloseStream(stream_proxy
);
300 // We assume that StopStream() is always called prior to CloseStream(), so
301 // that it is safe to delete the OnMoreDataConverter here.
302 CallbackMap::iterator it
= callbacks_
.find(stream_proxy
);
303 if (it
!= callbacks_
.end()) {
305 callbacks_
.erase(it
);
308 // Start the reinitialization timer if there are no active proxies and we're
309 // not using the originally requested output parameters. This allows us to
310 // recover from transient output creation errors.
311 if (!dispatcher_
->HasOutputProxies() && callbacks_
.empty() &&
312 !output_params_
.Equals(original_output_params_
)) {
313 reinitialize_timer_
.Reset();
317 void AudioOutputResampler::Shutdown() {
318 DCHECK(task_runner_
->BelongsToCurrentThread());
320 // No AudioOutputProxy objects should hold a reference to us when we get
322 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
324 dispatcher_
->Shutdown();
325 DCHECK(callbacks_
.empty());
328 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters
& input_params
,
329 const AudioParameters
& output_params
)
330 : io_ratio_(static_cast<double>(input_params
.GetBytesPerSecond()) /
331 output_params
.GetBytesPerSecond()),
332 source_callback_(nullptr),
333 input_bytes_per_second_(input_params
.GetBytesPerSecond()),
334 audio_converter_(input_params
, output_params
, false) {}
336 OnMoreDataConverter::~OnMoreDataConverter() {
337 // Ensure Stop() has been called so we don't end up with an AudioOutputStream
338 // calling back into OnMoreData() after destruction.
339 CHECK(!source_callback_
);
342 void OnMoreDataConverter::Start(
343 AudioOutputStream::AudioSourceCallback
* callback
) {
344 CHECK(!source_callback_
);
345 source_callback_
= callback
;
347 // While AudioConverter can handle multiple inputs, we're using it only with
348 // a single input currently. Eventually this may be the basis for a browser
350 audio_converter_
.AddInput(this);
353 void OnMoreDataConverter::Stop() {
354 CHECK(source_callback_
);
355 source_callback_
= nullptr;
356 audio_converter_
.RemoveInput(this);
359 int OnMoreDataConverter::OnMoreData(AudioBus
* dest
,
360 uint32 total_bytes_delay
) {
361 current_total_bytes_delay_
= total_bytes_delay
;
362 audio_converter_
.Convert(dest
);
364 // Always return the full number of frames requested, ProvideInput()
365 // will pad with silence if it wasn't able to acquire enough data.
366 return dest
->frames();
369 double OnMoreDataConverter::ProvideInput(AudioBus
* dest
,
370 base::TimeDelta buffer_delay
) {
371 // Adjust playback delay to include |buffer_delay|.
372 // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
373 // AudioBus is just float data. Use TimeDelta instead.
374 uint32 new_total_bytes_delay
= base::saturated_cast
<uint32
>(
375 io_ratio_
* (current_total_bytes_delay_
+
376 buffer_delay
.InSecondsF() * input_bytes_per_second_
));
378 // Retrieve data from the original callback.
379 const int frames
= source_callback_
->OnMoreData(dest
, new_total_bytes_delay
);
381 // Zero any unfilled frames if anything was filled, otherwise we'll just
382 // return a volume of zero and let AudioConverter drop the output.
383 if (frames
> 0 && frames
< dest
->frames())
384 dest
->ZeroFramesPartial(frames
, dest
->frames() - frames
);
385 return frames
> 0 ? 1 : 0;
388 void OnMoreDataConverter::OnError(AudioOutputStream
* stream
) {
389 source_callback_
->OnError(stream
);