audio_output_resampler.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/message_loop/message_loop.h"
11#include "base/metrics/histogram.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 private:
47  // AudioConverter::InputCallback implementation.
48  virtual double ProvideInput(AudioBus* audio_bus,
49                              base::TimeDelta buffer_delay) OVERRIDE;
50
51  // Ratio of input bytes to output bytes used to correct playback delay with
52  // regard to buffering and resampling.
53  double io_ratio_;
54
55  // Source callback and associated lock.
56  base::Lock source_lock_;
57  AudioOutputStream::AudioSourceCallback* source_callback_;
58
59  // |source| passed to OnMoreIOData() which should be passed downstream.
60  AudioBus* source_bus_;
61
62  // Last AudioBuffersState object received via OnMoreData(), used to correct
63  // playback delay by ProvideInput() and passed on to |source_callback_|.
64  AudioBuffersState current_buffers_state_;
65
66  const int input_bytes_per_second_;
67
68  // Handles resampling, buffering, and channel mixing between input and output
69  // parameters.
70  AudioConverter audio_converter_;
71
72  DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
73};
74
75// Record UMA statistics for hardware output configuration.
76static void RecordStats(const AudioParameters& output_params) {
77  UMA_HISTOGRAM_ENUMERATION(
78      "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(),
79      limits::kMaxBitsPerSample);
80  UMA_HISTOGRAM_ENUMERATION(
81      "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
82      CHANNEL_LAYOUT_MAX);
83  UMA_HISTOGRAM_ENUMERATION(
84      "Media.HardwareAudioChannelCount", output_params.channels(),
85      limits::kMaxChannels);
86
87  AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
88  if (asr != kUnexpectedAudioSampleRate) {
89    UMA_HISTOGRAM_ENUMERATION(
90        "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate);
91  } else {
92    UMA_HISTOGRAM_COUNTS(
93        "Media.HardwareAudioSamplesPerSecondUnexpected",
94        output_params.sample_rate());
95  }
96}
97
98// Record UMA statistics for hardware output configuration after fallback.
99static void RecordFallbackStats(const AudioParameters& output_params) {
100  UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
101  UMA_HISTOGRAM_ENUMERATION(
102      "Media.FallbackHardwareAudioBitsPerChannel",
103      output_params.bits_per_sample(), limits::kMaxBitsPerSample);
104  UMA_HISTOGRAM_ENUMERATION(
105      "Media.FallbackHardwareAudioChannelLayout",
106      output_params.channel_layout(), CHANNEL_LAYOUT_MAX);
107  UMA_HISTOGRAM_ENUMERATION(
108      "Media.FallbackHardwareAudioChannelCount",
109      output_params.channels(), limits::kMaxChannels);
110
111  AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate());
112  if (asr != kUnexpectedAudioSampleRate) {
113    UMA_HISTOGRAM_ENUMERATION(
114        "Media.FallbackHardwareAudioSamplesPerSecond",
115        asr, kUnexpectedAudioSampleRate);
116  } else {
117    UMA_HISTOGRAM_COUNTS(
118        "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
119        output_params.sample_rate());
120  }
121}
122
123// Only Windows has a high latency output driver that is not the same as the low
124// latency path.
125#if defined(OS_WIN)
126// Converts low latency based |output_params| into high latency appropriate
127// output parameters in error situations.
128static AudioParameters SetupFallbackParams(
129    const AudioParameters& input_params, const AudioParameters& output_params) {
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(input_params.frames_per_buffer(), kMinLowLatencyFrameSize);
136
137  return AudioParameters(
138      AudioParameters::AUDIO_PCM_LINEAR, input_params.channel_layout(),
139      input_params.sample_rate(), input_params.bits_per_sample(),
140      frames_per_buffer);
141}
142#endif
143
144AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
145                                           const AudioParameters& input_params,
146                                           const AudioParameters& output_params,
147                                           const std::string& output_device_id,
148                                           const std::string& input_device_id,
149                                           const base::TimeDelta& close_delay)
150    : AudioOutputDispatcher(audio_manager, input_params, output_device_id,
151                            input_device_id),
152      close_delay_(close_delay),
153      output_params_(output_params),
154      streams_opened_(false) {
155  DCHECK(input_params.IsValid());
156  DCHECK(output_params.IsValid());
157  DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
158
159  // Record UMA statistics for the hardware configuration.
160  RecordStats(output_params);
161
162  Initialize();
163}
164
165AudioOutputResampler::~AudioOutputResampler() {
166  DCHECK(callbacks_.empty());
167}
168
169void AudioOutputResampler::Initialize() {
170  DCHECK(!streams_opened_);
171  DCHECK(callbacks_.empty());
172  dispatcher_ = new AudioOutputDispatcherImpl(
173      audio_manager_, output_params_, output_device_id_, input_device_id_,
174      close_delay_);
175}
176
177bool AudioOutputResampler::OpenStream() {
178  DCHECK_EQ(base::MessageLoop::current(), message_loop_);
179
180  if (dispatcher_->OpenStream()) {
181    // Only record the UMA statistic if we didn't fallback during construction
182    // and only for the first stream we open.
183    if (!streams_opened_ &&
184        output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
185      UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
186    }
187    streams_opened_ = true;
188    return true;
189  }
190
191  // If we've already tried to open the stream in high latency mode or we've
192  // successfully opened a stream previously, there's nothing more to be done.
193  if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
194      streams_opened_ || !callbacks_.empty()) {
195    return false;
196  }
197
198  DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
199
200  // Record UMA statistics about the hardware which triggered the failure so
201  // we can debug and triage later.
202  RecordFallbackStats(output_params_);
203
204  // Only Windows has a high latency output driver that is not the same as the
205  // low latency path.
206#if defined(OS_WIN)
207  DLOG(ERROR) << "Unable to open audio device in low latency mode.  Falling "
208              << "back to high latency audio output.";
209
210  output_params_ = SetupFallbackParams(params_, output_params_);
211  Initialize();
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_.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(base::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(base::MessageLoop::current(), message_loop_);
259  dispatcher_->StreamVolumeSet(stream_proxy, volume);
260}
261
262void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
263  DCHECK_EQ(base::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(base::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(base::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
394}  // namespace media
395