Fix the Android Mandoline ui build.
[chromium-blink-merge.git] / media / audio / audio_output_resampler.cc
blobf0534448d041c72fc8e3f2f5245c0d379be71b4e
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"
7 #include "base/bind.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/time/time.h"
14 #include "base/trace_event/trace_event.h"
15 #include "build/build_config.h"
16 #include "media/audio/audio_io.h"
17 #include "media/audio/audio_output_dispatcher_impl.h"
18 #include "media/audio/audio_output_proxy.h"
19 #include "media/audio/sample_rates.h"
20 #include "media/base/audio_converter.h"
21 #include "media/base/limits.h"
23 namespace media {
25 class OnMoreDataConverter
26 : public AudioOutputStream::AudioSourceCallback,
27 public AudioConverter::InputCallback {
28 public:
29 OnMoreDataConverter(const AudioParameters& input_params,
30 const AudioParameters& output_params);
31 ~OnMoreDataConverter() override;
33 // AudioSourceCallback interface.
34 int OnMoreData(AudioBus* dest, uint32 total_bytes_delay) override;
35 void OnError(AudioOutputStream* stream) override;
37 // Sets |source_callback_|. If this is not a new object, then Stop() must be
38 // called before Start().
39 void Start(AudioOutputStream::AudioSourceCallback* callback);
41 // Clears |source_callback_| and flushes the resampler.
42 void Stop();
44 bool started() { return source_callback_ != nullptr; }
46 private:
47 // AudioConverter::InputCallback implementation.
48 double ProvideInput(AudioBus* audio_bus,
49 base::TimeDelta buffer_delay) override;
51 // Ratio of input bytes to output bytes used to correct playback delay with
52 // regard to buffering and resampling.
53 const double io_ratio_;
55 // Source callback.
56 AudioOutputStream::AudioSourceCallback* source_callback_;
58 // Last |total_bytes_delay| received via OnMoreData(), used to correct
59 // playback delay by ProvideInput() and passed on to |source_callback_|.
60 uint32 current_total_bytes_delay_;
62 const int input_bytes_per_second_;
64 // Handles resampling, buffering, and channel mixing between input and output
65 // parameters.
66 AudioConverter audio_converter_;
68 DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
71 // Record UMA statistics for hardware output configuration.
72 static void RecordStats(const AudioParameters& output_params) {
73 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
74 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
75 // to report a discrete value.
76 UMA_HISTOGRAM_ENUMERATION(
77 "Media.HardwareAudioBitsPerChannel",
78 output_params.bits_per_sample(),
79 limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX
80 UMA_HISTOGRAM_ENUMERATION(
81 "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
82 CHANNEL_LAYOUT_MAX + 1);
83 UMA_HISTOGRAM_ENUMERATION(
84 "Media.HardwareAudioChannelCount", output_params.channels(),
85 limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX
87 AudioSampleRate asr;
88 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
89 UMA_HISTOGRAM_ENUMERATION(
90 "Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1);
91 } else {
92 UMA_HISTOGRAM_COUNTS(
93 "Media.HardwareAudioSamplesPerSecondUnexpected",
94 output_params.sample_rate());
98 // Record UMA statistics for hardware output configuration after fallback.
99 static void RecordFallbackStats(const AudioParameters& output_params) {
100 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
101 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
102 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
103 // to report a discrete value.
104 UMA_HISTOGRAM_ENUMERATION(
105 "Media.FallbackHardwareAudioBitsPerChannel",
106 output_params.bits_per_sample(),
107 limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX
108 UMA_HISTOGRAM_ENUMERATION(
109 "Media.FallbackHardwareAudioChannelLayout",
110 output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1);
111 UMA_HISTOGRAM_ENUMERATION(
112 "Media.FallbackHardwareAudioChannelCount", output_params.channels(),
113 limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX
115 AudioSampleRate asr;
116 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
117 UMA_HISTOGRAM_ENUMERATION(
118 "Media.FallbackHardwareAudioSamplesPerSecond",
119 asr, kAudioSampleRateMax + 1);
120 } else {
121 UMA_HISTOGRAM_COUNTS(
122 "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
123 output_params.sample_rate());
127 // Converts low latency based |output_params| into high latency appropriate
128 // output parameters in error situations.
129 void AudioOutputResampler::SetupFallbackParams() {
130 // Only Windows has a high latency output driver that is not the same as the low
131 // latency path.
132 #if defined(OS_WIN)
133 // Choose AudioParameters appropriate for opening the device in high latency
134 // mode. |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
135 // MAXIMUM frame size for low latency.
136 static const int kMinLowLatencyFrameSize = 2048;
137 const int frames_per_buffer =
138 std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize);
140 output_params_ = AudioParameters(
141 AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
142 params_.sample_rate(), params_.bits_per_sample(),
143 frames_per_buffer);
144 device_id_ = "";
145 Initialize();
146 #endif
149 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
150 const AudioParameters& input_params,
151 const AudioParameters& output_params,
152 const std::string& output_device_id,
153 const base::TimeDelta& close_delay)
154 : AudioOutputDispatcher(audio_manager, input_params, output_device_id),
155 close_delay_(close_delay),
156 output_params_(output_params),
157 original_output_params_(output_params),
158 streams_opened_(false),
159 reinitialize_timer_(FROM_HERE,
160 close_delay_,
161 base::Bind(&AudioOutputResampler::Reinitialize,
162 base::Unretained(this)),
163 false) {
164 DCHECK(input_params.IsValid());
165 DCHECK(output_params.IsValid());
166 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
168 // Record UMA statistics for the hardware configuration.
169 RecordStats(output_params);
171 Initialize();
174 AudioOutputResampler::~AudioOutputResampler() {
175 DCHECK(callbacks_.empty());
178 void AudioOutputResampler::Reinitialize() {
179 DCHECK(task_runner_->BelongsToCurrentThread());
180 DCHECK(streams_opened_);
182 // We can only reinitialize the dispatcher if it has no active proxies. Check
183 // if one has been created since the reinitialization timer was started.
184 if (dispatcher_->HasOutputProxies())
185 return;
187 // Log a trace event so we can get feedback in the field when this happens.
188 TRACE_EVENT0("audio", "AudioOutputResampler::Reinitialize");
190 dispatcher_->Shutdown();
191 output_params_ = original_output_params_;
192 streams_opened_ = false;
193 Initialize();
196 void AudioOutputResampler::Initialize() {
197 DCHECK(!streams_opened_);
198 DCHECK(callbacks_.empty());
199 dispatcher_ = new AudioOutputDispatcherImpl(
200 audio_manager_, output_params_, device_id_, close_delay_);
203 bool AudioOutputResampler::OpenStream() {
204 DCHECK(task_runner_->BelongsToCurrentThread());
206 if (dispatcher_->OpenStream()) {
207 // Only record the UMA statistic if we didn't fallback during construction
208 // and only for the first stream we open.
209 if (!streams_opened_ &&
210 output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
211 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
213 streams_opened_ = true;
214 return true;
217 // If we've already tried to open the stream in high latency mode or we've
218 // successfully opened a stream previously, there's nothing more to be done.
219 if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
220 streams_opened_ || !callbacks_.empty()) {
221 return false;
224 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
226 // Record UMA statistics about the hardware which triggered the failure so
227 // we can debug and triage later.
228 RecordFallbackStats(output_params_);
230 // Only Windows has a high latency output driver that is not the same as the
231 // low latency path.
232 #if defined(OS_WIN)
233 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling "
234 << "back to high latency audio output.";
236 SetupFallbackParams();
237 if (dispatcher_->OpenStream()) {
238 streams_opened_ = true;
239 return true;
241 #endif
243 DLOG(ERROR) << "Unable to open audio device in high latency mode. Falling "
244 << "back to fake audio output.";
246 // Finally fall back to a fake audio output device.
247 output_params_.Reset(
248 AudioParameters::AUDIO_FAKE, params_.channel_layout(),
249 params_.channels(), params_.sample_rate(),
250 params_.bits_per_sample(), params_.frames_per_buffer());
251 Initialize();
252 if (dispatcher_->OpenStream()) {
253 streams_opened_ = true;
254 return true;
257 return false;
260 bool AudioOutputResampler::StartStream(
261 AudioOutputStream::AudioSourceCallback* callback,
262 AudioOutputProxy* stream_proxy) {
263 DCHECK(task_runner_->BelongsToCurrentThread());
265 OnMoreDataConverter* resampler_callback = nullptr;
266 CallbackMap::iterator it = callbacks_.find(stream_proxy);
267 if (it == callbacks_.end()) {
268 resampler_callback = new OnMoreDataConverter(params_, output_params_);
269 callbacks_[stream_proxy] = resampler_callback;
270 } else {
271 resampler_callback = it->second;
274 resampler_callback->Start(callback);
275 bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
276 if (!result)
277 resampler_callback->Stop();
278 return result;
281 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
282 double volume) {
283 DCHECK(task_runner_->BelongsToCurrentThread());
284 dispatcher_->StreamVolumeSet(stream_proxy, volume);
287 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
288 DCHECK(task_runner_->BelongsToCurrentThread());
289 dispatcher_->StopStream(stream_proxy);
291 // Now that StopStream() has completed the underlying physical stream should
292 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
293 // OnMoreDataConverter.
294 CallbackMap::iterator it = callbacks_.find(stream_proxy);
295 if (it != callbacks_.end())
296 it->second->Stop();
299 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
300 DCHECK(task_runner_->BelongsToCurrentThread());
301 dispatcher_->CloseStream(stream_proxy);
303 // We assume that StopStream() is always called prior to CloseStream(), so
304 // that it is safe to delete the OnMoreDataConverter here.
305 CallbackMap::iterator it = callbacks_.find(stream_proxy);
306 if (it != callbacks_.end()) {
307 delete it->second;
308 callbacks_.erase(it);
311 // Start the reinitialization timer if there are no active proxies and we're
312 // not using the originally requested output parameters. This allows us to
313 // recover from transient output creation errors.
314 if (!dispatcher_->HasOutputProxies() && callbacks_.empty() &&
315 !output_params_.Equals(original_output_params_)) {
316 reinitialize_timer_.Reset();
320 void AudioOutputResampler::Shutdown() {
321 DCHECK(task_runner_->BelongsToCurrentThread());
323 // No AudioOutputProxy objects should hold a reference to us when we get
324 // to this stage.
325 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
327 dispatcher_->Shutdown();
328 DCHECK(callbacks_.empty());
331 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
332 const AudioParameters& output_params)
333 : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
334 output_params.GetBytesPerSecond()),
335 source_callback_(nullptr),
336 input_bytes_per_second_(input_params.GetBytesPerSecond()),
337 audio_converter_(input_params, output_params, false) {}
339 OnMoreDataConverter::~OnMoreDataConverter() {
340 // Ensure Stop() has been called so we don't end up with an AudioOutputStream
341 // calling back into OnMoreData() after destruction.
342 CHECK(!source_callback_);
345 void OnMoreDataConverter::Start(
346 AudioOutputStream::AudioSourceCallback* callback) {
347 CHECK(!source_callback_);
348 source_callback_ = callback;
350 // While AudioConverter can handle multiple inputs, we're using it only with
351 // a single input currently. Eventually this may be the basis for a browser
352 // side mixer.
353 audio_converter_.AddInput(this);
356 void OnMoreDataConverter::Stop() {
357 CHECK(source_callback_);
358 source_callback_ = nullptr;
359 audio_converter_.RemoveInput(this);
362 int OnMoreDataConverter::OnMoreData(AudioBus* dest,
363 uint32 total_bytes_delay) {
364 current_total_bytes_delay_ = total_bytes_delay;
365 audio_converter_.Convert(dest);
367 // Always return the full number of frames requested, ProvideInput()
368 // will pad with silence if it wasn't able to acquire enough data.
369 return dest->frames();
372 double OnMoreDataConverter::ProvideInput(AudioBus* dest,
373 base::TimeDelta buffer_delay) {
374 // Adjust playback delay to include |buffer_delay|.
375 // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
376 // AudioBus is just float data. Use TimeDelta instead.
377 uint32 new_total_bytes_delay = base::saturated_cast<uint32>(
378 io_ratio_ * (current_total_bytes_delay_ +
379 buffer_delay.InSecondsF() * input_bytes_per_second_));
381 // Retrieve data from the original callback.
382 const int frames = source_callback_->OnMoreData(dest, new_total_bytes_delay);
384 // Zero any unfilled frames if anything was filled, otherwise we'll just
385 // return a volume of zero and let AudioConverter drop the output.
386 if (frames > 0 && frames < dest->frames())
387 dest->ZeroFramesPartial(frames, dest->frames() - frames);
388 return frames > 0 ? 1 : 0;
391 void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
392 source_callback_->OnError(stream);
395 } // namespace media