Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/audio_output_resampler.h" | 5 #include "media/audio/audio_output_resampler.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/bind_helpers.h" | 8 #include "base/bind_helpers.h" |
| 9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
| 10 #include "base/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 frames_per_buffer); | 167 frames_per_buffer); |
| 168 } | 168 } |
| 169 | 169 |
| 170 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, | 170 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, |
| 171 const AudioParameters& input_params, | 171 const AudioParameters& input_params, |
| 172 const AudioParameters& output_params, | 172 const AudioParameters& output_params, |
| 173 const base::TimeDelta& close_delay) | 173 const base::TimeDelta& close_delay) |
| 174 : AudioOutputDispatcher(audio_manager, input_params), | 174 : AudioOutputDispatcher(audio_manager, input_params), |
| 175 io_ratio_(1), | 175 io_ratio_(1), |
| 176 close_delay_(close_delay), | 176 close_delay_(close_delay), |
| 177 output_params_(output_params) { | 177 output_params_(output_params), |
| 178 streams_opened_(false) { | |
| 178 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); | 179 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); |
| 179 | 180 |
| 180 // Record UMA statistics for the hardware configuration. | 181 // Record UMA statistics for the hardware configuration. |
| 181 RecordStats(output_params); | 182 RecordStats(output_params); |
| 182 | 183 |
| 183 // Immediately fallback if we're given invalid output parameters. This may | 184 // Immediately fallback if we're given invalid output parameters. This may |
| 184 // happen if the OS provided us junk values for the hardware configuration. | 185 // happen if the OS provided us junk values for the hardware configuration. |
| 185 if (!output_params_.IsValid()) { | 186 if (!output_params_.IsValid()) { |
| 186 LOG(ERROR) << "Invalid audio output parameters received; using fallback " | 187 LOG(ERROR) << "Invalid audio output parameters received; using fallback " |
| 187 << "path. Channels: " << output_params_.channels() << ", " | 188 << "path. Channels: " << output_params_.channels() << ", " |
| 188 << "Sample Rate: " << output_params_.sample_rate() << ", " | 189 << "Sample Rate: " << output_params_.sample_rate() << ", " |
| 189 << "Bits Per Sample: " << output_params_.bits_per_sample() | 190 << "Bits Per Sample: " << output_params_.bits_per_sample() |
| 190 << ", Frames Per Buffer: " << output_params_.frames_per_buffer(); | 191 << ", Frames Per Buffer: " << output_params_.frames_per_buffer(); |
| 191 // Record UMA statistics about the hardware which triggered the failure so | 192 // Record UMA statistics about the hardware which triggered the failure so |
| 192 // we can debug and triage later. | 193 // we can debug and triage later. |
| 193 RecordFallbackStats(output_params); | 194 RecordFallbackStats(output_params); |
| 194 output_params_ = SetupFallbackParams(input_params, output_params); | 195 output_params_ = SetupFallbackParams(input_params, output_params); |
| 195 } | 196 } |
| 196 | 197 |
| 197 Initialize(); | 198 Initialize(); |
| 198 } | 199 } |
| 199 | 200 |
| 200 AudioOutputResampler::~AudioOutputResampler() {} | 201 AudioOutputResampler::~AudioOutputResampler() { |
| 202 DCHECK(callbacks_.empty()); | |
| 203 } | |
| 201 | 204 |
| 202 void AudioOutputResampler::Initialize() { | 205 void AudioOutputResampler::Initialize() { |
| 206 DCHECK(!streams_opened_); | |
| 207 DCHECK(callbacks_.empty()); | |
| 208 | |
| 203 io_ratio_ = 1; | 209 io_ratio_ = 1; |
| 204 | 210 |
| 205 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 | 211 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 |
| 206 DCHECK_EQ(params_.channels(), output_params_.channels()); | 212 DCHECK_EQ(params_.channels(), output_params_.channels()); |
| 207 // Only resample or rebuffer if the input parameters don't match the output | 213 // Only resample or rebuffer if the input parameters don't match the output |
| 208 // parameters to avoid any unnecessary work. | 214 // parameters to avoid any unnecessary work. |
| 209 if (params_.channels() != output_params_.channels() || | 215 if (params_.channels() != output_params_.channels() || |
| 210 params_.sample_rate() != output_params_.sample_rate() || | 216 params_.sample_rate() != output_params_.sample_rate() || |
| 211 params_.bits_per_sample() != output_params_.bits_per_sample() || | 217 params_.bits_per_sample() != output_params_.bits_per_sample() || |
| 212 params_.frames_per_buffer() != output_params_.frames_per_buffer()) { | 218 params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 230 DVLOG(1) << "Input and output params are the same; in pass-through mode."; | 236 DVLOG(1) << "Input and output params are the same; in pass-through mode."; |
| 231 } | 237 } |
| 232 | 238 |
| 233 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once | 239 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once |
| 234 // we've stabilized the issues there. | 240 // we've stabilized the issues there. |
| 235 dispatcher_ = new AudioOutputDispatcherImpl( | 241 dispatcher_ = new AudioOutputDispatcherImpl( |
| 236 audio_manager_, output_params_, close_delay_); | 242 audio_manager_, output_params_, close_delay_); |
| 237 } | 243 } |
| 238 | 244 |
| 239 bool AudioOutputResampler::OpenStream() { | 245 bool AudioOutputResampler::OpenStream() { |
| 246 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 247 | |
| 240 if (dispatcher_->OpenStream()) { | 248 if (dispatcher_->OpenStream()) { |
| 241 // Only record the UMA statistic if we didn't fallback during construction. | 249 // Only record the UMA statistic if we didn't fallback during construction |
| 242 if (output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) | 250 // and only for the first stream we open. |
| 251 if (!streams_opened_ && | |
| 252 output_params_.format() == AudioParameters::AUDIO_PCM_LOW_LATENCY) { | |
| 243 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false); | 253 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false); |
| 254 } | |
| 255 streams_opened_ = true; | |
| 244 return true; | 256 return true; |
| 245 } | 257 } |
| 246 | 258 |
| 247 // If we've already tried to open the stream in high latency mode, there's | 259 // If we've already tried to open the stream in high latency mode or we've |
| 248 // nothing more to be done. | 260 // successfully opened a stream previously, there's nothing more to be done. |
| 249 if (output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR) | 261 if (output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR || |
| 262 streams_opened_ || !callbacks_.empty()) { | |
| 250 return false; | 263 return false; |
| 264 } | |
| 265 | |
| 266 DCHECK_EQ(output_params_.format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); | |
| 251 | 267 |
| 252 if (CommandLine::ForCurrentProcess()->HasSwitch( | 268 if (CommandLine::ForCurrentProcess()->HasSwitch( |
| 253 switches::kDisableAudioFallback)) { | 269 switches::kDisableAudioFallback)) { |
| 254 LOG(ERROR) << "Open failed and automatic fallback to high latency audio " | 270 LOG(ERROR) << "Open failed and automatic fallback to high latency audio " |
| 255 << "path is disabled, aborting."; | 271 << "path is disabled, aborting."; |
| 256 return false; | 272 return false; |
| 257 } | 273 } |
| 258 | 274 |
| 259 // TODO(dalecurtis): Is it better to recreate the whole |dispatcher_| ? See | |
| 260 // http://crbug.com/149815 | |
| 261 dispatcher_->CloseStream(NULL); | |
| 262 | |
| 263 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling " | 275 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling " |
| 264 << "back to high latency audio output."; | 276 << "back to high latency audio output."; |
| 265 | 277 |
| 266 // Record UMA statistics about the hardware which triggered the failure so | 278 // Record UMA statistics about the hardware which triggered the failure so |
| 267 // we can debug and triage later. | 279 // we can debug and triage later. |
| 268 RecordFallbackStats(output_params_); | 280 RecordFallbackStats(output_params_); |
| 269 output_params_ = SetupFallbackParams(params_, output_params_); | 281 output_params_ = SetupFallbackParams(params_, output_params_); |
| 270 Initialize(); | 282 Initialize(); |
| 271 | 283 |
| 272 // Retry, if this fails, there's nothing left to do but report the error back. | 284 // Retry, if this fails, there's nothing left to do but report the error back. |
| 273 return dispatcher_->OpenStream(); | 285 return dispatcher_->OpenStream(); |
| 274 } | 286 } |
| 275 | 287 |
| 276 bool AudioOutputResampler::StartStream( | 288 bool AudioOutputResampler::StartStream( |
| 277 AudioOutputStream::AudioSourceCallback* callback, | 289 AudioOutputStream::AudioSourceCallback* callback, |
| 278 AudioOutputProxy* stream_proxy) { | 290 AudioOutputProxy* stream_proxy) { |
| 291 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 292 | |
| 279 OnMoreDataResampler* resampler_callback = NULL; | 293 OnMoreDataResampler* resampler_callback = NULL; |
| 280 { | 294 CallbackMap::iterator it = callbacks_.find(stream_proxy); |
| 281 base::AutoLock auto_lock(callbacks_lock_); | 295 if (it == callbacks_.end()) { |
| 282 CallbackMap::iterator it = callbacks_.find(stream_proxy); | 296 resampler_callback = new OnMoreDataResampler( |
| 283 if (it == callbacks_.end()) { | 297 io_ratio_, params_, output_params_); |
| 284 resampler_callback = new OnMoreDataResampler( | 298 callbacks_[stream_proxy] = resampler_callback; |
| 285 io_ratio_, params_, output_params_); | 299 } else { |
| 286 callbacks_[stream_proxy] = resampler_callback; | 300 resampler_callback = it->second; |
| 287 } else { | |
| 288 resampler_callback = it->second; | |
| 289 } | |
| 290 resampler_callback->Start(callback); | |
| 291 } | 301 } |
| 302 resampler_callback->Start(callback); | |
| 292 return dispatcher_->StartStream(resampler_callback, stream_proxy); | 303 return dispatcher_->StartStream(resampler_callback, stream_proxy); |
| 293 } | 304 } |
| 294 | 305 |
| 295 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, | 306 void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, |
| 296 double volume) { | 307 double volume) { |
| 308 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 297 dispatcher_->StreamVolumeSet(stream_proxy, volume); | 309 dispatcher_->StreamVolumeSet(stream_proxy, volume); |
| 298 } | 310 } |
| 299 | 311 |
| 300 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { | 312 void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { |
| 313 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
| 301 dispatcher_->StopStream(stream_proxy); | 314 dispatcher_->StopStream(stream_proxy); |
| 302 | 315 |
| 303 // Now that StopStream() has completed the underlying physical stream should | 316 // Now that StopStream() has completed the underlying physical stream should |
| 304 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the | 317 // be stopped and no longer calling OnMoreData(), making it safe to Stop() the |
| 305 // OnMoreDataResampler. | 318 // OnMoreDataResampler. |
| 306 { | 319 CallbackMap::iterator it = callbacks_.find(stream_proxy); |
| 307 base::AutoLock auto_lock(callbacks_lock_); | 320 if (it != callbacks_.end()) |
| 308 CallbackMap::iterator it = callbacks_.find(stream_proxy); | 321 it->second->Stop(); |
| 309 if (it != callbacks_.end()) | |
| 310 it->second->Stop(); | |
| 311 } | |
| 312 } | 322 } |
| 313 | 323 |
| 314 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { | 324 void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { |
| 315 // Force StopStream() before CloseStream(). | 325 DCHECK_EQ(MessageLoop::current(), message_loop_); |
| 316 // TODO(dalecurtis): This shouldn't be necessary, but somewhere in the chain | |
| 317 // CloseStream() is occurring without a StopStream() which causes the callback | |
| 318 // provided by OnMoreDataResampler to go away before the output stream is | |
| 319 // ready. http://crbug.com/150619 | |
| 320 StopStream(stream_proxy); | |
| 321 dispatcher_->CloseStream(stream_proxy); | 326 dispatcher_->CloseStream(stream_proxy); |
| 322 | 327 |
| 323 // We assume that StopStream() is always called prior to CloseStream(), so | 328 // We assume that StopStream() is always called prior to CloseStream(), so |
| 324 // that it is safe to delete the OnMoreDataResampler here. | 329 // that it is safe to delete the OnMoreDataResampler here. |
| 325 { | 330 CallbackMap::iterator it = callbacks_.find(stream_proxy); |
| 326 base::AutoLock auto_lock(callbacks_lock_); | 331 if (it != callbacks_.end()) { |
| 327 CallbackMap::iterator it = callbacks_.find(stream_proxy); | 332 delete it->second; |
| 328 if (it != callbacks_.end()) { | 333 callbacks_.erase(it); |
| 329 delete it->second; | |
| 330 callbacks_.erase(it); | |
| 331 } | |
| 332 } | 334 } |
| 333 } | 335 } |
| 334 | 336 |
| 335 void AudioOutputResampler::Shutdown() { | 337 void AudioOutputResampler::Shutdown() { |
| 338 DCHECK_EQ(MessageLoop::current(), message_loop_); | |
|
tommi (sloooow) - chröme
2012/09/21 08:46:27
thanks for adding these DCHECKs. Aside from the ru
| |
| 339 | |
| 340 // No AudioOutputProxy objects should hold a reference to us when we get | |
| 341 // to this stage. | |
| 342 DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference"; | |
| 343 | |
| 336 dispatcher_->Shutdown(); | 344 dispatcher_->Shutdown(); |
| 337 DCHECK(callbacks_.empty()); | 345 DCHECK(callbacks_.empty()); |
| 338 } | 346 } |
| 339 | 347 |
| 340 OnMoreDataResampler::OnMoreDataResampler( | 348 OnMoreDataResampler::OnMoreDataResampler( |
| 341 double io_ratio, const AudioParameters& input_params, | 349 double io_ratio, const AudioParameters& input_params, |
| 342 const AudioParameters& output_params) | 350 const AudioParameters& output_params) |
| 343 : io_ratio_(io_ratio), | 351 : io_ratio_(io_ratio), |
| 344 source_callback_(NULL), | 352 source_callback_(NULL), |
| 345 outstanding_audio_bytes_(0), | 353 outstanding_audio_bytes_(0), |
| (...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 470 source_callback_->OnError(stream, code); | 478 source_callback_->OnError(stream, code); |
| 471 } | 479 } |
| 472 | 480 |
| 473 void OnMoreDataResampler::WaitTillDataReady() { | 481 void OnMoreDataResampler::WaitTillDataReady() { |
| 474 base::AutoLock auto_lock(source_lock_); | 482 base::AutoLock auto_lock(source_lock_); |
| 475 if (source_callback_ && !outstanding_audio_bytes_) | 483 if (source_callback_ && !outstanding_audio_bytes_) |
| 476 source_callback_->WaitTillDataReady(); | 484 source_callback_->WaitTillDataReady(); |
| 477 } | 485 } |
| 478 | 486 |
| 479 } // namespace media | 487 } // namespace media |
| OLD | NEW |