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