Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(599)

Unified Diff: media/audio/audio_output_resampler.cc

Issue 10918098: Introduce AudioOutputResampler for browser side resampling. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: First draft! Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698