summaryrefslogtreecommitdiffstats
path: root/content/renderer/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-14 01:18:08 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-14 01:18:08 +0000
commit25803d153b035ae0be9056103aefef2a50c7f355 (patch)
treeb26da65c1ccfec19061e8d8b910241c336dd06d4 /content/renderer/media
parent3f8bd9a957402a8d977a929b8c48bc22fac5d216 (diff)
downloadchromium_src-25803d153b035ae0be9056103aefef2a50c7f355.zip
chromium_src-25803d153b035ae0be9056103aefef2a50c7f355.tar.gz
chromium_src-25803d153b035ae0be9056103aefef2a50c7f355.tar.bz2
Moves audio files from content/renderer/ to content/renderer/media.
Patch by henrika@chromium.com: http://codereview.chromium.org/7003053/ BUG=none TEST=none git-svn-id: svn://svn.chromium.org/chrome/trunk/src@88939 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/renderer/media')
-rw-r--r--content/renderer/media/audio_device.cc254
-rw-r--r--content/renderer/media/audio_device.h121
-rw-r--r--content/renderer/media/audio_input_device.cc241
-rw-r--r--content/renderer/media/audio_input_device.h122
-rw-r--r--content/renderer/media/audio_input_message_filter.cc109
-rw-r--r--content/renderer/media/audio_input_message_filter.h90
-rw-r--r--content/renderer/media/audio_message_filter.cc144
-rw-r--r--content/renderer/media/audio_message_filter.h120
-rw-r--r--content/renderer/media/audio_message_filter_unittest.cc197
-rw-r--r--content/renderer/media/audio_renderer_impl.h2
10 files changed, 1399 insertions, 1 deletions
diff --git a/content/renderer/media/audio_device.cc b/content/renderer/media/audio_device.cc
new file mode 100644
index 0000000..39f9460
--- /dev/null
+++ b/content/renderer/media/audio_device.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/audio_device.h"
+
+#include "base/memory/singleton.h"
+#include "base/message_loop.h"
+#include "content/common/audio_messages.h"
+#include "content/common/child_process.h"
+#include "content/common/view_messages.h"
+#include "content/renderer/render_thread.h"
+#include "media/audio/audio_util.h"
+
+scoped_refptr<AudioMessageFilter> AudioDevice::filter_;
+
+namespace {
+
+// AudioMessageFilterCreator is intended to be used as a singleton so we can
+// get access to a shared AudioMessageFilter.
+// Example usage:
+// AudioMessageFilter* filter = AudioMessageFilterCreator::SharedFilter();
+
+class AudioMessageFilterCreator {
+ public:
+ AudioMessageFilterCreator() {
+ int routing_id;
+ RenderThread::current()->Send(
+ new ViewHostMsg_GenerateRoutingID(&routing_id));
+ filter_ = new AudioMessageFilter(routing_id);
+ RenderThread::current()->AddFilter(filter_);
+ }
+
+ static AudioMessageFilter* SharedFilter() {
+ return GetInstance()->filter_.get();
+ }
+
+ static AudioMessageFilterCreator* GetInstance() {
+ return Singleton<AudioMessageFilterCreator>::get();
+ }
+
+ private:
+ scoped_refptr<AudioMessageFilter> filter_;
+};
+
+} // namespace
+
+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),
+ audio_delay_milliseconds_(0),
+ volume_(1.0),
+ stream_id_(0) {
+ audio_data_.reserve(channels);
+ for (int i = 0; i < channels; ++i) {
+ float* channel_data = new float[buffer_size];
+ audio_data_.push_back(channel_data);
+ }
+ // Lazily create the message filter and share across AudioDevice instances.
+ filter_ = AudioMessageFilterCreator::SharedFilter();
+}
+
+AudioDevice::~AudioDevice() {
+ // Make sure we have been shut down.
+ DCHECK_EQ(0, stream_id_);
+ Stop();
+ for (int i = 0; i < channels_; ++i)
+ delete [] audio_data_[i];
+}
+
+bool AudioDevice::Start() {
+ // Make sure we don't call Start() more than once.
+ DCHECK_EQ(0, stream_id_);
+ if (stream_id_)
+ return false;
+
+ AudioParameters params;
+ params.format = AudioParameters::AUDIO_PCM_LOW_LATENCY;
+ params.channels = channels_;
+ params.sample_rate = static_cast<int>(sample_rate_);
+ params.bits_per_sample = bits_per_sample_;
+ params.samples_per_packet = buffer_size_;
+
+ // Ensure that the initialization task is posted on the I/O thread by
+ // accessing the I/O message loop directly. This approach avoids a race
+ // condition which could exist if the message loop of the filter was
+ // used instead.
+ DCHECK(ChildProcess::current()) << "Must be in the renderer";
+ MessageLoop* message_loop = ChildProcess::current()->io_message_loop();
+ if (!message_loop)
+ return false;
+
+ message_loop->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioDevice::InitializeOnIOThread, params));
+
+ return true;
+}
+
+bool AudioDevice::Stop() {
+ if (!stream_id_)
+ return false;
+
+ filter_->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioDevice::ShutDownOnIOThread));
+
+ if (audio_thread_.get()) {
+ socket_->Close();
+ audio_thread_->Join();
+ }
+
+ return true;
+}
+
+bool AudioDevice::SetVolume(double volume) {
+ if (!stream_id_)
+ return false;
+
+ if (volume < 0 || volume > 1.0)
+ return false;
+
+ filter_->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioDevice::SetVolumeOnIOThread, volume));
+
+ volume_ = volume;
+
+ return true;
+}
+
+bool AudioDevice::GetVolume(double* volume) {
+ if (!stream_id_)
+ return false;
+
+ // Return a locally cached version of the current scaling factor.
+ *volume = volume_;
+
+ return true;
+}
+
+void AudioDevice::InitializeOnIOThread(const AudioParameters& params) {
+ stream_id_ = filter_->AddDelegate(this);
+ filter_->Send(new AudioHostMsg_CreateStream(0, stream_id_, params, true));
+}
+
+void AudioDevice::StartOnIOThread() {
+ if (stream_id_)
+ filter_->Send(new AudioHostMsg_PlayStream(0, stream_id_));
+}
+
+void AudioDevice::ShutDownOnIOThread() {
+ // Make sure we don't call shutdown more than once.
+ if (!stream_id_)
+ return;
+
+ filter_->Send(new AudioHostMsg_CloseStream(0, stream_id_));
+ filter_->RemoveDelegate(stream_id_);
+ stream_id_ = 0;
+}
+
+void AudioDevice::SetVolumeOnIOThread(double volume) {
+ if (stream_id_)
+ filter_->Send(new AudioHostMsg_SetVolume(0, 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(
+ base::SharedMemoryHandle handle,
+ base::SyncSocket::Handle socket_handle,
+ uint32 length) {
+#if defined(OS_WIN)
+ DCHECK(handle);
+ DCHECK(socket_handle);
+#else
+ DCHECK_GE(handle.fd, 0);
+ DCHECK_GE(socket_handle, 0);
+#endif
+ DCHECK(length);
+
+ // TODO(crogers) : check that length is big enough for buffer_size_
+
+ shared_memory_.reset(new base::SharedMemory(handle, false));
+ shared_memory_->Map(length);
+
+ socket_.reset(new base::SyncSocket(socket_handle));
+ // Allow the client to pre-populate the buffer.
+ FireRenderCallback();
+
+ audio_thread_.reset(
+ new base::DelegateSimpleThread(this, "renderer_audio_thread"));
+ audio_thread_->Start();
+
+ if (filter_) {
+ filter_->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioDevice::StartOnIOThread));
+ }
+}
+
+void AudioDevice::OnVolume(double volume) {
+ NOTIMPLEMENTED();
+}
+
+// Our audio thread runs here.
+void AudioDevice::Run() {
+ audio_thread_->SetThreadPriority(base::kThreadPriority_RealtimeAudio);
+
+ int pending_data;
+ 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) {
+ {
+ // Convert the number of pending bytes in the render buffer
+ // into milliseconds.
+ audio_delay_milliseconds_ = pending_data / bytes_per_ms;
+ }
+
+ FireRenderCallback();
+ }
+}
+
+void AudioDevice::FireRenderCallback() {
+ if (callback_) {
+ // Update the audio-delay measurement then ask client to render audio.
+ callback_->Render(audio_data_, buffer_size_, audio_delay_milliseconds_);
+
+ // Interleave, scale, and clip to int16.
+ int16* output_buffer16 = static_cast<int16*>(shared_memory_data());
+ media::InterleaveFloatToInt16(audio_data_, output_buffer16, buffer_size_);
+ }
+}
diff --git a/content/renderer/media/audio_device.h b/content/renderer/media/audio_device.h
new file mode 100644
index 0000000..88c2a0c
--- /dev/null
+++ b/content/renderer/media/audio_device.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_MEDIA_AUDIO_DEVICE_H_
+#define CONTENT_RENDERER_MEDIA_AUDIO_DEVICE_H_
+#pragma once
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "base/threading/simple_thread.h"
+#include "content/renderer/media/audio_message_filter.h"
+
+struct AudioParameters;
+
+// Each instance of AudioDevice corresponds to one host stream.
+// This class is not thread-safe, so its methods must be called from
+// the same thread.
+class AudioDevice : public AudioMessageFilter::Delegate,
+ public base::DelegateSimpleThread::Delegate,
+ public base::RefCountedThreadSafe<AudioDevice> {
+ public:
+ class RenderCallback {
+ public:
+ virtual void Render(const std::vector<float*>& audio_data,
+ size_t number_of_frames,
+ size_t audio_delay_milliseconds) = 0;
+ protected:
+ virtual ~RenderCallback() {}
+ };
+
+ // |buffer_size| is the number of sample-frames.
+ AudioDevice(size_t buffer_size,
+ int channels,
+ double sample_rate,
+ RenderCallback* callback);
+ virtual ~AudioDevice();
+
+ // Starts audio playback. Returns |true| on success.
+ bool Start();
+
+ // Stops audio playback. Returns |true| on success.
+ bool Stop();
+
+ // Sets the playback volume, with range [0.0, 1.0] inclusive.
+ // Returns |true| on success.
+ bool SetVolume(double volume);
+
+ // Gets the playback volume, with range [0.0, 1.0] inclusive.
+ // Returns |true| on success.
+ bool GetVolume(double* volume);
+
+ double sample_rate() const { return sample_rate_; }
+ size_t buffer_size() const { return buffer_size_; }
+
+ private:
+ // I/O thread backends to above functions.
+ void InitializeOnIOThread(const AudioParameters& params);
+ void StartOnIOThread();
+ void ShutDownOnIOThread();
+ void SetVolumeOnIOThread(double volume);
+
+ // AudioMessageFilter::Delegate implementation.
+ virtual void OnRequestPacket(AudioBuffersState buffers_state);
+ virtual void OnStateChanged(AudioStreamState state);
+ virtual void OnCreated(base::SharedMemoryHandle handle, uint32 length);
+ virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle,
+ base::SyncSocket::Handle socket_handle,
+ uint32 length);
+ virtual void OnVolume(double volume);
+
+ // DelegateSimpleThread::Delegate implementation.
+ virtual void Run();
+
+ // Format
+ size_t buffer_size_; // in sample-frames
+ int channels_;
+ int bits_per_sample_;
+ double sample_rate_;
+
+ // Calls the client's callback for rendering audio.
+ void FireRenderCallback();
+ RenderCallback* callback_;
+
+ // The client callback renders audio into here.
+ std::vector<float*> audio_data_;
+
+ // 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.
+ int audio_delay_milliseconds_;
+
+ // The current volume scaling [0.0, 1.0] of the audio stream.
+ double volume_;
+
+ // Callbacks for rendering audio occur on this thread.
+ scoped_ptr<base::DelegateSimpleThread> audio_thread_;
+
+ // IPC message stuff.
+ base::SharedMemory* shared_memory() { return shared_memory_.get(); }
+ base::SyncSocket* socket() { return socket_.get(); }
+ void* shared_memory_data() { return shared_memory()->memory(); }
+
+ // MessageFilter used to send/receive IPC. THIS MUST ONLY BE ACCESSED ON THE
+ // I/O thread except to send messages and get the message loop.
+ static scoped_refptr<AudioMessageFilter> filter_;
+
+ // Our ID on the message filter. THIS MUST ONLY BE ACCESSED ON THE I/O THREAD
+ // or else you could race with the initialize function which sets it.
+ int32 stream_id_;
+
+ scoped_ptr<base::SharedMemory> shared_memory_;
+ scoped_ptr<base::SyncSocket> socket_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioDevice);
+};
+
+#endif // CONTENT_RENDERER_MEDIA_AUDIO_DEVICE_H_
diff --git a/content/renderer/media/audio_input_device.cc b/content/renderer/media/audio_input_device.cc
new file mode 100644
index 0000000..4fe3364
--- /dev/null
+++ b/content/renderer/media/audio_input_device.cc
@@ -0,0 +1,241 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/audio_input_device.h"
+
+#include "base/memory/singleton.h"
+#include "base/message_loop.h"
+#include "content/common/audio_messages.h"
+#include "content/common/child_process.h"
+#include "content/common/view_messages.h"
+#include "content/renderer/render_thread.h"
+#include "media/audio/audio_util.h"
+
+scoped_refptr<AudioInputMessageFilter> AudioInputDevice::filter_;
+
+namespace {
+
+// AudioMessageFilterCreator is intended to be used as a singleton so we can
+// get access to a shared AudioInputMessageFilter.
+// Example usage:
+// AudioInputMessageFilter* filter =
+// AudioInputMessageFilterCreator::SharedFilter();
+
+class AudioInputMessageFilterCreator {
+ public:
+ AudioInputMessageFilterCreator() {
+ int routing_id;
+ RenderThread::current()->Send(
+ new ViewHostMsg_GenerateRoutingID(&routing_id));
+ filter_ = new AudioInputMessageFilter(routing_id);
+ RenderThread::current()->AddFilter(filter_);
+ }
+
+ static AudioInputMessageFilter* SharedFilter() {
+ return GetInstance()->filter_.get();
+ }
+
+ static AudioInputMessageFilterCreator* GetInstance() {
+ return Singleton<AudioInputMessageFilterCreator>::get();
+ }
+
+ private:
+ scoped_refptr<AudioInputMessageFilter> filter_;
+};
+
+} // namespace
+
+AudioInputDevice::AudioInputDevice(size_t buffer_size,
+ int channels,
+ double sample_rate,
+ CaptureCallback* callback)
+ : buffer_size_(buffer_size),
+ channels_(channels),
+ bits_per_sample_(16),
+ sample_rate_(sample_rate),
+ callback_(callback),
+ audio_delay_milliseconds_(0),
+ volume_(1.0),
+ stream_id_(0) {
+ audio_data_.reserve(channels);
+ for (int i = 0; i < channels; ++i) {
+ float* channel_data = new float[buffer_size];
+ audio_data_.push_back(channel_data);
+ }
+ // Lazily create the message filter and share across AudioInputDevice
+ // instances.
+ filter_ = AudioInputMessageFilterCreator::SharedFilter();
+}
+
+AudioInputDevice::~AudioInputDevice() {
+ // Make sure we have been shut down.
+ DCHECK_EQ(0, stream_id_);
+ Stop();
+ for (int i = 0; i < channels_; ++i)
+ delete [] audio_data_[i];
+}
+
+bool AudioInputDevice::Start() {
+ // Make sure we don't call Start() more than once.
+ DCHECK_EQ(0, stream_id_);
+ if (stream_id_)
+ return false;
+
+ AudioParameters params;
+ // TODO(henrika): add support for low-latency mode?
+ params.format = AudioParameters::AUDIO_PCM_LINEAR;
+ params.channels = channels_;
+ params.sample_rate = static_cast<int>(sample_rate_);
+ params.bits_per_sample = bits_per_sample_;
+ params.samples_per_packet = buffer_size_;
+
+ // Ensure that the initialization task is posted on the I/O thread by
+ // accessing the I/O message loop directly. This approach avoids a race
+ // condition which could exist if the message loop of the filter was
+ // used instead.
+ DCHECK(ChildProcess::current()) << "Must be in the renderer";
+ MessageLoop* message_loop = ChildProcess::current()->io_message_loop();
+ if (!message_loop)
+ return false;
+
+ message_loop->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioInputDevice::InitializeOnIOThread, params));
+
+ return true;
+}
+
+bool AudioInputDevice::Stop() {
+ if (!stream_id_)
+ return false;
+
+ filter_->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioInputDevice::ShutDownOnIOThread));
+
+ if (audio_thread_.get()) {
+ socket_->Close();
+ audio_thread_->Join();
+ }
+
+ return true;
+}
+
+bool AudioInputDevice::SetVolume(double volume) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool AudioInputDevice::GetVolume(double* volume) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void AudioInputDevice::InitializeOnIOThread(const AudioParameters& params) {
+ stream_id_ = filter_->AddDelegate(this);
+ filter_->Send(
+ new AudioInputHostMsg_CreateStream(0, stream_id_, params, true));
+}
+
+void AudioInputDevice::StartOnIOThread() {
+ if (stream_id_)
+ filter_->Send(new AudioInputHostMsg_RecordStream(0, stream_id_));
+}
+
+void AudioInputDevice::ShutDownOnIOThread() {
+ // Make sure we don't call shutdown more than once.
+ if (!stream_id_)
+ return;
+
+ filter_->Send(new AudioInputHostMsg_CloseStream(0, stream_id_));
+ filter_->RemoveDelegate(stream_id_);
+ stream_id_ = 0;
+}
+
+void AudioInputDevice::SetVolumeOnIOThread(double volume) {
+ if (stream_id_)
+ filter_->Send(new AudioInputHostMsg_SetVolume(0, stream_id_, volume));
+}
+
+void AudioInputDevice::OnLowLatencyCreated(
+ base::SharedMemoryHandle handle,
+ base::SyncSocket::Handle socket_handle,
+ uint32 length) {
+#if defined(OS_WIN)
+ DCHECK(handle);
+ DCHECK(socket_handle);
+#else
+ DCHECK_GE(handle.fd, 0);
+ DCHECK_GE(socket_handle, 0);
+#endif
+ DCHECK(length);
+
+ // TODO(henrika) : check that length is big enough for buffer_size_
+
+ shared_memory_.reset(new base::SharedMemory(handle, false));
+ shared_memory_->Map(length);
+
+ socket_.reset(new base::SyncSocket(socket_handle));
+
+ // TODO(henrika): we could optionally set the thread to high-priority
+ audio_thread_.reset(
+ new base::DelegateSimpleThread(this, "renderer_audio_input_thread"));
+ audio_thread_->Start();
+
+ if (filter_) {
+ filter_->message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &AudioInputDevice::StartOnIOThread));
+ }
+}
+
+void AudioInputDevice::OnVolume(double volume) {
+ NOTIMPLEMENTED();
+}
+
+// Our audio thread runs here. We receive captured audio samples on
+// this thread.
+void AudioInputDevice::Run() {
+ int pending_data;
+ 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) {
+ // TODO(henrika): investigate the provided |pending_data| value
+ // and ensure that it is actually an accurate delay estimation.
+
+ // Convert the number of pending bytes in the capture buffer
+ // into milliseconds.
+ audio_delay_milliseconds_ = pending_data / bytes_per_ms;
+
+ FireCaptureCallback();
+ }
+}
+
+void AudioInputDevice::FireCaptureCallback() {
+ if (!callback_)
+ return;
+
+ const size_t number_of_frames = buffer_size_;
+
+ // Read 16-bit samples from shared memory (browser writes to it).
+ int16* input_audio = static_cast<int16*>(shared_memory_data());
+ const int bytes_per_sample = sizeof(input_audio[0]);
+
+ // Deinterleave each channel and convert to 32-bit floating-point
+ // with nominal range -1.0 -> +1.0.
+ for (int channel_index = 0; channel_index < channels_; ++channel_index) {
+ media::DeinterleaveAudioChannel(input_audio,
+ audio_data_[channel_index],
+ channels_,
+ channel_index,
+ bytes_per_sample,
+ number_of_frames);
+ }
+
+ // Deliver captured data to the client in floating point format
+ // and update the audio-delay measurement.
+ callback_->Capture(audio_data_,
+ number_of_frames,
+ audio_delay_milliseconds_);
+}
diff --git a/content/renderer/media/audio_input_device.h b/content/renderer/media/audio_input_device.h
new file mode 100644
index 0000000..7c0c37c
--- /dev/null
+++ b/content/renderer/media/audio_input_device.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_MEDIA_AUDIO_INPUT_DEVICE_H_
+#define CONTENT_RENDERER_MEDIA_AUDIO_INPUT_DEVICE_H_
+#pragma once
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/shared_memory.h"
+#include "base/threading/simple_thread.h"
+#include "content/renderer/media/audio_input_message_filter.h"
+
+struct AudioParameters;
+
+// Each instance of AudioInputDevice corresponds to one host stream.
+// This class is not thread-safe, so its methods must be called from
+// the same thread.
+
+// TODO(henrika): This class is based on the AudioDevice class and it has
+// many components in common. Investigate potential for re-factoring.
+class AudioInputDevice : public AudioInputMessageFilter::Delegate,
+ public base::DelegateSimpleThread::Delegate,
+ public base::RefCountedThreadSafe<AudioInputDevice> {
+ public:
+ class CaptureCallback {
+ public:
+ virtual void Capture(const std::vector<float*>& audio_data,
+ size_t number_of_frames,
+ size_t audio_delay_milliseconds) = 0;
+ protected:
+ virtual ~CaptureCallback() {}
+ };
+
+ // |buffer_size| is the number of sample-frames.
+ AudioInputDevice(size_t buffer_size,
+ int channels,
+ double sample_rate,
+ CaptureCallback* callback);
+ virtual ~AudioInputDevice();
+
+ // Starts audio capturing. Returns |true| on success.
+ bool Start();
+
+ // Stops audio capturing. Returns |true| on success.
+ bool Stop();
+
+ // Sets the capture volume scaling, with range [0.0, 1.0] inclusive.
+ // Returns |true| on success.
+ bool SetVolume(double volume);
+
+ // Gets the capture volume scaling, with range [0.0, 1.0] inclusive.
+ // Returns |true| on success.
+ bool GetVolume(double* volume);
+
+ double sample_rate() const { return sample_rate_; }
+
+ size_t buffer_size() const { return buffer_size_; }
+
+ private:
+ // I/O thread backends to above functions.
+ void InitializeOnIOThread(const AudioParameters& params);
+ void StartOnIOThread();
+ void ShutDownOnIOThread();
+ void SetVolumeOnIOThread(double volume);
+
+ // AudioInputMessageFilter::Delegate implementation
+ virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle,
+ base::SyncSocket::Handle socket_handle,
+ uint32 length);
+ virtual void OnVolume(double volume);
+
+ // DelegateSimpleThread::Delegate implementation.
+ virtual void Run();
+
+ // Format
+ size_t buffer_size_; // in sample-frames
+ int channels_;
+ int bits_per_sample_;
+ double sample_rate_;
+
+ // Calls the client's callback for capturing audio.
+ void FireCaptureCallback();
+ CaptureCallback* callback_;
+
+ // The client callback receives captured audio here.
+ std::vector<float*> audio_data_;
+
+ // The client stores the last reported audio delay in this member.
+ // The delay shall reflect the amount of audio which still resides in
+ // the input buffer, i.e., the expected audio input delay.
+ int audio_delay_milliseconds_;
+
+ // The current volume scaling [0.0, 1.0] of the audio stream.
+ double volume_;
+
+ // Callbacks for capturing audio occur on this thread.
+ scoped_ptr<base::DelegateSimpleThread> audio_thread_;
+
+ // IPC message stuff.
+ base::SharedMemory* shared_memory() { return shared_memory_.get(); }
+ base::SyncSocket* socket() { return socket_.get(); }
+ void* shared_memory_data() { return shared_memory()->memory(); }
+
+ // MessageFilter used to send/receive IPC. THIS MUST ONLY BE ACCESSED ON THE
+ // I/O thread except to send messages and get the message loop.
+ static scoped_refptr<AudioInputMessageFilter> filter_;
+
+ // Our ID on the message filter. THIS MUST ONLY BE ACCESSED ON THE I/O THREAD
+ // or else you could race with the initialize function which sets it.
+ int32 stream_id_;
+
+ scoped_ptr<base::SharedMemory> shared_memory_;
+ scoped_ptr<base::SyncSocket> socket_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AudioInputDevice);
+};
+
+#endif // CONTENT_RENDERER_MEDIA_AUDIO_INPUT_DEVICE_H_
diff --git a/content/renderer/media/audio_input_message_filter.cc b/content/renderer/media/audio_input_message_filter.cc
new file mode 100644
index 0000000..9f99e86
--- /dev/null
+++ b/content/renderer/media/audio_input_message_filter.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/audio_input_message_filter.h"
+
+#include "base/message_loop.h"
+#include "base/time.h"
+#include "content/common/audio_messages.h"
+#include "ipc/ipc_logging.h"
+
+AudioInputMessageFilter::AudioInputMessageFilter(int32 route_id)
+ : channel_(NULL),
+ route_id_(route_id),
+ message_loop_(NULL) {
+ VLOG(1) << "AudioInputMessageFilter(route_id=" << route_id << ")";
+}
+
+AudioInputMessageFilter::~AudioInputMessageFilter() {}
+
+// Called on the IPC thread.
+bool AudioInputMessageFilter::Send(IPC::Message* message) {
+ if (!channel_) {
+ delete message;
+ return false;
+ }
+
+ if (MessageLoop::current() != message_loop_) {
+ // Can only access the IPC::Channel on the IPC thread since it's not thread
+ // safe.
+ message_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(this,
+ &AudioInputMessageFilter::Send,
+ message));
+ return true;
+ }
+
+ message->set_routing_id(route_id_);
+ return channel_->Send(message);
+}
+
+bool AudioInputMessageFilter::OnMessageReceived(const IPC::Message& message) {
+ if (message.routing_id() != route_id_)
+ return false;
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AudioInputMessageFilter, message)
+ IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyLowLatencyStreamCreated,
+ OnLowLatencyStreamCreated)
+ IPC_MESSAGE_HANDLER(AudioInputMsg_NotifyStreamVolume, OnStreamVolume)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void AudioInputMessageFilter::OnFilterAdded(IPC::Channel* channel) {
+ VLOG(1) << "AudioInputMessageFilter::OnFilterAdded()";
+ // Captures the message loop for IPC.
+ message_loop_ = MessageLoop::current();
+ channel_ = channel;
+}
+
+void AudioInputMessageFilter::OnFilterRemoved() {
+ channel_ = NULL;
+}
+
+void AudioInputMessageFilter::OnChannelClosing() {
+ channel_ = NULL;
+}
+
+void AudioInputMessageFilter::OnLowLatencyStreamCreated(
+ int stream_id,
+ base::SharedMemoryHandle handle,
+#if defined(OS_WIN)
+ base::SyncSocket::Handle socket_handle,
+#else
+ base::FileDescriptor socket_descriptor,
+#endif
+ uint32 length) {
+ Delegate* delegate = delegates_.Lookup(stream_id);
+ if (!delegate) {
+ DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
+ " audio capturer.";
+ return;
+ }
+#if !defined(OS_WIN)
+ base::SyncSocket::Handle socket_handle = socket_descriptor.fd;
+#endif
+ // Forward message to the stream delegate.
+ delegate->OnLowLatencyCreated(handle, socket_handle, length);
+}
+
+void AudioInputMessageFilter::OnStreamVolume(int stream_id, double volume) {
+ Delegate* delegate = delegates_.Lookup(stream_id);
+ if (!delegate) {
+ DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
+ " audio capturer.";
+ return;
+ }
+ delegate->OnVolume(volume);
+}
+
+int32 AudioInputMessageFilter::AddDelegate(Delegate* delegate) {
+ return delegates_.Add(delegate);
+}
+
+void AudioInputMessageFilter::RemoveDelegate(int32 id) {
+ delegates_.Remove(id);
+}
diff --git a/content/renderer/media/audio_input_message_filter.h b/content/renderer/media/audio_input_message_filter.h
new file mode 100644
index 0000000..3721e83
--- /dev/null
+++ b/content/renderer/media/audio_input_message_filter.h
@@ -0,0 +1,90 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// MessageFilter that handles audio input messages and delegates them to
+// audio capturers. Created on render thread, AudioMessageFilter is operated on
+// IO thread (main thread of render process), it intercepts audio messages
+// and process them on IO thread since these messages are time critical.
+// This implementation only supports low-latency (based on SyncSocket)
+// messaging.
+
+#ifndef CONTENT_RENDERER_MEDIA_AUDIO_INPUT_MESSAGE_FILTER_H_
+#define CONTENT_RENDERER_MEDIA_AUDIO_INPUT_MESSAGE_FILTER_H_
+#pragma once
+
+#include "base/id_map.h"
+#include "base/shared_memory.h"
+#include "base/sync_socket.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "media/audio/audio_buffers_state.h"
+
+class MessageLoop;
+
+class AudioInputMessageFilter : public IPC::ChannelProxy::MessageFilter {
+ public:
+ class Delegate {
+ public:
+ // Called when a low-latency audio input stream has been created in the
+ // browser process.
+ virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle,
+ base::SyncSocket::Handle socket_handle,
+ uint32 length) = 0;
+
+ // Called when notification of input stream volume is received from the
+ // browser process.
+ virtual void OnVolume(double volume) = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ explicit AudioInputMessageFilter(int32 route_id);
+ virtual ~AudioInputMessageFilter();
+
+ // Add a delegate to the map and return id of the entry.
+ int32 AddDelegate(Delegate* delegate);
+
+ // Remove a delegate referenced by |id| from the map.
+ void RemoveDelegate(int32 id);
+
+ // Sends an IPC message using |channel_|.
+ bool Send(IPC::Message* message);
+
+ MessageLoop* message_loop() { return message_loop_; }
+
+ private:
+ // TODO(henrika): add unit tests (compare with AudioMessageFilter)
+
+ // IPC::ChannelProxy::MessageFilter override. Called on IO thread.
+ virtual bool OnMessageReceived(const IPC::Message& message);
+ virtual void OnFilterAdded(IPC::Channel* channel);
+ virtual void OnFilterRemoved();
+ virtual void OnChannelClosing();
+
+ // Received when browser process has created an audio input stream of low
+ // latency.
+ void OnLowLatencyStreamCreated(int stream_id, base::SharedMemoryHandle handle,
+#if defined(OS_WIN)
+ base::SyncSocket::Handle socket_handle,
+#else
+ base::FileDescriptor socket_descriptor,
+#endif
+ uint32 length);
+
+ // Notification of volume property of an audio input stream.
+ void OnStreamVolume(int stream_id, double volume);
+
+ // A map of stream ids to delegates.
+ IDMap<Delegate> delegates_;
+
+ IPC::Channel* channel_;
+
+ int32 route_id_;
+
+ MessageLoop* message_loop_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(AudioInputMessageFilter);
+};
+
+#endif // CONTENT_RENDERER_MEDIA_AUDIO_INPUT_MESSAGE_FILTER_H_
diff --git a/content/renderer/media/audio_message_filter.cc b/content/renderer/media/audio_message_filter.cc
new file mode 100644
index 0000000..a906a9c
--- /dev/null
+++ b/content/renderer/media/audio_message_filter.cc
@@ -0,0 +1,144 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/media/audio_message_filter.h"
+
+#include "base/message_loop.h"
+#include "base/time.h"
+#include "content/common/audio_messages.h"
+#include "ipc/ipc_logging.h"
+
+AudioMessageFilter::AudioMessageFilter(int32 route_id)
+ : channel_(NULL),
+ route_id_(route_id),
+ message_loop_(NULL) {
+}
+
+AudioMessageFilter::~AudioMessageFilter() {
+}
+
+// Called on the IPC thread.
+bool AudioMessageFilter::Send(IPC::Message* message) {
+ if (!channel_) {
+ delete message;
+ return false;
+ }
+
+ if (MessageLoop::current() != message_loop_) {
+ // Can only access the IPC::Channel on the IPC thread since it's not thread
+ // safe.
+ message_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &AudioMessageFilter::Send, message));
+ return true;
+ }
+
+ message->set_routing_id(route_id_);
+ return channel_->Send(message);
+}
+
+bool AudioMessageFilter::OnMessageReceived(const IPC::Message& message) {
+ if (message.routing_id() != route_id_)
+ return false;
+
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AudioMessageFilter, message)
+ IPC_MESSAGE_HANDLER(AudioMsg_RequestPacket, OnRequestPacket)
+ IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamCreated, OnStreamCreated)
+ IPC_MESSAGE_HANDLER(AudioMsg_NotifyLowLatencyStreamCreated,
+ OnLowLatencyStreamCreated)
+ IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamStateChanged, OnStreamStateChanged)
+ IPC_MESSAGE_HANDLER(AudioMsg_NotifyStreamVolume, OnStreamVolume)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void AudioMessageFilter::OnFilterAdded(IPC::Channel* channel) {
+ // Captures the message loop for IPC.
+ message_loop_ = MessageLoop::current();
+ channel_ = channel;
+}
+
+void AudioMessageFilter::OnFilterRemoved() {
+ channel_ = NULL;
+}
+
+void AudioMessageFilter::OnChannelClosing() {
+ channel_ = NULL;
+}
+
+void AudioMessageFilter::OnRequestPacket(const IPC::Message& msg,
+ int stream_id,
+ AudioBuffersState buffers_state) {
+ Delegate* delegate = delegates_.Lookup(stream_id);
+ if (!delegate) {
+ DLOG(WARNING) << "Got audio packet request for a non-existent or removed"
+ " audio renderer.";
+ return;
+ }
+
+ delegate->OnRequestPacket(buffers_state);
+}
+
+void AudioMessageFilter::OnStreamCreated(int stream_id,
+ base::SharedMemoryHandle handle,
+ uint32 length) {
+ Delegate* delegate = delegates_.Lookup(stream_id);
+ if (!delegate) {
+ DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
+ " audio renderer.";
+ return;
+ }
+ delegate->OnCreated(handle, length);
+}
+
+void AudioMessageFilter::OnLowLatencyStreamCreated(
+ int stream_id,
+ base::SharedMemoryHandle handle,
+#if defined(OS_WIN)
+ base::SyncSocket::Handle socket_handle,
+#else
+ base::FileDescriptor socket_descriptor,
+#endif
+ uint32 length) {
+ Delegate* delegate = delegates_.Lookup(stream_id);
+ if (!delegate) {
+ DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
+ " audio renderer.";
+ return;
+ }
+#if !defined(OS_WIN)
+ base::SyncSocket::Handle socket_handle = socket_descriptor.fd;
+#endif
+ delegate->OnLowLatencyCreated(handle, socket_handle, length);
+}
+
+void AudioMessageFilter::OnStreamStateChanged(
+ int stream_id, AudioStreamState state) {
+ Delegate* delegate = delegates_.Lookup(stream_id);
+ if (!delegate) {
+ DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
+ " audio renderer.";
+ return;
+ }
+ delegate->OnStateChanged(state);
+}
+
+void AudioMessageFilter::OnStreamVolume(int stream_id, double volume) {
+ Delegate* delegate = delegates_.Lookup(stream_id);
+ if (!delegate) {
+ DLOG(WARNING) << "Got audio stream event for a non-existent or removed"
+ " audio renderer.";
+ return;
+ }
+ delegate->OnVolume(volume);
+}
+
+int32 AudioMessageFilter::AddDelegate(Delegate* delegate) {
+ return delegates_.Add(delegate);
+}
+
+void AudioMessageFilter::RemoveDelegate(int32 id) {
+ delegates_.Remove(id);
+}
diff --git a/content/renderer/media/audio_message_filter.h b/content/renderer/media/audio_message_filter.h
new file mode 100644
index 0000000..e4d4db0
--- /dev/null
+++ b/content/renderer/media/audio_message_filter.h
@@ -0,0 +1,120 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// MessageFilter that handles audio messages and delegates them to audio
+// renderers. Created on render thread, AudioMessageFilter is operated on
+// IO thread (main thread of render process), it intercepts audio messages
+// and process them on IO thread since these messages are time critical.
+
+#ifndef CONTENT_RENDERER_MEDIA_AUDIO_MESSAGE_FILTER_H_
+#define CONTENT_RENDERER_MEDIA_AUDIO_MESSAGE_FILTER_H_
+#pragma once
+
+#include "base/gtest_prod_util.h"
+#include "base/id_map.h"
+#include "base/shared_memory.h"
+#include "base/sync_socket.h"
+#include "content/common/audio_stream_state.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "media/audio/audio_buffers_state.h"
+
+class MessageLoop;
+
+namespace base {
+class Time;
+}
+
+class AudioMessageFilter : public IPC::ChannelProxy::MessageFilter {
+ public:
+ class Delegate {
+ public:
+ // Called when an audio packet is requested from the browser process.
+ virtual void OnRequestPacket(AudioBuffersState buffers_state) = 0;
+
+ // Called when state of an audio stream has changed in the browser process.
+ virtual void OnStateChanged(AudioStreamState state) = 0;
+
+ // Called when an audio stream has been created in the browser process.
+ virtual void OnCreated(base::SharedMemoryHandle handle, uint32 length) = 0;
+
+ // Called when a low-latency audio stream has been created in the browser
+ // process.
+ virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle,
+ base::SyncSocket::Handle socket_handle,
+ uint32 length) = 0;
+
+ // Called when notification of stream volume is received from the browser
+ // process.
+ virtual void OnVolume(double volume) = 0;
+
+ protected:
+ virtual ~Delegate() {}
+ };
+
+ explicit AudioMessageFilter(int32 route_id);
+ virtual ~AudioMessageFilter();
+
+ // Add a delegate to the map and return id of the entry.
+ int32 AddDelegate(Delegate* delegate);
+
+ // Remove a delegate referenced by |id| from the map.
+ void RemoveDelegate(int32 id);
+
+ // Sends an IPC message using |channel_|.
+ bool Send(IPC::Message* message);
+
+ MessageLoop* message_loop() { return message_loop_; }
+
+ private:
+ // For access to |message_loop_|.
+ friend class AudioRendererImplTest;
+
+ FRIEND_TEST_ALL_PREFIXES(AudioMessageFilterTest, Basic);
+ FRIEND_TEST_ALL_PREFIXES(AudioMessageFilterTest, Delegates);
+
+ // IPC::ChannelProxy::MessageFilter override. Called on IO thread.
+ virtual bool OnMessageReceived(const IPC::Message& message);
+ virtual void OnFilterAdded(IPC::Channel* channel);
+ virtual void OnFilterRemoved();
+ virtual void OnChannelClosing();
+
+ // Received when browser process wants more audio packet.
+ void OnRequestPacket(const IPC::Message& msg, int stream_id,
+ AudioBuffersState buffers_state);
+
+ // Received when browser process has created an audio output stream.
+ void OnStreamCreated(int stream_id, base::SharedMemoryHandle handle,
+ uint32 length);
+
+ // Received when browser process has created an audio output stream of low
+ // latency.
+ void OnLowLatencyStreamCreated(int stream_id, base::SharedMemoryHandle handle,
+#if defined(OS_WIN)
+ base::SyncSocket::Handle socket_handle,
+#else
+ base::FileDescriptor socket_descriptor,
+#endif
+ uint32 length);
+
+
+ // Received when internal state of browser process' audio output device has
+ // changed.
+ void OnStreamStateChanged(int stream_id, AudioStreamState state);
+
+ // Notification of volume property of an audio output stream.
+ void OnStreamVolume(int stream_id, double volume);
+
+ // A map of stream ids to delegates.
+ IDMap<Delegate> delegates_;
+
+ IPC::Channel* channel_;
+
+ int32 route_id_;
+
+ MessageLoop* message_loop_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioMessageFilter);
+};
+
+#endif // CONTENT_RENDERER_MEDIA_AUDIO_MESSAGE_FILTER_H_
diff --git a/content/renderer/media/audio_message_filter_unittest.cc b/content/renderer/media/audio_message_filter_unittest.cc
new file mode 100644
index 0000000..37da0a6
--- /dev/null
+++ b/content/renderer/media/audio_message_filter_unittest.cc
@@ -0,0 +1,197 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "content/common/audio_messages.h"
+#include "content/renderer/media/audio_message_filter.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MockAudioDelegate : public AudioMessageFilter::Delegate {
+ public:
+ MockAudioDelegate() {
+ Reset();
+ }
+
+ virtual void OnRequestPacket(AudioBuffersState buffers_state) {
+ request_packet_received_ = true;
+ buffers_state_ = buffers_state;
+ }
+
+ virtual void OnStateChanged(AudioStreamState state) {
+ state_changed_received_ = true;
+ state_ = state;
+ }
+
+ virtual void OnCreated(base::SharedMemoryHandle handle, uint32 length) {
+ created_received_ = true;
+ handle_ = handle;
+ length_ = length;
+ }
+
+ virtual void OnLowLatencyCreated(base::SharedMemoryHandle handle,
+ base::SyncSocket::Handle,
+ uint32 length) {
+ }
+
+ virtual void OnVolume(double volume) {
+ volume_received_ = true;
+ volume_ = volume;
+ }
+
+ void Reset() {
+ request_packet_received_ = false;
+ buffers_state_ = AudioBuffersState();
+ buffers_state_.timestamp = base::Time();
+
+ state_changed_received_ = false;
+ state_ = kAudioStreamError;
+
+ created_received_ = false;
+ handle_ = base::SharedMemory::NULLHandle();
+ length_ = 0;
+
+ volume_received_ = false;
+ volume_ = 0;
+ }
+
+ bool request_packet_received() { return request_packet_received_; }
+ AudioBuffersState buffers_state() { return buffers_state_; }
+
+ bool state_changed_received() { return state_changed_received_; }
+ AudioStreamState state() { return state_; }
+
+ bool created_received() { return created_received_; }
+ base::SharedMemoryHandle handle() { return handle_; }
+ uint32 length() { return length_; }
+
+ bool volume_received() { return volume_received_; }
+ double volume() { return volume_; }
+
+ private:
+ bool request_packet_received_;
+ AudioBuffersState buffers_state_;
+
+ bool state_changed_received_;
+ AudioStreamState state_;
+
+ bool created_received_;
+ base::SharedMemoryHandle handle_;
+ uint32 length_;
+
+ bool volume_received_;
+ double volume_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockAudioDelegate);
+};
+
+} // namespace
+
+TEST(AudioMessageFilterTest, Basic) {
+ MessageLoop message_loop(MessageLoop::TYPE_IO);
+
+ const int kRouteId = 0;
+ scoped_refptr<AudioMessageFilter> filter(new AudioMessageFilter(kRouteId));
+
+ MockAudioDelegate delegate;
+ int stream_id = filter->AddDelegate(&delegate);
+
+ // AudioMsg_RequestPacket
+ const int kSizeInBuffer = 1024;
+ AudioBuffersState buffers_state(kSizeInBuffer, 0);
+
+ EXPECT_FALSE(delegate.request_packet_received());
+ filter->OnMessageReceived(AudioMsg_RequestPacket(
+ kRouteId, stream_id, buffers_state));
+ EXPECT_TRUE(delegate.request_packet_received());
+ EXPECT_EQ(kSizeInBuffer, delegate.buffers_state().pending_bytes);
+ EXPECT_EQ(0, delegate.buffers_state().hardware_delay_bytes);
+ EXPECT_TRUE(buffers_state.timestamp == delegate.buffers_state().timestamp);
+ delegate.Reset();
+
+ // AudioMsg_NotifyStreamStateChanged
+ EXPECT_FALSE(delegate.state_changed_received());
+ filter->OnMessageReceived(
+ AudioMsg_NotifyStreamStateChanged(kRouteId, stream_id,
+ kAudioStreamPlaying));
+ EXPECT_TRUE(delegate.state_changed_received());
+ EXPECT_TRUE(kAudioStreamPlaying == delegate.state());
+ delegate.Reset();
+
+ // AudioMsg_NotifyStreamCreated
+ const uint32 kLength = 1024;
+ EXPECT_FALSE(delegate.created_received());
+ filter->OnMessageReceived(
+ AudioMsg_NotifyStreamCreated(kRouteId,
+ stream_id,
+ base::SharedMemory::NULLHandle(),
+ kLength));
+ EXPECT_TRUE(delegate.created_received());
+ EXPECT_FALSE(base::SharedMemory::IsHandleValid(delegate.handle()));
+ EXPECT_EQ(kLength, delegate.length());
+ delegate.Reset();
+
+ // AudioMsg_NotifyStreamVolume
+ const double kVolume = 1.0;
+ EXPECT_FALSE(delegate.volume_received());
+ filter->OnMessageReceived(
+ AudioMsg_NotifyStreamVolume(kRouteId, stream_id, kVolume));
+ EXPECT_TRUE(delegate.volume_received());
+ EXPECT_EQ(kVolume, delegate.volume());
+ delegate.Reset();
+
+ message_loop.RunAllPending();
+}
+
+TEST(AudioMessageFilterTest, Delegates) {
+ MessageLoop message_loop(MessageLoop::TYPE_IO);
+
+ const int kRouteId = 0;
+ scoped_refptr<AudioMessageFilter> filter(new AudioMessageFilter(kRouteId));
+
+ MockAudioDelegate delegate1;
+ MockAudioDelegate delegate2;
+
+ int stream_id1 = filter->AddDelegate(&delegate1);
+ int stream_id2 = filter->AddDelegate(&delegate2);
+
+ // Send an IPC message. Make sure the correct delegate gets called.
+ EXPECT_FALSE(delegate1.request_packet_received());
+ EXPECT_FALSE(delegate2.request_packet_received());
+ filter->OnMessageReceived(
+ AudioMsg_RequestPacket(kRouteId, stream_id1, AudioBuffersState()));
+ EXPECT_TRUE(delegate1.request_packet_received());
+ EXPECT_FALSE(delegate2.request_packet_received());
+ delegate1.Reset();
+
+ EXPECT_FALSE(delegate1.request_packet_received());
+ EXPECT_FALSE(delegate2.request_packet_received());
+ filter->OnMessageReceived(
+ AudioMsg_RequestPacket(kRouteId, stream_id2, AudioBuffersState()));
+ EXPECT_FALSE(delegate1.request_packet_received());
+ EXPECT_TRUE(delegate2.request_packet_received());
+ delegate2.Reset();
+
+ // Send a message of a different route id, a message is not received.
+ EXPECT_FALSE(delegate1.request_packet_received());
+ filter->OnMessageReceived(
+ AudioMsg_RequestPacket(kRouteId + 1, stream_id1, AudioBuffersState()));
+ EXPECT_FALSE(delegate1.request_packet_received());
+
+ // Remove the delegates. Make sure they won't get called.
+ filter->RemoveDelegate(stream_id1);
+ EXPECT_FALSE(delegate1.request_packet_received());
+ filter->OnMessageReceived(
+ AudioMsg_RequestPacket(kRouteId, stream_id1, AudioBuffersState()));
+ EXPECT_FALSE(delegate1.request_packet_received());
+
+ filter->RemoveDelegate(stream_id2);
+ EXPECT_FALSE(delegate2.request_packet_received());
+ filter->OnMessageReceived(
+ AudioMsg_RequestPacket(kRouteId, stream_id2, AudioBuffersState()));
+ EXPECT_FALSE(delegate2.request_packet_received());
+
+ message_loop.RunAllPending();
+}
diff --git a/content/renderer/media/audio_renderer_impl.h b/content/renderer/media/audio_renderer_impl.h
index c583c05..72c0ef6 100644
--- a/content/renderer/media/audio_renderer_impl.h
+++ b/content/renderer/media/audio_renderer_impl.h
@@ -43,7 +43,7 @@
#include "base/message_loop.h"
#include "base/shared_memory.h"
#include "base/synchronization/lock.h"
-#include "content/renderer/audio_message_filter.h"
+#include "content/renderer/media/audio_message_filter.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_manager.h"
#include "media/base/filters.h"