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 |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..91ebaf2f58e15f7f347670282fa81f794c303545 |
| --- /dev/null |
| +++ b/media/audio/audio_output_resampler.cc |
| @@ -0,0 +1,207 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "media/audio/audio_output_resampler.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/stringprintf.h" |
| +#include "base/compiler_specific.h" |
|
scherkus (not reviewing)
2012/09/07 13:30:16
a->z ordering
DaleCurtis
2012/09/10 12:06:24
Removed.
|
| +#include "base/message_loop.h" |
| +#include "base/time.h" |
| +#include "media/audio/audio_io.h" |
| +#include "media/audio/audio_output_dispatcher_impl.h" |
| +#include "media/audio/audio_output_proxy.h" |
| +#include "media/audio/audio_util.h" |
| +#include "media/base/audio_fifo.h" |
| +#include "media/base/multi_channel_resampler.h" |
| + |
| + |
| +namespace media { |
| + |
| +AudioOutputResampler::AudioOutputResampler(AudioManager* audio_manager, |
| + const AudioParameters& input_params, |
| + const base::TimeDelta& close_delay) |
| + : AudioOutputDispatcher(audio_manager, input_params), |
| + source_callback_(NULL), |
| + outstanding_audio_bytes_(0), |
| + io_ratio_(0), |
| + output_bytes_per_frame_(0), |
| + input_bytes_per_frame_(0) { |
| + AudioParameters output_params = input_params; |
| + int hardware_sample_rate = GetAudioHardwareSampleRate(); |
| + int hardware_buffer_size = GetAudioHardwareBufferSize(); |
| + |
| + // Only resample if necessary since it's expensive. |
| + // TODO(dalecurtis): Restrict to PCM low latency before landing. Right now it |
| + // converts all non-low latency calls as well for testing. |
| + if (input_params.sample_rate() != hardware_sample_rate || |
| + input_params.frames_per_buffer() != hardware_buffer_size) { |
| + // Create output parameters based on the audio hardware configuration. |
| + // TODO(dalecurtis): This should include channel count and bit depth. |
| + output_params = AudioParameters( |
| + AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(), |
| + hardware_sample_rate, 16, GetAudioHardwareBufferSize()); |
| + input_bytes_per_frame_ = input_params.GetBytesPerFrame(); |
| + output_bytes_per_frame_ = output_params.GetBytesPerFrame(); |
| + |
| + // Calculate the first part of the I/O ratio for the resampler. |
| + io_ratio_ = |
| + input_params.sample_rate() / static_cast<double>(hardware_sample_rate); |
| + resampler_.reset(new MultiChannelResampler( |
| + output_params.channels(), |
| + io_ratio_, |
| + base::Bind( |
| + &AudioOutputResampler::ProvideInput, base::Unretained(this)))); |
| + |
| + // Include the difference in output bits per sample. |
| + io_ratio_ *= static_cast<double>(input_params.bits_per_sample()) / |
| + output_params.bits_per_sample(); |
| + |
| + // TODO(dalecurtis): Initialize AudioPullFIFO(input_params.buffer_frames()). |
| + temp_bus_ = AudioBus::Create(input_params); |
| + audio_fifo_.reset(new AudioFifo( |
|
Chris Rogers
2012/09/06 19:18:25
Even if we're not doing sample-rate conversion, we
DaleCurtis
2012/09/07 14:17:35
Good point, I'll rework it so buffer size differen
DaleCurtis
2012/09/10 12:06:24
Done.
|
| + input_params.channels(), input_params.frames_per_buffer())); |
| + } |
| + |
| + // 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); |
| +} |
| + |
| +AudioOutputResampler::~AudioOutputResampler() {} |
| + |
| +bool AudioOutputResampler::OpenStream() { |
| + return dispatcher_->OpenStream(); |
| +} |
| + |
| +bool AudioOutputResampler::StartStream( |
| + AudioOutputStream::AudioSourceCallback* callback, |
| + AudioOutputProxy* stream_proxy) { |
| + { |
| + base::AutoLock auto_lock(source_lock_); |
| + source_callback_ = callback; |
| + } |
| + return dispatcher_->StartStream(this, stream_proxy); |
|
scherkus (not reviewing)
2012/09/07 13:30:16
which thread is calling these methods?
is dispatc
DaleCurtis
2012/09/07 14:17:35
Called by the audio thread, AudioOutputDispatcherI
|
| +} |
| + |
| +void AudioOutputResampler::StopStream(AudioOutputProxy* stream_proxy) { |
| + { |
| + base::AutoLock auto_lock(source_lock_); |
| + source_callback_ = NULL; |
| + outstanding_audio_bytes_ = 0; |
| + audio_fifo_->Clear(); |
| + resampler_->Flush(); |
| + } |
| + dispatcher_->StopStream(stream_proxy); |
| +} |
| + |
| +void AudioOutputResampler::StreamVolumeSet(AudioOutputProxy* stream_proxy, |
| + double volume) { |
| + dispatcher_->StreamVolumeSet(stream_proxy, volume); |
| +} |
| + |
| +void AudioOutputResampler::CloseStream(AudioOutputProxy* stream_proxy) { |
| + { |
| + base::AutoLock auto_lock(source_lock_); |
| + source_callback_ = NULL; |
| + outstanding_audio_bytes_ = 0; |
| + audio_fifo_->Clear(); |
| + resampler_->Flush(); |
| + } |
| + dispatcher_->CloseStream(stream_proxy); |
| +} |
| + |
| +void AudioOutputResampler::Shutdown() { |
| + { |
| + base::AutoLock auto_lock(source_lock_); |
| + source_callback_ = NULL; |
| + outstanding_audio_bytes_ = 0; |
| + audio_fifo_->Clear(); |
| + resampler_->Flush(); |
| + } |
| + dispatcher_->Shutdown(); |
| +} |
| + |
| +int AudioOutputResampler::OnMoreData(AudioBus* audio_bus, |
| + AudioBuffersState buffers_state) { |
| + if (!resampler_.get()) { |
| + // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? |
|
scherkus (not reviewing)
2012/09/07 13:30:16
what's ASB?
DaleCurtis
2012/09/07 14:17:35
A typo :) Should be ASCB.. Essentially instead of
|
| + base::AutoLock auto_lock(source_lock_); |
| + return source_callback_->OnMoreData(audio_bus, buffers_state); |
|
Chris Rogers
2012/09/06 19:18:25
We need to have a conditional here to check if we
DaleCurtis
2012/09/07 14:17:35
Will investigate, shouldn't be too hard.
DaleCurtis
2012/09/10 12:06:24
Done.
|
| + } |
| + |
| + current_buffers_state_ = buffers_state; |
| + resampler_->Resample(audio_bus, audio_bus->frames()); |
| + |
| + // Calculate how much data is left in the internal FIFO and resampler buffers. |
| + outstanding_audio_bytes_ -= audio_bus->frames() * output_bytes_per_frame_; |
| + CHECK_GE(outstanding_audio_bytes_, 0); |
| + |
| + // Always return the full number of frames requested, ProvideInput() will pad |
| + // with silence if it wasn't able to acquire enough data. |
| + // NOTE: This will not work with the current non-low latency <audio> pipeline. |
| + return audio_bus->frames(); |
| +} |
| + |
| +void AudioOutputResampler::ProvideInput(AudioBus* audio_bus) { |
| + // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? |
| + base::AutoLock auto_lock(source_lock_); |
| + // We may have waited for |source_lock_| while a call cleared the callback. |
| + if (!source_callback_) |
| + return; |
| + |
| + // Serve frames out of the FIFO if they're available. |
| + int frames_to_write = audio_bus->frames(); |
| + if (audio_fifo_->frames_in_fifo() > 0) { |
| + int frames = std::min(audio_bus->frames(), audio_fifo_->frames_in_fifo()); |
| + CHECK(audio_fifo_->Consume(audio_bus, 0, frames)); |
|
scherkus (not reviewing)
2012/09/07 13:30:17
:)
|
| + frames_to_write -= frames; |
| + } |
| + |
| + // If we were able to fulfill the request from the FIFO, we're done. |
| + if (frames_to_write == 0) |
| + return; |
| + |
| + // Adjust playback delay to include the state of the internal buffers used by |
| + // the resampler and the FIFO. Since the sample rate and bits per channel |
| + // may be different, we need to scale this value appropriately |
| + // TODO(dalecurtis): Move to the FIFO side callback once the FIFO is added. |
| + AudioBuffersState new_buffers_state; |
| + new_buffers_state.pending_bytes = io_ratio_ * |
| + (current_buffers_state_.total_bytes() + outstanding_audio_bytes_); |
| + |
| + // Retrieve data from the original callback. |
| + int frames = source_callback_->OnMoreData(temp_bus_.get(), new_buffers_state); |
| + |
| + // TODO(dalecurtis): Technically only <audio> returns fewer frames than |
| + // requested and it won't use this path... but since I tested with <audio> on |
| + // the high latency path it was necessary. Remove later. |
| + |
| + // Scale the number of frames we got back in terms of input bytes to output |
| + // bytes accordingly. |
| + outstanding_audio_bytes_ += (frames * input_bytes_per_frame_) / io_ratio_; |
| + |
| + // Put everything into the FIFO and fulfill the rest of the request. |
| + CHECK(audio_fifo_->Push(temp_bus_.get(), frames)); |
| + CHECK(audio_fifo_->Consume( |
| + audio_bus, audio_bus->frames() - frames_to_write, frames_to_write)); |
| +} |
| + |
| +void AudioOutputResampler::OnError(AudioOutputStream* stream, int code) { |
| + // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? |
| + base::AutoLock auto_lock(source_lock_); |
| + if (source_callback_) |
| + source_callback_->OnError(stream, code); |
| +} |
| + |
| +void AudioOutputResampler::WaitTillDataReady() { |
| + // TODO(dalecurtis): Copy ASB to local variable, release lock prior to call? |
| + base::AutoLock auto_lock(source_lock_); |
| + if (source_callback_ && !outstanding_audio_bytes_) |
| + source_callback_->WaitTillDataReady(); |
| +} |
| + |
| +} // namespace media |