audio_output_resampler.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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/command_line.h"
10#include "base/compiler_specific.h"
11#include "base/message_loop.h"
12#include "base/metrics/histogram.h"
13#include "base/time.h"
14#include "build/build_config.h"
15#include "media/audio/audio_io.h"
16#include "media/audio/audio_output_dispatcher_impl.h"
17#include "media/audio/audio_output_proxy.h"
18#include "media/audio/audio_util.h"
19#include "media/audio/sample_rates.h"
20#include "media/base/audio_converter.h"
21#include "media/base/limits.h"
22#include "media/base/media_switches.h"
23
24namespace media {
25
26class OnMoreDataConverter
27    : public AudioOutputStream::AudioSourceCallback,
28      public AudioConverter::InputCallback {
29 public:
30  OnMoreDataConverter(const AudioParameters& input_params,
31                      const AudioParameters& output_params);
32  virtual ~OnMoreDataConverter();
33
34  // AudioSourceCallback interface.
35  virtual int OnMoreData(AudioBus* dest,
36                         AudioBuffersState buffers_state) OVERRIDE;
37  virtual int OnMoreIOData(AudioBus* source,
38                           AudioBus* dest,
39                           AudioBuffersState buffers_state) OVERRIDE;
40  virtual void OnError(AudioOutputStream* stream) OVERRIDE;
41  virtual void WaitTillDataReady() OVERRIDE;
42
43  // Sets |source_callback_|.  If this is not a new object, then Stop() must be
44  // called before Start().
45  void Start(AudioOutputStream::AudioSourceCallback* callback);
46
47  // Clears |source_callback_| and flushes the resampler.
48  void Stop();
49
50 private:
51  // AudioConverter::InputCallback implementation.
52  virtual double ProvideInput(AudioBus* audio_bus,
53                              base::TimeDelta buffer_delay) OVERRIDE;
54
55  // Ratio of input bytes to output bytes used to correct playback delay with
56  // regard to buffering and resampling.
57  double io_ratio_;
58
59  // Source callback and associated lock.
60  base::Lock source_lock_;
61  AudioOutputStream::AudioSourceCallback* source_callback_;
62
63  // |source| passed to OnMoreIOData() which should be passed downstream.
64  AudioBus* source_bus_;
65
66  // Last AudioBuffersState object received via OnMoreData(), used to correct
67  // playback delay by ProvideInput() and passed on to |source_callback_|.
68  AudioBuffersState current_buffers_state_;
69
70  const int input_bytes_per_second_;
71
72  // Handles resampling, buffering, and channel mixing between input and output
73  // parameters.
74  AudioConverter audio_converter_;
75
76  DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
77};
78
79// Record UMA statistics for hardware output configuration.
80static void RecordStats(const AudioParameters& output_params) {
81  UMA_HISTOGRAM_ENUMERATION(
82      "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(),
83      limits::kMaxBitsPerSample);
84  UMA_HISTOGRAM_ENUMERATION(
85      "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
86      CHANNEL_LAYOUT_MAX);
87  UMA_HISTOGRAM_ENUMERATION(
88      "Media.HardwareAudioChannelCount", output_params.channels(),
89      limits::kMaxChannels);
90
91  AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
92  if (asr != kUnexpectedAudioSampleRate) {
93    UMA_HISTOGRAM_ENUMERATION(
94        "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate);
95  } else {
96    UMA_HISTOGRAM_COUNTS(
97        "Media.HardwareAudioSamplesPerSecondUnexpected",
98        output_params.sample_rate());
99  }
100}
101
102// Record UMA statistics for hardware output configuration after fallback.
103static void RecordFallbackStats(const AudioParameters& output_params) {
104  UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
105  UMA_HISTOGRAM_ENUMERATION(
106      "Media.FallbackHardwareAudioBitsPerChannel",
107      output_params.bits_per_sample(), limits::kMaxBitsPerSample);
108  UMA_HISTOGRAM_ENUMERATION(
109      "Media.FallbackHardwareAudioChannelLayout",
110      output_params.channel_layout(), CHANNEL_LAYOUT_MAX);
111  UMA_HISTOGRAM_ENUMERATION(
112      "Media.FallbackHardwareAudioChannelCount",
113      output_params.channels(), limits::kMaxChannels);
114
115  AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
116  if (asr != kUnexpectedAudioSampleRate) {
117    UMA_HISTOGRAM_ENUMERATION(
118        "Media.FallbackHardwareAudioSamplesPerSecond",
119        asr, kUnexpectedAudioSampleRate);
120  } else {
121    UMA_HISTOGRAM_COUNTS(
122        "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
123        output_params.sample_rate());
124  }
125}
126
127// Converts low latency based |output_params| into high latency appropriate
128// output parameters in error situations.
129static AudioParameters SetupFallbackParams(
130    const AudioParameters& input_params, const AudioParameters& output_params) {
131  // Choose AudioParameters appropriate for opening the device in high latency
132  // mode.  |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
133  // MAXIMUM frame size for low latency.
134  static const int kMinLowLatencyFrameSize = 2048;
135  int frames_per_buffer = std::min(
136      std::max(input_params.frames_per_buffer(), kMinLowLatencyFrameSize),
137      static_cast<int>(
138          GetHighLatencyOutputBufferSize(input_params.sample_rate())));
139
140  return AudioParameters(
141      AudioParameters::AUDIO_PCM_LINEAR, input_params.channel_layout(),
142      input_params.sample_rate(), input_params.bits_per_sample(),
143      frames_per_buffer);
144}
145
146AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
147                                           const AudioParameters& input_params,
148                                           const AudioParameters& output_params,
149                                           const base::TimeDelta& close_delay)
150    : AudioOutputDispatcher(audio_manager, input_params),
151      close_delay_(close_delay),
152      output_params_(output_params),
153      streams_opened_(false) {
154  DCHECK(input_params.IsValid());
155  DCHECK(output_params.IsValid());
156  DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
157
158  // Record UMA statistics for the hardware configuration.
159  RecordStats(output_params);
160
161  Initialize();
162}
163
164AudioOutputResampler::~AudioOutputResampler() {
165  DCHECK(callbacks_.empty());
166}
167
168void AudioOutputResampler::Initialize() {
169  DCHECK(!streams_opened_);
170  DCHECK(callbacks_.empty());
171  dispatcher_ = new AudioOutputDispatcherImpl(
172      audio_manager_, output_params_, close_delay_);
173}
174
175bool AudioOutputResampler::OpenStream() {
176  DCHECK_EQ(MessageLoop::current(), message_loop_);
177
178  if (dispatcher_->OpenStream()) {
179    // Only record the UMA statistic if we didn't fallback during construction
180    // and only for the first stream we open.
181    if (!streams_opened_ &&
182        output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
183      UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
184    }
185    streams_opened_ = true;
186    return true;
187  }
188
189  // If we've already tried to open the stream in high latency mode or we've
190  // successfully opened a stream previously, there's nothing more to be done.
191  if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
192      streams_opened_ || !callbacks_.empty()) {
193    return false;
194  }
195
196  DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
197
198  if (CommandLine::ForCurrentProcess()->HasSwitch(
199          switches::kDisableAudioFallback)) {
200    LOG(ERROR) << "Open failed and automatic fallback to high latency audio "
201               << "path is disabled, aborting.";
202    return false;
203  }
204
205  DLOG(ERROR) << "Unable to open audio device in low latency mode.  Falling "
206              << "back to high latency audio output.";
207
208  // Record UMA statistics about the hardware which triggered the failure so
209  // we can debug and triage later.
210  RecordFallbackStats(output_params_);
211  output_params_ = SetupFallbackParams(params_, output_params_);
212  Initialize();
213  if (dispatcher_->OpenStream()) {
214    streams_opened_ = true;
215    return true;
216  }
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_.input_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_EQ(MessageLoop::current(), message_loop_);
239
240  OnMoreDataConverter* resampler_callback = NULL;
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_EQ(MessageLoop::current(), message_loop_);
259  dispatcher_->StreamVolumeSet(stream_proxy, volume);
260}
261
262void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
263  DCHECK_EQ(MessageLoop::current(), message_loop_);
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_EQ(MessageLoop::current(), message_loop_);
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_EQ(MessageLoop::current(), message_loop_);
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    : source_callback_(NULL),
301      source_bus_(NULL),
302      input_bytes_per_second_(input_params.GetBytesPerSecond()),
303      audio_converter_(input_params, output_params, false) {
304  io_ratio_ =
305      static_cast<double>(input_params.GetBytesPerSecond()) /
306      output_params.GetBytesPerSecond();
307}
308
309OnMoreDataConverter::~OnMoreDataConverter() {
310  // Ensure Stop() has been called so we don't end up with an AudioOutputStream
311  // calling back into OnMoreData() after destruction.
312  CHECK(!source_callback_);
313}
314
315void OnMoreDataConverter::Start(
316    AudioOutputStream::AudioSourceCallback* callback) {
317  base::AutoLock auto_lock(source_lock_);
318  CHECK(!source_callback_);
319  source_callback_ = callback;
320
321  // While AudioConverter can handle multiple inputs, we're using it only with
322  // a single input currently.  Eventually this may be the basis for a browser
323  // side mixer.
324  audio_converter_.AddInput(this);
325}
326
327void OnMoreDataConverter::Stop() {
328  base::AutoLock auto_lock(source_lock_);
329  CHECK(source_callback_);
330  source_callback_ = NULL;
331  audio_converter_.RemoveInput(this);
332}
333
334int OnMoreDataConverter::OnMoreData(AudioBus* dest,
335                                    AudioBuffersState buffers_state) {
336  return OnMoreIOData(NULL, dest, buffers_state);
337}
338
339int OnMoreDataConverter::OnMoreIOData(AudioBus* source,
340                                      AudioBus* dest,
341                                      AudioBuffersState buffers_state) {
342  base::AutoLock auto_lock(source_lock_);
343  // While we waited for |source_lock_| the callback might have been cleared.
344  if (!source_callback_) {
345    dest->Zero();
346    return dest->frames();
347  }
348
349  source_bus_ = source;
350  current_buffers_state_ = buffers_state;
351  audio_converter_.Convert(dest);
352
353  // Always return the full number of frames requested, ProvideInput_Locked()
354  // will pad with silence if it wasn't able to acquire enough data.
355  return dest->frames();
356}
357
358double OnMoreDataConverter::ProvideInput(AudioBus* dest,
359                                         base::TimeDelta buffer_delay) {
360  source_lock_.AssertAcquired();
361
362  // Adjust playback delay to include |buffer_delay|.
363  // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
364  // AudioBus is just float data.  Use TimeDelta instead.
365  AudioBuffersState new_buffers_state;
366  new_buffers_state.pending_bytes =
367      io_ratio_ * (current_buffers_state_.total_bytes() +
368                   buffer_delay.InSecondsF() * input_bytes_per_second_);
369
370  // Retrieve data from the original callback.
371  int frames = source_callback_->OnMoreIOData(
372      source_bus_, dest, new_buffers_state);
373
374  // |source_bus_| should only be provided once.
375  // TODO(dalecurtis, crogers): This is not a complete fix.  If ProvideInput()
376  // is called multiple times, we need to do something more clever here.
377  source_bus_ = NULL;
378
379  // Zero any unfilled frames if anything was filled, otherwise we'll just
380  // return a volume of zero and let AudioConverter drop the output.
381  if (frames > 0 && frames < dest->frames())
382    dest->ZeroFramesPartial(frames, dest->frames() - frames);
383
384  // TODO(dalecurtis): Return the correct volume here.
385  return frames > 0 ? 1 : 0;
386}
387
388void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
389  base::AutoLock auto_lock(source_lock_);
390  if (source_callback_)
391    source_callback_->OnError(stream);
392}
393
394void OnMoreDataConverter::WaitTillDataReady() {
395  base::AutoLock auto_lock(source_lock_);
396  if (source_callback_)
397    source_callback_->WaitTillDataReady();
398}
399
400}  // namespace media
401