webapps: fix theme color on status bar for Galaxy devices
[chromium-blink-merge.git] / media / audio / audio_output_resampler.cc
blobd85768b7b6a62b7e1716be298f9bf29cff13cda1
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/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"
20 namespace media {
22 class OnMoreDataConverter
23 : public AudioOutputStream::AudioSourceCallback,
24 public AudioConverter::InputCallback {
25 public:
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.
39 void Stop();
41 bool started() { return source_callback_ != nullptr; }
43 private:
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_;
52 // Source callback.
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
62 // parameters.
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
84 AudioSampleRate asr;
85 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
86 UMA_HISTOGRAM_ENUMERATION(
87 "Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1);
88 } else {
89 UMA_HISTOGRAM_COUNTS(
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
112 AudioSampleRate asr;
113 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
114 UMA_HISTOGRAM_ENUMERATION(
115 "Media.FallbackHardwareAudioSamplesPerSecond",
116 asr, kAudioSampleRateMax + 1);
117 } else {
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
128 // latency path.
129 #if defined(OS_WIN)
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(),
140 frames_per_buffer);
141 device_id_ = "";
142 Initialize();
143 #endif
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,
157 close_delay_,
158 base::Bind(&AudioOutputResampler::Reinitialize,
159 base::Unretained(this)),
160 false) {
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);
168 Initialize();
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())
182 return;
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;
190 Initialize();
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;
211 return 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()) {
218 return false;
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
228 // low latency path.
229 #if defined(OS_WIN)
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;
236 return true;
238 #endif
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_ = params_;
245 output_params_.set_format(AudioParameters::AUDIO_FAKE);
247 Initialize();
248 if (dispatcher_->OpenStream()) {
249 streams_opened_ = true;
250 return true;
253 return false;
256 bool AudioOutputResampler::StartStream(
257 AudioOutputStream::AudioSourceCallback* callback,
258 AudioOutputProxy* stream_proxy) {
259 DCHECK(task_runner_->BelongsToCurrentThread());
261 OnMoreDataConverter* resampler_callback = nullptr;
262 CallbackMap::iterator it = callbacks_.find(stream_proxy);
263 if (it == callbacks_.end()) {
264 resampler_callback = new OnMoreDataConverter(params_, output_params_);
265 callbacks_[stream_proxy] = resampler_callback;
266 } else {
267 resampler_callback = it->second;
270 resampler_callback->Start(callback);
271 bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
272 if (!result)
273 resampler_callback->Stop();
274 return result;
277 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
278 double volume) {
279 DCHECK(task_runner_->BelongsToCurrentThread());
280 dispatcher_->StreamVolumeSet(stream_proxy, volume);
283 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
284 DCHECK(task_runner_->BelongsToCurrentThread());
285 dispatcher_->StopStream(stream_proxy);
287 // Now that StopStream() has completed the underlying physical stream should
288 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
289 // OnMoreDataConverter.
290 CallbackMap::iterator it = callbacks_.find(stream_proxy);
291 if (it != callbacks_.end())
292 it->second->Stop();
295 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
296 DCHECK(task_runner_->BelongsToCurrentThread());
297 dispatcher_->CloseStream(stream_proxy);
299 // We assume that StopStream() is always called prior to CloseStream(), so
300 // that it is safe to delete the OnMoreDataConverter here.
301 CallbackMap::iterator it = callbacks_.find(stream_proxy);
302 if (it != callbacks_.end()) {
303 delete it->second;
304 callbacks_.erase(it);
307 // Start the reinitialization timer if there are no active proxies and we're
308 // not using the originally requested output parameters. This allows us to
309 // recover from transient output creation errors.
310 if (!dispatcher_->HasOutputProxies() && callbacks_.empty() &&
311 !output_params_.Equals(original_output_params_)) {
312 reinitialize_timer_.Reset();
316 void AudioOutputResampler::Shutdown() {
317 DCHECK(task_runner_->BelongsToCurrentThread());
319 // No AudioOutputProxy objects should hold a reference to us when we get
320 // to this stage.
321 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
323 dispatcher_->Shutdown();
324 DCHECK(callbacks_.empty());
327 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
328 const AudioParameters& output_params)
329 : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
330 output_params.GetBytesPerSecond()),
331 source_callback_(nullptr),
332 input_bytes_per_second_(input_params.GetBytesPerSecond()),
333 audio_converter_(input_params, output_params, false) {}
335 OnMoreDataConverter::~OnMoreDataConverter() {
336 // Ensure Stop() has been called so we don't end up with an AudioOutputStream
337 // calling back into OnMoreData() after destruction.
338 CHECK(!source_callback_);
341 void OnMoreDataConverter::Start(
342 AudioOutputStream::AudioSourceCallback* callback) {
343 CHECK(!source_callback_);
344 source_callback_ = callback;
346 // While AudioConverter can handle multiple inputs, we're using it only with
347 // a single input currently. Eventually this may be the basis for a browser
348 // side mixer.
349 audio_converter_.AddInput(this);
352 void OnMoreDataConverter::Stop() {
353 CHECK(source_callback_);
354 source_callback_ = nullptr;
355 audio_converter_.RemoveInput(this);
358 int OnMoreDataConverter::OnMoreData(AudioBus* dest,
359 uint32 total_bytes_delay) {
360 current_total_bytes_delay_ = total_bytes_delay;
361 audio_converter_.Convert(dest);
363 // Always return the full number of frames requested, ProvideInput()
364 // will pad with silence if it wasn't able to acquire enough data.
365 return dest->frames();
368 double OnMoreDataConverter::ProvideInput(AudioBus* dest,
369 base::TimeDelta buffer_delay) {
370 // Adjust playback delay to include |buffer_delay|.
371 // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
372 // AudioBus is just float data. Use TimeDelta instead.
373 uint32 new_total_bytes_delay = base::saturated_cast<uint32>(
374 io_ratio_ * (current_total_bytes_delay_ +
375 buffer_delay.InSecondsF() * input_bytes_per_second_));
377 // Retrieve data from the original callback.
378 const int frames = source_callback_->OnMoreData(dest, new_total_bytes_delay);
380 // Zero any unfilled frames if anything was filled, otherwise we'll just
381 // return a volume of zero and let AudioConverter drop the output.
382 if (frames > 0 && frames < dest->frames())
383 dest->ZeroFramesPartial(frames, dest->frames() - frames);
384 return frames > 0 ? 1 : 0;
387 void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
388 source_callback_->OnError(stream);
391 } // namespace media