summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorvrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-09 18:27:52 +0000
committervrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-09 18:27:52 +0000
commit9b9871e8aefed53147b511d1494b584c1b2bef50 (patch)
tree025b114736caa36ecc36cf596a75cda8cac6d501 /content
parente5d7b8399240c3bc5fc0fd9d7a65158bbd0d2c6a (diff)
downloadchromium_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.cc166
-rw-r--r--content/renderer/media/audio_device.h77
-rw-r--r--content/renderer/media/audio_renderer_impl.cc502
-rw-r--r--content/renderer/media/audio_renderer_impl.h152
-rw-r--r--content/renderer/media/audio_renderer_impl_unittest.cc155
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();