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/compiler_specific.h" | 9 #include "base/compiler_specific.h" |
| 10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
| 11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
| 12 #include "base/stringprintf.h" | |
| 12 #include "base/time.h" | 13 #include "base/time.h" |
| 13 #include "media/audio/audio_io.h" | 14 #include "media/audio/audio_io.h" |
| 14 #include "media/audio/audio_output_dispatcher_impl.h" | 15 #include "media/audio/audio_output_dispatcher_impl.h" |
| 15 #include "media/audio/audio_output_proxy.h" | 16 #include "media/audio/audio_output_proxy.h" |
| 16 #include "media/audio/audio_util.h" | 17 #include "media/audio/audio_util.h" |
| 17 #include "media/audio/sample_rates.h" | 18 #include "media/audio/sample_rates.h" |
| 18 #include "media/base/audio_pull_fifo.h" | 19 #include "media/base/audio_pull_fifo.h" |
| 19 #include "media/base/limits.h" | 20 #include "media/base/limits.h" |
| 20 #include "media/base/multi_channel_resampler.h" | 21 #include "media/base/multi_channel_resampler.h" |
| 21 | 22 |
| 22 #if defined(OS_WIN) | 23 #if defined(OS_WIN) |
| 23 #include "media/audio/win/audio_low_latency_output_win.h" | 24 #include "media/audio/win/audio_low_latency_output_win.h" |
| 24 #endif | 25 #endif |
| 25 | 26 |
| 26 namespace media { | 27 namespace media { |
| 27 | 28 |
| 28 static void RecordStats(const AudioParameters& output_params) { | 29 // Record UMA statistics for hardware output configuration. |prefix| will be |
| 30 // prepended to the statistic name. | |
| 31 static void RecordStats(const char* prefix, | |
| 32 const AudioParameters& output_params) { | |
| 29 UMA_HISTOGRAM_ENUMERATION( | 33 UMA_HISTOGRAM_ENUMERATION( |
| 30 "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(), | 34 base::StringPrintf("Media.%sHardwareAudioBitsPerChannel", prefix), |
| 31 limits::kMaxBitsPerSample); | 35 output_params.bits_per_sample(), limits::kMaxBitsPerSample); |
| 32 #if defined(OS_WIN) | 36 #if defined(OS_WIN) |
| 33 // TODO(dalecurtis): Since channel mixing is handle by the output device right | 37 // TODO(dalecurtis): Since channel mixing is handle by the output device right |
| 34 // now and not by AudioOutputResampler, we need to query for hardware channel | 38 // now and not by AudioOutputResampler, we need to query for hardware channel |
| 35 // information. Remove once AOR handles this, http://crbug.com/138762 | 39 // information. Remove once AOR handles this, http://crbug.com/138762 |
| 36 UMA_HISTOGRAM_ENUMERATION( | 40 UMA_HISTOGRAM_ENUMERATION( |
| 37 "Media.HardwareAudioChannelLayout", | 41 base::StringPrintf("Media.%sHardwareAudioChannelLayout", prefix), |
| 38 WASAPIAudioOutputStream::HardwareChannelLayout(), CHANNEL_LAYOUT_MAX); | 42 WASAPIAudioOutputStream::HardwareChannelLayout(), CHANNEL_LAYOUT_MAX); |
| 39 UMA_HISTOGRAM_ENUMERATION( | 43 UMA_HISTOGRAM_ENUMERATION( |
| 40 "Media.HardwareAudioChannelCount", | 44 base::StringPrintf("Media.%sHardwareAudioChannelCount", prefix), |
| 41 WASAPIAudioOutputStream::HardwareChannelCount(), limits::kMaxChannels); | 45 WASAPIAudioOutputStream::HardwareChannelCount(), limits::kMaxChannels); |
| 42 #else | 46 #else |
| 43 UMA_HISTOGRAM_ENUMERATION( | 47 UMA_HISTOGRAM_ENUMERATION( |
| 44 "Media.HardwareAudioChannelLayout", output_params.channel_layout(), | 48 base::StringPrintf("Media.%sHardwareAudioChannelLayout", prefix), |
| 45 CHANNEL_LAYOUT_MAX); | 49 output_params.channel_layout(), CHANNEL_LAYOUT_MAX); |
| 46 UMA_HISTOGRAM_ENUMERATION( | 50 UMA_HISTOGRAM_ENUMERATION( |
| 47 "Media.HardwareAudioChannelCount", output_params.channels(), | 51 base::StringPrintf("Media.%sHardwareAudioChannelCount", prefix), |
| 48 limits::kMaxChannels); | 52 output_params.channels(), limits::kMaxChannels); |
| 49 #endif | 53 #endif |
| 50 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate()); | 54 AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate()); |
| 51 if (asr != kUnexpectedAudioSampleRate) { | 55 if (asr != kUnexpectedAudioSampleRate) { |
| 52 UMA_HISTOGRAM_ENUMERATION( | 56 UMA_HISTOGRAM_ENUMERATION( |
| 53 "Media.HardwareAudioSamplesPerSecond", asr, kUnexpectedAudioSampleRate); | 57 base::StringPrintf("Media.%sHardwareAudioSamplesPerSecond", prefix), |
| 58 asr, kUnexpectedAudioSampleRate); | |
| 54 } else { | 59 } else { |
| 55 UMA_HISTOGRAM_COUNTS( | 60 UMA_HISTOGRAM_COUNTS( |
| 56 "Media.HardwareAudioSamplesPerSecondUnexpected", | 61 base::StringPrintf("Media.%sHardwareAudioSamplesPerSecondUnexpected", |
| 57 output_params.sample_rate()); | 62 prefix), output_params.sample_rate()); |
| 58 } | 63 } |
| 59 } | 64 } |
| 60 | 65 |
| 61 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, | 66 AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, |
| 62 const AudioParameters& input_params, | 67 const AudioParameters& input_params, |
| 63 const AudioParameters& output_params, | 68 const AudioParameters& output_params, |
| 64 const base::TimeDelta& close_delay) | 69 const base::TimeDelta& close_delay) |
| 65 : AudioOutputDispatcher(audio_manager, input_params), | 70 : AudioOutputDispatcher(audio_manager, input_params), |
| 66 source_callback_(NULL), | 71 source_callback_(NULL), |
| 67 io_ratio_(1), | 72 io_ratio_(1), |
| 68 input_bytes_per_frame_(input_params.GetBytesPerFrame()), | 73 close_delay_(close_delay), |
| 69 output_bytes_per_frame_(output_params.GetBytesPerFrame()), | 74 outstanding_audio_bytes_(0), |
| 70 outstanding_audio_bytes_(0) { | 75 output_params_(output_params) { |
| 76 Initialize(true); | |
|
scherkus (not reviewing)
2012/09/12 13:45:15
instead of passing true to call RecordStats ... ho
DaleCurtis
2012/09/12 14:16:51
Done.
| |
| 77 } | |
| 78 | |
| 79 AudioOutputResampler::~AudioOutputResampler() {} | |
| 80 | |
| 81 void AudioOutputResampler::Initialize(bool record_stats) { | |
| 82 io_ratio_ = 1; | |
| 83 | |
| 71 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 | 84 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 |
| 72 DCHECK_EQ(input_params.channels(), output_params.channels()); | 85 DCHECK_EQ(params_.channels(), output_params_.channels()); |
| 73 // Only resample or rebuffer if the input parameters don't match the output | 86 // Only resample or rebuffer if the input parameters don't match the output |
| 74 // parameters to avoid any unnecessary work. | 87 // parameters to avoid any unnecessary work. |
| 75 if (input_params.channels() != output_params.channels() || | 88 if (params_.channels() != output_params_.channels() || |
| 76 input_params.sample_rate() != output_params.sample_rate() || | 89 params_.sample_rate() != output_params_.sample_rate() || |
| 77 input_params.bits_per_sample() != output_params.bits_per_sample() || | 90 params_.bits_per_sample() != output_params_.bits_per_sample() || |
| 78 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { | 91 params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
| 79 // Only resample if necessary since it's expensive. | 92 // Only resample if necessary since it's expensive. |
| 80 if (input_params.sample_rate() != output_params.sample_rate()) { | 93 if (params_.sample_rate() != output_params_.sample_rate()) { |
| 81 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " | 94 DVLOG(1) << "Resampling from " << params_.sample_rate() << " to " |
| 82 << output_params.sample_rate(); | 95 << output_params_.sample_rate(); |
| 83 double io_sample_rate_ratio = input_params.sample_rate() / | 96 double io_sample_rate_ratio = params_.sample_rate() / |
| 84 static_cast<double>(output_params.sample_rate()); | 97 static_cast<double>(output_params_.sample_rate()); |
| 85 // Include the I/O resampling ratio in our global I/O ratio. | 98 // Include the I/O resampling ratio in our global I/O ratio. |
| 86 io_ratio_ *= io_sample_rate_ratio; | 99 io_ratio_ *= io_sample_rate_ratio; |
| 87 resampler_.reset(new MultiChannelResampler( | 100 resampler_.reset(new MultiChannelResampler( |
| 88 output_params.channels(), io_sample_rate_ratio, base::Bind( | 101 output_params_.channels(), io_sample_rate_ratio, base::Bind( |
| 89 &AudioOutputResampler::ProvideInput, base::Unretained(this)))); | 102 &AudioOutputResampler::ProvideInput, base::Unretained(this)))); |
| 90 } | 103 } |
| 91 | 104 |
| 92 // Include bits per channel differences. | 105 // Include bits per channel differences. |
| 93 io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / | 106 io_ratio_ *= static_cast<double>(params_.bits_per_sample()) / |
| 94 output_params.bits_per_sample(); | 107 output_params_.bits_per_sample(); |
| 95 | 108 |
| 96 // Include channel count differences. | 109 // Include channel count differences. |
| 97 io_ratio_ *= static_cast<double>(input_params.channels()) / | 110 io_ratio_ *= static_cast<double>(params_.channels()) / |
| 98 output_params.channels(); | 111 output_params_.channels(); |
| 99 | 112 |
| 100 // Since the resampler / output device may want a different buffer size than | 113 // Since the resampler / output device may want a different buffer size than |
| 101 // the caller asked for, we need to use a FIFO to ensure that both sides | 114 // the caller asked for, we need to use a FIFO to ensure that both sides |
| 102 // read in chunk sizes they're configured for. | 115 // read in chunk sizes they're configured for. |
| 103 if (input_params.sample_rate() != output_params.sample_rate() || | 116 if (params_.sample_rate() != output_params_.sample_rate() || |
| 104 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { | 117 params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
| 105 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() | 118 DVLOG(1) << "Rebuffering from " << params_.frames_per_buffer() |
| 106 << " to " << output_params.frames_per_buffer(); | 119 << " to " << output_params_.frames_per_buffer(); |
| 107 audio_fifo_.reset(new AudioPullFifo( | 120 audio_fifo_.reset(new AudioPullFifo( |
| 108 input_params.channels(), input_params.frames_per_buffer(), base::Bind( | 121 params_.channels(), params_.frames_per_buffer(), base::Bind( |
| 109 &AudioOutputResampler::SourceCallback_Locked, | 122 &AudioOutputResampler::SourceCallback_Locked, |
| 110 base::Unretained(this)))); | 123 base::Unretained(this)))); |
| 111 } | 124 } |
| 112 | 125 |
| 113 DVLOG(1) << "I/O ratio is " << io_ratio_; | 126 DVLOG(1) << "I/O ratio is " << io_ratio_; |
| 114 } | 127 } |
| 115 | 128 |
| 116 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once | 129 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once |
| 117 // we've stabilized the issues there. | 130 // we've stabilized the issues there. |
| 118 dispatcher_ = new AudioOutputDispatcherImpl( | 131 dispatcher_ = new AudioOutputDispatcherImpl( |
| 119 audio_manager, output_params, close_delay); | 132 audio_manager_, output_params_, close_delay_); |
| 120 | 133 |
| 121 // Record UMA statistics for the hardware configuration. | 134 // Record UMA statistics for the hardware configuration. |
| 122 RecordStats(output_params); | 135 if (record_stats) |
| 136 RecordStats("", output_params_); | |
| 123 } | 137 } |
| 124 | 138 |
| 125 AudioOutputResampler::~AudioOutputResampler() {} | 139 bool AudioOutputResampler::OpenStream() { |
| 140 if (dispatcher_->OpenStream()) { | |
| 141 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false); | |
| 142 return true; | |
| 143 } | |
| 126 | 144 |
| 127 bool AudioOutputResampler::OpenStream() { | 145 // If we've already tried to open the stream in high latency mode, there's |
| 128 // TODO(dalecurtis): Automatically revert to high latency path if OpenStream() | 146 // nothing more to be done. |
| 129 // fails; use default high latency output values + rebuffering / resampling. | 147 if (output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR) |
| 148 return false; | |
| 149 | |
| 150 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling " | |
| 151 << "back to high latency audio output."; | |
| 152 | |
| 153 // Record UMA statistics about the hardware which triggered the failure so we | |
| 154 // can debug and triage later. | |
| 155 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true); | |
| 156 RecordStats("Fallback", output_params_); | |
| 157 | |
| 158 // Open failed! Attempt to open the output device in high latency mode using | |
| 159 // a new high latency appropriate buffer size. |kMinLowLatencyFrameSize| is | |
| 160 // arbitrarily based on Pepper Flash's MAXIMUM frame size for low latency. | |
| 161 static const int kMinLowLatencyFrameSize = 2048; | |
| 162 int frames_per_buffer = std::max( | |
| 163 std::min(params_.frames_per_buffer(), kMinLowLatencyFrameSize), | |
| 164 static_cast<int>(GetHighLatencyOutputBufferSize(params_.sample_rate()))); | |
| 165 | |
| 166 output_params_ = AudioParameters( | |
| 167 AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(), | |
| 168 params_.sample_rate(), params_.bits_per_sample(), frames_per_buffer); | |
| 169 Initialize(false); | |
| 170 | |
| 171 // Retry, if this fails, there's nothing left to do but report the error back. | |
| 130 return dispatcher_->OpenStream(); | 172 return dispatcher_->OpenStream(); |
| 131 } | 173 } |
| 132 | 174 |
| 133 bool AudioOutputResampler::StartStream( | 175 bool AudioOutputResampler::StartStream( |
| 134 AudioOutputStream::AudioSourceCallback* callback, | 176 AudioOutputStream::AudioSourceCallback* callback, |
| 135 AudioOutputProxy* stream_proxy) { | 177 AudioOutputProxy* stream_proxy) { |
| 136 { | 178 { |
| 137 base::AutoLock auto_lock(source_lock_); | 179 base::AutoLock auto_lock(source_lock_); |
| 138 source_callback_ = callback; | 180 source_callback_ = callback; |
| 139 } | 181 } |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 SourceCallback_Locked(dest); | 235 SourceCallback_Locked(dest); |
| 194 return dest->frames(); | 236 return dest->frames(); |
| 195 } | 237 } |
| 196 | 238 |
| 197 if (resampler_.get()) | 239 if (resampler_.get()) |
| 198 resampler_->Resample(dest, dest->frames()); | 240 resampler_->Resample(dest, dest->frames()); |
| 199 else | 241 else |
| 200 ProvideInput(dest); | 242 ProvideInput(dest); |
| 201 | 243 |
| 202 // Calculate how much data is left in the internal FIFO and resampler buffers. | 244 // Calculate how much data is left in the internal FIFO and resampler buffers. |
| 203 outstanding_audio_bytes_ -= dest->frames() * output_bytes_per_frame_; | 245 outstanding_audio_bytes_ -= |
| 246 dest->frames() * output_params_.GetBytesPerFrame(); | |
| 247 | |
| 204 // Due to rounding errors while multiplying against |io_ratio_|, | 248 // Due to rounding errors while multiplying against |io_ratio_|, |
| 205 // |outstanding_audio_bytes_| might (rarely) slip below zero. | 249 // |outstanding_audio_bytes_| might (rarely) slip below zero. |
| 206 if (outstanding_audio_bytes_ < 0) { | 250 if (outstanding_audio_bytes_ < 0) { |
| 207 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " | 251 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " |
| 208 << outstanding_audio_bytes_; | 252 << outstanding_audio_bytes_; |
| 209 outstanding_audio_bytes_ = 0; | 253 outstanding_audio_bytes_ = 0; |
| 210 } | 254 } |
| 211 | 255 |
| 212 // Always return the full number of frames requested, ProvideInput() will pad | 256 // Always return the full number of frames requested, ProvideInput() will pad |
| 213 // with silence if it wasn't able to acquire enough data. | 257 // with silence if it wasn't able to acquire enough data. |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 225 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); | 269 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); |
| 226 | 270 |
| 227 // Retrieve data from the original callback. Zero any unfilled frames. | 271 // Retrieve data from the original callback. Zero any unfilled frames. |
| 228 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state); | 272 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state); |
| 229 if (frames < audio_bus->frames()) | 273 if (frames < audio_bus->frames()) |
| 230 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); | 274 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); |
| 231 | 275 |
| 232 // Scale the number of frames we got back in terms of input bytes to output | 276 // Scale the number of frames we got back in terms of input bytes to output |
| 233 // bytes accordingly. | 277 // bytes accordingly. |
| 234 outstanding_audio_bytes_ += | 278 outstanding_audio_bytes_ += |
| 235 (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_; | 279 (audio_bus->frames() * params_.GetBytesPerFrame()) / io_ratio_; |
| 236 } | 280 } |
| 237 | 281 |
| 238 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { | 282 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { |
| 239 audio_fifo_->Consume(audio_bus, audio_bus->frames()); | 283 audio_fifo_->Consume(audio_bus, audio_bus->frames()); |
| 240 } | 284 } |
| 241 | 285 |
| 242 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { | 286 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { |
| 243 base::AutoLock auto_lock(source_lock_); | 287 base::AutoLock auto_lock(source_lock_); |
| 244 if (source_callback_) | 288 if (source_callback_) |
| 245 source_callback_->OnError(stream, code); | 289 source_callback_->OnError(stream, code); |
| 246 } | 290 } |
| 247 | 291 |
| 248 void AudioOutputResampler::WaitTillDataReady() { | 292 void AudioOutputResampler::WaitTillDataReady() { |
| 249 base::AutoLock auto_lock(source_lock_); | 293 base::AutoLock auto_lock(source_lock_); |
| 250 if (source_callback_ && !outstanding_audio_bytes_) | 294 if (source_callback_ && !outstanding_audio_bytes_) |
| 251 source_callback_->WaitTillDataReady(); | 295 source_callback_->WaitTillDataReady(); |
| 252 } | 296 } |
| 253 | 297 |
| 254 } // namespace media | 298 } // namespace media |
| OLD | NEW |