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 handled by the output device | 37 // TODO(dalecurtis): Since channel mixing is handled by the output device |
| 34 // right now and not by AudioOutputResampler, we need to query for hardware | 38 // right now and not by AudioOutputResampler, we need to query for hardware |
| 35 // channel information. Remove once AOR handles this, http://crbug.com/138762 | 39 // channel 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) { |
|
scherkus (not reviewing)
2012/09/12 14:24:47
it it worth DCHECKing that we're in LOW_LATENCY or
DaleCurtis
2012/09/12 14:32:34
No reason it wouldn't work, so I haven't felt the
| |
| 76 Initialize(); | |
| 77 // Record UMA statistics for the hardware configuration. | |
| 78 RecordStats("", output_params_); | |
| 79 } | |
| 80 | |
| 81 AudioOutputResampler::~AudioOutputResampler() {} | |
| 82 | |
| 83 void AudioOutputResampler::Initialize() { | |
| 84 io_ratio_ = 1; | |
| 85 | |
| 71 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 | 86 // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 |
| 72 DCHECK_EQ(input_params.channels(), output_params.channels()); | 87 DCHECK_EQ(params_.channels(), output_params_.channels()); |
| 73 // Only resample or rebuffer if the input parameters don't match the output | 88 // Only resample or rebuffer if the input parameters don't match the output |
| 74 // parameters to avoid any unnecessary work. | 89 // parameters to avoid any unnecessary work. |
| 75 if (input_params.channels() != output_params.channels() || | 90 if (params_.channels() != output_params_.channels() || |
| 76 input_params.sample_rate() != output_params.sample_rate() || | 91 params_.sample_rate() != output_params_.sample_rate() || |
| 77 input_params.bits_per_sample() != output_params.bits_per_sample() || | 92 params_.bits_per_sample() != output_params_.bits_per_sample() || |
| 78 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { | 93 params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
| 79 // Only resample if necessary since it's expensive. | 94 // Only resample if necessary since it's expensive. |
| 80 if (input_params.sample_rate() != output_params.sample_rate()) { | 95 if (params_.sample_rate() != output_params_.sample_rate()) { |
| 81 DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " | 96 DVLOG(1) << "Resampling from " << params_.sample_rate() << " to " |
| 82 << output_params.sample_rate(); | 97 << output_params_.sample_rate(); |
| 83 double io_sample_rate_ratio = input_params.sample_rate() / | 98 double io_sample_rate_ratio = params_.sample_rate() / |
| 84 static_cast<double>(output_params.sample_rate()); | 99 static_cast<double>(output_params_.sample_rate()); |
| 85 // Include the I/O resampling ratio in our global I/O ratio. | 100 // Include the I/O resampling ratio in our global I/O ratio. |
| 86 io_ratio_ *= io_sample_rate_ratio; | 101 io_ratio_ *= io_sample_rate_ratio; |
| 87 resampler_.reset(new MultiChannelResampler( | 102 resampler_.reset(new MultiChannelResampler( |
| 88 output_params.channels(), io_sample_rate_ratio, base::Bind( | 103 output_params_.channels(), io_sample_rate_ratio, base::Bind( |
| 89 &AudioOutputResampler::ProvideInput, base::Unretained(this)))); | 104 &AudioOutputResampler::ProvideInput, base::Unretained(this)))); |
| 90 } | 105 } |
| 91 | 106 |
| 92 // Include bits per channel differences. | 107 // Include bits per channel differences. |
| 93 io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / | 108 io_ratio_ *= static_cast<double>(params_.bits_per_sample()) / |
| 94 output_params.bits_per_sample(); | 109 output_params_.bits_per_sample(); |
| 95 | 110 |
| 96 // Include channel count differences. | 111 // Include channel count differences. |
| 97 io_ratio_ *= static_cast<double>(input_params.channels()) / | 112 io_ratio_ *= static_cast<double>(params_.channels()) / |
| 98 output_params.channels(); | 113 output_params_.channels(); |
| 99 | 114 |
| 100 // Since the resampler / output device may want a different buffer size than | 115 // 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 | 116 // the caller asked for, we need to use a FIFO to ensure that both sides |
| 102 // read in chunk sizes they're configured for. | 117 // read in chunk sizes they're configured for. |
| 103 if (input_params.sample_rate() != output_params.sample_rate() || | 118 if (params_.sample_rate() != output_params_.sample_rate() || |
| 104 input_params.frames_per_buffer() != output_params.frames_per_buffer()) { | 119 params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
| 105 DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() | 120 DVLOG(1) << "Rebuffering from " << params_.frames_per_buffer() |
| 106 << " to " << output_params.frames_per_buffer(); | 121 << " to " << output_params_.frames_per_buffer(); |
| 107 audio_fifo_.reset(new AudioPullFifo( | 122 audio_fifo_.reset(new AudioPullFifo( |
| 108 input_params.channels(), input_params.frames_per_buffer(), base::Bind( | 123 params_.channels(), params_.frames_per_buffer(), base::Bind( |
| 109 &AudioOutputResampler::SourceCallback_Locked, | 124 &AudioOutputResampler::SourceCallback_Locked, |
| 110 base::Unretained(this)))); | 125 base::Unretained(this)))); |
| 111 } | 126 } |
| 112 | 127 |
| 113 DVLOG(1) << "I/O ratio is " << io_ratio_; | 128 DVLOG(1) << "I/O ratio is " << io_ratio_; |
| 114 } | 129 } |
| 115 | 130 |
| 116 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once | 131 // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once |
| 117 // we've stabilized the issues there. | 132 // we've stabilized the issues there. |
| 118 dispatcher_ = new AudioOutputDispatcherImpl( | 133 dispatcher_ = new AudioOutputDispatcherImpl( |
| 119 audio_manager, output_params, close_delay); | 134 audio_manager_, output_params_, close_delay_); |
| 120 | |
| 121 // Record UMA statistics for the hardware configuration. | |
| 122 RecordStats(output_params); | |
| 123 } | 135 } |
| 124 | 136 |
| 125 AudioOutputResampler::~AudioOutputResampler() {} | 137 bool AudioOutputResampler::OpenStream() { |
| 138 if (dispatcher_->OpenStream()) { | |
| 139 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false); | |
| 140 return true; | |
| 141 } | |
| 126 | 142 |
| 127 bool AudioOutputResampler::OpenStream() { | 143 // 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() | 144 // nothing more to be done. |
| 129 // fails; use default high latency output values + rebuffering / resampling. | 145 if (output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR) |
| 146 return false; | |
| 147 | |
| 148 DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling " | |
| 149 << "back to high latency audio output."; | |
| 150 | |
| 151 // Record UMA statistics about the hardware which triggered the failure so we | |
| 152 // can debug and triage later. | |
| 153 UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true); | |
| 154 RecordStats("Fallback", output_params_); | |
| 155 | |
| 156 // Open failed! Attempt to open the output device in high latency mode using | |
| 157 // a new high latency appropriate buffer size. |kMinLowLatencyFrameSize| is | |
| 158 // arbitrarily based on Pepper Flash's MAXIMUM frame size for low latency. | |
| 159 static const int kMinLowLatencyFrameSize = 2048; | |
| 160 int frames_per_buffer = std::max( | |
| 161 std::min(params_.frames_per_buffer(), kMinLowLatencyFrameSize), | |
| 162 static_cast<int>(GetHighLatencyOutputBufferSize(params_.sample_rate()))); | |
| 163 | |
| 164 output_params_ = AudioParameters( | |
| 165 AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(), | |
| 166 params_.sample_rate(), params_.bits_per_sample(), frames_per_buffer); | |
| 167 Initialize(); | |
| 168 | |
| 169 // Retry, if this fails, there's nothing left to do but report the error back. | |
| 130 return dispatcher_->OpenStream(); | 170 return dispatcher_->OpenStream(); |
| 131 } | 171 } |
| 132 | 172 |
| 133 bool AudioOutputResampler::StartStream( | 173 bool AudioOutputResampler::StartStream( |
| 134 AudioOutputStream::AudioSourceCallback* callback, | 174 AudioOutputStream::AudioSourceCallback* callback, |
| 135 AudioOutputProxy* stream_proxy) { | 175 AudioOutputProxy* stream_proxy) { |
| 136 { | 176 { |
| 137 base::AutoLock auto_lock(source_lock_); | 177 base::AutoLock auto_lock(source_lock_); |
| 138 source_callback_ = callback; | 178 source_callback_ = callback; |
| 139 } | 179 } |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 SourceCallback_Locked(dest); | 233 SourceCallback_Locked(dest); |
| 194 return dest->frames(); | 234 return dest->frames(); |
| 195 } | 235 } |
| 196 | 236 |
| 197 if (resampler_.get()) | 237 if (resampler_.get()) |
| 198 resampler_->Resample(dest, dest->frames()); | 238 resampler_->Resample(dest, dest->frames()); |
| 199 else | 239 else |
| 200 ProvideInput(dest); | 240 ProvideInput(dest); |
| 201 | 241 |
| 202 // Calculate how much data is left in the internal FIFO and resampler buffers. | 242 // Calculate how much data is left in the internal FIFO and resampler buffers. |
| 203 outstanding_audio_bytes_ -= dest->frames() * output_bytes_per_frame_; | 243 outstanding_audio_bytes_ -= |
| 244 dest->frames() * output_params_.GetBytesPerFrame(); | |
| 245 | |
| 204 // Due to rounding errors while multiplying against |io_ratio_|, | 246 // Due to rounding errors while multiplying against |io_ratio_|, |
| 205 // |outstanding_audio_bytes_| might (rarely) slip below zero. | 247 // |outstanding_audio_bytes_| might (rarely) slip below zero. |
| 206 if (outstanding_audio_bytes_ < 0) { | 248 if (outstanding_audio_bytes_ < 0) { |
| 207 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " | 249 DLOG(ERROR) << "Outstanding audio bytes went negative! Value: " |
| 208 << outstanding_audio_bytes_; | 250 << outstanding_audio_bytes_; |
| 209 outstanding_audio_bytes_ = 0; | 251 outstanding_audio_bytes_ = 0; |
| 210 } | 252 } |
| 211 | 253 |
| 212 // Always return the full number of frames requested, ProvideInput() will pad | 254 // Always return the full number of frames requested, ProvideInput() will pad |
| 213 // with silence if it wasn't able to acquire enough data. | 255 // 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_); | 267 (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); |
| 226 | 268 |
| 227 // Retrieve data from the original callback. Zero any unfilled frames. | 269 // Retrieve data from the original callback. Zero any unfilled frames. |
| 228 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state); | 270 int frames = source_callback_->OnMoreData(audio_bus, new_buffers_state); |
| 229 if (frames < audio_bus->frames()) | 271 if (frames < audio_bus->frames()) |
| 230 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); | 272 audio_bus->ZeroFramesPartial(frames, audio_bus->frames() - frames); |
| 231 | 273 |
| 232 // Scale the number of frames we got back in terms of input bytes to output | 274 // Scale the number of frames we got back in terms of input bytes to output |
| 233 // bytes accordingly. | 275 // bytes accordingly. |
| 234 outstanding_audio_bytes_ += | 276 outstanding_audio_bytes_ += |
| 235 (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_; | 277 (audio_bus->frames() * params_.GetBytesPerFrame()) / io_ratio_; |
| 236 } | 278 } |
| 237 | 279 |
| 238 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { | 280 void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { |
| 239 audio_fifo_->Consume(audio_bus, audio_bus->frames()); | 281 audio_fifo_->Consume(audio_bus, audio_bus->frames()); |
| 240 } | 282 } |
| 241 | 283 |
| 242 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { | 284 void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { |
| 243 base::AutoLock auto_lock(source_lock_); | 285 base::AutoLock auto_lock(source_lock_); |
| 244 if (source_callback_) | 286 if (source_callback_) |
| 245 source_callback_->OnError(stream, code); | 287 source_callback_->OnError(stream, code); |
| 246 } | 288 } |
| 247 | 289 |
| 248 void AudioOutputResampler::WaitTillDataReady() { | 290 void AudioOutputResampler::WaitTillDataReady() { |
| 249 base::AutoLock auto_lock(source_lock_); | 291 base::AutoLock auto_lock(source_lock_); |
| 250 if (source_callback_ && !outstanding_audio_bytes_) | 292 if (source_callback_ && !outstanding_audio_bytes_) |
| 251 source_callback_->WaitTillDataReady(); | 293 source_callback_->WaitTillDataReady(); |
| 252 } | 294 } |
| 253 | 295 |
| 254 } // namespace media | 296 } // namespace media |
| OLD | NEW |