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/audio_util.h" 18 #include "media/audio/sample_rates.h" 19 #include "media/base/audio_converter.h" 20 #include "media/base/limits.h" 21 22 namespace media { 23 24 class OnMoreDataConverter 25 : public AudioOutputStream::AudioSourceCallback, 26 public AudioConverter::InputCallback { 27 public: 28 OnMoreDataConverter(const AudioParameters& input_params, 29 const AudioParameters& output_params); 30 virtual ~OnMoreDataConverter(); 31 32 // AudioSourceCallback interface. 33 virtual int OnMoreData(AudioBus* dest, 34 AudioBuffersState buffers_state) OVERRIDE; 35 virtual int OnMoreIOData(AudioBus* source, 36 AudioBus* dest, 37 AudioBuffersState buffers_state) OVERRIDE; 38 virtual void OnError(AudioOutputStream* stream) OVERRIDE; 39 40 // Sets |source_callback_|. If this is not a new object, then Stop() must be 41 // called before Start(). 42 void Start(AudioOutputStream::AudioSourceCallback* callback); 43 44 // Clears |source_callback_| and flushes the resampler. 45 void Stop(); 46 47 private: 48 // AudioConverter::InputCallback implementation. 49 virtual double ProvideInput(AudioBus* audio_bus, 50 base::TimeDelta buffer_delay) OVERRIDE; 51 52 // Ratio of input bytes to output bytes used to correct playback delay with 53 // regard to buffering and resampling. 54 double io_ratio_; 55 56 // Source callback and associated lock. 57 base::Lock source_lock_; 58 AudioOutputStream::AudioSourceCallback* source_callback_; 59 60 // |source| passed to OnMoreIOData() which should be passed downstream. 61 AudioBus* source_bus_; 62 63 // Last AudioBuffersState object received via OnMoreData(), used to correct 64 // playback delay by ProvideInput() and passed on to |source_callback_|. 65 AudioBuffersState current_buffers_state_; 66 67 const int input_bytes_per_second_; 68 69 // Handles resampling, buffering, and channel mixing between input and output 70 // parameters. 71 AudioConverter audio_converter_; 72 73 DISALLOW_COPY_AND_ASSIGN(OnMoreDataConverter); 74 }; 75 76 // Record UMA statistics for hardware output configuration. 77 static void RecordStats(const AudioParameters& output_params) { 78 UMA_HISTOGRAM_ENUMERATION( 79 "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(), 80 limits::kMaxBitsPerSample); 81 UMA_HISTOGRAM_ENUMERATION( 82 "Media.HardwareAudioChannelLayout", output_params.channel_layout(), 83 CHANNEL_LAYOUT_MAX); 84 UMA_HISTOGRAM_ENUMERATION( 85 "Media.HardwareAudioChannelCount", output_params.channels(), 86 limits::kMaxChannels); 87 88 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate()); 89 if (asr != kUnexpectedAudioSampleRate) { 90 UMA_HISTOGRAM_ENUMERATION( 91 "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate); 92 } else { 93 UMA_HISTOGRAM_COUNTS( 94 "Media.HardwareAudioSamplesPerSecondUnexpected", 95 output_params.sample_rate()); 96 } 97 } 98 99 // Record UMA statistics for hardware output configuration after fallback. 100 static void RecordFallbackStats(const AudioParameters& output_params) { 101 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true); 102 UMA_HISTOGRAM_ENUMERATION( 103 "Media.FallbackHardwareAudioBitsPerChannel", 104 output_params.bits_per_sample(), limits::kMaxBitsPerSample); 105 UMA_HISTOGRAM_ENUMERATION( 106 "Media.FallbackHardwareAudioChannelLayout", 107 output_params.channel_layout(), CHANNEL_LAYOUT_MAX); 108 UMA_HISTOGRAM_ENUMERATION( 109 "Media.FallbackHardwareAudioChannelCount", 110 output_params.channels(), limits::kMaxChannels); 111 112 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate()); 113 if (asr != kUnexpectedAudioSampleRate) { 114 UMA_HISTOGRAM_ENUMERATION( 115 "Media.FallbackHardwareAudioSamplesPerSecond", 116 asr, kUnexpectedAudioSampleRate); 117 } else { 118 UMA_HISTOGRAM_COUNTS( 119 "Media.FallbackHardwareAudioSamplesPerSecondUnexpected", 120 output_params.sample_rate()); 121 } 122 } 123 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 // Converts low latency based |output_params| into high latency appropriate 128 // output parameters in error situations. 129 static 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 #endif 146 147 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, 148 const AudioParameters& input_params, 149 const AudioParameters& output_params, 150 const std::string& input_device_id, 151 const base::TimeDelta& close_delay) 152 : AudioOutputDispatcher(audio_manager, input_params, input_device_id), 153 close_delay_(close_delay), 154 output_params_(output_params), 155 input_device_id_(input_device_id), 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 167 AudioOutputResampler::~AudioOutputResampler() { 168 DCHECK(callbacks_.empty()); 169 } 170 171 void AudioOutputResampler::Initialize() { 172 DCHECK(!streams_opened_); 173 DCHECK(callbacks_.empty()); 174 dispatcher_ = new AudioOutputDispatcherImpl( 175 audio_manager_, output_params_, input_device_id_, close_delay_); 176 } 177 178 bool AudioOutputResampler::OpenStream() { 179 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 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 output_params_ = SetupFallbackParams(params_, output_params_); 212 Initialize(); 213 if (dispatcher_->OpenStream()) { 214 streams_opened_ = true; 215 return true; 216 } 217 #endif 218 219 DLOG(ERROR) << "Unable to open audio device in high latency mode. Falling " 220 << "back to fake audio output."; 221 222 // Finally fall back to a fake audio output device. 223 output_params_.Reset( 224 AudioParameters::AUDIO_FAKE, params_.channel_layout(), 225 params_.channels(), params_.input_channels(), params_.sample_rate(), 226 params_.bits_per_sample(), params_.frames_per_buffer()); 227 Initialize(); 228 if (dispatcher_->OpenStream()) { 229 streams_opened_ = true; 230 return true; 231 } 232 233 return false; 234 } 235 236 bool AudioOutputResampler::StartStream( 237 AudioOutputStream::AudioSourceCallback* callback, 238 AudioOutputProxy* stream_proxy) { 239 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 240 241 OnMoreDataConverter* resampler_callback = NULL; 242 CallbackMap::iterator it = callbacks_.find(stream_proxy); 243 if (it == callbacks_.end()) { 244 resampler_callback = new OnMoreDataConverter(params_, output_params_); 245 callbacks_[stream_proxy] = resampler_callback; 246 } else { 247 resampler_callback = it->second; 248 } 249 250 resampler_callback->Start(callback); 251 bool result = dispatcher_->StartStream(resampler_callback, stream_proxy); 252 if (!result) 253 resampler_callback->Stop(); 254 return result; 255 } 256 257 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, 258 double volume) { 259 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 260 dispatcher_->StreamVolumeSet(stream_proxy, volume); 261 } 262 263 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { 264 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 265 dispatcher_->StopStream(stream_proxy); 266 267 // Now that StopStream() has completed the underlying physical stream should 268 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the 269 // OnMoreDataConverter. 270 CallbackMap::iterator it = callbacks_.find(stream_proxy); 271 if (it != callbacks_.end()) 272 it->second->Stop(); 273 } 274 275 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { 276 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 277 dispatcher_->CloseStream(stream_proxy); 278 279 // We assume that StopStream() is always called prior to CloseStream(), so 280 // that it is safe to delete the OnMoreDataConverter here. 281 CallbackMap::iterator it = callbacks_.find(stream_proxy); 282 if (it != callbacks_.end()) { 283 delete it->second; 284 callbacks_.erase(it); 285 } 286 } 287 288 void AudioOutputResampler::Shutdown() { 289 DCHECK_EQ(base::MessageLoop::current(), message_loop_); 290 291 // No AudioOutputProxy objects should hold a reference to us when we get 292 // to this stage. 293 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; 294 295 dispatcher_->Shutdown(); 296 DCHECK(callbacks_.empty()); 297 } 298 299 OnMoreDataConverter::OnMoreDataConverter(const AudioParameters& input_params, 300 const AudioParameters& output_params) 301 : source_callback_(NULL), 302 source_bus_(NULL), 303 input_bytes_per_second_(input_params.GetBytesPerSecond()), 304 audio_converter_(input_params, output_params, false) { 305 io_ratio_ = 306 static_cast<double>(input_params.GetBytesPerSecond()) / 307 output_params.GetBytesPerSecond(); 308 } 309 310 OnMoreDataConverter::~OnMoreDataConverter() { 311 // Ensure Stop() has been called so we don't end up with an AudioOutputStream 312 // calling back into OnMoreData() after destruction. 313 CHECK(!source_callback_); 314 } 315 316 void OnMoreDataConverter::Start( 317 AudioOutputStream::AudioSourceCallback* callback) { 318 base::AutoLock auto_lock(source_lock_); 319 CHECK(!source_callback_); 320 source_callback_ = callback; 321 322 // While AudioConverter can handle multiple inputs, we're using it only with 323 // a single input currently. Eventually this may be the basis for a browser 324 // side mixer. 325 audio_converter_.AddInput(this); 326 } 327 328 void OnMoreDataConverter::Stop() { 329 base::AutoLock auto_lock(source_lock_); 330 CHECK(source_callback_); 331 source_callback_ = NULL; 332 audio_converter_.RemoveInput(this); 333 } 334 335 int OnMoreDataConverter::OnMoreData(AudioBus* dest, 336 AudioBuffersState buffers_state) { 337 return OnMoreIOData(NULL, dest, buffers_state); 338 } 339 340 int OnMoreDataConverter::OnMoreIOData(AudioBus* source, 341 AudioBus* dest, 342 AudioBuffersState buffers_state) { 343 base::AutoLock auto_lock(source_lock_); 344 // While we waited for |source_lock_| the callback might have been cleared. 345 if (!source_callback_) { 346 dest->Zero(); 347 return dest->frames(); 348 } 349 350 source_bus_ = source; 351 current_buffers_state_ = buffers_state; 352 audio_converter_.Convert(dest); 353 354 // Always return the full number of frames requested, ProvideInput_Locked() 355 // will pad with silence if it wasn't able to acquire enough data. 356 return dest->frames(); 357 } 358 359 double OnMoreDataConverter::ProvideInput(AudioBus* dest, 360 base::TimeDelta buffer_delay) { 361 source_lock_.AssertAcquired(); 362 363 // Adjust playback delay to include |buffer_delay|. 364 // TODO(dalecurtis): Stop passing bytes around, it doesn't make sense since 365 // AudioBus is just float data. Use TimeDelta instead. 366 AudioBuffersState new_buffers_state; 367 new_buffers_state.pending_bytes = 368 io_ratio_ * (current_buffers_state_.total_bytes() + 369 buffer_delay.InSecondsF() * input_bytes_per_second_); 370 371 // Retrieve data from the original callback. 372 int frames = source_callback_->OnMoreIOData( 373 source_bus_, dest, new_buffers_state); 374 375 // |source_bus_| should only be provided once. 376 // TODO(dalecurtis, crogers): This is not a complete fix. If ProvideInput() 377 // is called multiple times, we need to do something more clever here. 378 source_bus_ = NULL; 379 380 // Zero any unfilled frames if anything was filled, otherwise we'll just 381 // return a volume of zero and let AudioConverter drop the output. 382 if (frames > 0 && frames < dest->frames()) 383 dest->ZeroFramesPartial(frames, dest->frames() - frames); 384 385 // TODO(dalecurtis): Return the correct volume here. 386 return frames > 0 ? 1 : 0; 387 } 388 389 void OnMoreDataConverter::OnError(AudioOutputStream* stream) { 390 base::AutoLock auto_lock(source_lock_); 391 if (source_callback_) 392 source_callback_->OnError(stream); 393 } 394 395 } // namespace media 396