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