diff options
author | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-09 18:27:52 +0000 |
---|---|---|
committer | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-09 18:27:52 +0000 |
commit | 9b9871e8aefed53147b511d1494b584c1b2bef50 (patch) | |
tree | 025b114736caa36ecc36cf596a75cda8cac6d501 /content | |
parent | e5d7b8399240c3bc5fc0fd9d7a65158bbd0d2c6a (diff) | |
download | chromium_src-9b9871e8aefed53147b511d1494b584c1b2bef50.zip chromium_src-9b9871e8aefed53147b511d1494b584c1b2bef50.tar.gz chromium_src-9b9871e8aefed53147b511d1494b584c1b2bef50.tar.bz2 |
Simplify AudioRendererImpl by using AudioDevice.
This helps us move closer to being able to do renderer-side mixing, for
improved performance.
See original CL for discussion: http://codereview.chromium.org/8477037/
BUG=none
TEST=audio_renderer_impl_unittest
(also verified that the media layout tests all pass, and did manual
testing with several audio files and YouTube videos)
Review URL: http://codereview.chromium.org/8785008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@113821 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-rw-r--r-- | content/renderer/media/audio_device.cc | 166 | ||||
-rw-r--r-- | content/renderer/media/audio_device.h | 77 | ||||
-rw-r--r-- | content/renderer/media/audio_renderer_impl.cc | 502 | ||||
-rw-r--r-- | content/renderer/media/audio_renderer_impl.h | 152 | ||||
-rw-r--r-- | content/renderer/media/audio_renderer_impl_unittest.cc | 155 |
5 files changed, 337 insertions, 715 deletions
diff --git a/content/renderer/media/audio_device.cc b/content/renderer/media/audio_device.cc index a1e3991..03ff1be 100644 --- a/content/renderer/media/audio_device.cc +++ b/content/renderer/media/audio_device.cc @@ -12,27 +12,75 @@ #include "content/common/media/audio_messages.h" #include "content/common/view_messages.h" #include "content/renderer/render_thread_impl.h" +#include "media/audio/audio_output_controller.h" #include "media/audio/audio_util.h" +AudioDevice::AudioDevice() + : buffer_size_(0), + channels_(0), + bits_per_sample_(16), + sample_rate_(0), + latency_format_(AudioParameters::AUDIO_PCM_LOW_LATENCY), + callback_(0), + is_initialized_(false), + audio_delay_milliseconds_(0), + volume_(1.0), + stream_id_(0), + play_on_start_(true), + is_started_(false), + memory_length_(0) { + filter_ = RenderThreadImpl::current()->audio_message_filter(); +} + AudioDevice::AudioDevice(size_t buffer_size, int channels, double sample_rate, RenderCallback* callback) - : buffer_size_(buffer_size), - channels_(channels), - bits_per_sample_(16), - sample_rate_(sample_rate), - callback_(callback), + : bits_per_sample_(16), + is_initialized_(false), audio_delay_milliseconds_(0), volume_(1.0), stream_id_(0), + play_on_start_(true), + is_started_(false), memory_length_(0) { filter_ = RenderThreadImpl::current()->audio_message_filter(); + Initialize(buffer_size, + channels, + sample_rate, + AudioParameters::AUDIO_PCM_LOW_LATENCY, + callback); +} + +void AudioDevice::Initialize(size_t buffer_size, + int channels, + double sample_rate, + AudioParameters::Format latency_format, + RenderCallback* callback) { + CHECK_EQ(0, stream_id_) << + "AudioDevice::Initialize() must be called before Start()"; + + buffer_size_ = buffer_size; + channels_ = channels; + sample_rate_ = sample_rate; + latency_format_ = latency_format; + callback_ = callback; + + // Cleanup from any previous initialization. + for (size_t i = 0; i < audio_data_.size(); ++i) + delete [] audio_data_[i]; + audio_data_.reserve(channels); for (int i = 0; i < channels; ++i) { float* channel_data = new float[buffer_size]; audio_data_.push_back(channel_data); } + + is_initialized_ = true; +} + +bool AudioDevice::IsInitialized() { + return is_initialized_; } AudioDevice::~AudioDevice() { @@ -45,7 +93,7 @@ AudioDevice::~AudioDevice() { void AudioDevice::Start() { AudioParameters params; - params.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; + params.format = latency_format_; params.channels = channels_; params.sample_rate = static_cast<int>(sample_rate_); params.bits_per_sample = bits_per_sample_; @@ -75,16 +123,19 @@ void AudioDevice::Stop() { if (!completion.TimedWait(kMaxTimeOut)) { LOG(ERROR) << "Failed to shut down audio output on IO thread"; } + ShutDownAudioThread(); +} - if (audio_thread_.get()) { - // Close the socket handler to terminate the main thread function in the - // audio thread. - { - base::SyncSocket socket(socket_handle_); - } - audio_thread_->Join(); - audio_thread_.reset(NULL); - } +void AudioDevice::Play() { + ChildProcess::current()->io_message_loop()->PostTask( + FROM_HERE, + base::Bind(&AudioDevice::PlayOnIOThread, this)); +} + +void AudioDevice::Pause(bool flush) { + ChildProcess::current()->io_message_loop()->PostTask( + FROM_HERE, + base::Bind(&AudioDevice::PauseOnIOThread, this, flush)); } bool AudioDevice::SetVolume(double volume) { @@ -106,7 +157,8 @@ void AudioDevice::GetVolume(double* volume) { } void AudioDevice::InitializeOnIOThread(const AudioParameters& params) { - // Make sure we don't call Start() more than once. + DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + // Make sure we don't create the stream more than once. DCHECK_EQ(0, stream_id_); if (stream_id_) return; @@ -115,15 +167,35 @@ void AudioDevice::InitializeOnIOThread(const AudioParameters& params) { Send(new AudioHostMsg_CreateStream(stream_id_, params, true)); } -void AudioDevice::StartOnIOThread() { - if (stream_id_) +void AudioDevice::PlayOnIOThread() { + DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + if (stream_id_ && is_started_) Send(new AudioHostMsg_PlayStream(stream_id_)); + else + play_on_start_ = true; +} + +void AudioDevice::PauseOnIOThread(bool flush) { + DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + if (stream_id_ && is_started_) { + Send(new AudioHostMsg_PauseStream(stream_id_)); + if (flush) + Send(new AudioHostMsg_FlushStream(stream_id_)); + } else { + // Note that |flush| isn't relevant here since this is the case where + // the stream is first starting. + play_on_start_ = false; + } } void AudioDevice::ShutDownOnIOThread(base::WaitableEvent* completion) { + DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + is_started_ = false; + // Make sure we don't call shutdown more than once. if (!stream_id_) { - completion->Signal(); + if (completion) + completion->Signal(); return; } @@ -131,30 +203,29 @@ void AudioDevice::ShutDownOnIOThread(base::WaitableEvent* completion) { Send(new AudioHostMsg_CloseStream(stream_id_)); stream_id_ = 0; - completion->Signal(); + if (completion) + completion->Signal(); } void AudioDevice::SetVolumeOnIOThread(double volume) { + DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); if (stream_id_) Send(new AudioHostMsg_SetVolume(stream_id_, volume)); } void AudioDevice::OnRequestPacket(AudioBuffersState buffers_state) { // This method does not apply to the low-latency system. - NOTIMPLEMENTED(); } void AudioDevice::OnStateChanged(AudioStreamState state) { if (state == kAudioStreamError) { DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)"; } - NOTIMPLEMENTED(); } void AudioDevice::OnCreated( base::SharedMemoryHandle handle, uint32 length) { // Not needed in this simple implementation. - NOTIMPLEMENTED(); } void AudioDevice::OnLowLatencyCreated( @@ -186,14 +257,21 @@ void AudioDevice::OnLowLatencyCreated( DCHECK_GE(length, buffer_size_ * sizeof(int16) * channels_); socket_handle_ = socket_handle; + { + // Synchronize with ShutDownAudioThread(). + base::AutoLock auto_lock(lock_); + + DCHECK(!audio_thread_.get()); + audio_thread_.reset( + new base::DelegateSimpleThread(this, "renderer_audio_thread")); + audio_thread_->Start(); + } - audio_thread_.reset( - new base::DelegateSimpleThread(this, "renderer_audio_thread")); - audio_thread_->Start(); - - MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&AudioDevice::StartOnIOThread, this)); + // We handle the case where Play() and/or Pause() may have been called + // multiple times before OnLowLatencyCreated() gets called. + is_started_ = true; + if (play_on_start_) + PlayOnIOThread(); } void AudioDevice::OnVolume(double volume) { @@ -219,9 +297,14 @@ void AudioDevice::Run() { const int samples_per_ms = static_cast<int>(sample_rate_) / 1000; const int bytes_per_ms = channels_ * (bits_per_sample_ / 8) * samples_per_ms; - while ((sizeof(pending_data) == socket.Receive(&pending_data, - sizeof(pending_data))) && - (pending_data >= 0)) { + while (sizeof(pending_data) == + socket.Receive(&pending_data, sizeof(pending_data))) { + if (pending_data == media::AudioOutputController::kPauseMark) { + memset(shared_memory.memory(), 0, memory_length_); + continue; + } else if (pending_data < 0) { + break; + } // Convert the number of pending bytes in the render buffer // into milliseconds. audio_delay_milliseconds_ = pending_data / bytes_per_ms; @@ -237,8 +320,25 @@ void AudioDevice::FireRenderCallback(int16* data) { callback_->Render(audio_data_, buffer_size_, audio_delay_milliseconds_); // Interleave, scale, and clip to int16. + // TODO(crogers): avoid converting to integer here, and pass the data + // to the browser process as float, so we don't lose precision for + // audio hardware which has better than 16bit precision. media::InterleaveFloatToInt16(audio_data_, data, buffer_size_); } } + +void AudioDevice::ShutDownAudioThread() { + // Synchronize with OnLowLatencyCreated(). + base::AutoLock auto_lock(lock_); + if (audio_thread_.get()) { + // Close the socket handler to terminate the main thread function in the + // audio thread. + { + base::SyncSocket socket(socket_handle_); + } + audio_thread_->Join(); + audio_thread_.reset(NULL); + } +} diff --git a/content/renderer/media/audio_device.h b/content/renderer/media/audio_device.h index 22c69dd..30930b3 100644 --- a/content/renderer/media/audio_device.h +++ b/content/renderer/media/audio_device.h @@ -25,21 +25,29 @@ // // Start -> InitializeOnIOThread ------> AudioHostMsg_CreateStream --------> // <- OnLowLatencyCreated <- AudioMsg_NotifyLowLatencyStreamCreated <- -// ---> StartOnIOThread -----------> AudioHostMsg_PlayStream --------> +// ---> PlayOnIOThread -----------> AudioHostMsg_PlayStream --------> +// +// Optionally Play() / Pause() sequences may occur: +// Play -> PlayOnIOThread --------------> AudioHostMsg_PlayStream ---------> +// Pause -> PauseOnIOThread ------------> AudioHostMsg_PauseStream --------> +// (note that Play() / Pause() sequences before OnLowLatencyCreated are +// deferred until OnLowLatencyCreated, with the last valid state being used) // // AudioDevice::Render => audio transport on audio thread with low latency => // | // Stop --> ShutDownOnIOThread --------> AudioHostMsg_CloseStream -> Close // -// This class utilizes three threads during its lifetime, namely: +// This class utilizes several threads during its lifetime, namely: // 1. Creating thread. -// Must be the main render thread. Start and Stop should be called on -// this thread. -// 2. IO thread. +// Must be the main render thread. +// 2. Control thread (may be the main render thread or another thread). +// The methods: Start(), Stop(), Play(), Pause(), SetVolume() +// must be called on the same thread. +// 3. IO thread (internal implementation detail - not exposed to public API) // The thread within which this class receives all the IPC messages and // IPC communications can only happen in this thread. -// 3. Audio transport thread. -// Responsible for calling the RenderCallback and feed audio samples to +// 4. Audio transport thread. +// Responsible for calling the RenderCallback and feeding audio samples to // the audio layer in the browser process using sync sockets and shared // memory. // @@ -47,6 +55,8 @@ // // - Start() is asynchronous/non-blocking. // - Stop() is synchronous/blocking. +// - Play() is asynchronous/non-blocking. +// - Pause() is asynchronous/non-blocking. // - The user must call Stop() before deleting the class instance. #ifndef CONTENT_RENDERER_MEDIA_AUDIO_DEVICE_H_ @@ -58,11 +68,11 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/shared_memory.h" +#include "base/synchronization/lock.h" #include "base/threading/simple_thread.h" #include "content/common/content_export.h" #include "content/renderer/media/audio_message_filter.h" - -struct AudioParameters; +#include "media/audio/audio_parameters.h" class CONTENT_EXPORT AudioDevice : public AudioMessageFilter::Delegate, @@ -79,18 +89,42 @@ class CONTENT_EXPORT AudioDevice }; // Methods called on main render thread ------------------------------------- + + // Minimal constructor where Initialize() must be called later. + AudioDevice(); + AudioDevice(size_t buffer_size, int channels, double sample_rate, RenderCallback* callback); virtual ~AudioDevice(); + void Initialize(size_t buffer_size, + int channels, + double sample_rate, + AudioParameters::Format latency_format, + RenderCallback* callback); + bool IsInitialized(); + // Starts audio playback. void Start(); // Stops audio playback. void Stop(); + // Resumes playback if currently paused. + // TODO(crogers): it should be possible to remove the extra complexity + // of Play() and Pause() with additional re-factoring work in + // AudioRendererImpl. + void Play(); + + // Pauses playback. + // If |flush| is true then any pending audio that is in the pipeline + // (has not yet reached the hardware) will be discarded. In this case, + // when Play() is later called, no previous pending audio will be + // rendered. + void Pause(bool flush); + // Sets the playback volume, with range [0.0, 1.0] inclusive. // Returns |true| on success. bool SetVolume(double volume); @@ -118,7 +152,8 @@ class CONTENT_EXPORT AudioDevice // be executed on that thread. They interact with AudioMessageFilter and // sends IPC messages on that thread. void InitializeOnIOThread(const AudioParameters& params); - void StartOnIOThread(); + void PlayOnIOThread(); + void PauseOnIOThread(bool flush); void ShutDownOnIOThread(base::WaitableEvent* completion); void SetVolumeOnIOThread(double volume); @@ -132,17 +167,24 @@ class CONTENT_EXPORT AudioDevice // DelegateSimpleThread::Delegate implementation. virtual void Run() OVERRIDE; + // Closes socket and joins with the audio thread. + void ShutDownAudioThread(); + // Format size_t buffer_size_; // in sample-frames int channels_; int bits_per_sample_; double sample_rate_; + AudioParameters::Format latency_format_; RenderCallback* callback_; // The client callback renders audio into here. std::vector<float*> audio_data_; + // Set to |true| once Initialize() has been called. + bool is_initialized_; + // The client stores the last reported audio delay in this member. // The delay shall reflect the amount of audio which still resides in // the output buffer, i.e., the expected audio output delay. @@ -160,13 +202,26 @@ class CONTENT_EXPORT AudioDevice // Our stream ID on the message filter. Only accessed on the IO thread. int32 stream_id_; + // State of Play() / Pause() calls before OnLowLatencyCreated() is called. + bool play_on_start_; + + // Set to |true| when OnLowLatencyCreated() is called. + // Set to |false| when ShutDownOnIOThread() is called. + // This is for use with play_on_start_ to track Play() / Pause() state. + bool is_started_; + // Data transfer between browser and render process uses a combination // of sync sockets and shared memory to provide lowest possible latency. base::SharedMemoryHandle shared_memory_handle_; base::SyncSocket::Handle socket_handle_; int memory_length_; - DISALLOW_IMPLICIT_CONSTRUCTORS(AudioDevice); + // Protects lifetime of: + // socket_handle_ + // audio_thread_ + base::Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(AudioDevice); }; #endif // CONTENT_RENDERER_MEDIA_AUDIO_DEVICE_H_ diff --git a/content/renderer/media/audio_renderer_impl.cc b/content/renderer/media/audio_renderer_impl.cc index 3e617f5..768142ad 100644 --- a/content/renderer/media/audio_renderer_impl.cc +++ b/content/renderer/media/audio_renderer_impl.cc @@ -9,61 +9,47 @@ #include <algorithm> #include "base/bind.h" -#include "base/command_line.h" #include "content/common/child_process.h" #include "content/common/media/audio_messages.h" -#include "content/public/common/content_switches.h" #include "content/renderer/render_thread_impl.h" #include "media/audio/audio_buffers_state.h" -#include "media/audio/audio_output_controller.h" #include "media/audio/audio_util.h" -#include "media/base/filter_host.h" -// Static variable that says what code path we are using -- low or high -// latency. Made separate variable so we don't have to go to command line -// for every DCHECK(). -AudioRendererImpl::LatencyType AudioRendererImpl::latency_type_ = - AudioRendererImpl::kUninitializedLatency; +// We define GetBufferSizeForSampleRate() instead of using +// GetAudioHardwareBufferSize() in audio_util because we're using +// the AUDIO_PCM_LINEAR flag, instead of AUDIO_PCM_LOW_LATENCY, +// which the audio_util functions assume. +// +// See: http://code.google.com/p/chromium/issues/detail?id=103627 +// for a more detailed description of the subtleties. +static size_t GetBufferSizeForSampleRate(int sample_rate) { + // kNominalBufferSize has been tested on Windows, Mac OS X, and Linux + // using the low-latency audio codepath (SyncSocket implementation) + // with the AUDIO_PCM_LINEAR flag. + const size_t kNominalBufferSize = 2048; + + if (sample_rate <= 48000) + return kNominalBufferSize; + else if (sample_rate <= 96000) + return kNominalBufferSize * 2; + return kNominalBufferSize * 4; +} AudioRendererImpl::AudioRendererImpl() : AudioRendererBase(), bytes_per_second_(0), - stream_created_(false), - stream_id_(0), - shared_memory_(NULL), - shared_memory_size_(0), - stopped_(false), - pending_request_(false) { - filter_ = RenderThreadImpl::current()->audio_message_filter(); - // Figure out if we are planning to use high or low latency code path. - // We are initializing only one variable and double initialization is Ok, - // so there would not be any issues caused by CPU memory model. - if (latency_type_ == kUninitializedLatency) { - // Urgent workaround for - // http://code.google.com/p/chromium-os/issues/detail?id=21491 - // TODO(enal): Fix it properly. -#if defined(OS_CHROMEOS) - latency_type_ = kHighLatency; -#else - if (!CommandLine::ForCurrentProcess()->HasSwitch( - switches::kHighLatencyAudio)) { - latency_type_ = kLowLatency; - } else { - latency_type_ = kHighLatency; - } -#endif - } + stopped_(false) { + // We create the AudioDevice here because it must be created in the + // main thread. But we don't yet know the audio format (sample-rate, etc.) + // at this point. Later, when OnInitialize() is called, we have + // the audio format information and call the AudioDevice::Initialize() + // method to fully initialize it. + audio_device_ = new AudioDevice(); } AudioRendererImpl::~AudioRendererImpl() { } -// static -void AudioRendererImpl::set_latency_type(LatencyType latency_type) { - DCHECK_EQ(kUninitializedLatency, latency_type_); - latency_type_ = latency_type; -} - base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { if (bytes_per_second_) { return base::TimeDelta::FromMicroseconds( @@ -92,69 +78,47 @@ void AudioRendererImpl::UpdateEarliestEndTime(int bytes_filled, bool AudioRendererImpl::OnInitialize(int bits_per_channel, ChannelLayout channel_layout, int sample_rate) { - AudioParameters params(AudioParameters::AUDIO_PCM_LINEAR, channel_layout, - sample_rate, bits_per_channel, 0); - - bytes_per_second_ = params.GetBytesPerSecond(); + // We use the AUDIO_PCM_LINEAR flag because AUDIO_PCM_LOW_LATENCY + // does not currently support all the sample-rates that we require. + // Please see: http://code.google.com/p/chromium/issues/detail?id=103627 + // for more details. + audio_parameters_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, + channel_layout, + sample_rate, + bits_per_channel, + 0); - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::CreateStreamTask, this, params)); - return true; -} + bytes_per_second_ = audio_parameters_.GetBytesPerSecond(); -void AudioRendererImpl::OnStop() { - // Since joining with the audio thread can acquire lock_, we make sure to - // Join() with it not under lock. - base::DelegateSimpleThread* audio_thread = NULL; - { - base::AutoLock auto_lock(lock_); - if (stopped_) - return; - stopped_ = true; - - DCHECK_EQ(!audio_thread_.get(), !socket_.get()); - if (socket_.get()) - socket_->Close(); - if (audio_thread_.get()) - audio_thread = audio_thread_.get(); - - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::DestroyTask, this)); - } + DCHECK(audio_device_.get()); - if (audio_thread) - audio_thread->Join(); -} + if (!audio_device_->IsInitialized()) { + audio_device_->Initialize( + GetBufferSizeForSampleRate(sample_rate), + audio_parameters_.channels, + audio_parameters_.sample_rate, + audio_parameters_.format, + this); -void AudioRendererImpl::NotifyDataAvailableIfNecessary() { - if (latency_type_ == kHighLatency) { - // Post a task to render thread to notify a packet reception. - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::NotifyPacketReadyTask, this)); + audio_device_->Start(); } + + return true; } -void AudioRendererImpl::ConsumeAudioSamples( - scoped_refptr<media::Buffer> buffer_in) { - base::AutoLock auto_lock(lock_); +void AudioRendererImpl::OnStop() { if (stopped_) return; - // TODO(hclam): handle end of stream here. + DCHECK(audio_device_.get()); + audio_device_->Stop(); - // Use the base class to queue the buffer. - AudioRendererBase::ConsumeAudioSamples(buffer_in); - - NotifyDataAvailableIfNecessary(); + stopped_ = true; } void AudioRendererImpl::SetPlaybackRate(float rate) { DCHECK_LE(0.0f, rate); - base::AutoLock auto_lock(lock_); // Handle the case where we stopped due to IO message loop dying. if (stopped_) { AudioRendererBase::SetPlaybackRate(rate); @@ -165,363 +129,121 @@ void AudioRendererImpl::SetPlaybackRate(float rate) { // Play: GetPlaybackRate() == 0.0 && rate != 0.0 // Pause: GetPlaybackRate() != 0.0 && rate == 0.0 if (GetPlaybackRate() == 0.0f && rate != 0.0f) { - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::PlayTask, this)); + DoPlay(); } else if (GetPlaybackRate() != 0.0f && rate == 0.0f) { // Pause is easy, we can always pause. - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::PauseTask, this)); + DoPause(); } AudioRendererBase::SetPlaybackRate(rate); - - // If we are playing, give a kick to try fulfilling the packet request as - // the previous packet request may be stalled by a pause. - if (rate > 0.0f) { - NotifyDataAvailableIfNecessary(); - } } void AudioRendererImpl::Pause(const base::Closure& callback) { AudioRendererBase::Pause(callback); - base::AutoLock auto_lock(lock_); if (stopped_) return; - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::PauseTask, this)); + DoPause(); } void AudioRendererImpl::Seek(base::TimeDelta time, const media::FilterStatusCB& cb) { AudioRendererBase::Seek(time, cb); - base::AutoLock auto_lock(lock_); if (stopped_) return; - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::SeekTask, this)); + DoSeek(); } - void AudioRendererImpl::Play(const base::Closure& callback) { AudioRendererBase::Play(callback); - base::AutoLock auto_lock(lock_); if (stopped_) return; if (GetPlaybackRate() != 0.0f) { - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::PlayTask, this)); + DoPlay(); } else { - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::PauseTask, this)); + DoPause(); } } void AudioRendererImpl::SetVolume(float volume) { - base::AutoLock auto_lock(lock_); if (stopped_) return; - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioRendererImpl::SetVolumeTask, this, volume)); + DCHECK(audio_device_.get()); + audio_device_->SetVolume(volume); } -void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle, - uint32 length) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - DCHECK_EQ(kHighLatency, latency_type_); - - base::AutoLock auto_lock(lock_); - if (stopped_) - return; - - shared_memory_.reset(new base::SharedMemory(handle, false)); - shared_memory_->Map(length); - shared_memory_size_ = length; -} - -void AudioRendererImpl::CreateSocket(base::SyncSocket::Handle socket_handle) { - DCHECK_EQ(kLowLatency, latency_type_); -#if defined(OS_WIN) - DCHECK(socket_handle); -#else - DCHECK_GE(socket_handle, 0); -#endif - socket_.reset(new base::SyncSocket(socket_handle)); -} - -void AudioRendererImpl::CreateAudioThread() { - DCHECK_EQ(kLowLatency, latency_type_); - audio_thread_.reset( - new base::DelegateSimpleThread(this, "renderer_audio_thread")); - audio_thread_->Start(); -} - -void AudioRendererImpl::OnLowLatencyCreated( - base::SharedMemoryHandle handle, - base::SyncSocket::Handle socket_handle, - uint32 length) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - DCHECK_EQ(kLowLatency, latency_type_); -#if defined(OS_WIN) - DCHECK(handle); -#else - DCHECK_GE(handle.fd, 0); -#endif - DCHECK_NE(0u, length); - - base::AutoLock auto_lock(lock_); - if (stopped_) - return; - - shared_memory_.reset(new base::SharedMemory(handle, false)); - shared_memory_->Map(media::TotalSharedMemorySizeInBytes(length)); - shared_memory_size_ = length; - - CreateSocket(socket_handle); - CreateAudioThread(); -} - -void AudioRendererImpl::OnRequestPacket(AudioBuffersState buffers_state) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - DCHECK_EQ(kHighLatency, latency_type_); - { - base::AutoLock auto_lock(lock_); - DCHECK(!pending_request_); - pending_request_ = true; - request_buffers_state_ = buffers_state; - } - - // Try to fill in the fulfill the packet request. - NotifyPacketReadyTask(); -} - -void AudioRendererImpl::OnStateChanged(AudioStreamState state) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - - base::AutoLock auto_lock(lock_); - if (stopped_) - return; - - switch (state) { - case kAudioStreamError: - // We receive this error if we counter an hardware error on the browser - // side. We can proceed with ignoring the audio stream. - // TODO(hclam): We need more handling of these kind of error. For example - // re-try creating the audio output stream on the browser side or fail - // nicely and report to demuxer that the whole audio stream is discarded. - host()->DisableAudioRenderer(); - break; - // TODO(hclam): handle these events. - case kAudioStreamPlaying: - case kAudioStreamPaused: - break; - default: - NOTREACHED(); - break; - } -} - -void AudioRendererImpl::OnVolume(double volume) { - // TODO(hclam): decide whether we need to report the current volume to - // pipeline. -} - -void AudioRendererImpl::CreateStreamTask(const AudioParameters& audio_params) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - - base::AutoLock auto_lock(lock_); - if (stopped_) - return; - - stream_created_ = true; - - // Make sure we don't call create more than once. - DCHECK_EQ(0, stream_id_); - stream_id_ = filter_->AddDelegate(this); - ChildProcess::current()->io_message_loop()->AddDestructionObserver(this); - - AudioParameters params_to_send(audio_params); - // Let the browser choose packet size. - params_to_send.samples_per_packet = 0; - - Send(new AudioHostMsg_CreateStream(stream_id_, - params_to_send, - latency_type_ == kLowLatency)); -} - -void AudioRendererImpl::PlayTask() { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - +void AudioRendererImpl::DoPlay() { earliest_end_time_ = base::Time::Now(); - Send(new AudioHostMsg_PlayStream(stream_id_)); + DCHECK(audio_device_.get()); + audio_device_->Play(); } -void AudioRendererImpl::PauseTask() { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - - Send(new AudioHostMsg_PauseStream(stream_id_)); +void AudioRendererImpl::DoPause() { + DCHECK(audio_device_.get()); + audio_device_->Pause(false); } -void AudioRendererImpl::SeekTask() { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - +void AudioRendererImpl::DoSeek() { earliest_end_time_ = base::Time::Now(); - // We have to pause the audio stream before we can flush. - Send(new AudioHostMsg_PauseStream(stream_id_)); - Send(new AudioHostMsg_FlushStream(stream_id_)); -} - -void AudioRendererImpl::DestroyTask() { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - - base::AutoLock auto_lock(lock_); - // Errors can cause us to get here before CreateStreamTask ever ran, in which - // case there's nothing to do. - if (!stream_created_) - return; - - // Make sure we don't call destroy more than once. - DCHECK_NE(0, stream_id_); - filter_->RemoveDelegate(stream_id_); - Send(new AudioHostMsg_CloseStream(stream_id_)); - // During shutdown this may be NULL; don't worry about deregistering in that - // case. - if (ChildProcess::current()) - ChildProcess::current()->io_message_loop()->RemoveDestructionObserver(this); - stream_id_ = 0; -} - -void AudioRendererImpl::SetVolumeTask(double volume) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - base::AutoLock auto_lock(lock_); - if (stopped_) - return; - Send(new AudioHostMsg_SetVolume(stream_id_, volume)); + // Pause and flush the stream when we seek to a new location. + DCHECK(audio_device_.get()); + audio_device_->Pause(true); } -void AudioRendererImpl::NotifyPacketReadyTask() { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - DCHECK_EQ(kHighLatency, latency_type_); - - base::AutoLock auto_lock(lock_); - if (stopped_) +void AudioRendererImpl::Render(const std::vector<float*>& audio_data, + size_t number_of_frames, + size_t audio_delay_milliseconds) { + if (stopped_ || GetPlaybackRate() == 0.0f) { + // Output silence if stopped. + for (size_t i = 0; i < audio_data.size(); ++i) + memset(audio_data[i], 0, sizeof(float) * number_of_frames); return; - if (pending_request_ && GetPlaybackRate() > 0.0f) { - DCHECK(shared_memory_.get()); - - // Adjust the playback delay. - base::Time current_time = base::Time::Now(); - - base::TimeDelta request_delay = - ConvertToDuration(request_buffers_state_.total_bytes()); - - // Add message delivery delay. - if (current_time > request_buffers_state_.timestamp) { - base::TimeDelta receive_latency = - current_time - request_buffers_state_.timestamp; - - // If the receive latency is too much it may offset all the delay. - if (receive_latency >= request_delay) { - request_delay = base::TimeDelta(); - } else { - request_delay -= receive_latency; - } - } - - // Finally we need to adjust the delay according to playback rate. - if (GetPlaybackRate() != 1.0f) { - request_delay = base::TimeDelta::FromMicroseconds( - static_cast<int64>(ceil(request_delay.InMicroseconds() * - GetPlaybackRate()))); - } - - bool buffer_empty = (request_buffers_state_.pending_bytes == 0) && - (current_time >= earliest_end_time_); - - // For high latency mode we don't write length into shared memory, - // it is explicit part of AudioHostMsg_NotifyPacketReady() message, - // so no need to reserve first word of buffer for length. - uint32 filled = FillBuffer(static_cast<uint8*>(shared_memory_->memory()), - shared_memory_size_, request_delay, - buffer_empty); - UpdateEarliestEndTime(filled, request_delay, current_time); - pending_request_ = false; - - // Then tell browser process we are done filling into the buffer. - Send(new AudioHostMsg_NotifyPacketReady(stream_id_, filled)); } -} -void AudioRendererImpl::WillDestroyCurrentMessageLoop() { - DCHECK(!ChildProcess::current() || // During shutdown. - (MessageLoop::current() == - ChildProcess::current()->io_message_loop())); + // Adjust the playback delay. + base::Time current_time = base::Time::Now(); - // We treat the IO loop going away the same as stopping. - { - base::AutoLock auto_lock(lock_); - if (stopped_) - return; + base::TimeDelta request_delay = + base::TimeDelta::FromMilliseconds(audio_delay_milliseconds); - stopped_ = true; + // Finally we need to adjust the delay according to playback rate. + if (GetPlaybackRate() != 1.0f) { + request_delay = base::TimeDelta::FromMicroseconds( + static_cast<int64>(ceil(request_delay.InMicroseconds() * + GetPlaybackRate()))); } - DestroyTask(); -} -// Our audio thread runs here. We receive requests for more data and send it -// on this thread. -void AudioRendererImpl::Run() { - DCHECK_EQ(kLowLatency, latency_type_); - audio_thread_->SetThreadPriority(base::kThreadPriority_RealtimeAudio); - - int bytes; - while (sizeof(bytes) == socket_->Receive(&bytes, sizeof(bytes))) { - if (bytes == media::AudioOutputController::kPauseMark) { - // When restarting playback, host should get new data, - // not what is currently in the buffer. - media::SetActualDataSizeInBytes(shared_memory_.get(), - shared_memory_size_, - 0); - continue; - } - else if (bytes < 0) - break; - base::AutoLock auto_lock(lock_); - if (stopped_) - break; - float playback_rate = GetPlaybackRate(); - if (playback_rate <= 0.0f) - continue; - DCHECK(shared_memory_.get()); - base::TimeDelta request_delay = ConvertToDuration(bytes); + uint32 bytes_per_frame = + audio_parameters_.bits_per_sample * audio_parameters_.channels / 8; - // We need to adjust the delay according to playback rate. - if (playback_rate != 1.0f) { - request_delay = base::TimeDelta::FromMicroseconds( - static_cast<int64>(ceil(request_delay.InMicroseconds() * - playback_rate))); - } - base::Time time_now = base::Time::Now(); - uint32 size = FillBuffer(static_cast<uint8*>(shared_memory_->memory()), - shared_memory_size_, + const size_t buf_size = number_of_frames * bytes_per_frame; + scoped_array<uint8> buf(new uint8[buf_size]); + + base::Time time_now = base::Time::Now(); + uint32 filled = FillBuffer(buf.get(), + buf_size, request_delay, time_now >= earliest_end_time_); - media::SetActualDataSizeInBytes(shared_memory_.get(), - shared_memory_size_, - size); - UpdateEarliestEndTime(size, request_delay, time_now); + DCHECK_LE(filled, buf_size); + + uint32 filled_frames = filled / bytes_per_frame; + + // Deinterleave each audio channel. + int channels = audio_data.size(); + for (int channel_index = 0; channel_index < channels; ++channel_index) { + media::DeinterleaveAudioChannel(buf.get(), + audio_data[channel_index], + channels, + channel_index, + bytes_per_frame / channels, + filled_frames); + + // If FillBuffer() didn't give us enough data then zero out the remainder. + if (filled_frames < number_of_frames) { + int frames_to_zero = number_of_frames - filled_frames; + memset(audio_data[channel_index], 0, sizeof(float) * frames_to_zero); + } } } - -void AudioRendererImpl::Send(IPC::Message* message) { - filter_->Send(message); -} diff --git a/content/renderer/media/audio_renderer_impl.h b/content/renderer/media/audio_renderer_impl.h index 0bff566..1c8ec6a 100644 --- a/content/renderer/media/audio_renderer_impl.h +++ b/content/renderer/media/audio_renderer_impl.h @@ -2,78 +2,41 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// Audio rendering unit utilizing audio output stream provided by browser -// process through IPC. -// -// Relationship of classes. -// -// AudioRendererHost AudioRendererImpl -// ^ ^ -// | | -// v IPC v -// RenderMessageFilter <---------> AudioMessageFilter -// -// Implementation of interface with audio device is in AudioRendererHost and -// it provides services and entry points in RenderMessageFilter, allowing -// usage of IPC calls to interact with audio device. AudioMessageFilter acts -// as a portal for IPC calls and does no more than delegation. -// -// Transportation of audio buffer is done by using shared memory, after -// OnCreateStream is executed, OnCreated would be called along with a -// SharedMemoryHandle upon successful creation of audio output stream in the -// browser process. The same piece of shared memory would be used during the -// lifetime of this unit. +// Audio rendering unit utilizing AudioDevice. // // This class lives inside three threads during it's lifetime, namely: -// 1. IO thread. -// The thread within which this class receives all the IPC messages and -// IPC communications can only happen in this thread. +// 1. Render thread. +// This object is created on the render thread. // 2. Pipeline thread -// Initialization of filter and proper stopping of filters happens here. -// Properties of this filter is also set in this thread. -// 3. Audio decoder thread (If there's one.) -// Responsible for decoding audio data and gives raw PCM data to this object. +// OnInitialize() is called here with the audio format. +// Play/Pause/Seek also happens here. +// 3. Audio thread created by the AudioDevice. +// Render() is called here where audio data is decoded into raw PCM data. #ifndef CONTENT_RENDERER_MEDIA_AUDIO_RENDERER_IMPL_H_ #define CONTENT_RENDERER_MEDIA_AUDIO_RENDERER_IMPL_H_ #pragma once +#include <vector> + #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/shared_memory.h" #include "base/synchronization/lock.h" -#include "base/threading/simple_thread.h" -#include "content/common/content_export.h" -#include "content/renderer/media/audio_message_filter.h" +#include "content/renderer/media/audio_device.h" #include "media/audio/audio_io.h" -#include "media/audio/audio_manager.h" -#include "media/base/filters.h" +#include "media/audio/audio_parameters.h" #include "media/filters/audio_renderer_base.h" class AudioMessageFilter; class CONTENT_EXPORT AudioRendererImpl : public media::AudioRendererBase, - public AudioMessageFilter::Delegate, - public base::DelegateSimpleThread::Delegate, - public MessageLoop::DestructionObserver { + public AudioDevice::RenderCallback { public: // Methods called on Render thread ------------------------------------------ AudioRendererImpl(); virtual ~AudioRendererImpl(); - // Methods called on IO thread ---------------------------------------------- - // AudioMessageFilter::Delegate methods, called by AudioMessageFilter. - virtual void OnRequestPacket(AudioBuffersState buffers_state) OVERRIDE; - virtual void OnStateChanged(AudioStreamState state) OVERRIDE; - virtual void OnCreated(base::SharedMemoryHandle handle, - uint32 length) OVERRIDE; - virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle, - base::SyncSocket::Handle socket_handle, - uint32 length) OVERRIDE; - virtual void OnVolume(double volume) OVERRIDE; - // Methods called on pipeline thread ---------------------------------------- // media::Filter implementation. virtual void SetPlaybackRate(float rate) OVERRIDE; @@ -86,26 +49,14 @@ class CONTENT_EXPORT AudioRendererImpl virtual void SetVolume(float volume) OVERRIDE; protected: - // Methods called on audio renderer thread ---------------------------------- + // Methods called on pipeline thread ---------------------------------------- // These methods are called from AudioRendererBase. virtual bool OnInitialize(int bits_per_channel, ChannelLayout channel_layout, int sample_rate) OVERRIDE; virtual void OnStop() OVERRIDE; - // Called when the decoder completes a Read(). - virtual void ConsumeAudioSamples( - scoped_refptr<media::Buffer> buffer_in) OVERRIDE; - private: - // We are using either low- or high-latency code path. - enum LatencyType { - kUninitializedLatency = 0, - kLowLatency, - kHighLatency - }; - static LatencyType latency_type_; - // For access to constructor and IO thread methods. friend class AudioRendererImplTest; friend class DelegateCaller; @@ -118,38 +69,17 @@ class CONTENT_EXPORT AudioRendererImpl // number of channels, sample rate and sample bits. base::TimeDelta ConvertToDuration(int bytes); - // Methods call on IO thread ------------------------------------------------ - // The following methods are tasks posted on the IO thread that needs to - // be executed on that thread. They interact with AudioMessageFilter and - // sends IPC messages on that thread. - void CreateStreamTask(const AudioParameters& params); - void PlayTask(); - void PauseTask(); - void SeekTask(); - void SetVolumeTask(double volume); - void NotifyPacketReadyTask(); - void DestroyTask(); - - // Called on IO thread when message loop is dying. - virtual void WillDestroyCurrentMessageLoop() OVERRIDE; - - // DelegateSimpleThread::Delegate implementation. - virtual void Run() OVERRIDE; - - // (Re-)starts playback. - void NotifyDataAvailableIfNecessary(); - - // Creates socket. Virtual so tests can override. - virtual void CreateSocket(base::SyncSocket::Handle socket_handle); + // Methods called on pipeline thread ---------------------------------------- + void DoPlay(); + void DoPause(); + void DoSeek(); - // Launching audio thread. Virtual so tests can override. - virtual void CreateAudioThread(); + // AudioDevice::RenderCallback implementation. + virtual void Render(const std::vector<float*>& audio_data, + size_t number_of_frames, + size_t audio_delay_milliseconds) OVERRIDE; // Accessors used by tests. - static LatencyType latency_type() { - return latency_type_; - } - base::Time earliest_end_time() const { return earliest_end_time_; } @@ -162,12 +92,6 @@ class CONTENT_EXPORT AudioRendererImpl return bytes_per_second_; } - // Should be called before any class instance is created. - static void set_latency_type(LatencyType latency_type); - - // Helper method for IPC send calls. - void Send(IPC::Message* message); - // Estimate earliest time when current buffer can stop playing. void UpdateEarliestEndTime(int bytes_filled, base::TimeDelta request_delay, @@ -176,39 +100,11 @@ class CONTENT_EXPORT AudioRendererImpl // Used to calculate audio delay given bytes. uint32 bytes_per_second_; - // Whether the stream has been created yet. - bool stream_created_; - - // ID of the stream created in the browser process. - int32 stream_id_; - - // Memory shared by the browser process for audio buffer. - scoped_ptr<base::SharedMemory> shared_memory_; - uint32 shared_memory_size_; - - // Cached audio message filter (lives on the main render thread). - scoped_refptr<AudioMessageFilter> filter_; - - // Low latency IPC stuff. - scoped_ptr<base::SyncSocket> socket_; - - // That thread waits for audio input. - scoped_ptr<base::DelegateSimpleThread> audio_thread_; - - // Protects: - // - |stopped_| - // - |pending_request_| - // - |request_buffers_state_| - base::Lock lock_; - // A flag that indicates this filter is called to stop. bool stopped_; - // A flag that indicates an outstanding packet request. - bool pending_request_; - - // State of the audio buffers at time of the last request. - AudioBuffersState request_buffers_state_; + // audio_device_ is the sink (destination) for rendered audio. + scoped_refptr<AudioDevice> audio_device_; // We're supposed to know amount of audio data OS or hardware buffered, but // that is not always so -- on my Linux box @@ -226,6 +122,8 @@ class CONTENT_EXPORT AudioRendererImpl // than nothing. base::Time earliest_end_time_; + AudioParameters audio_parameters_; + DISALLOW_COPY_AND_ASSIGN(AudioRendererImpl); }; diff --git a/content/renderer/media/audio_renderer_impl_unittest.cc b/content/renderer/media/audio_renderer_impl_unittest.cc index 89b6fdd..3a653db 100644 --- a/content/renderer/media/audio_renderer_impl_unittest.cc +++ b/content/renderer/media/audio_renderer_impl_unittest.cc @@ -10,7 +10,6 @@ #include "base/time.h" #include "content/common/child_process.h" #include "content/common/child_thread.h" -#include "content/common/media/audio_messages.h" #include "content/renderer/media/audio_renderer_impl.h" #include "content/renderer/mock_content_renderer_client.h" #include "content/renderer/render_process.h" @@ -44,41 +43,6 @@ class MockRenderProcess : public RenderProcess { }; } -// This class defines a set of methods which will be used in combination -// with NewRunnableMethod to form tasks which will be posted on the -// IO thread. All methods emulate AudioMessageFilter::Delegate calls. -class DelegateCaller : public base::RefCountedThreadSafe<DelegateCaller> { - public: - explicit DelegateCaller(AudioRendererImpl* renderer) - : renderer_(renderer) {} - - void OnCreated(base::SharedMemoryHandle handle, uint32 length) { - if (renderer_->latency_type() == AudioRendererImpl::kHighLatency) { - renderer_->OnCreated(handle, length); - } else { - renderer_->OnLowLatencyCreated(handle, 0, length); - } - } - void OnStateChanged(AudioStreamState state) { - renderer_->OnStateChanged(state); - } - void OnRequestPacket(AudioBuffersState buffers_state) { - renderer_->OnRequestPacket(buffers_state); - } - void OnVolume(double volume) { - renderer_->OnVolume(volume); - } - void DestroyCurrentMessageLoop() { - renderer_->WillDestroyCurrentMessageLoop(); - } - private: - friend class base::RefCountedThreadSafe<DelegateCaller>; - virtual ~DelegateCaller() {} - - scoped_refptr<AudioRendererImpl> renderer_; - DISALLOW_COPY_AND_ASSIGN(DelegateCaller); -}; - // This task can be posted on the IO thread and will signal an event when // done. The caller can then wait for this signal to ensure that no // additional tasks remain in the task queue. @@ -96,32 +60,18 @@ class WaitTask : public Task { DISALLOW_COPY_AND_ASSIGN(WaitTask); }; -// Class we would be testing. The only difference between it and "real" one -// is that test class does not open sockets and launch audio thread. +// Class we would be testing. class TestAudioRendererImpl : public AudioRendererImpl { public: explicit TestAudioRendererImpl() : AudioRendererImpl() { } - private: - virtual void CreateSocket(base::SyncSocket::Handle socket_handle) {} - virtual void CreateAudioThread() {} }; class AudioRendererImplTest : public ::testing::Test, public IPC::Channel::Listener { public: - static void SetUpTestCase() { - // Set low latency mode, as it soon would be on by default. - if (AudioRendererImpl::latency_type() == - AudioRendererImpl::kUninitializedLatency) { - AudioRendererImpl::set_latency_type(AudioRendererImpl::kLowLatency); - } - DCHECK_EQ(AudioRendererImpl::kLowLatency, - AudioRendererImpl::latency_type()); - } - // IPC::Channel::Listener implementation. virtual bool OnMessageReceived(const IPC::Message& message) { NOTIMPLEMENTED(); @@ -149,9 +99,6 @@ class AudioRendererImplTest render_thread_ = new RenderThreadImpl(kThreadName); mock_process_->set_main_thread(render_thread_); - // Create temporary shared memory. - CHECK(shared_mem_.CreateAnonymous(kSize)); - // Setup expectations for initialization. decoder_ = new media::MockAudioDecoder(); @@ -164,30 +111,12 @@ class AudioRendererImplTest // Create and initialize the audio renderer. renderer_ = new TestAudioRendererImpl(); - renderer_->set_host(&host_); renderer_->Initialize(decoder_, media::NewExpectedClosure(), NewUnderflowClosure()); - // Wraps delegate calls into tasks. - delegate_caller_ = new DelegateCaller(renderer_); - // We need an event to verify that all tasks are done before leaving // our tests. event_.reset(new base::WaitableEvent(false, false)); - - // Duplicate the shared memory handle so both the test and the callee can - // close their copy. - base::SharedMemoryHandle duplicated_handle; - EXPECT_TRUE(shared_mem_.ShareToProcess(base::GetCurrentProcessHandle(), - &duplicated_handle)); - - // Set things up and ensure that the call comes from the IO thread - // as all AudioMessageFilter::Delegate methods. - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::OnCreated, delegate_caller_.get(), - duplicated_handle, kSize)); - WaitForIOThreadCompletion(); } virtual void TearDown() { @@ -215,12 +144,9 @@ class AudioRendererImplTest scoped_ptr<IPC::Channel> channel_; RenderThreadImpl* render_thread_; // owned by mock_process_ scoped_ptr<MockRenderProcess> mock_process_; - base::SharedMemory shared_mem_; - media::MockFilterHost host_; scoped_refptr<media::MockAudioDecoder> decoder_; scoped_refptr<AudioRendererImpl> renderer_; scoped_ptr<base::WaitableEvent> event_; - scoped_refptr<DelegateCaller> delegate_caller_; private: DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); @@ -256,37 +182,6 @@ TEST_F(AudioRendererImplTest, Stop) { // Tasks will be posted internally on the IO thread. renderer_->Stop(media::NewExpectedClosure()); - // Run AudioMessageFilter::Delegate methods, which can be executed after being - // stopped. AudioRendererImpl shouldn't create any messages in this state. - // All delegate method calls are posted on the IO thread since it is - // a requirement. - if (renderer_->latency_type() == AudioRendererImpl::kHighLatency) { - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::OnRequestPacket, - delegate_caller_.get(), AudioBuffersState(kSize, 0))); - } - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::OnStateChanged, - delegate_caller_.get(), kAudioStreamError)); - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::OnStateChanged, - delegate_caller_.get(), kAudioStreamPlaying)); - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::OnStateChanged, - delegate_caller_.get(), kAudioStreamPaused)); - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::OnCreated, - delegate_caller_.get(), shared_mem_.handle(), kSize)); - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::OnVolume, - delegate_caller_.get(), 0.5)); - WaitForIOThreadCompletion(); // It's possible that the upstream decoder replies right after being stopped. @@ -294,54 +189,6 @@ TEST_F(AudioRendererImplTest, Stop) { renderer_->ConsumeAudioSamples(buffer); } -TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetPlaybackRate) { - // Emulate "killing the message loop" and verify that SetPlaybackRate() - // still works. - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::DestroyCurrentMessageLoop, - delegate_caller_.get())); - WaitForIOThreadCompletion(); - - // No tasks will be posted on the IO thread here since we are in - // a "stopped" state. - renderer_->SetPlaybackRate(0.0f); - renderer_->SetPlaybackRate(1.0f); - renderer_->SetPlaybackRate(0.0f); - renderer_->Stop(media::NewExpectedClosure()); -} - -TEST_F(AudioRendererImplTest, DestroyedMessageLoop_SetVolume) { - // Emulate "killing the message loop" and verify that SetVolume() - // still works. - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::DestroyCurrentMessageLoop, - delegate_caller_.get())); - WaitForIOThreadCompletion(); - - // No tasks will be posted on the IO thread here since we are in - // a "stopped" state. - renderer_->SetVolume(0.5f); - renderer_->Stop(media::NewExpectedClosure()); -} - -TEST_F(AudioRendererImplTest, DestroyedMessageLoop_ConsumeAudioSamples) { - // Emulate "killing the message loop" and verify that OnReadComplete() - // still works. - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&DelegateCaller::DestroyCurrentMessageLoop, - delegate_caller_.get())); - WaitForIOThreadCompletion(); - - // No tasks will be posted on the IO thread here since we are in - // a "stopped" state. - scoped_refptr<media::Buffer> buffer(new media::DataBuffer(kSize)); - renderer_->ConsumeAudioSamples(buffer); - renderer_->Stop(media::NewExpectedClosure()); -} - TEST_F(AudioRendererImplTest, UpdateEarliestEndTime) { renderer_->SetPlaybackRate(1.0f); WaitForIOThreadCompletion(); |