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