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