• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 namespace media {
22 
23 class 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 void OnError(AudioOutputStream* stream) OVERRIDE;
35 
36   // Sets |source_callback_|.  If this is not a new object, then Stop() must be
37   // called before Start().
38   void Start(AudioOutputStream::AudioSourceCallback* callback);
39 
40   // Clears |source_callback_| and flushes the resampler.
41   void Stop();
42 
started()43   bool started() { return source_callback_ != nullptr; }
44 
45  private:
46   // AudioConverter::InputCallback implementation.
47   virtual double ProvideInput(AudioBus* audio_bus,
48                               base::TimeDelta buffer_delay) OVERRIDE;
49 
50   // Ratio of input bytes to output bytes used to correct playback delay with
51   // regard to buffering and resampling.
52   const double io_ratio_;
53 
54   // Source callback.
55   AudioOutputStream::AudioSourceCallback* source_callback_;
56 
57   // Last AudioBuffersState object received via OnMoreData(), used to correct
58   // playback delay by ProvideInput() and passed on to |source_callback_|.
59   AudioBuffersState current_buffers_state_;
60 
61   const int input_bytes_per_second_;
62 
63   // Handles resampling, buffering, and channel mixing between input and output
64   // parameters.
65   AudioConverter audio_converter_;
66 
67   DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter);
68 };
69 
70 // Record UMA statistics for hardware output configuration.
RecordStats(const AudioParameters & output_params)71 static void RecordStats(const AudioParameters& output_params) {
72   // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
73   // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
74   // to report a discrete value.
75   UMA_HISTOGRAM_ENUMERATION(
76       "Media.HardwareAudioBitsPerChannel",
77       output_params.bits_per_sample(),
78       limits::kMaxBitsPerSample);  // PRESUBMIT_IGNORE_UMA_MAX
79   UMA_HISTOGRAM_ENUMERATION(
80       "Media.HardwareAudioChannelLayout", output_params.channel_layout(),
81       CHANNEL_LAYOUT_MAX + 1);
82   UMA_HISTOGRAM_ENUMERATION(
83       "Media.HardwareAudioChannelCount", output_params.channels(),
84       limits::kMaxChannels);  // PRESUBMIT_IGNORE_UMA_MAX
85 
86   AudioSampleRate asr;
87   if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
88     UMA_HISTOGRAM_ENUMERATION(
89         "Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1);
90   } else {
91     UMA_HISTOGRAM_COUNTS(
92         "Media.HardwareAudioSamplesPerSecondUnexpected",
93         output_params.sample_rate());
94   }
95 }
96 
97 // Record UMA statistics for hardware output configuration after fallback.
RecordFallbackStats(const AudioParameters & output_params)98 static void RecordFallbackStats(const AudioParameters& output_params) {
99   UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true);
100   // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py
101   // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION
102   // to report a discrete value.
103   UMA_HISTOGRAM_ENUMERATION(
104       "Media.FallbackHardwareAudioBitsPerChannel",
105       output_params.bits_per_sample(),
106       limits::kMaxBitsPerSample);  // PRESUBMIT_IGNORE_UMA_MAX
107   UMA_HISTOGRAM_ENUMERATION(
108       "Media.FallbackHardwareAudioChannelLayout",
109       output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1);
110   UMA_HISTOGRAM_ENUMERATION(
111       "Media.FallbackHardwareAudioChannelCount", output_params.channels(),
112       limits::kMaxChannels);  // PRESUBMIT_IGNORE_UMA_MAX
113 
114   AudioSampleRate asr;
115   if (ToAudioSampleRate(output_params.sample_rate(), &asr)) {
116     UMA_HISTOGRAM_ENUMERATION(
117         "Media.FallbackHardwareAudioSamplesPerSecond",
118         asr, kAudioSampleRateMax + 1);
119   } else {
120     UMA_HISTOGRAM_COUNTS(
121         "Media.FallbackHardwareAudioSamplesPerSecondUnexpected",
122         output_params.sample_rate());
123   }
124 }
125 
126 // Converts low latency based |output_params| into high latency appropriate
127 // output parameters in error situations.
SetupFallbackParams()128 void AudioOutputResampler::SetupFallbackParams() {
129 // Only Windows has a high latency output driver that is not the same as the low
130 // latency path.
131 #if defined(OS_WIN)
132   // Choose AudioParameters appropriate for opening the device in high latency
133   // mode.  |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's
134   // MAXIMUM frame size for low latency.
135   static const int kMinLowLatencyFrameSize = 2048;
136   const int frames_per_buffer =
137       std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize);
138 
139   output_params_ = AudioParameters(
140       AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(),
141       params_.sample_rate(), params_.bits_per_sample(),
142       frames_per_buffer);
143   device_id_ = "";
144   Initialize();
145 #endif
146 }
147 
AudioOutputResampler(AudioManager * audio_manager,const AudioParameters & input_params,const AudioParameters & output_params,const std::string & output_device_id,const base::TimeDelta & close_delay)148 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager,
149                                            const AudioParameters& input_params,
150                                            const AudioParameters& output_params,
151                                            const std::string& output_device_id,
152                                            const base::TimeDelta& close_delay)
153     : AudioOutputDispatcher(audio_manager, input_params, output_device_id),
154       close_delay_(close_delay),
155       output_params_(output_params),
156       streams_opened_(false) {
157   DCHECK(input_params.IsValid());
158   DCHECK(output_params.IsValid());
159   DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
160 
161   // Record UMA statistics for the hardware configuration.
162   RecordStats(output_params);
163 
164   Initialize();
165 }
166 
~AudioOutputResampler()167 AudioOutputResampler::~AudioOutputResampler() {
168   DCHECK(callbacks_.empty());
169 }
170 
Initialize()171 void AudioOutputResampler::Initialize() {
172   DCHECK(!streams_opened_);
173   DCHECK(callbacks_.empty());
174   dispatcher_ = new AudioOutputDispatcherImpl(
175       audio_manager_, output_params_, device_id_, close_delay_);
176 }
177 
OpenStream()178 bool AudioOutputResampler::OpenStream() {
179   DCHECK(task_runner_->BelongsToCurrentThread());
180 
181   if (dispatcher_->OpenStream()) {
182     // Only record the UMA statistic if we didn't fallback during construction
183     // and only for the first stream we open.
184     if (!streams_opened_ &&
185         output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) {
186       UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false);
187     }
188     streams_opened_ = true;
189     return true;
190   }
191 
192   // If we've already tried to open the stream in high latency mode or we've
193   // successfully opened a stream previously, there's nothing more to be done.
194   if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY ||
195       streams_opened_ || !callbacks_.empty()) {
196     return false;
197   }
198 
199   DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
200 
201   // Record UMA statistics about the hardware which triggered the failure so
202   // we can debug and triage later.
203   RecordFallbackStats(output_params_);
204 
205   // Only Windows has a high latency output driver that is not the same as the
206   // low latency path.
207 #if defined(OS_WIN)
208   DLOG(ERROR) << "Unable to open audio device in low latency mode.  Falling "
209               << "back to high latency audio output.";
210 
211   SetupFallbackParams();
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_.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 
StartStream(AudioOutputStream::AudioSourceCallback * callback,AudioOutputProxy * stream_proxy)235 bool AudioOutputResampler::StartStream(
236     AudioOutputStream::AudioSourceCallback* callback,
237     AudioOutputProxy* stream_proxy) {
238   DCHECK(task_runner_->BelongsToCurrentThread());
239 
240   OnMoreDataConverter* resampler_callback = nullptr;
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 
StreamVolumeSet(AudioOutputProxy * stream_proxy,double volume)256 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy,
257                                            double volume) {
258   DCHECK(task_runner_->BelongsToCurrentThread());
259   dispatcher_->StreamVolumeSet(stream_proxy, volume);
260 }
261 
StopStream(AudioOutputProxy * stream_proxy)262 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) {
263   DCHECK(task_runner_->BelongsToCurrentThread());
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 
CloseStream(AudioOutputProxy * stream_proxy)274 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) {
275   DCHECK(task_runner_->BelongsToCurrentThread());
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 
Shutdown()287 void AudioOutputResampler::Shutdown() {
288   DCHECK(task_runner_->BelongsToCurrentThread());
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 
OnMoreDataConverter(const AudioParameters & input_params,const AudioParameters & output_params)298 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params,
299                                          const AudioParameters& output_params)
300     : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) /
301                 output_params.GetBytesPerSecond()),
302       source_callback_(nullptr),
303       input_bytes_per_second_(input_params.GetBytesPerSecond()),
304       audio_converter_(input_params, output_params, false) {}
305 
~OnMoreDataConverter()306 OnMoreDataConverter::~OnMoreDataConverter() {
307   // Ensure Stop() has been called so we don't end up with an AudioOutputStream
308   // calling back into OnMoreData() after destruction.
309   CHECK(!source_callback_);
310 }
311 
Start(AudioOutputStream::AudioSourceCallback * callback)312 void OnMoreDataConverter::Start(
313     AudioOutputStream::AudioSourceCallback* callback) {
314   CHECK(!source_callback_);
315   source_callback_ = callback;
316 
317   // While AudioConverter can handle multiple inputs, we're using it only with
318   // a single input currently.  Eventually this may be the basis for a browser
319   // side mixer.
320   audio_converter_.AddInput(this);
321 }
322 
Stop()323 void OnMoreDataConverter::Stop() {
324   CHECK(source_callback_);
325   source_callback_ = nullptr;
326   audio_converter_.RemoveInput(this);
327 }
328 
OnMoreData(AudioBus * dest,AudioBuffersState buffers_state)329 int OnMoreDataConverter::OnMoreData(AudioBus* dest,
330                                     AudioBuffersState buffers_state) {
331   current_buffers_state_ = buffers_state;
332   audio_converter_.Convert(dest);
333 
334   // Always return the full number of frames requested, ProvideInput()
335   // will pad with silence if it wasn't able to acquire enough data.
336   return dest->frames();
337 }
338 
ProvideInput(AudioBus * dest,base::TimeDelta buffer_delay)339 double OnMoreDataConverter::ProvideInput(AudioBus* dest,
340                                          base::TimeDelta buffer_delay) {
341   // Adjust playback delay to include |buffer_delay|.
342   // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since
343   // AudioBus is just float data.  Use TimeDelta instead.
344   AudioBuffersState new_buffers_state;
345   new_buffers_state.pending_bytes =
346       io_ratio_ * (current_buffers_state_.total_bytes() +
347                    buffer_delay.InSecondsF() * input_bytes_per_second_);
348 
349   // Retrieve data from the original callback.
350   const int frames = source_callback_->OnMoreData(dest, new_buffers_state);
351 
352   // Zero any unfilled frames if anything was filled, otherwise we'll just
353   // return a volume of zero and let AudioConverter drop the output.
354   if (frames > 0 && frames < dest->frames())
355     dest->ZeroFramesPartial(frames, dest->frames() - frames);
356   return frames > 0 ? 1 : 0;
357 }
358 
OnError(AudioOutputStream * stream)359 void OnMoreDataConverter::OnError(AudioOutputStream* stream) {
360   source_callback_->OnError(stream);
361 }
362 
363 }  // namespace media
364