Chromium Code Reviews| Index: media/audio/audio_output_resampler.cc |
| diff --git a/media/audio/audio_output_resampler.cc b/media/audio/audio_output_resampler.cc |
| index c52d340491ef9c09c7ce52e3cf8a3a89c64a9307..ae3af591ae0ae93444bc39693c28c2c5f390a39e 100644 |
| --- a/media/audio/audio_output_resampler.cc |
| +++ b/media/audio/audio_output_resampler.cc |
| @@ -21,6 +21,7 @@ |
| namespace media { |
| +// Record UMA statistics for hardware output configuration. |
| static void RecordStats(const AudioParameters& output_params) { |
| UMA_HISTOGRAM_ENUMERATION( |
| "Media.HardwareAudioBitsPerChannel", output_params.bits_per_sample(), |
| @@ -43,6 +44,30 @@ static void RecordStats(const AudioParameters& output_params) { |
| } |
| } |
| +// Record UMA statistics for hardware output configuration after fallback. |
| +static void RecordFallbackStats(const AudioParameters& output_params) { |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "Media.FallbackHardwareAudioBitsPerChannel", |
| + output_params.bits_per_sample(), limits::kMaxBitsPerSample); |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "Media.FallbackHardwareAudioChannelLayout", |
| + output_params.channel_layout(), CHANNEL_LAYOUT_MAX); |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "Media.FallbackHardwareAudioChannelCount", |
| + output_params.channels(), limits::kMaxChannels); |
|
Chris Rogers
2012/09/12 18:45:45
Any chance we can record the buffer-size - frames_
|
| + |
| + AudioSampleRate asr = media::AsAudioSampleRate(output_params.sample_rate()); |
| + if (asr != kUnexpectedAudioSampleRate) { |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "Media.FallbackHardwareAudioSamplesPerSecond", |
| + asr, kUnexpectedAudioSampleRate); |
| + } else { |
| + UMA_HISTOGRAM_COUNTS( |
| + "Media.FallbackHardwareAudioSamplesPerSecondUnexpected", |
| + output_params.sample_rate()); |
| + } |
| +} |
| + |
| AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, |
| const AudioParameters& input_params, |
| const AudioParameters& output_params, |
| @@ -50,47 +75,57 @@ AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, |
| : AudioOutputDispatcher(audio_manager, input_params), |
| source_callback_(NULL), |
| io_ratio_(1), |
| - input_bytes_per_frame_(input_params.GetBytesPerFrame()), |
| - output_bytes_per_frame_(output_params.GetBytesPerFrame()), |
| - outstanding_audio_bytes_(0) { |
| + close_delay_(close_delay), |
| + outstanding_audio_bytes_(0), |
| + output_params_(output_params) { |
| + Initialize(); |
| + // Record UMA statistics for the hardware configuration. |
| + RecordStats(output_params_); |
| +} |
| + |
| +AudioOutputResampler::~AudioOutputResampler() {} |
| + |
| +void AudioOutputResampler::Initialize() { |
| + io_ratio_ = 1; |
| + |
| // TODO(dalecurtis): Add channel remixing. http://crbug.com/138762 |
| - DCHECK_EQ(input_params.channels(), output_params.channels()); |
| + DCHECK_EQ(params_.channels(), output_params_.channels()); |
| // Only resample or rebuffer if the input parameters don't match the output |
| // parameters to avoid any unnecessary work. |
| - if (input_params.channels() != output_params.channels() || |
| - input_params.sample_rate() != output_params.sample_rate() || |
| - input_params.bits_per_sample() != output_params.bits_per_sample() || |
| - input_params.frames_per_buffer() != output_params.frames_per_buffer()) { |
| + if (params_.channels() != output_params_.channels() || |
| + params_.sample_rate() != output_params_.sample_rate() || |
| + params_.bits_per_sample() != output_params_.bits_per_sample() || |
| + params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
| // Only resample if necessary since it's expensive. |
| - if (input_params.sample_rate() != output_params.sample_rate()) { |
| - DVLOG(1) << "Resampling from " << input_params.sample_rate() << " to " |
| - << output_params.sample_rate(); |
| - double io_sample_rate_ratio = input_params.sample_rate() / |
| - static_cast<double>(output_params.sample_rate()); |
| + if (params_.sample_rate() != output_params_.sample_rate()) { |
| + DVLOG(1) << "Resampling from " << params_.sample_rate() << " to " |
| + << output_params_.sample_rate(); |
| + double io_sample_rate_ratio = params_.sample_rate() / |
| + static_cast<double>(output_params_.sample_rate()); |
| // Include the I/O resampling ratio in our global I/O ratio. |
| io_ratio_ *= io_sample_rate_ratio; |
| resampler_.reset(new MultiChannelResampler( |
| - output_params.channels(), io_sample_rate_ratio, base::Bind( |
| + output_params_.channels(), io_sample_rate_ratio, base::Bind( |
| &AudioOutputResampler::ProvideInput, base::Unretained(this)))); |
| } |
| // Include bits per channel differences. |
| - io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / |
| - output_params.bits_per_sample(); |
| + io_ratio_ *= static_cast<double>(params_.bits_per_sample()) / |
| + output_params_.bits_per_sample(); |
| // Include channel count differences. |
| - io_ratio_ *= static_cast<double>(input_params.channels()) / |
| - output_params.channels(); |
| + io_ratio_ *= static_cast<double>(params_.channels()) / |
| + output_params_.channels(); |
| // Since the resampler / output device may want a different buffer size than |
| // the caller asked for, we need to use a FIFO to ensure that both sides |
| // read in chunk sizes they're configured for. |
| - if (input_params.sample_rate() != output_params.sample_rate() || |
| - input_params.frames_per_buffer() != output_params.frames_per_buffer()) { |
| - DVLOG(1) << "Rebuffering from " << input_params.frames_per_buffer() |
| - << " to " << output_params.frames_per_buffer(); |
| + if (params_.sample_rate() != output_params_.sample_rate() || |
| + params_.frames_per_buffer() != output_params_.frames_per_buffer()) { |
| + DVLOG(1) << "Rebuffering from " << params_.frames_per_buffer() |
| + << " to " << output_params_.frames_per_buffer(); |
| audio_fifo_.reset(new AudioPullFifo( |
| - input_params.channels(), input_params.frames_per_buffer(), base::Bind( |
| + params_.channels(), params_.frames_per_buffer(), base::Bind( |
| &AudioOutputResampler::SourceCallback_Locked, |
| base::Unretained(this)))); |
| } |
| @@ -101,17 +136,42 @@ AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, |
| // TODO(dalecurtis): All this code should be merged into AudioOutputMixer once |
| // we've stabilized the issues there. |
| dispatcher_ = new AudioOutputDispatcherImpl( |
| - audio_manager, output_params, close_delay); |
| - |
| - // Record UMA statistics for the hardware configuration. |
| - RecordStats(output_params); |
| + audio_manager_, output_params_, close_delay_); |
| } |
| -AudioOutputResampler::~AudioOutputResampler() {} |
| - |
| bool AudioOutputResampler::OpenStream() { |
| - // TODO(dalecurtis): Automatically revert to high latency path if OpenStream() |
| - // fails; use default high latency output values + rebuffering / resampling. |
| + if (dispatcher_->OpenStream()) { |
| + UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", false); |
| + return true; |
| + } |
| + |
| + // If we've already tried to open the stream in high latency mode, there's |
| + // nothing more to be done. |
| + if (output_params_.format() == AudioParameters::AUDIO_PCM_LINEAR) |
| + return false; |
| + |
| + DLOG(ERROR) << "Unable to open audio device in low latency mode. Falling " |
| + << "back to high latency audio output."; |
| + |
| + // Record UMA statistics about the hardware which triggered the failure so we |
| + // can debug and triage later. |
| + UMA_HISTOGRAM_BOOLEAN("Media.FallbackToHighLatencyAudioPath", true); |
| + RecordFallbackStats(output_params_); |
| + |
| + // Open failed! Attempt to open the output device in high latency mode using |
| + // a new high latency appropriate buffer size. |kMinLowLatencyFrameSize| is |
| + // arbitrarily based on Pepper Flash's MAXIMUM frame size for low latency. |
| + static const int kMinLowLatencyFrameSize = 2048; |
| + int frames_per_buffer = std::max( |
| + std::min(params_.frames_per_buffer(), kMinLowLatencyFrameSize), |
| + static_cast<int>(GetHighLatencyOutputBufferSize(params_.sample_rate()))); |
| + |
| + output_params_ = AudioParameters( |
| + AudioParameters::AUDIO_PCM_LINEAR, params_.channel_layout(), |
| + params_.sample_rate(), params_.bits_per_sample(), frames_per_buffer); |
| + Initialize(); |
| + |
| + // Retry, if this fails, there's nothing left to do but report the error back. |
| return dispatcher_->OpenStream(); |
| } |
| @@ -185,7 +245,9 @@ int AudioOutputResampler::OnMoreIOData(AudioBus* source, |
| ProvideInput(dest); |
| // Calculate how much data is left in the internal FIFO and resampler buffers. |
| - outstanding_audio_bytes_ -= dest->frames() * output_bytes_per_frame_; |
| + outstanding_audio_bytes_ -= |
| + dest->frames() * output_params_.GetBytesPerFrame(); |
| + |
| // Due to rounding errors while multiplying against |io_ratio_|, |
| // |outstanding_audio_bytes_| might (rarely) slip below zero. |
| if (outstanding_audio_bytes_ < 0) { |
| @@ -217,7 +279,7 @@ void AudioOutputResampler::SourceCallback_Locked(AudioBus* audio_bus) { |
| // Scale the number of frames we got back in terms of input bytes to output |
| // bytes accordingly. |
| outstanding_audio_bytes_ += |
| - (audio_bus->frames() * input_bytes_per_frame_) / io_ratio_; |
| + (audio_bus->frames() * params_.GetBytesPerFrame()) / io_ratio_; |
| } |
| void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { |