diff options
-rw-r--r-- | media/build/media.vcproj | 8 | ||||
-rw-r--r-- | media/filters/null_audio_renderer.cc | 132 | ||||
-rw-r--r-- | media/filters/null_audio_renderer.h | 85 | ||||
-rw-r--r-- | media/media_lib.scons | 2 |
4 files changed, 227 insertions, 0 deletions
diff --git a/media/build/media.vcproj b/media/build/media.vcproj index 8f9ce19..5b1dff7 100644 --- a/media/build/media.vcproj +++ b/media/build/media.vcproj @@ -204,6 +204,14 @@ RelativePath="..\filters\file_data_source.h" > </File> + <File + RelativePath="..\filters\null_audio_renderer.cc" + > + </File> + <File + RelativePath="..\filters\null_audio_renderer.h" + > + </File> </Filter> <Filter Name="audio" diff --git a/media/filters/null_audio_renderer.cc b/media/filters/null_audio_renderer.cc new file mode 100644 index 0000000..f136991 --- /dev/null +++ b/media/filters/null_audio_renderer.cc @@ -0,0 +1,132 @@ +// 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/null_audio_renderer.h" + +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; + +NullAudioRenderer::NullAudioRenderer() + : decoder_(NULL), + playback_rate_(0.0f), + thread_(NULL), + initialized_(false), + shutdown_(false) { +} + +NullAudioRenderer::~NullAudioRenderer() { + Stop(); +} + +// 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_); +} + +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; + + // 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_)); + } + + // Schedule reads for every released buffer to maintain "playback". + for (int i = 0; i < released_buffers; ++i) { + ScheduleRead(); + } + + // Sleep and update the clock when we wake up. + PlatformThread::Sleep(sleep_ms); + if (timestamp.InMicroseconds() > 0) { + host_->SetTime(timestamp); + } + } +} + +void NullAudioRenderer::ScheduleRead() { + host_->PostTask(NewRunnableMethod(decoder_, &AudioDecoder::Read, + new AssignableBuffer<NullAudioRenderer, Buffer>(this))); +} + +} // namespace media diff --git a/media/filters/null_audio_renderer.h b/media/filters/null_audio_renderer.h new file mode 100644 index 0000000..821e059 --- /dev/null +++ b/media/filters/null_audio_renderer.h @@ -0,0 +1,85 @@ +// 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. + +#ifndef MEDIA_FILTERS_NULL_AUDIO_RENDERER_H_ +#define MEDIA_FILTERS_NULL_AUDIO_RENDERER_H_ + +// NullAudioRenderer effectively uses an extra thread to "throw away" the +// audio data at a rate resembling normal playback speed. It's just like +// decoding to /dev/null! +// +// NullAudioRenderer can also be used in situations where the client has no +// audio device or we haven't written an audio implementation for a particular +// platform yet. +// +// It supports any type of MediaFormat as long as the mime type has been set to +// audio/x-uncompressed. Playback rate is also supported and NullAudioRenderer +// will slow down and speed up accordingly. + +#include <deque> + +#include "base/lock.h" +#include "base/platform_thread.h" +#include "media/base/buffers.h" +#include "media/base/factory.h" +#include "media/base/filters.h" + +namespace media { + +class NullAudioRenderer : public AudioRenderer, PlatformThread::Delegate { + public: + static FilterFactory* CreateFilterFactory() { + return new FilterFactoryImpl0<NullAudioRenderer>(); + } + + // Compatible with any audio/x-uncompressed MediaFormat. + 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: + 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_; + + // Current playback rate. + float playback_rate_; + + // Queued audio data. + typedef std::deque<Buffer*> BufferQueue; + BufferQueue input_queue_; + Lock input_lock_; + + // Seperate thread used to throw away data. + PlatformThreadHandle thread_; + + // Various state flags. + bool initialized_; + bool shutdown_; + + DISALLOW_COPY_AND_ASSIGN(NullAudioRenderer); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_NULL_AUDIO_RENDERER_H_ diff --git a/media/media_lib.scons b/media/media_lib.scons index 73badec..1f88e35 100644 --- a/media/media_lib.scons +++ b/media/media_lib.scons @@ -46,6 +46,8 @@ input_files = ChromeFileList([ 'filters/audio_renderer_impl.h', 'filters/file_data_source.cc', 'filters/file_data_source.h', + 'filters/null_audio_renderer.cc', + 'filters/null_audio_renderer.h', ]), MSVSFilter('audio', [ 'audio/win/audio_manager_win.h', |