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