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 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 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. 71static 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. 98static 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. 128void 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 148AudioOutputResampler::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 167AudioOutputResampler::~AudioOutputResampler() { 168 DCHECK(callbacks_.empty()); 169} 170 171void 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 178bool 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 235bool 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 256void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, 257 double volume) { 258 DCHECK(task_runner_->BelongsToCurrentThread()); 259 dispatcher_->StreamVolumeSet(stream_proxy, volume); 260} 261 262void 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 274void 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 287void 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 298OnMoreDataConverter::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 306OnMoreDataConverter::~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 312void 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 323void OnMoreDataConverter::Stop() { 324 CHECK(source_callback_); 325 source_callback_ = nullptr; 326 audio_converter_.RemoveInput(this); 327} 328 329int 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 339double 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 359void OnMoreDataConverter::OnError(AudioOutputStream* stream) { 360 source_callback_->OnError(stream); 361} 362 363} // namespace media 364