audio_output_resampler.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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.
4
5#include "media/audio/audio_output_resampler.h"
6
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/single_thread_task_runner.h"
12#include "base/time/time.h"
13#include "build/build_config.h"
14#include "media/audio/audio_io.h"
15#include "media/audio/audio_output_dispatcher_impl.h"
16#include "media/audio/audio_output_proxy.h"
17#include "media/audio/sample_rates.h"
18#include "media/base/audio_converter.h"
19#include "media/base/limits.h"
20
21namespace media {
22
23class OnMoreDataConverter
24    : public AudioOutputStream::AudioSourceCallback,
25      public AudioConverter::InputCallback {
26 public:
27  OnMoreDataConverter(const AudioParameters& input_params,
28                      const AudioParameters& output_params);
29  virtual ~OnMoreDataConverter();
30
31  // AudioSourceCallback interface.
32  virtual int OnMoreData(AudioBus* dest,
33                         AudioBuffersState buffers_state) OVERRIDE;
34  virtual void OnError(AudioOutputStream* stream) OVERRIDE;
35
36  // Sets |source_callback_|.  If this is not a new object, then Stop() must be
37  // called before Start().
38  void Start(AudioOutputStream::AudioSourceCallback* callback);
39
40  // Clears |source_callback_| and flushes the resampler.
41  void Stop();
42
43  bool started() { return source_callback_ != nullptr; }
44
45 private:
46  // AudioConverter::InputCallback implementation.
47  virtual double ProvideInput(AudioBus* audio_bus,
48                              base::TimeDelta buffer_delay) OVERRIDE;
49
50  // Ratio of input bytes to output bytes used to correct playback delay with
51  // regard to buffering and resampling.
52  const double io_ratio_;
53
54  // Source callback.
55  AudioOutputStream::AudioSourceCallback* source_callback_;
56
57  // Last AudioBuffersState object received via OnMoreData(), used to correct
58  // playback delay by ProvideInput() and passed on to |source_callback_|.
59  AudioBuffersState current_buffers_state_;
60
61  const int input_bytes_per_second_;
62
63  // Handles resampling, buffering, and channel mixing between input and output
64  // parameters.
65  AudioConverter audio_converter_;
66
67  DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
68};
69
70// Record UMA statistics for hardware output configuration.
71static void RecordStats(const AudioParameters& output_params) {
72  // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
73  // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
74  // to report a discrete value.
75  UMA_HISTOGRAM_ENUMERATION(
76      "Media.HardwareAudioBitsPerChannel",
77      output_params.bits_per_sample(),
78      limits::kMaxBitsPerSample);  // PRESUBMIT_IGNORE_UMA_MAX
79  UMA_HISTOGRAM_ENUMERATION(
80      "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
81      CHANNEL_LAYOUT_MAX + 1);
82  UMA_HISTOGRAM_ENUMERATION(
83      "Media.HardwareAudioChannelCount", output_params.channels(),
84      limits::kMaxChannels);  // PRESUBMIT_IGNORE_UMA_MAX
85
86  AudioSampleRate asr;
87  if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
88    UMA_HISTOGRAM_ENUMERATION(
89        "Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1);
90  } else {
91    UMA_HISTOGRAM_COUNTS(
92        "Media.HardwareAudioSamplesPerSecondUnexpected",
93        output_params.sample_rate());
94  }
95}
96
97// Record UMA statistics for hardware output configuration after fallback.
98static void RecordFallbackStats(const AudioParameters& output_params) {
99  UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
100  // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
101  // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
102  // to report a discrete value.
103  UMA_HISTOGRAM_ENUMERATION(
104      "Media.FallbackHardwareAudioBitsPerChannel",
105      output_params.bits_per_sample(),
106      limits::kMaxBitsPerSample);  // PRESUBMIT_IGNORE_UMA_MAX
107  UMA_HISTOGRAM_ENUMERATION(
108      "Media.FallbackHardwareAudioChannelLayout",
109      output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1);
110  UMA_HISTOGRAM_ENUMERATION(
111      "Media.FallbackHardwareAudioChannelCount", output_params.channels(),
112      limits::kMaxChannels);  // PRESUBMIT_IGNORE_UMA_MAX
113
114  AudioSampleRate asr;
115  if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
116    UMA_HISTOGRAM_ENUMERATION(
117        "Media.FallbackHardwareAudioSamplesPerSecond",
118        asr, kAudioSampleRateMax + 1);
119  } else {
120    UMA_HISTOGRAM_COUNTS(
121        "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
122        output_params.sample_rate());
123  }
124}
125
126// Converts low latency based |output_params| into high latency appropriate
127// output parameters in error situations.
128void AudioOutputResampler::SetupFallbackParams() {
129// Only Windows has a high latency output driver that is not the same as the low
130// latency path.
131#if defined(OS_WIN)
132  // Choose AudioParameters appropriate for opening the device in high latency
133  // mode.  |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
134  // MAXIMUM frame size for low latency.
135  static const int kMinLowLatencyFrameSize = 2048;
136  const int frames_per_buffer =
137      std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize);
138
139  output_params_ = AudioParameters(
140      AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
141      params_.sample_rate(), params_.bits_per_sample(),
142      frames_per_buffer);
143  device_id_ = "";
144  Initialize();
145#endif
146}
147
148AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
149                                           const AudioParameters& input_params,
150                                           const AudioParameters& output_params,
151                                           const std::string& output_device_id,
152                                           const base::TimeDelta& close_delay)
153    : AudioOutputDispatcher(audio_manager, input_params, output_device_id),
154      close_delay_(close_delay),
155      output_params_(output_params),
156      streams_opened_(false) {
157  DCHECK(input_params.IsValid());
158  DCHECK(output_params.IsValid());
159  DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
160
161  // Record UMA statistics for the hardware configuration.
162  RecordStats(output_params);
163
164  Initialize();
165}
166
167AudioOutputResampler::~AudioOutputResampler() {
168  DCHECK(callbacks_.empty());
169}
170
171void AudioOutputResampler::Initialize() {
172  DCHECK(!streams_opened_);
173  DCHECK(callbacks_.empty());
174  dispatcher_ = new AudioOutputDispatcherImpl(
175      audio_manager_, output_params_, device_id_, close_delay_);
176}
177
178bool AudioOutputResampler::OpenStream() {
179  DCHECK(task_runner_->BelongsToCurrentThread());
180
181  if (dispatcher_->OpenStream()) {
182    // Only record the UMA statistic if we didn't fallback during construction
183    // and only for the first stream we open.
184    if (!streams_opened_ &&
185        output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
186      UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
187    }
188    streams_opened_ = true;
189    return true;
190  }
191
192  // If we've already tried to open the stream in high latency mode or we've
193  // successfully opened a stream previously, there's nothing more to be done.
194  if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
195      streams_opened_ || !callbacks_.empty()) {
196    return false;
197  }
198
199  DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
200
201  // Record UMA statistics about the hardware which triggered the failure so
202  // we can debug and triage later.
203  RecordFallbackStats(output_params_);
204
205  // Only Windows has a high latency output driver that is not the same as the
206  // low latency path.
207#if defined(OS_WIN)
208  DLOG(ERROR) << "Unable to open audio device in low latency mode.  Falling "
209              << "back to high latency audio output.";
210
211  SetupFallbackParams();
212  if (dispatcher_->OpenStream()) {
213    streams_opened_ = true;
214    return true;
215  }
216#endif
217
218  DLOG(ERROR) << "Unable to open audio device in high latency mode.  Falling "
219              << "back to fake audio output.";
220
221  // Finally fall back to a fake audio output device.
222  output_params_.Reset(
223      AudioParameters::AUDIO_FAKE, params_.channel_layout(),
224      params_.channels(), params_.sample_rate(),
225      params_.bits_per_sample(), params_.frames_per_buffer());
226  Initialize();
227  if (dispatcher_->OpenStream()) {
228    streams_opened_ = true;
229    return true;
230  }
231
232  return false;
233}
234
235bool AudioOutputResampler::StartStream(
236    AudioOutputStream::AudioSourceCallback* callback,
237    AudioOutputProxy* stream_proxy) {
238  DCHECK(task_runner_->BelongsToCurrentThread());
239
240  OnMoreDataConverter* resampler_callback = nullptr;
241  CallbackMap::iterator it = callbacks_.find(stream_proxy);
242  if (it == callbacks_.end()) {
243    resampler_callback = new OnMoreDataConverter(params_, output_params_);
244    callbacks_[stream_proxy] = resampler_callback;
245  } else {
246    resampler_callback = it->second;
247  }
248
249  resampler_callback->Start(callback);
250  bool result = dispatcher_->StartStream(resampler_callback, stream_proxy);
251  if (!result)
252    resampler_callback->Stop();
253  return result;
254}
255
256void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
257                                           double volume) {
258  DCHECK(task_runner_->BelongsToCurrentThread());
259  dispatcher_->StreamVolumeSet(stream_proxy, volume);
260}
261
262void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
263  DCHECK(task_runner_->BelongsToCurrentThread());
264  dispatcher_->StopStream(stream_proxy);
265
266  // Now that StopStream() has completed the underlying physical stream should
267  // be stopped and no longer calling OnMoreData(), making it safe to Stop() the
268  // OnMoreDataConverter.
269  CallbackMap::iterator it = callbacks_.find(stream_proxy);
270  if (it != callbacks_.end())
271    it->second->Stop();
272}
273
274void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
275  DCHECK(task_runner_->BelongsToCurrentThread());
276  dispatcher_->CloseStream(stream_proxy);
277
278  // We assume that StopStream() is always called prior to CloseStream(), so
279  // that it is safe to delete the OnMoreDataConverter here.
280  CallbackMap::iterator it = callbacks_.find(stream_proxy);
281  if (it != callbacks_.end()) {
282    delete it->second;
283    callbacks_.erase(it);
284  }
285}
286
287void AudioOutputResampler::Shutdown() {
288  DCHECK(task_runner_->BelongsToCurrentThread());
289
290  // No AudioOutputProxy objects should hold a reference to us when we get
291  // to this stage.
292  DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
293
294  dispatcher_->Shutdown();
295  DCHECK(callbacks_.empty());
296}
297
298OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
299                                         const AudioParameters& output_params)
300    : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
301                output_params.GetBytesPerSecond()),
302      source_callback_(nullptr),
303      input_bytes_per_second_(input_params.GetBytesPerSecond()),
304      audio_converter_(input_params, output_params, false) {}
305
306OnMoreDataConverter::~OnMoreDataConverter() {
307  // Ensure Stop() has been called so we don't end up with an AudioOutputStream
308  // calling back into OnMoreData() after destruction.
309  CHECK(!source_callback_);
310}
311
312void OnMoreDataConverter::Start(
313    AudioOutputStream::AudioSourceCallback* callback) {
314  CHECK(!source_callback_);
315  source_callback_ = callback;
316
317  // While AudioConverter can handle multiple inputs, we're using it only with
318  // a single input currently.  Eventually this may be the basis for a browser
319  // side mixer.
320  audio_converter_.AddInput(this);
321}
322
323void OnMoreDataConverter::Stop() {
324  CHECK(source_callback_);
325  source_callback_ = nullptr;
326  audio_converter_.RemoveInput(this);
327}
328
329int OnMoreDataConverter::OnMoreData(AudioBus* dest,
330                                    AudioBuffersState buffers_state) {
331  current_buffers_state_ = buffers_state;
332  audio_converter_.Convert(dest);
333
334  // Always return the full number of frames requested, ProvideInput()
335  // will pad with silence if it wasn't able to acquire enough data.
336  return dest->frames();
337}
338
339double OnMoreDataConverter::ProvideInput(AudioBus* dest,
340                                         base::TimeDelta buffer_delay) {
341  // Adjust playback delay to include |buffer_delay|.
342  // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
343  // AudioBus is just float data.  Use TimeDelta instead.
344  AudioBuffersState new_buffers_state;
345  new_buffers_state.pending_bytes =
346      io_ratio_ * (current_buffers_state_.total_bytes() +
347                   buffer_delay.InSecondsF() * input_bytes_per_second_);
348
349  // Retrieve data from the original callback.
350  const int frames = source_callback_->OnMoreData(dest, new_buffers_state);
351
352  // Zero any unfilled frames if anything was filled, otherwise we'll just
353  // return a volume of zero and let AudioConverter drop the output.
354  if (frames > 0 && frames < dest->frames())
355    dest->ZeroFramesPartial(frames, dest->frames() - frames);
356  return frames > 0 ? 1 : 0;
357}
358
359void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
360  source_callback_->OnError(stream);
361}
362
363}  // namespace media
364