summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-24 23:55:06 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-02-24 23:55:06 +0000
commitfe8bc697ccc31200cf1f12dc7827d3c287c0fc89 (patch)
tree5f159b6f85bb4c2a1d17abc75acb8740ea5df223 /media
parentbde5f237ca7527195df2866f5b0a5893b7a182dc (diff)
downloadchromium_src-fe8bc697ccc31200cf1f12dc7827d3c287c0fc89.zip
chromium_src-fe8bc697ccc31200cf1f12dc7827d3c287c0fc89.tar.gz
chromium_src-fe8bc697ccc31200cf1f12dc7827d3c287c0fc89.tar.bz2
Refactored AudioRendererImpl and NullAudioRenderer into a new base class AudioRendererBase.
AudioRendererBase handles the threaded queueing of audio data and provides simplified interfaces and methods for subclasses. As the saying goes, "threads are hard" so reducing the amount of multi-threaded code people have to write is always a good thing. Review URL: http://codereview.chromium.org/28042 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10309 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/build/media.vcproj8
-rw-r--r--media/filters/audio_renderer_base.cc154
-rw-r--r--media/filters/audio_renderer_base.h109
-rw-r--r--media/filters/audio_renderer_impl.cc168
-rw-r--r--media/filters/audio_renderer_impl.h39
-rw-r--r--media/filters/null_audio_renderer.cc129
-rw-r--r--media/filters/null_audio_renderer.h39
-rw-r--r--media/media_lib.scons2
8 files changed, 376 insertions, 272 deletions
diff --git a/media/build/media.vcproj b/media/build/media.vcproj
index fbe6921..4cbd6c7 100644
--- a/media/build/media.vcproj
+++ b/media/build/media.vcproj
@@ -189,6 +189,14 @@
Name="filters"
>
<File
+ RelativePath="..\filters\audio_renderer_base.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\filters\audio_renderer_base.h"
+ >
+ </File>
+ <File
RelativePath="..\filters\audio_renderer_impl.cc"
>
</File>
diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc
new file mode 100644
index 0000000..b661837
--- /dev/null
+++ b/media/filters/audio_renderer_base.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2009 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/base/filter_host.h"
+#include "media/filters/audio_renderer_base.h"
+
+namespace media {
+
+// The maximum size of the queue, which also acts as the number of initial reads
+// to perform for buffering. The size of the queue should never exceed this
+// number since we read only after we've dequeued and released a buffer in
+// callback thread.
+//
+// This is sort of a magic number, but for 44.1kHz stereo audio this will give
+// us enough data to fill approximately 4 complete callback buffers.
+const size_t AudioRendererBase::kDefaultMaxQueueSize = 16;
+
+AudioRendererBase::AudioRendererBase(size_t max_queue_size)
+ : decoder_(NULL),
+ max_queue_size_(max_queue_size),
+ data_offset_(0),
+ initialized_(false),
+ stopped_(false) {
+}
+
+AudioRendererBase::~AudioRendererBase() {
+ // Stop() should have been called and OnAssignment() should have stopped
+ // enqueuing data.
+ DCHECK(stopped_);
+ DCHECK(queue_.empty());
+}
+
+void AudioRendererBase::Stop() {
+ OnStop();
+
+ AutoLock auto_lock(lock_);
+ while (queue_.empty()) {
+ queue_.front()->Release();
+ queue_.pop_front();
+ }
+ stopped_ = true;
+}
+
+bool AudioRendererBase::Initialize(AudioDecoder* decoder) {
+ DCHECK(decoder);
+ decoder_ = decoder;
+
+ // Schedule our initial reads.
+ for (size_t i = 0; i < max_queue_size_; ++i) {
+ ScheduleRead();
+ }
+
+ // Defer initialization until all scheduled reads have completed.
+ return OnInitialize(decoder_->GetMediaFormat());
+}
+
+void AudioRendererBase::OnAssignment(Buffer* buffer_in) {
+ bool initialization_complete = false;
+ {
+ AutoLock auto_lock(lock_);
+ if (!stopped_) {
+ buffer_in->AddRef();
+ queue_.push_back(buffer_in);
+ DCHECK(queue_.size() <= max_queue_size_);
+ }
+
+ // See if we're finally initialized.
+ // TODO(scherkus): handle end of stream cases where we'll never reach max
+ // queue size.
+ if (!initialized_ && queue_.size() == max_queue_size_) {
+ initialization_complete = true;
+ }
+ }
+
+ if (initialization_complete) {
+ initialized_ = true;
+ host_->InitializationComplete();
+ }
+}
+
+size_t AudioRendererBase::FillBuffer(uint8* dest, size_t len) {
+ size_t result = 0;
+ size_t buffers_released = 0;
+ base::TimeDelta timestamp;
+ {
+ AutoLock auto_lock(lock_);
+ // Loop until the buffer has been filled.
+ while (len > 0) {
+ if (queue_.empty()) {
+ // TODO(scherkus): consider blocking here until more data arrives.
+ break;
+ }
+ Buffer* buffer = queue_.front();
+
+ // Determine how much to copy.
+ const char* data = buffer->GetData() + data_offset_;
+ size_t data_len = buffer->GetDataSize() - data_offset_;
+ data_len = std::min(len, data_len);
+
+ // Copy into buffer.
+ memcpy(dest, data, data_len);
+ len -= data_len;
+ dest += data_len;
+ data_offset_ += data_len;
+ result += data_len;
+
+ // Check to see if we're finished with the front buffer.
+ if (data_offset_ == buffer->GetDataSize()) {
+ // Dequeue the buffer.
+ queue_.pop_front();
+
+ // Remember the latest time value and how many buffers we've released.
+ timestamp = buffer->GetTimestamp();
+ buffer->Release();
+ ++buffers_released;
+
+ // Reset our offset into the front buffer.
+ data_offset_ = 0;
+ }
+ }
+ }
+
+ // If we've released any buffers, update the clock and read more buffers from
+ // the decoder.
+ if (buffers_released > 0) {
+ host_->SetTime(timestamp);
+ for (size_t i = 0; i < buffers_released; ++i) {
+ ScheduleRead();
+ }
+ }
+ return result;
+}
+
+void AudioRendererBase::ScheduleRead() {
+ host_->PostTask(NewRunnableMethod(decoder_, &AudioDecoder::Read,
+ new AssignableBuffer<AudioRendererBase, Buffer>(this)));
+}
+
+// static
+bool AudioRendererBase::ParseMediaFormat(const MediaFormat* media_format,
+ int* channels_out,
+ int* sample_rate_out,
+ int* sample_bits_out) {
+ // TODO(scherkus): might be handy to support NULL parameters.
+ std::string mime_type;
+ return media_format->GetAsString(MediaFormat::kMimeType, &mime_type) &&
+ media_format->GetAsInteger(MediaFormat::kChannels, channels_out) &&
+ media_format->GetAsInteger(MediaFormat::kSampleRate, sample_rate_out) &&
+ media_format->GetAsInteger(MediaFormat::kSampleBits, sample_bits_out) &&
+ mime_type.compare(mime_type::kUncompressedAudio) == 0;
+}
+
+} // namespace media
diff --git a/media/filters/audio_renderer_base.h b/media/filters/audio_renderer_base.h
new file mode 100644
index 0000000..fc0b13b
--- /dev/null
+++ b/media/filters/audio_renderer_base.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2009 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.
+
+// AudioRendererBase takes care of the tricky queuing work and provides simple
+// methods for subclasses to peek and poke at audio data. In addition to
+// AudioRenderer interface methods this classes doesn't implement, subclasses
+// must also implement the following methods:
+// OnInitialized
+// OnStop
+//
+// The general assumption is that subclasses start a callback-based audio thread
+// which needs to be filled with decoded audio data. AudioDecoderBase provides
+// FillBuffer which handles filling the provided buffer, dequeuing items,
+// scheduling additional reads and updating the clock. In a sense,
+// AudioRendererBase is the producer and the subclass is the consumer.
+
+#ifndef MEDIA_FILTERS_AUDIO_RENDERER_BASE_H_
+#define MEDIA_FILTERS_AUDIO_RENDERER_BASE_H_
+
+#include <deque>
+
+#include "base/lock.h"
+#include "media/base/buffers.h"
+#include "media/base/factory.h"
+#include "media/base/filters.h"
+
+namespace media {
+
+class AudioRendererBase : public AudioRenderer {
+ public:
+ // MediaFilter implementation.
+ virtual void Stop();
+
+ // AudioRenderer implementation.
+ virtual bool Initialize(AudioDecoder* decoder);
+
+ // AssignableBuffer<AudioRendererBase, BufferInterface> implementation.
+ void OnAssignment(Buffer* buffer_in);
+
+ protected:
+ // The default maximum size of the queue.
+ static const size_t kDefaultMaxQueueSize;
+
+ // Only allow a factory to create this class.
+ AudioRendererBase(size_t max_queue_size);
+ virtual ~AudioRendererBase();
+
+ // Called by Initialize(). |media_format| is the format of the AudioDecoder.
+ // Subclasses should return true if they were able to initialize, false
+ // otherwise.
+ virtual bool OnInitialize(const MediaFormat* media_format) = 0;
+
+ // Called by Stop(). Subclasses should perform any necessary cleanup during
+ // this time, such as stopping any running threads.
+ virtual void OnStop() = 0;
+
+ // Fills the given buffer with audio data by dequeuing buffers and copying the
+ // data into the |dest|. FillBuffer also takes care of updating the clock.
+ // Returns the number of bytes copied into |dest|, which may be less than
+ // equal to |len|.
+ //
+ // If this method is returns less bytes than |len| (including zero), it could
+ // be a sign that the pipeline is stalled or unable to stream the data fast
+ // enough. In such scenarios, the callee should zero out unused portions
+ // of their buffer to playback silence.
+ //
+ // Safe to call on any thread.
+ size_t FillBuffer(uint8* dest, size_t len);
+
+ // Helper to parse a media format and return whether we were successful
+ // retrieving all the information we care about.
+ static bool ParseMediaFormat(const MediaFormat* media_format,
+ int* channels_out, int* sample_rate_out,
+ int* sample_bits_out);
+
+ private:
+ // Audio decoder.
+ AudioDecoder* decoder_;
+
+ // Maximum queue size, configuration parameter passed in during construction.
+ size_t max_queue_size_;
+
+ // Queued audio data.
+ typedef std::deque<Buffer*> BufferQueue;
+ BufferQueue queue_;
+ Lock lock_;
+
+ // Remembers the amount of remaining audio data for the front buffer.
+ size_t data_offset_;
+
+ // Whether or not we're initialized.
+ bool initialized_;
+
+ // Whether or not we've stopped.
+ bool stopped_;
+
+ // Posts a task on the pipeline thread to read a sample from the decoder. The
+ // resulting buffer will be placed in the queue.
+ //
+ // Safe to call on any thread.
+ void ScheduleRead();
+
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererBase);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_AUDIO_RENDERER_BASE_H_
diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc
index f17cce5..6412d38 100644
--- a/media/filters/audio_renderer_impl.cc
+++ b/media/filters/audio_renderer_impl.cc
@@ -7,35 +7,16 @@
namespace media {
-// The number of initial reads to perform for buffering. There should never
-// be more than |kInitialReads| in the queue since we read only after we're
-// finished with a buffer in the callback thread.
-//
-// This is sort of a magic number, but for 44.1kHz stereo audio this will give
-// us enough data to fill approximately 4 complete callback buffers.
-static const size_t kInitialReads = 16;
-
// We'll try to fill 4096 samples per buffer, which is roughly ~92ms of audio
// data for a 44.1kHz audio source.
static const size_t kSamplesPerBuffer = 4096;
AudioRendererImpl::AudioRendererImpl()
- : decoder_(NULL),
- stream_(NULL),
- initialized_(false),
- data_offset_(0) {
+ : AudioRendererBase(kDefaultMaxQueueSize),
+ stream_(NULL) {
}
AudioRendererImpl::~AudioRendererImpl() {
- // Empty buffer queue (callback will simply keep exiting).
- input_lock_.Acquire();
- while (!input_queue_.empty()) {
- Buffer* buffer = input_queue_.front();
- input_queue_.pop_front();
- buffer->Release();
- }
- input_lock_.Release();
-
// Close down the audio device.
if (stream_) {
stream_->Stop();
@@ -51,11 +32,6 @@ bool AudioRendererImpl::IsMediaFormatSupported(
return ParseMediaFormat(media_format, &channels, &sample_rate, &sample_bits);
}
-void AudioRendererImpl::Stop() {
- DCHECK(stream_);
- stream_->Stop();
-}
-
void AudioRendererImpl::SetPlaybackRate(float playback_rate) {
DCHECK(stream_);
// TODO(scherkus): handle playback rates not equal to 1.0.
@@ -66,112 +42,17 @@ void AudioRendererImpl::SetPlaybackRate(float playback_rate) {
}
}
-bool AudioRendererImpl::Initialize(AudioDecoder* decoder) {
- decoder_ = decoder;
- const MediaFormat* media_format = decoder_->GetMediaFormat();
- std::string mime_type;
- int channels;
- int sample_rate;
- int sample_bits;
- if (!ParseMediaFormat(media_format, &channels, &sample_rate, &sample_bits)) {
- return false;
- }
-
- // Create our audio stream.
- stream_ = AudioManager::GetAudioManager()->MakeAudioStream(
- AudioManager::AUDIO_PCM_LINEAR, channels, sample_rate, sample_bits);
- DCHECK(stream_);
-
- // Calculate buffer size and open the stream.
- size_t size = kSamplesPerBuffer * channels * sample_bits / 8;
- if (!stream_->Open(size)) {
- stream_->Close();
- stream_ = NULL;
- return false;
- }
-
- // Schedule our initial reads.
- for (size_t i = 0; i < kInitialReads; ++i) {
- ScheduleRead();
- }
-
- // Defer initialization until all scheduled reads have completed.
- return true;
-}
-
void AudioRendererImpl::SetVolume(float volume) {
stream_->SetVolume(volume, volume);
}
-void AudioRendererImpl::OnAssignment(Buffer* buffer_in) {
- AutoLock auto_lock(input_lock_);
- buffer_in->AddRef();
- input_queue_.push_back(buffer_in);
- DCHECK(input_queue_.size() <= kInitialReads);
-
- // See if we're finally initialized.
- // TODO(scherkus): handle end of stream.
- if (!initialized_ && input_queue_.size() == kInitialReads) {
- initialized_ = true;
- host_->InitializationComplete();
- }
-}
-
size_t AudioRendererImpl::OnMoreData(AudioOutputStream* stream, void* dest_void,
size_t len) {
// TODO(scherkus): handle end of stream.
DCHECK(stream_ == stream);
- // TODO(scherkus): maybe change interface to pass in char/uint8 or similar.
- char* dest = reinterpret_cast<char*>(dest_void);
- size_t result = 0;
-
- // Loop until the buffer has been filled.
- while (len > 0) {
- Buffer* buffer = NULL;
- {
- AutoLock auto_lock(input_lock_);
- if (input_queue_.empty()) {
- // TODO(scherkus): consider blocking here until more data arrives.
- return result;
- }
-
- // Access the front buffer.
- buffer = input_queue_.front();
- }
-
- // Determine how much to copy.
- const char* data = buffer->GetData() + data_offset_;
- size_t data_len = buffer->GetDataSize() - data_offset_;
- data_len = std::min(len, data_len);
-
- // Copy into buffer.
- memcpy(dest, data, data_len);
- len -= data_len;
- dest += data_len;
- data_offset_ += data_len;
- result += data_len;
-
- // Check to see if we're finished with the front buffer.
- if (data_offset_ == buffer->GetDataSize()) {
- // Update the clock.
- host_->SetTime(buffer->GetTimestamp());
-
- // Dequeue the buffer.
- {
- AutoLock auto_lock(input_lock_);
- input_queue_.pop_front();
- }
-
- // Release and request another buffer from the decoder.
- buffer->Release();
- ScheduleRead();
-
- // Reset our offset into the front buffer.
- data_offset_ = 0;
- }
- }
- return result;
+ // TODO(scherkus): maybe change OnMoreData to pass in char/uint8 or similar.
+ return FillBuffer(reinterpret_cast<uint8*>(dest_void), len);
}
void AudioRendererImpl::OnClose(AudioOutputStream* stream) {
@@ -184,22 +65,35 @@ void AudioRendererImpl::OnError(AudioOutputStream* stream, int code) {
NOTIMPLEMENTED();
}
-// static
-bool AudioRendererImpl::ParseMediaFormat(const MediaFormat* media_format,
- int* channels_out,
- int* sample_rate_out,
- int* sample_bits_out) {
- std::string mime_type;
- return media_format->GetAsString(MediaFormat::kMimeType, &mime_type) &&
- media_format->GetAsInteger(MediaFormat::kChannels, channels_out) &&
- media_format->GetAsInteger(MediaFormat::kSampleRate, sample_rate_out) &&
- media_format->GetAsInteger(MediaFormat::kSampleBits, sample_bits_out) &&
- mime_type.compare(mime_type::kUncompressedAudio) == 0;
+bool AudioRendererImpl::OnInitialize(const MediaFormat* media_format) {
+ // Parse out audio parameters.
+ int channels;
+ int sample_rate;
+ int sample_bits;
+ if (!ParseMediaFormat(media_format, &channels, &sample_rate, &sample_bits)) {
+ return false;
+ }
+
+ // Create our audio stream.
+ stream_ = AudioManager::GetAudioManager()->MakeAudioStream(
+ AudioManager::AUDIO_PCM_LINEAR, channels, sample_rate, sample_bits);
+ DCHECK(stream_);
+ if (!stream_)
+ return false;
+
+ // Calculate buffer size and open the stream.
+ size_t size = kSamplesPerBuffer * channels * sample_bits / 8;
+ if (!stream_->Open(size)) {
+ stream_->Close();
+ stream_ = NULL;
+ return false;
+ }
+ return true;
}
-void AudioRendererImpl::ScheduleRead() {
- host_->PostTask(NewRunnableMethod(decoder_, &AudioDecoder::Read,
- new AssignableBuffer<AudioRendererImpl, Buffer>(this)));
+void AudioRendererImpl::OnStop() {
+ DCHECK(stream_);
+ stream_->Stop();
}
} // namespace media
diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h
index 0dd4bc0..85ebca0 100644
--- a/media/filters/audio_renderer_impl.h
+++ b/media/filters/audio_renderer_impl.h
@@ -17,12 +17,14 @@
#include "media/base/buffers.h"
#include "media/base/factory.h"
#include "media/base/filters.h"
+#include "media/filters/audio_renderer_base.h"
namespace media {
-class AudioRendererImpl : public AudioRenderer,
+class AudioRendererImpl : public AudioRendererBase,
public AudioOutputStream::AudioSourceCallback {
public:
+ // FilterFactory provider.
static FilterFactory* CreateFilterFactory() {
return new FilterFactoryImpl0<AudioRendererImpl>();
}
@@ -30,53 +32,30 @@ class AudioRendererImpl : public AudioRenderer,
static bool IsMediaFormatSupported(const MediaFormat* media_format);
// MediaFilter implementation.
- virtual void Stop();
virtual void SetPlaybackRate(float playback_rate);
// AudioRenderer implementation.
- virtual bool Initialize(AudioDecoder* decoder);
virtual void SetVolume(float volume);
- // AssignableBuffer<AudioRendererImpl, BufferInterface> implementation.
- void OnAssignment(Buffer* buffer_in);
-
// AudioSourceCallback implementation.
virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, size_t len);
virtual void OnClose(AudioOutputStream* stream);
virtual void OnError(AudioOutputStream* stream, int code);
- private:
+ protected:
+ // Only allow a factory to create this class.
friend class FilterFactoryImpl0<AudioRendererImpl>;
AudioRendererImpl();
virtual ~AudioRendererImpl();
- // Helper to parse a media format and return whether we were successful
- // retrieving all the information we care about.
- static bool ParseMediaFormat(const MediaFormat* media_format,
- int* channels_out, int* sample_rate_out,
- int* sample_bits_out);
-
- // Posts a task on the pipeline thread to read a sample from the decoder.
- // Safe to call on any thread.
- void ScheduleRead();
-
- // Audio decoder.
- AudioDecoder* decoder_;
+ // AudioRendererBase implementation.
+ virtual bool OnInitialize(const MediaFormat* media_format);
+ virtual void OnStop();
+ private:
// Audio output stream device.
AudioOutputStream* stream_;
- // Whether or not we have initialized.
- bool initialized_;
-
- // Queued audio data.
- typedef std::deque<Buffer*> BufferQueue;
- BufferQueue input_queue_;
- Lock input_lock_;
-
- // Holds any remaining audio data that couldn't fit into the callback buffer.
- size_t data_offset_;
-
DISALLOW_COPY_AND_ASSIGN(AudioRendererImpl);
};
diff --git a/media/filters/null_audio_renderer.cc b/media/filters/null_audio_renderer.cc
index a3bd072..f30ed59 100644
--- a/media/filters/null_audio_renderer.cc
+++ b/media/filters/null_audio_renderer.cc
@@ -9,19 +9,17 @@
namespace media {
-// The number of reads to perform during initialization for preroll purposes.
-static const size_t kInitialReads = 16;
-
-// The number of buffers we consume before sleeping. By doing so we can sleep
-// for longer, reducing CPU load and avoiding situations where audio samples
-// are so short that the OS sleeps our thread for too long.
-static const size_t kBuffersPerSleep = 4;
+// How "long" our buffer should be in terms of milliseconds. In OnInitialize
+// we calculate the size of one second of audio data and use this number to
+// allocate a buffer to pass to FillBuffer.
+static const size_t kBufferSizeInMilliseconds = 100;
NullAudioRenderer::NullAudioRenderer()
- : decoder_(NULL),
+ : AudioRendererBase(kDefaultMaxQueueSize),
playback_rate_(0.0f),
+ bytes_per_millisecond_(0),
+ buffer_size_(0),
thread_(NULL),
- initialized_(false),
shutdown_(false) {
}
@@ -32,103 +30,64 @@ NullAudioRenderer::~NullAudioRenderer() {
// static
bool NullAudioRenderer::IsMediaFormatSupported(
const MediaFormat* media_format) {
- DCHECK(media_format);
- std::string mime_type;
- return media_format->GetAsString(MediaFormat::kMimeType, &mime_type) &&
- mime_type.compare(mime_type::kUncompressedAudio) == 0;
-}
-
-void NullAudioRenderer::Stop() {
- shutdown_ = true;
- if (thread_)
- PlatformThread::Join(thread_);
+ int channels;
+ int sample_rate;
+ int sample_bits;
+ return ParseMediaFormat(media_format, &channels, &sample_rate, &sample_bits);
}
void NullAudioRenderer::SetPlaybackRate(float playback_rate) {
playback_rate_ = playback_rate;
}
-bool NullAudioRenderer::Initialize(AudioDecoder* decoder) {
- DCHECK(decoder);
- decoder_ = decoder;
-
- // It's safe to start the thread now because it simply sleeps when playback
- // rate is 0.0f.
- if (!PlatformThread::Create(0, this, &thread_))
- return false;
-
- // Schedule our initial reads.
- for (size_t i = 0; i < kInitialReads; ++i) {
- ScheduleRead();
- }
-
- // Defer initialization until all scheduled reads have completed.
- return true;
-}
-
void NullAudioRenderer::SetVolume(float volume) {
// Do nothing.
}
-void NullAudioRenderer::OnAssignment(Buffer* buffer_in) {
- bool initialized = false;
- {
- AutoLock auto_lock(input_lock_);
- buffer_in->AddRef();
- input_queue_.push_back(buffer_in);
- DCHECK(input_queue_.size() <= kInitialReads);
-
- // See if we're finally initialized.
- // TODO(scherkus): handle end of stream.
- initialized = !initialized_ && input_queue_.size() == kInitialReads;
- }
-
- if (initialized) {
- initialized_ = true;
- host_->InitializationComplete();
- }
-}
-
void NullAudioRenderer::ThreadMain() {
// Loop until we're signaled to stop.
while (!shutdown_) {
- base::TimeDelta timestamp;
- base::TimeDelta duration;
- int sleep_ms = 0;
- int released_buffers = 0;
+ float sleep_in_milliseconds = 0.0f;
// Only consume buffers when actually playing.
- if (playback_rate_ > 0.0f) {
- AutoLock auto_lock(input_lock_);
- for (size_t i = 0; i < kBuffersPerSleep && !input_queue_.empty(); ++i) {
- scoped_refptr<Buffer> buffer = input_queue_.front();
- input_queue_.pop_front();
- buffer->Release();
- timestamp = buffer->GetTimestamp();
- duration += buffer->GetDuration();
- ++released_buffers;
- }
- // Apply the playback rate to our sleep duration.
- sleep_ms =
- static_cast<int>(floor(duration.InMillisecondsF() / playback_rate_));
- }
+ if (playback_rate_ > 0.0f) {
+ size_t bytes = FillBuffer(buffer_.get(), buffer_size_);
- // Schedule reads for every released buffer to maintain "playback".
- for (int i = 0; i < released_buffers; ++i) {
- ScheduleRead();
+ // Calculate our sleep duration, taking playback rate into consideration.
+ sleep_in_milliseconds =
+ floor(bytes / static_cast<float>(bytes_per_millisecond_));
+ sleep_in_milliseconds /= playback_rate_;
}
- // Sleep and update the clock when we wake up.
- PlatformThread::Sleep(sleep_ms);
- if (timestamp.InMicroseconds() > 0) {
- host_->SetTime(timestamp);
- }
+ PlatformThread::Sleep(static_cast<int>(sleep_in_milliseconds));
+ }
+}
+
+bool NullAudioRenderer::OnInitialize(const MediaFormat* media_format) {
+ // Parse out audio parameters.
+ int channels;
+ int sample_rate;
+ int sample_bits;
+ if (!ParseMediaFormat(media_format, &channels, &sample_rate, &sample_bits)) {
+ return false;
}
+
+ // Calculate our bytes per millisecond value and allocate our buffer.
+ bytes_per_millisecond_ = (channels * sample_rate * sample_bits / 8)
+ / base::Time::kMillisecondsPerSecond;
+ buffer_size_ = bytes_per_millisecond_ * kBufferSizeInMilliseconds;
+ buffer_.reset(new uint8[buffer_size_]);
+ DCHECK(buffer_.get());
+
+ // It's safe to start the thread now because it simply sleeps when playback
+ // rate is 0.0f.
+ return PlatformThread::Create(0, this, &thread_);
}
-void NullAudioRenderer::ScheduleRead() {
- host_->PostTask(NewRunnableMethod(decoder_, &AudioDecoder::Read,
- new AssignableBuffer<NullAudioRenderer, Buffer>(this)));
+void NullAudioRenderer::OnStop() {
+ shutdown_ = true;
+ if (thread_)
+ PlatformThread::Join(thread_);
}
} // namespace media
diff --git a/media/filters/null_audio_renderer.h b/media/filters/null_audio_renderer.h
index 821e059..2b6c092 100644
--- a/media/filters/null_audio_renderer.h
+++ b/media/filters/null_audio_renderer.h
@@ -24,11 +24,13 @@
#include "media/base/buffers.h"
#include "media/base/factory.h"
#include "media/base/filters.h"
+#include "media/filters/audio_renderer_base.h"
namespace media {
-class NullAudioRenderer : public AudioRenderer, PlatformThread::Delegate {
+class NullAudioRenderer : public AudioRendererBase, PlatformThread::Delegate {
public:
+ // FilterFactory provider.
static FilterFactory* CreateFilterFactory() {
return new FilterFactoryImpl0<NullAudioRenderer>();
}
@@ -37,44 +39,41 @@ class NullAudioRenderer : public AudioRenderer, PlatformThread::Delegate {
static bool IsMediaFormatSupported(const MediaFormat* media_format);
// MediaFilter implementation.
- virtual void Stop();
virtual void SetPlaybackRate(float playback_rate);
// AudioRenderer implementation.
- virtual bool Initialize(AudioDecoder* decoder);
virtual void SetVolume(float volume);
- // AssignableBuffer<NullAudioRenderer, BufferInterface> implementation.
- void OnAssignment(Buffer* buffer_in);
-
// PlatformThread::Delegate implementation.
virtual void ThreadMain();
- private:
+ protected:
+ // Only allow a factory to create this class.
friend class FilterFactoryImpl0<NullAudioRenderer>;
NullAudioRenderer();
virtual ~NullAudioRenderer();
- // Posts a task on the pipeline thread to read a sample from the decoder.
- // Safe to call on any thread.
- void ScheduleRead();
-
- // Audio decoder.
- AudioDecoder* decoder_;
+ // AudioRendererBase implementation.
+ virtual bool OnInitialize(const MediaFormat* media_format);
+ virtual void OnStop();
+ private:
// Current playback rate.
float playback_rate_;
- // Queued audio data.
- typedef std::deque<Buffer*> BufferQueue;
- BufferQueue input_queue_;
- Lock input_lock_;
+ // A number to convert bytes written in FillBuffer to milliseconds based on
+ // the audio format. Calculated in OnInitialize by looking at the decoder's
+ // MediaFormat.
+ size_t bytes_per_millisecond_;
+
+ // A buffer passed to FillBuffer to advance playback.
+ scoped_ptr<uint8> buffer_;
+ size_t buffer_size_;
- // Seperate thread used to throw away data.
+ // Separate thread used to throw away data.
PlatformThreadHandle thread_;
- // Various state flags.
- bool initialized_;
+ // Shutdown flag.
bool shutdown_;
DISALLOW_COPY_AND_ASSIGN(NullAudioRenderer);
diff --git a/media/media_lib.scons b/media/media_lib.scons
index b8eac68..a7a89fa 100644
--- a/media/media_lib.scons
+++ b/media/media_lib.scons
@@ -42,6 +42,8 @@ input_files = ChromeFileList([
'base/synchronizer.h',
]),
MSVSFilter('filters', [
+ 'filters/audio_renderer_base.cc',
+ 'filters/audio_renderer_base.h',
'filters/audio_renderer_impl.cc',
'filters/audio_renderer_impl.h',
'filters/file_data_source.cc',