audio_output_resampler.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 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 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. 74static void RecordStats(const AudioParameters& output_params) { 75 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py 76 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION 77 // to report a discrete value. 78 UMA_HISTOGRAM_ENUMERATION( 79 "Media.HardwareAudioBitsPerChannel", 80 output_params.bits_per_sample(), 81 limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX 82 UMA_HISTOGRAM_ENUMERATION( 83 "Media.HardwareAudioChannelLayout", output_params.channel_layout(), 84 CHANNEL_LAYOUT_MAX + 1); 85 UMA_HISTOGRAM_ENUMERATION( 86 "Media.HardwareAudioChannelCount", output_params.channels(), 87 limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX 88 89 AudioSampleRate asr; 90 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) { 91 UMA_HISTOGRAM_ENUMERATION( 92 "Media.HardwareAudioSamplesPerSecond", asr, kAudioSampleRateMax + 1); 93 } else { 94 UMA_HISTOGRAM_COUNTS( 95 "Media.HardwareAudioSamplesPerSecondUnexpected", 96 output_params.sample_rate()); 97 } 98} 99 100// Record UMA statistics for hardware output configuration after fallback. 101static void RecordFallbackStats(const AudioParameters& output_params) { 102 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true); 103 // Note the 'PRESUBMIT_IGNORE_UMA_MAX's below, these silence the PRESUBMIT.py 104 // check for uma enum max usage, since we're abusing UMA_HISTOGRAM_ENUMERATION 105 // to report a discrete value. 106 UMA_HISTOGRAM_ENUMERATION( 107 "Media.FallbackHardwareAudioBitsPerChannel", 108 output_params.bits_per_sample(), 109 limits::kMaxBitsPerSample); // PRESUBMIT_IGNORE_UMA_MAX 110 UMA_HISTOGRAM_ENUMERATION( 111 "Media.FallbackHardwareAudioChannelLayout", 112 output_params.channel_layout(), CHANNEL_LAYOUT_MAX + 1); 113 UMA_HISTOGRAM_ENUMERATION( 114 "Media.FallbackHardwareAudioChannelCount", output_params.channels(), 115 limits::kMaxChannels); // PRESUBMIT_IGNORE_UMA_MAX 116 117 AudioSampleRate asr; 118 if (ToAudioSampleRate(output_params.sample_rate(), &asr)) { 119 UMA_HISTOGRAM_ENUMERATION( 120 "Media.FallbackHardwareAudioSamplesPerSecond", 121 asr, kAudioSampleRateMax + 1); 122 } else { 123 UMA_HISTOGRAM_COUNTS( 124 "Media.FallbackHardwareAudioSamplesPerSecondUnexpected", 125 output_params.sample_rate()); 126 } 127} 128 129// Converts low latency based |output_params| into high latency appropriate 130// output parameters in error situations. 131void AudioOutputResampler::SetupFallbackParams() { 132// Only Windows has a high latency output driver that is not the same as the low 133// latency path. 134#if defined(OS_WIN) 135 // Choose AudioParameters appropriate for opening the device in high latency 136 // mode. |kMinLowLatencyFrameSize| is arbitrarily based on Pepper Flash's 137 // MAXIMUM frame size for low latency. 138 static const int kMinLowLatencyFrameSize = 2048; 139 const int frames_per_buffer = 140 std::max(params_.frames_per_buffer(), kMinLowLatencyFrameSize); 141 142 output_params_ = AudioParameters( 143 AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(), 144 params_.sample_rate(), params_.bits_per_sample(), 145 frames_per_buffer); 146 device_id_ = ""; 147 Initialize(); 148#endif 149} 150 151AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, 152 const AudioParameters& input_params, 153 const AudioParameters& output_params, 154 const std::string& output_device_id, 155 const base::TimeDelta& close_delay) 156 : AudioOutputDispatcher(audio_manager, input_params, output_device_id), 157 close_delay_(close_delay), 158 output_params_(output_params), 159 streams_opened_(false) { 160 DCHECK(input_params.IsValid()); 161 DCHECK(output_params.IsValid()); 162 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); 163 164 // Record UMA statistics for the hardware configuration. 165 RecordStats(output_params); 166 167 Initialize(); 168} 169 170AudioOutputResampler::~AudioOutputResampler() { 171 DCHECK(callbacks_.empty()); 172} 173 174void AudioOutputResampler::Initialize() { 175 DCHECK(!streams_opened_); 176 DCHECK(callbacks_.empty()); 177 dispatcher_ = new AudioOutputDispatcherImpl( 178 audio_manager_, output_params_, device_id_, close_delay_); 179} 180 181bool AudioOutputResampler::OpenStream() { 182 DCHECK(task_runner_->BelongsToCurrentThread()); 183 184 if (dispatcher_->OpenStream()) { 185 // Only record the UMA statistic if we didn't fallback during construction 186 // and only for the first stream we open. 187 if (!streams_opened_ && 188 output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) { 189 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false); 190 } 191 streams_opened_ = true; 192 return true; 193 } 194 195 // If we've already tried to open the stream in high latency mode or we've 196 // successfully opened a stream previously, there's nothing more to be done. 197 if (output_params_.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY || 198 streams_opened_ || !callbacks_.empty()) { 199 return false; 200 } 201 202 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); 203 204 // Record UMA statistics about the hardware which triggered the failure so 205 // we can debug and triage later. 206 RecordFallbackStats(output_params_); 207 208 // Only Windows has a high latency output driver that is not the same as the 209 // low latency path. 210#if defined(OS_WIN) 211 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling " 212 << "back to high latency audio output."; 213 214 SetupFallbackParams(); 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(task_runner_->BelongsToCurrentThread()); 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(task_runner_->BelongsToCurrentThread()); 262 dispatcher_->StreamVolumeSet(stream_proxy, volume); 263} 264 265void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { 266 DCHECK(task_runner_->BelongsToCurrentThread()); 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(task_runner_->BelongsToCurrentThread()); 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(task_runner_->BelongsToCurrentThread()); 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 301void AudioOutputResampler::CloseStreamsForWedgeFix() { 302 DCHECK(task_runner_->BelongsToCurrentThread()); 303 304 // Stop and close all active streams. Once all streams across all dispatchers 305 // have been closed the AudioManager will call RestartStreamsForWedgeFix(). 306 for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end(); 307 ++it) { 308 if (it->second->started()) 309 dispatcher_->StopStream(it->first); 310 dispatcher_->CloseStream(it->first); 311 } 312 313 // Close all idle streams as well. 314 dispatcher_->CloseStreamsForWedgeFix(); 315} 316 317void AudioOutputResampler::RestartStreamsForWedgeFix() { 318 DCHECK(task_runner_->BelongsToCurrentThread()); 319 // By opening all streams first and then starting them one by one we ensure 320 // the dispatcher only opens streams for those which will actually be used. 321 for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end(); 322 ++it) { 323 dispatcher_->OpenStream(); 324 } 325 for (CallbackMap::iterator it = callbacks_.begin(); it != callbacks_.end(); 326 ++it) { 327 if (it->second->started()) 328 dispatcher_->StartStream(it->second, it->first); 329 } 330} 331 332OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params, 333 const AudioParameters& output_params) 334 : io_ratio_(static_cast<double>(input_params.GetBytesPerSecond()) / 335 output_params.GetBytesPerSecond()), 336 source_callback_(NULL), 337 input_bytes_per_second_(input_params.GetBytesPerSecond()), 338 audio_converter_(input_params, output_params, false) {} 339 340OnMoreDataConverter::~OnMoreDataConverter() { 341 // Ensure Stop() has been called so we don't end up with an AudioOutputStream 342 // calling back into OnMoreData() after destruction. 343 CHECK(!source_callback_); 344} 345 346void OnMoreDataConverter::Start( 347 AudioOutputStream::AudioSourceCallback* callback) { 348 CHECK(!source_callback_); 349 source_callback_ = callback; 350 351 // While AudioConverter can handle multiple inputs, we're using it only with 352 // a single input currently. Eventually this may be the basis for a browser 353 // side mixer. 354 audio_converter_.AddInput(this); 355} 356 357void OnMoreDataConverter::Stop() { 358 CHECK(source_callback_); 359 source_callback_ = NULL; 360 audio_converter_.RemoveInput(this); 361} 362 363int OnMoreDataConverter::OnMoreData(AudioBus* dest, 364 AudioBuffersState buffers_state) { 365 return OnMoreIOData(NULL, dest, buffers_state); 366} 367 368int OnMoreDataConverter::OnMoreIOData(AudioBus* source, 369 AudioBus* dest, 370 AudioBuffersState buffers_state) { 371 // Note: The input portion of OnMoreIOData() is not supported when a converter 372 // has been injected. Downstream clients prefer silence to potentially split 373 // apart input data. 374 375 current_buffers_state_ = buffers_state; 376 audio_converter_.Convert(dest); 377 378 // Always return the full number of frames requested, ProvideInput() 379 // will pad with silence if it wasn't able to acquire enough data. 380 return dest->frames(); 381} 382 383double OnMoreDataConverter::ProvideInput(AudioBus* dest, 384 base::TimeDelta buffer_delay) { 385 // Adjust playback delay to include |buffer_delay|. 386 // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since 387 // AudioBus is just float data. Use TimeDelta instead. 388 AudioBuffersState new_buffers_state; 389 new_buffers_state.pending_bytes = 390 io_ratio_ * (current_buffers_state_.total_bytes() + 391 buffer_delay.InSecondsF() * input_bytes_per_second_); 392 393 // Retrieve data from the original callback. 394 const int frames = source_callback_->OnMoreIOData( 395 NULL, dest, new_buffers_state); 396 397 // Zero any unfilled frames if anything was filled, otherwise we'll just 398 // return a volume of zero and let AudioConverter drop the output. 399 if (frames > 0 && frames < dest->frames()) 400 dest->ZeroFramesPartial(frames, dest->frames() - frames); 401 return frames > 0 ? 1 : 0; 402} 403 404void OnMoreDataConverter::OnError(AudioOutputStream* stream) { 405 source_callback_->OnError(stream); 406} 407 408} // namespace media 409