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