diff options
author | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-07 21:32:31 +0000 |
---|---|---|
committer | hclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-07 21:32:31 +0000 |
commit | 8cf0758dc5b5863bf1fef28e0ca748aabe074c17 (patch) | |
tree | ab61d069f29a508c41ac90a8326b1d7a8be66bc4 | |
parent | 110f48f0a6c0cc8af1d449119be2f540d81f75f5 (diff) | |
download | chromium_src-8cf0758dc5b5863bf1fef28e0ca748aabe074c17.zip chromium_src-8cf0758dc5b5863bf1fef28e0ca748aabe074c17.tar.gz chromium_src-8cf0758dc5b5863bf1fef28e0ca748aabe074c17.tar.bz2 |
Fix some threading issues and some refactoring in AudioRendererHost
1. AudioRendererHost should not wait on IO thread.
Calling AudioOutputStream::Start requests some
audio packets by calling IPCAudioSource::OnMoreData,
resulting in a deadlock. I implemented a prefetching
logic to solve this problem, source has to know
how many internal buffers the implementation of
AudioOutputStream is using and prepare the
same amount of packets before calling
AudioOutputStream::Start.
2. When IPCAudioSource::OnMoreData is waiting for
packet to arrive, calling AudioOutputStream::Stop
will result in a deadlock since it expects
OnMoreData to return. Should wake up all waiting
threads before calling AudioOutputStream::Stop.
3. Did some refactoring and move the message handlers
completely into AudioRendererHost, it was clumsy
to call to AudioRendererHost from ResourceMessageFilter.
Also moved all logics of using shared memory for
packet passing, prefetching to IPCAudioSource.
AudioRendererHost is now merely a container that
manages and delegates calls to IPCAudioSource. This
refactoring is helpful because the next step is to
implement standalone IPC channel for audio.
Review URL: http://codereview.chromium.org/57023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13288 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/renderer_host/audio_renderer_host.cc | 396 | ||||
-rw-r--r-- | chrome/browser/renderer_host/audio_renderer_host.h | 194 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_message_filter.cc | 48 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_message_filter.h | 12 | ||||
-rw-r--r-- | chrome/common/render_messages.h | 19 | ||||
-rw-r--r-- | media/audio/audio_output.h | 14 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac.cc | 8 | ||||
-rw-r--r-- | media/audio/mac/audio_output_mac.h | 11 | ||||
-rw-r--r-- | media/audio/win/audio_output_win.cc | 4 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 4 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.h | 1 |
11 files changed, 471 insertions, 240 deletions
diff --git a/chrome/browser/renderer_host/audio_renderer_host.cc b/chrome/browser/renderer_host/audio_renderer_host.cc index 58f2f40..8ee88e6 100644 --- a/chrome/browser/renderer_host/audio_renderer_host.cc +++ b/chrome/browser/renderer_host/audio_renderer_host.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/lock.h" #include "base/message_loop.h" #include "base/process.h" #include "base/shared_memory.h" @@ -14,75 +15,246 @@ AudioRendererHost::IPCAudioSource::IPCAudioSource( AudioRendererHost* host, - int32 render_view_id, - int32 stream_id, + int process_id, + int route_id, + int stream_id, AudioOutputStream* stream, size_t packet_size) : host_(host), - render_view_id_(render_view_id), + process_id_(process_id), + route_id_(route_id), stream_id_(stream_id), stream_(stream), - closed_(false), + packet_size_(packet_size), + state_(AudioOutputStream::STATE_CREATED), + stop_providing_packets_(false), packet_read_event_(false, false), last_packet_size_(0) { - // Make sure we can create and map the shared memory. - DCHECK(shared_memory_.Create(L"", false, false, packet_size) && - shared_memory_.Map(packet_size)); } AudioRendererHost::IPCAudioSource::~IPCAudioSource() { } +// static +AudioRendererHost::IPCAudioSource* + AudioRendererHost::IPCAudioSource::CreateIPCAudioSource( + AudioRendererHost* host, + int process_id, + int route_id, + int stream_id, + base::ProcessHandle process_handle, + AudioManager::Format format, + int channels, + int sample_rate, + char bits_per_sample, + size_t packet_size) { + // Create the stream in the first place. + AudioOutputStream* stream = + AudioManager::GetAudioManager()->MakeAudioStream( + format, channels, sample_rate, bits_per_sample); + if (stream && !stream->Open(packet_size)) { + stream->Close(); + stream = NULL; + } + + if (stream) { + IPCAudioSource* source = new IPCAudioSource( + host, process_id, route_id, stream_id, stream, packet_size); + // If we can open the stream, proceed with sharing the shared memory. + base::SharedMemoryHandle foreign_memory_handle; + + // Try to create, map and share the memory for the renderer process. + // If they all succeeded then send a message to renderer to indicate + // success. + if (source->shared_memory_.Create(L"", false, false, packet_size) && + source->shared_memory_.Map(packet_size) && + source->shared_memory_.ShareToProcess(process_handle, + &foreign_memory_handle)) { + host->Send(new ViewMsg_NotifyAudioStreamCreated( + route_id, stream_id, foreign_memory_handle, packet_size)); + return source; + } + + source->Close(); + delete source; + } + + host->SendErrorMessage(route_id, stream_id, 0); + return NULL; +} + +void AudioRendererHost::IPCAudioSource::Start() { + // Only perform the start logic if this source has just created. + if (!stream_ || state_ != AudioOutputStream::STATE_CREATED) + return; + + // We don't start the stream immediately but prefetch some initial buffers + // so as to fill all internal buffers of the AudioOutputStream. The number + // of buffers to prefetch can be determined by + // AudioOutputStream::GetNumBuffers(). + if (stream_->GetNumBuffers()) { + // If the audio output stream does have internal buffer(s), request a + // packet from renderer and start the prefetching. + host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_)); + } else { + // If the audio output stream does not use any internal buffers, we are + // safe to start it here. + state_ = AudioOutputStream::STATE_STARTED; + stream_->Start(this); + host_->Send(new ViewMsg_NotifyAudioStreamStateChanged( + route_id_, stream_id_, AudioOutputStream::STATE_STARTED, 0)); + } +} + +void AudioRendererHost::IPCAudioSource::Close() { + // We need to wake up all waiting audio thread before calling stop. + StopWaitingForPacket(); + + if (!stream_) + return; + stream_->Stop(); + stream_->Close(); + // After stream is closed it is destroyed, so don't keep a reference to it. + stream_ = NULL; +} + +void AudioRendererHost::IPCAudioSource::SetVolume(double left, double right) { + // TODO(hclam): maybe send an error message back to renderer if this object + // is in a wrong state. + if (!stream_) + return; + stream_->SetVolume(left, right); +} + +void AudioRendererHost::IPCAudioSource::GetVolume() { + // TODO(hclam): maybe send an error message back to renderer if this object + // is in a wrong state. + if (!stream_) + return; + double left_channel, right_channel; + stream_->GetVolume(&left_channel, &right_channel); + host_->Send(new ViewMsg_NotifyAudioStreamVolume(route_id_, stream_id_, + left_channel, right_channel)); +} + size_t AudioRendererHost::IPCAudioSource::OnMoreData(AudioOutputStream* stream, void* dest, size_t max_size) { - host_->Send(new ViewMsg_RequestAudioPacket(render_view_id_, stream_id_)); + { + AutoLock auto_lock(lock_); + // If we are ever stopped, don't ask for more audio packet from the + // renderer. + if (stop_providing_packets_) + return 0; + } + + // If we have an initial packet, use it immediately only in IO thread. + // There's a case when IO thread is blocked and audio hardware thread can + // reach here to consume initial packets. + if (MessageLoop::current() == host_->io_loop()) { + if (!initial_buffers_.empty()) { + uint8* initial_packet = initial_buffers_.front().first; + size_t initial_packet_size = initial_buffers_.front().second; + initial_buffers_.pop_front(); + size_t copied = + SafeCopyBuffer(dest, max_size, initial_packet, initial_packet_size); + delete [] initial_packet; + return copied; + } + NOTREACHED(); + } + + // We reach here because we ran out of initial packets, we need to ask the + // renderer to give us more. In this case we have to wait until the renderer + // gives us packet so we can't sleep on IO thread. + DCHECK(MessageLoop::current() != host_->io_loop()); + + // Send an IPC message to request audio packet from renderer and wait on the + // audio hardware thread. + host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_)); packet_read_event_.Wait(); - if (closed_) - return 0; - // Make sure it's safe to copy. - if (last_packet_size_ > max_size){ - host_->SendErrorMessage(render_view_id_, stream_id_, 0); - host_->DestroySource(this); - return 0; + + size_t last_packet_size = 0; + { + AutoLock auto_lock(lock_); + last_packet_size = last_packet_size_; } - memcpy(dest, shared_memory_.memory(), last_packet_size_); - return last_packet_size_; + + return SafeCopyBuffer(dest, max_size, + shared_memory_.memory(), last_packet_size); } void AudioRendererHost::IPCAudioSource::OnClose(AudioOutputStream* stream) { - closed_ = true; - packet_read_event_.Signal(); + StopWaitingForPacket(); } void AudioRendererHost::IPCAudioSource::OnError(AudioOutputStream* stream, int code) { - host_->SendErrorMessage(render_view_id_, stream_id_, code); + host_->SendErrorMessage(route_id_, stream_id_, code); // The following method call would cause this object to be destroyed on IO // thread. host_->DestroySource(this); } void AudioRendererHost::IPCAudioSource::NotifyPacketReady(size_t packet_size) { - // If reported size is greater than capacity of the shared memory, close the - // stream. - if (packet_size > shared_memory_.max_size()) { - host_->SendErrorMessage(render_view_id_, stream_id_, 0); + if (packet_size > packet_size_) { + // If reported size is greater than capacity of the shared memory, close the + // stream. + host_->SendErrorMessage(route_id_, stream_id_, 0); // We don't need to do packet_read_event_.Signal() here because the // contained stream should be closed by the following call and OnClose will // be received. host_->DestroySource(this); + return; + } + + if (state_ == AudioOutputStream::STATE_CREATED) { + // If we are in a created state, that means we are performing prefetching. + uint8* packet = new uint8[packet_size]; + memcpy(packet, shared_memory_.memory(), packet_size); + initial_buffers_.push_back(std::make_pair(packet, packet_size)); + // If there's not enough initial packets prepared, ask more. + if (initial_buffers_.size() < stream_->GetNumBuffers()) { + host_->Send(new ViewMsg_RequestAudioPacket(route_id_, stream_id_)); + } else { + state_ = AudioOutputStream::STATE_STARTED; + stream_->Start(this); + host_->Send(new ViewMsg_NotifyAudioStreamStateChanged( + route_id_, stream_id_, AudioOutputStream::STATE_STARTED, 0)); + } } else { + AutoLock auto_lock(lock_); last_packet_size_ = packet_size; packet_read_event_.Signal(); } } +void AudioRendererHost::IPCAudioSource::StopWaitingForPacket() { + AutoLock auto_lock(lock_); + stop_providing_packets_ = true; + last_packet_size_ = 0; + packet_read_event_.Signal(); +} + +size_t AudioRendererHost::IPCAudioSource::SafeCopyBuffer( + void* dest, size_t dest_size, const void* src, size_t src_size) { + if (src_size > dest_size) { + host_->SendErrorMessage(route_id_, stream_id_, 0); + host_->DestroySource(this); + return 0; + } + memcpy(dest, src, src_size); + return src_size; +} + //----------------------------------------------------------------------------- // AudioRendererHost implementations. AudioRendererHost::AudioRendererHost(MessageLoop* message_loop) - : io_loop_(message_loop) { + : process_id_(0), + process_handle_(0), + ipc_sender_(NULL), + io_loop_(message_loop) { // Make sure we perform actual initialization operations in the thread where // this object should live. io_loop_->PostTask(FROM_HERE, @@ -99,124 +271,147 @@ void AudioRendererHost::Destroy() { FROM_HERE, NewRunnableMethod(this, &AudioRendererHost::OnDestroyed)); } -void AudioRendererHost::CreateStream( - base::ProcessHandle process_handle, int32 render_view_id, int32 stream_id, - AudioManager::Format format, int channels, int sample_rate, - int bits_per_sample, size_t packet_size) { +// Event received when IPC channel is connected to the renderer process. +void AudioRendererHost::IPCChannelConnected(int process_id, + base::ProcessHandle process_handle, + IPC::Message::Sender* ipc_sender) { DCHECK(MessageLoop::current() == io_loop_); - DCHECK(Lookup(render_view_id, stream_id) == NULL); + process_id_ = process_id; + process_handle_ = process_handle; + ipc_sender_ = ipc_sender; +} - // Create the stream in the first place. - AudioOutputStream* stream = AudioManager::GetAudioManager()->MakeAudioStream( - format, channels, sample_rate, bits_per_sample); - if (!stream) - return; - if (!stream->Open(packet_size)) { - stream->Close(); - stream = NULL; - return; - } +// Event received when IPC channel is closing. +void AudioRendererHost::IPCChannelClosing() { + DCHECK(MessageLoop::current() == io_loop_); + ipc_sender_ = NULL; + process_handle_ = 0; + process_id_ = 0; + DestroyAllSources(); +} - IPCAudioSource* source = new IPCAudioSource( - this, render_view_id, stream_id, stream, packet_size); - // If we can open the stream, proceed with sharing the shared memory. - base::SharedMemoryHandle foreign_memory_handle; - if (source->shared_memory()->ShareToProcess(process_handle, - &foreign_memory_handle)) { - sources_.insert(make_pair(SourceID(render_view_id, stream_id), source)); - Send(new ViewMsg_NotifyAudioStreamCreated( - render_view_id, stream_id, foreign_memory_handle, packet_size)); - } else { - DestroySource(source); - SendErrorMessage(render_view_id, stream_id, 0); +bool AudioRendererHost::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + if (!IsAudioRendererHostMessage(message)) + return false; + *message_was_ok = true; + + IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost, message, *message_was_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_CreateAudioStream, OnCreateStream) + IPC_MESSAGE_HANDLER(ViewHostMsg_StartAudioStream, OnStartStream) + IPC_MESSAGE_HANDLER(ViewHostMsg_CloseAudioStream, OnCloseStream) + IPC_MESSAGE_HANDLER(ViewHostMsg_NotifyAudioPacketReady, OnNotifyPacketReady) + IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioVolume, OnGetVolume) + IPC_MESSAGE_HANDLER(ViewHostMsg_SetAudioVolume, OnSetVolume) + IPC_END_MESSAGE_MAP_EX() + + return true; +} + +bool AudioRendererHost::IsAudioRendererHostMessage( + const IPC::Message& message) { + switch (message.type()) { + case ViewHostMsg_CreateAudioStream::ID: + case ViewHostMsg_StartAudioStream::ID: + case ViewHostMsg_CloseAudioStream::ID: + case ViewHostMsg_NotifyAudioPacketReady::ID: + case ViewHostMsg_GetAudioVolume::ID: + case ViewHostMsg_SetAudioVolume::ID: + return true; + default: + break; + } + return false; +} + +void AudioRendererHost::OnCreateStream( + const IPC::Message& msg, int stream_id, + const ViewHostMsg_Audio_CreateStream& params) { + DCHECK(MessageLoop::current() == io_loop_); + DCHECK(Lookup(msg.routing_id(), stream_id) == NULL); + + IPCAudioSource* source = IPCAudioSource::CreateIPCAudioSource( + this, + process_id_, + msg.routing_id(), + stream_id, + process_handle_, + params.format, + params.channels, + params.sample_rate, + params.bits_per_sample, + params.packet_size); + + // If we have created the source successfully, adds it to the map. + if (source) { + sources_.insert( + std::make_pair( + SourceID(source->route_id(), source->stream_id()), source)); } } -void AudioRendererHost::Start(int32 render_view_id, int32 stream_id) { +void AudioRendererHost::OnStartStream(const IPC::Message& msg, int stream_id) { DCHECK(MessageLoop::current() == io_loop_); - IPCAudioSource* source = Lookup(render_view_id, stream_id); + IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); if (source) { - source->stream()->Start(source); - Send(new ViewMsg_NotifyAudioStreamStateChanged( - render_view_id, stream_id, AudioOutputStream::STATE_STARTED, 0)); + source->Start(); } else { - SendErrorMessage(render_view_id, stream_id, 0); + SendErrorMessage(msg.routing_id(), stream_id, 0); } } -void AudioRendererHost::Close(int32 render_view_id, int32 stream_id) { +void AudioRendererHost::OnCloseStream(const IPC::Message& msg, int stream_id) { DCHECK(MessageLoop::current() == io_loop_); - IPCAudioSource* source = Lookup(render_view_id, stream_id); - // When we get called here, audio renderer in renderer process has been - // destroyed, we don't bother to an error message back, just destroy if we - // can. + IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); if (source) { DestroySource(source); } } -void AudioRendererHost::SetVolume(int32 render_view_id, int32 stream_id, - double left_channel, double right_channel) { +void AudioRendererHost::OnSetVolume(const IPC::Message& msg, int stream_id, + double left_channel, double right_channel) { DCHECK(MessageLoop::current() == io_loop_); - IPCAudioSource* source = Lookup(render_view_id, stream_id); + IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); if (source) { - source->stream()->SetVolume(left_channel, right_channel); + source->SetVolume(left_channel, right_channel); } else { - SendErrorMessage(render_view_id, stream_id, 0); + SendErrorMessage(msg.routing_id(), stream_id, 0); } } -void AudioRendererHost::GetVolume(int32 render_view_id, int32 stream_id) { +void AudioRendererHost::OnGetVolume(const IPC::Message& msg, int stream_id) { DCHECK(MessageLoop::current() == io_loop_); - IPCAudioSource* source = Lookup(render_view_id, stream_id); + IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); if (source) { - double left_channel, right_channel; - source->stream()->GetVolume(&left_channel, &right_channel); - Send(new ViewMsg_NotifyAudioStreamVolume( - render_view_id, stream_id, left_channel, right_channel)); + source->GetVolume(); } else { - SendErrorMessage(render_view_id, stream_id, 0); + SendErrorMessage(msg.routing_id(), stream_id, 0); } } -void AudioRendererHost::NotifyPacketReady(int32 render_view_id, - int32 stream_id, size_t packet_size) { +void AudioRendererHost::OnNotifyPacketReady(const IPC::Message& msg, + int stream_id, size_t packet_size) { DCHECK(MessageLoop::current() == io_loop_); - IPCAudioSource* source = Lookup(render_view_id, stream_id); + IPCAudioSource* source = Lookup(msg.routing_id(), stream_id); if (source) { source->NotifyPacketReady(packet_size); } else { - SendErrorMessage(render_view_id, stream_id, 0); + SendErrorMessage(msg.routing_id(), stream_id, 0); } } -// Event received when IPC channel is connected to the renderer process. -void AudioRendererHost::IPCChannelConnected(IPC::Message::Sender* ipc_sender) { - DCHECK(MessageLoop::current() == io_loop_); - ipc_sender_ = ipc_sender; -} - -// Event received when IPC channel is closing. -void AudioRendererHost::IPCChannelClosing() { - DCHECK(MessageLoop::current() == io_loop_); - ipc_sender_ = NULL; - DestroyAllSources(); -} - void AudioRendererHost::OnInitialized() { DCHECK(MessageLoop::current() == io_loop_); // Increase the ref count of this object so it is active until we do // Release(). AddRef(); - // Also create the AudioManager singleton in this thread. - // TODO(hclam): figure out a better location to initialize the AudioManager - // singleton. The following method call seems to create a memory leak. - // AudioManager::GetAudioManager(); } void AudioRendererHost::OnDestroyed() { DCHECK(MessageLoop::current() == io_loop_); ipc_sender_ = NULL; + process_handle_ = 0; + process_id_ = 0; DestroyAllSources(); // Decrease the reference to this object, which may lead to self-destruction. Release(); @@ -232,9 +427,8 @@ void AudioRendererHost::OnSend(IPC::Message* message) { void AudioRendererHost::OnDestroySource(IPCAudioSource* source) { DCHECK(MessageLoop::current() == io_loop_); if (source) { - sources_.erase(SourceID(source->render_view_id(), source->stream_id())); - source->stream()->Stop(); - source->stream()->Close(); + sources_.erase(SourceID(source->route_id(), source->stream_id())); + source->Close(); delete source; } } @@ -251,10 +445,10 @@ void AudioRendererHost::DestroyAllSources() { DCHECK(sources_.empty()); } -AudioRendererHost::IPCAudioSource* AudioRendererHost::Lookup(int render_view_id, +AudioRendererHost::IPCAudioSource* AudioRendererHost::Lookup(int route_id, int stream_id) { DCHECK(MessageLoop::current() == io_loop_); - SourceMap::iterator i = sources_.find(SourceID(render_view_id, stream_id)); + SourceMap::iterator i = sources_.find(SourceID(route_id, stream_id)); if (i != sources_.end()) return i->second; return NULL; diff --git a/chrome/browser/renderer_host/audio_renderer_host.h b/chrome/browser/renderer_host/audio_renderer_host.h index 9ddeada0..c7dd0a2 100644 --- a/chrome/browser/renderer_host/audio_renderer_host.h +++ b/chrome/browser/renderer_host/audio_renderer_host.h @@ -14,7 +14,7 @@ // transforms the pull data model to a push model used for IPC. When asked by // AudioOutputStream for an audio packet, it would send a message to renderer, // passing a SharedMemoryHandle for filling the buffer. -// NotifyPacketReady(|render_view_id|, |stream_id|) would be called when the +// NotifyPacketReady(|route_id|, |stream_id|) would be called when the // buffer is filled and ready to be consumed. // // This class is owned by BrowserRenderProcessHost, and instantiated on UI @@ -29,16 +29,14 @@ // which essentially post a task of OnDestroyed() on IO thread. Inside // OnDestroyed(), audio output streams are destroyed and Release() is called // which may result in self-destruction. -// -// TODO(hclam): Have these things done: -// 1. Make AudioManager a singleton and construct/destruct it properly. #ifndef CHROME_BROWSER_RENDERER_HOST_AUDIO_RENDERER_HOST_H_ #define CHROME_BROWSER_RENDERER_HOST_AUDIO_RENDERER_HOST_H_ #include <map> +#include <deque> -#include "base/lock.h" +#include "base/process.h" #include "base/ref_counted.h" #include "base/shared_memory.h" #include "base/waitable_event.h" @@ -46,7 +44,9 @@ #include "media/audio/audio_output.h" class AudioManager; +class Lock; class MessageLoop; +struct ViewHostMsg_Audio_CreateStream; class AudioRendererHost : public base::RefCountedThreadSafe<AudioRendererHost> { private: @@ -68,54 +68,65 @@ class AudioRendererHost : public base::RefCountedThreadSafe<AudioRendererHost> { // The following public methods are called from ResourceMessageFilter in the // IO thread. - // Creates an audio output stream with the specified format. If this call is - // successful this object would keep an internal entry of the stream about the - // required properties. - // ViewMsg_NotifyAudioStreamCreated is sent to renderer if this call is - // successful, else ViewMsg_NotifyAudioStreamStateChanged with error. - void CreateStream(base::ProcessHandle process_handle, int32 render_view_id, - int32 stream_id, AudioManager::Format format, int channels, - int sample_rate, int bits_per_sample, size_t packet_size); - - // Start the audio output strean. ViewMsg_NotifyAudioStreamStateChanged will - // be sent back to renderer, with - // ViewMsg_AudioStreamState::AUDIO_STREAM_STARTED upon success, - // ViewMsg_AudioStreamState::AUDIO_STREAM_ERROR upon error. - void Start(int32 render_view_id, int32 stream_id); - - // Close the audio output stream. If this call is successful, the - // AudioOutputStream correspond to (|render_view_id|, |stream_id|) would go - // unmanaged by this class, subsequent calls to this object with the same - // (|render_view_id|, |stream_id|) would fail. This method call does not - // generate a return IPC message back to the renderer. - void Close(int32 render_view_id, int32 stream_id); - - // Set the volume for the stream specified. No returning IPC message to - // renderer upon success, ViewMsg_NotifyAudioStreamStateChanged with - // ViewMsg_AudioStreamState::AUDIO_STREAM_ERROR is sent upon error. - void SetVolume(int32 render_view_id, int32 stream_id, - double left_channel, double right_channel); - - // Get the volume of the stream specified. NotifyAudioStreamVolume is sent - // back to renderer upon success. ViewMsg_NotifyAudioStreamStateChanged with - // ViewMsg_AudioStreamState::AUDIO_STREAM_ERROR is sent upon error. - void GetVolume(int32 render_view_id, int32 stream_id); - - // Notify packet has been prepared for stream specified by |stream_id|. The - // buffer associated with (|render_view_id|, |stream_id|) has been filled and - // is ready to be consumed. - void NotifyPacketReady(int32 render_view_id, int32 stream_id, - size_t packet_size); - // Event received when IPC channel is connected with the renderer process. - void IPCChannelConnected(IPC::Message::Sender* ipc_sender); + void IPCChannelConnected(int process_id, base::ProcessHandle process_handle, + IPC::Message::Sender* ipc_sender); // Event received when IPC channel is closing. void IPCChannelClosing(); + // Returns true if the message is a audio related message and was processed. + // If it was, message_was_ok will be false iff the message was corrupt. + bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok); + private: //--------------------------------------------------------------------------- // Methods called on IO thread. + // Returns true if the message is an audio related message and should be + // handled by this class. + bool IsAudioRendererHostMessage(const IPC::Message& message); + + // Audio related IPC message handlers. + // Creates an audio output stream with the specified format. If this call is + // successful this object would keep an internal entry of the stream for the + // required properties. See IPCAudioSource::CreateIPCAudioSource for more + // details. + void OnCreateStream(const IPC::Message& msg, int stream_id, + const ViewHostMsg_Audio_CreateStream& params); + + // Starts the audio output stream. Delegates the start method call to the + // corresponding IPCAudioSource::Start, ViewMsg_NotifyAudioStreamStateChanged + // with AudioOutputStream::AUDIO_STREAM_ERROR is sent back to renderer if the + // required IPCAudioSource is not found. + void OnStartStream(const IPC::Message& msg, int stream_id); + + // Closes the audio output stream, delegates the close method call to the + // corresponding IPCAudioSource::Close, no returning IPC message to renderer + // upon success and failure. + void OnCloseStream(const IPC::Message& msg, int stream_id); + + // Set the volume for the stream specified. Delegates the SetVolume method + // call to IPCAudioSource. No returning IPC message to renderer upon success. + // ViewMsg_NotifyAudioStreamStateChanged with + // AudioOutputStream::AUDIO_STREAM_ERROR is sent back to renderer if the + // required IPCAudioSource is not found. + void OnSetVolume(const IPC::Message& msg, int stream_id, + double left_channel, double right_channel); + + // Gets the volume of the stream specified, delegates to corresponding + // IPCAudioSource::GetVolume, see the method for more details. + // ViewMsg_NotifyAudioStreamStateChanged with + // AudioOutputStream::AUDIO_STREAM_ERROR is sent back to renderer if the + // required IPCAudioSource is not found. + void OnGetVolume(const IPC::Message& msg, int stream_id); + + // Notify packet has been prepared for stream, delegates to corresponding + // IPCAudioSource::NotifyPacketReady, see the method for more details. + // ViewMsg_NotifyAudioStreamStateChanged with + // AudioOutputStream::AUDIO_STREAM_ERROR is sent back to renderer if the + // required IPCAudioSource is not found. + void OnNotifyPacketReady(const IPC::Message& msg, int stream_id, + size_t packet_size); // Called on IO thread when this object is created and initialized. void OnInitialized(); @@ -124,14 +135,10 @@ class AudioRendererHost : public base::RefCountedThreadSafe<AudioRendererHost> { // Destroy() is called from owner of this class in UI thread. void OnDestroyed(); - // Send IPC messages using ipc_sender_. + // Sends IPC messages using ipc_sender_. void OnSend(IPC::Message* message); - // A helper method that does the following things: - // 1. Stop the audio stream contained in the source. - // 2. Close the audio stream contained in the source. - // 3. Remove the source from the map. - // 4. Destruct the source. + // Closes the source, deletes it and removes it from the internal map. // Destruction of source and associated stream should always be done by this // method. *DO NOT* call this method from other than IPCAudioSource and from // this class. @@ -159,6 +166,8 @@ class AudioRendererHost : public base::RefCountedThreadSafe<AudioRendererHost> { // A helper method for calling OnDestroySource on IO thread. void DestroySource(IPCAudioSource* source); + MessageLoop* io_loop() { return io_loop_; } + // The container for AudioOutputStream and serves audio packet for it by IPC. // This class does nothing more than sending IPC when OnMoreData is called // or error is received from the hardware audio thread, it also serves the @@ -166,40 +175,87 @@ class AudioRendererHost : public base::RefCountedThreadSafe<AudioRendererHost> { // Lifetime of the audio output stream is not controlled by this class. class IPCAudioSource : public AudioOutputStream::AudioSourceCallback { public: - IPCAudioSource(AudioRendererHost* host, // Host of this source. - int32 render_view_id, // Routing ID to RenderView. - int32 stream_id, // ID of this source. - AudioOutputStream* stream, // Stream associated. - size_t packet_size); // Size of shared memory - // buffer for writing. + // Factory method for creating an IPCAudioSource, returns NULL if failed. + static IPCAudioSource* CreateIPCAudioSource( + AudioRendererHost* host, // Host of this source. + int process_id, // Process ID of renderer. + int route_id, // Routing ID to RenderView. + int stream_id, // ID of this source. + base::ProcessHandle process_handle, // Process handle of renderer. + AudioManager::Format format, // Format of the stream. + int channels, // Number of channels. + int sample_rate, // Sampling frequency/rate. + char bits_per_sample, // Number of bits per sample. + size_t packet_size // Number of bytes per packet. + ); ~IPCAudioSource(); - // AudioSourceCallback methods. - virtual size_t OnMoreData(AudioOutputStream* stream, - void* dest, size_t max_size); - virtual void OnClose(AudioOutputStream* stream); - virtual void OnError(AudioOutputStream* stream, int code); + // Methods to control playback of the stream. + // Starts the audio output stream. This method does not call to + // AudioOutputStream::Start immediately, but instead try get enough initial + // audio packets from the renderer before actual starting. If pre-rolling + // has completed and the audio output stream was actually called to start + // ViewMsg_NotifyAudioStreamStateChanged with + // AudioOutputStream::AUDIO_STREAM_STARTED is sent back to the renderer. + void Start(); + + // Closes the audio output stream. After calling this method all activities + // of the audio output stream are stopped. + void Close(); + + // Sets the volume of the audio output stream. There's no IPC messages + // sent back to the renderer upon success and failure. + void SetVolume(double left, double right); + + // Gets the volume of the audio output stream. + // ViewMsg_NotifyAudioStreamVolume is sent back to renderer with volume + // information if succeeded. + void GetVolume(); // Notify this source that buffer has been filled and is ready to be // consumed. void NotifyPacketReady(size_t packet_size); - AudioOutputStream* stream() { return stream_; } - base::SharedMemory* shared_memory() { return &shared_memory_; } - int32 render_view_id() { return render_view_id_; } - int32 stream_id() { return stream_id_; } + // AudioSourceCallback methods. + virtual size_t OnMoreData(AudioOutputStream* stream, + void* dest, size_t max_size); + virtual void OnClose(AudioOutputStream* stream); + virtual void OnError(AudioOutputStream* stream, int code); + + int process_id() { return process_id_; } + int route_id() { return route_id_; } + int stream_id() { return stream_id_; } private: + IPCAudioSource(AudioRendererHost* host, // Host of this source. + int process_id, // Process ID of renderer. + int route_id, // Routing ID to RenderView. + int stream_id, // ID of this source. + AudioOutputStream* stream, // Stream associated. + size_t packet_size); // Size of shared memory + // buffer for writing. + void StopWaitingForPacket(); + size_t SafeCopyBuffer(void* dest, size_t dest_size, + const void* src, size_t src_size); + AudioRendererHost* host_; - int32 render_view_id_; - int32 stream_id_; + int process_id_; + int route_id_; + int stream_id_; AudioOutputStream* stream_; - bool closed_; + size_t packet_size_; + + AudioOutputStream::State state_; + bool stop_providing_packets_; base::SharedMemory shared_memory_; base::WaitableEvent packet_read_event_; + Lock lock_; size_t last_packet_size_; + std::deque<std::pair<uint8*, size_t> > initial_buffers_; }; + int process_id_; + base::ProcessHandle process_handle_; IPC::Message::Sender* ipc_sender_; // A map of id to audio sources. diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 1bffc6a..bd61cc7 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -182,7 +182,7 @@ void ResourceMessageFilter::OnChannelConnected(int32 peer_pid) { set_handle(peer_handle); // Hook AudioRendererHost to this object after channel is connected so it can // this object for sending messages. - audio_renderer_host_->IPCChannelConnected(this); + audio_renderer_host_->IPCChannelConnected(render_process_id_, handle(), this); } // Called on the IPC thread: @@ -204,6 +204,8 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) { message, this, &msg_is_ok) || app_cache_dispatcher_host_->OnMessageReceived( message, &msg_is_ok); + if (!handled && msg_is_ok) + handled = audio_renderer_host_->OnMessageReceived(message, &msg_is_ok); if (!handled) { handled = true; IPC_BEGIN_MESSAGE_MAP_EX(ResourceMessageFilter, message, msg_is_ok) @@ -265,13 +267,6 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& message) { IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_ScriptedPrint, OnScriptedPrint) #endif - IPC_MESSAGE_HANDLER(ViewHostMsg_CreateAudioStream, OnCreateAudioStream) - IPC_MESSAGE_HANDLER(ViewHostMsg_StartAudioStream, OnStartAudioStream) - IPC_MESSAGE_HANDLER(ViewHostMsg_CloseAudioStream, OnCloseAudioStream) - IPC_MESSAGE_HANDLER(ViewHostMsg_NotifyAudioPacketReady, - OnNotifyAudioPacketReady) - IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioVolume, OnGetAudioVolume) - IPC_MESSAGE_HANDLER(ViewHostMsg_SetAudioVolume, OnSetAudioVolume) #if defined(OS_MACOSX) IPC_MESSAGE_HANDLER(ViewHostMsg_AllocTransportDIB, OnAllocTransportDIB) @@ -768,43 +763,6 @@ void ResourceMessageFilter::OnRendererHistograms( Histogram::DeserializeHistogramList(histograms); } -void ResourceMessageFilter::OnCreateAudioStream( - const IPC::Message& msg, int stream_id, - const ViewHostMsg_Audio_CreateStream& params) { - audio_renderer_host_->CreateStream( - handle(), msg.routing_id(), stream_id, params.format, - params.channels, params.sample_rate, params.bits_per_sample, - params.packet_size); -} - -void ResourceMessageFilter::OnNotifyAudioPacketReady( - const IPC::Message& msg, int stream_id, size_t packet_size) { - audio_renderer_host_->NotifyPacketReady(msg.routing_id(), - stream_id, packet_size); -} - -void ResourceMessageFilter::OnStartAudioStream( - const IPC::Message& msg, int stream_id) { - audio_renderer_host_->Start(msg.routing_id(), stream_id); -} - -void ResourceMessageFilter::OnCloseAudioStream( - const IPC::Message& msg, int stream_id) { - audio_renderer_host_->Close(msg.routing_id(), stream_id); -} - -void ResourceMessageFilter::OnGetAudioVolume( - const IPC::Message& msg, int stream_id) { - audio_renderer_host_->GetVolume(msg.routing_id(), stream_id); -} - -void ResourceMessageFilter::OnSetAudioVolume( - const IPC::Message& msg, int stream_id, - double left_channel, double right_channel) { - audio_renderer_host_->SetVolume( - msg.routing_id(), stream_id, left_channel, right_channel); -} - #if defined(OS_MACOSX) void ResourceMessageFilter::OnAllocTransportDIB( size_t size, TransportDIB::Handle* handle) { diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index d801583..b813872 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -198,18 +198,6 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, scoped_refptr<printing::PrinterQuery> printer_query, IPC::Message* reply_msg); #endif - - // Audio related IPC message handlers. - void OnCreateAudioStream(const IPC::Message& msg, int stream_id, - const ViewHostMsg_Audio_CreateStream& params); - void OnNotifyAudioPacketReady(const IPC::Message& msg, int stream_id, - size_t packet_size); - void OnStartAudioStream(const IPC::Message& msg, int stream_id); - void OnCloseAudioStream(const IPC::Message& msg, int stream_id); - void OnGetAudioVolume(const IPC::Message& msg, int stream_id); - void OnSetAudioVolume(const IPC::Message& msg, int stream_id, - double left_channel, double right_channel); - // Browser side transport DIB allocation void OnAllocTransportDIB(size_t size, TransportDIB::Handle* result); diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 32f8f57..77d56164 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -1776,12 +1776,21 @@ struct ParamTraits<AudioOutputStream::State> { static void Log(const param_type& p, std::wstring* l) { std::wstring state; switch (p) { - case AudioOutputStream::STATE_PAUSED: - state = L"AudioOutputStream::STATE_PAUSED"; - break; + case AudioOutputStream::STATE_CREATED: + state = L"AudioOutputStream::STATE_CREATED"; + break; case AudioOutputStream::STATE_STARTED: - state = L"AudioOutputStream::STATE_STARTED"; - break; + state = L"AudioOutputStream::STATE_STARTED"; + break; + case AudioOutputStream::STATE_PAUSED: + state = L"AudioOutputStream::STATE_PAUSED"; + break; + case AudioOutputStream::STATE_STOPPED: + state = L"AudioOutputStream::STATE_STOPPED"; + break; + case AudioOutputStream::STATE_CLOSED: + state = L"AudioOutputStream::STATE_CLOSED"; + break; case AudioOutputStream::STATE_ERROR: state = L"AudioOutputStream::STATE_ERROR"; break; diff --git a/media/audio/audio_output.h b/media/audio/audio_output.h index 0cfca08..289b2af 100644 --- a/media/audio/audio_output.h +++ b/media/audio/audio_output.h @@ -42,8 +42,11 @@ class AudioOutputStream { public: enum State { - STATE_STARTED = 0, // The output stream is started. + STATE_CREATED = 0, // The output stream is created. + STATE_STARTED, // The output stream is started. STATE_PAUSED, // The output stream is paused. + STATE_STOPPED, // The output stream is stopped. + STATE_CLOSED, // The output stream is closed. STATE_ERROR, // The output stream is in error state. }; @@ -82,6 +85,10 @@ class AudioOutputStream { virtual bool Open(size_t packet_size) = 0; // Starts playing audio and generating AudioSourceCallback::OnMoreData(). + // Since implementor of AudioOutputStream may have internal buffers, right + // after calling this method initial buffers are fetched. User of this + // object should prepare |AudioOutputStream::GetNumBuffers()| before calling + // AudioOutputStream::Start(). virtual void Start(AudioSourceCallback* callback) = 0; // Stops playing audio. Effect might no be instantaneous as the hardware @@ -100,6 +107,11 @@ class AudioOutputStream { // After calling this method, the object should not be used anymore. virtual void Close() = 0; + // Gets the number of internal buffers used in this output stream. This + // method is useful for providing information about how user of this object + // should prepare initial buffers before calling AudioOutputStream::Start(). + virtual size_t GetNumBuffers() = 0; + protected: virtual ~AudioOutputStream() {} }; diff --git a/media/audio/mac/audio_output_mac.cc b/media/audio/mac/audio_output_mac.cc index 3866a56..da6772f 100644 --- a/media/audio/mac/audio_output_mac.cc +++ b/media/audio/mac/audio_output_mac.cc @@ -9,7 +9,7 @@ PCMQueueOutAudioOutputStream::PCMQueueOutAudioOutputStream( AudioManagerMac* manager, int channels, int sampling_rate, - char bits_per_sample) + char bits_per_sample) : format_(), audio_queue_(NULL), buffer_(), @@ -91,7 +91,7 @@ void PCMQueueOutAudioOutputStream::Close() { } void PCMQueueOutAudioOutputStream::Stop() { - // TODO(cpu): Implement. + // TODO(cpu): Implement. } void PCMQueueOutAudioOutputStream::SetVolume(double left_level, @@ -104,6 +104,10 @@ void PCMQueueOutAudioOutputStream::GetVolume(double* left_level, // TODO(cpu): Implement. } +size_t PCMQueueOutAudioOutputStream::GetNumBuffers() { + return kNumBuffers; +} + void PCMQueueOutAudioOutputStream::RenderCallback(void* p_this, AudioQueueRef queue, AudioQueueBufferRef buffer) { diff --git a/media/audio/mac/audio_output_mac.h b/media/audio/mac/audio_output_mac.h index eb3c091..d1129b3 100644 --- a/media/audio/mac/audio_output_mac.h +++ b/media/audio/mac/audio_output_mac.h @@ -25,9 +25,9 @@ class PCMQueueOutAudioOutputStream : public AudioOutputStream { int channels, int sampling_rate, char bits_per_sample); // The dtor is typically called by the AudioManager only and it is usually - // triggered by calling AudioOutputStream::Close(). + // triggered by calling AudioOutputStream::Close(). virtual ~PCMQueueOutAudioOutputStream(); - + // Implementation of AudioOutputStream. virtual bool Open(size_t packet_size); virtual void Close(); @@ -35,11 +35,12 @@ class PCMQueueOutAudioOutputStream : public AudioOutputStream { virtual void Stop(); virtual void SetVolume(double left_level, double right_level); virtual void GetVolume(double* left_level, double* right_level); - + virtual size_t GetNumBuffers(); + private: // The audio is double buffered. static const size_t kNumBuffers = 2; - + // The OS calls back here when an audio buffer has been processed. static void RenderCallback(void* p_this, AudioQueueRef queue, AudioQueueBufferRef buffer); @@ -56,7 +57,7 @@ class PCMQueueOutAudioOutputStream : public AudioOutputStream { AudioSourceCallback* source_; // Our creator, the audio manager needs to be notified when we close. AudioManagerMac* manager_; - + DISALLOW_COPY_AND_ASSIGN(PCMQueueOutAudioOutputStream); }; diff --git a/media/audio/win/audio_output_win.cc b/media/audio/win/audio_output_win.cc index 880555b..5990e27 100644 --- a/media/audio/win/audio_output_win.cc +++ b/media/audio/win/audio_output_win.cc @@ -61,6 +61,10 @@ class AudioOutputStreamMockWin : public AudioOutputStream { *right_level = right_volume_; } + virtual size_t GetNumBuffers() { + return 1; + } + virtual void Close() { callback_->OnClose(this); callback_ = NULL; diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 95f70ab..59efa4e 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -193,6 +193,10 @@ void PCMWaveOutAudioOutputStream::GetVolume(double* left_level, return; } +size_t PCMWaveOutAudioOutputStream::GetNumBuffers() { + return kNumBuffers; +} + void PCMWaveOutAudioOutputStream::HandleError(MMRESULT error) { DLOG(WARNING) << "PCMWaveOutAudio error " << error; callback_->OnError(this, error); diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h index 1c8ffa6..8b90e69 100644 --- a/media/audio/win/waveout_output_win.h +++ b/media/audio/win/waveout_output_win.h @@ -40,6 +40,7 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { virtual void Stop(); virtual void SetVolume(double left_level, double right_level); virtual void GetVolume(double* left_level, double* right_level); + virtual size_t GetNumBuffers(); // Sends a buffer to the audio driver for playback. void QueueNextPacket(WAVEHDR* buffer); |