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