diff options
-rw-r--r-- | content/content_renderer.gypi | 2 | ||||
-rw-r--r-- | content/renderer/media/audio_device.cc | 55 | ||||
-rw-r--r-- | content/renderer/media/audio_device.h | 14 | ||||
-rw-r--r-- | content/renderer/media/audio_input_device.cc | 88 | ||||
-rw-r--r-- | content/renderer/media/audio_input_device.h | 6 | ||||
-rw-r--r-- | content/renderer/media/scoped_loop_observer.cc | 42 | ||||
-rw-r--r-- | content/renderer/media/scoped_loop_observer.h | 45 | ||||
-rw-r--r-- | media/audio/audio_manager_base.cc | 2 |
8 files changed, 179 insertions, 75 deletions
diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index bae62ff..c0a7f7b 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -117,6 +117,8 @@ 'renderer/media/renderer_webaudiodevice_impl.h', 'renderer/media/rtc_video_decoder.cc', 'renderer/media/rtc_video_decoder.h', + 'renderer/media/scoped_loop_observer.cc', + 'renderer/media/scoped_loop_observer.h', 'renderer/media/video_capture_impl.cc', 'renderer/media/video_capture_impl.h', 'renderer/media/video_capture_impl_manager.cc', diff --git a/content/renderer/media/audio_device.cc b/content/renderer/media/audio_device.cc index 5c0a399..bfaad28 100644 --- a/content/renderer/media/audio_device.cc +++ b/content/renderer/media/audio_device.cc @@ -4,7 +4,6 @@ #include "content/renderer/media/audio_device.h" -#include "base/bind.h" #include "base/debug/trace_event.h" #include "base/message_loop.h" #include "base/threading/thread_restrictions.h" @@ -17,7 +16,8 @@ #include "media/audio/audio_util.h" AudioDevice::AudioDevice() - : buffer_size_(0), + : ScopedLoopObserver(ChildProcess::current()->io_message_loop()), + buffer_size_(0), channels_(0), bits_per_sample_(16), sample_rate_(0), @@ -38,7 +38,8 @@ AudioDevice::AudioDevice(size_t buffer_size, int channels, double sample_rate, RenderCallback* callback) - : bits_per_sample_(16), + : ScopedLoopObserver(ChildProcess::current()->io_message_loop()), + bits_per_sample_(16), is_initialized_(false), audio_delay_milliseconds_(0), volume_(1.0), @@ -100,13 +101,12 @@ void AudioDevice::Start() { params.bits_per_sample = bits_per_sample_; params.samples_per_packet = buffer_size_; - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, + message_loop()->PostTask(FROM_HERE, base::Bind(&AudioDevice::InitializeOnIOThread, this, params)); } void AudioDevice::Stop() { - DCHECK(MessageLoop::current() != ChildProcess::current()->io_message_loop()); + DCHECK(!message_loop()->BelongsToCurrentThread()); // Stop and shutdown the audio thread from the IO thread. // This operation must be synchronous for now since the |callback_| pointer @@ -114,21 +114,19 @@ void AudioDevice::Stop() { // returns (and FireRenderCallback might dereference a bogus pointer). // TODO(tommi): Add an Uninitialize() method to AudioRendererSink? base::WaitableEvent done(true, false); - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioDevice::ShutDownOnIOThread, this, &done)); - done.Wait(); + if (message_loop()->PostTask(FROM_HERE, + base::Bind(&AudioDevice::ShutDownOnIOThread, this, &done))) { + done.Wait(); + } } void AudioDevice::Play() { - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, + message_loop()->PostTask(FROM_HERE, base::Bind(&AudioDevice::PlayOnIOThread, this)); } void AudioDevice::Pause(bool flush) { - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, + message_loop()->PostTask(FROM_HERE, base::Bind(&AudioDevice::PauseOnIOThread, this, flush)); } @@ -136,9 +134,10 @@ bool AudioDevice::SetVolume(double volume) { if (volume < 0 || volume > 1.0) return false; - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioDevice::SetVolumeOnIOThread, this, volume)); + if (!message_loop()->PostTask(FROM_HERE, + base::Bind(&AudioDevice::SetVolumeOnIOThread, this, volume))) { + return false; + } volume_ = volume; @@ -151,7 +150,7 @@ void AudioDevice::GetVolume(double* volume) { } void AudioDevice::InitializeOnIOThread(const AudioParameters& params) { - DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); // Make sure we don't create the stream more than once. DCHECK_EQ(0, stream_id_); if (stream_id_) @@ -162,7 +161,7 @@ void AudioDevice::InitializeOnIOThread(const AudioParameters& params) { } void AudioDevice::PlayOnIOThread() { - DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); if (stream_id_ && is_started_) Send(new AudioHostMsg_PlayStream(stream_id_)); else @@ -170,7 +169,7 @@ void AudioDevice::PlayOnIOThread() { } void AudioDevice::PauseOnIOThread(bool flush) { - DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); if (stream_id_ && is_started_) { Send(new AudioHostMsg_PauseStream(stream_id_)); if (flush) @@ -183,7 +182,8 @@ void AudioDevice::PauseOnIOThread(bool flush) { } void AudioDevice::ShutDownOnIOThread(base::WaitableEvent* signal) { - DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); + // NOTE: |signal| may be NULL. // Make sure we don't call shutdown more than once. if (stream_id_) { @@ -196,11 +196,12 @@ void AudioDevice::ShutDownOnIOThread(base::WaitableEvent* signal) { ShutDownAudioThread(); } - signal->Signal(); + if (signal) + signal->Signal(); } void AudioDevice::SetVolumeOnIOThread(double volume) { - DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); if (stream_id_) Send(new AudioHostMsg_SetVolume(stream_id_, volume)); } @@ -216,7 +217,7 @@ void AudioDevice::OnStreamCreated( base::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle, uint32 length) { - DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); DCHECK_GE(length, buffer_size_ * sizeof(int16) * channels_); #if defined(OS_WIN) DCHECK(handle); @@ -314,7 +315,7 @@ size_t AudioDevice::FireRenderCallback(int16* data) { } void AudioDevice::ShutDownAudioThread() { - DCHECK_EQ(MessageLoop::current(), ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); if (audio_thread_.get()) { // Close the socket to terminate the main thread function in the @@ -327,3 +328,7 @@ void AudioDevice::ShutDownAudioThread() { audio_socket_.reset(); } } + +void AudioDevice::WillDestroyCurrentMessageLoop() { + ShutDownOnIOThread(NULL); +} diff --git a/content/renderer/media/audio_device.h b/content/renderer/media/audio_device.h index f4654bd..0c20dea 100644 --- a/content/renderer/media/audio_device.h +++ b/content/renderer/media/audio_device.h @@ -66,23 +66,23 @@ #include <vector> #include "base/basictypes.h" +#include "base/bind.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/scoped_loop_observer.h" #include "media/audio/audio_parameters.h" #include "media/base/audio_renderer_sink.h" -namespace base { -class WaitableEvent; -} - class CONTENT_EXPORT AudioDevice : NON_EXPORTED_BASE(public media::AudioRendererSink), public AudioMessageFilter::Delegate, - public base::DelegateSimpleThread::Delegate { + public base::DelegateSimpleThread::Delegate, + public ScopedLoopObserver { public: // Methods called on main render thread ------------------------------------- @@ -169,6 +169,10 @@ class CONTENT_EXPORT AudioDevice // Closes socket and joins with the audio thread. void ShutDownAudioThread(); + // MessageLoop::DestructionObserver implementation for the IO loop. + // If the IO loop dies before we do, we shut down the audio thread from here. + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + // Format size_t buffer_size_; // in sample-frames int channels_; diff --git a/content/renderer/media/audio_input_device.cc b/content/renderer/media/audio_input_device.cc index 0c81e38..05de9d0 100644 --- a/content/renderer/media/audio_input_device.cc +++ b/content/renderer/media/audio_input_device.cc @@ -20,7 +20,8 @@ AudioInputDevice::AudioInputDevice(size_t buffer_size, double sample_rate, CaptureCallback* callback, CaptureEventHandler* event_handler) - : callback_(callback), + : ScopedLoopObserver(ChildProcess::current()->io_message_loop()), + callback_(callback), event_handler_(event_handler), audio_delay_milliseconds_(0), volume_(1.0), @@ -32,10 +33,10 @@ AudioInputDevice::AudioInputDevice(size_t buffer_size, filter_ = RenderThreadImpl::current()->audio_input_message_filter(); audio_data_.reserve(channels); #if defined(OS_MACOSX) - VLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Mac OS X."; + DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Mac OS X."; audio_parameters_.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; #elif defined(OS_WIN) - VLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Windows."; + DVLOG(1) << "Using AUDIO_PCM_LOW_LATENCY as input mode on Windows."; audio_parameters_.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; #else // TODO(henrika): add support for AUDIO_PCM_LOW_LATENCY on Linux as well. @@ -60,34 +61,30 @@ AudioInputDevice::~AudioInputDevice() { } void AudioInputDevice::Start() { - VLOG(1) << "Start()"; - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, + DVLOG(1) << "Start()"; + message_loop()->PostTask(FROM_HERE, base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); } void AudioInputDevice::SetDevice(int session_id) { - VLOG(1) << "SetDevice (session_id=" << session_id << ")"; - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, - session_id)); + DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; + message_loop()->PostTask(FROM_HERE, + base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); } void AudioInputDevice::Stop() { - DCHECK(MessageLoop::current() != ChildProcess::current()->io_message_loop()); - VLOG(1) << "Stop()"; + DCHECK(!message_loop()->BelongsToCurrentThread()); + DVLOG(1) << "Stop()"; base::WaitableEvent completion(false, false); - ChildProcess::current()->io_message_loop()->PostTask( - FROM_HERE, - base::Bind(&AudioInputDevice::ShutDownOnIOThread, this, - &completion)); - - // We wait here for the IO task to be completed to remove race conflicts - // with OnLowLatencyCreated() and to ensure that Stop() acts as a synchronous - // function call. - completion.Wait(); + if (message_loop()->PostTask(FROM_HERE, + base::Bind(&AudioInputDevice::ShutDownOnIOThread, this, + &completion))) { + // We wait here for the IO task to be completed to remove race conflicts + // with OnLowLatencyCreated() and to ensure that Stop() acts as a + // synchronous function call. + completion.Wait(); + } } bool AudioInputDevice::SetVolume(double volume) { @@ -101,7 +98,7 @@ bool AudioInputDevice::GetVolume(double* volume) { } void AudioInputDevice::InitializeOnIOThread() { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); // Make sure we don't call Start() more than once. DCHECK_EQ(0, stream_id_); if (stream_id_) @@ -122,38 +119,37 @@ void AudioInputDevice::InitializeOnIOThread() { } void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); session_id_ = session_id; } void AudioInputDevice::StartOnIOThread() { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); if (stream_id_) Send(new AudioInputHostMsg_RecordStream(stream_id_)); } void AudioInputDevice::ShutDownOnIOThread(base::WaitableEvent* completion) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); + // NOTE: |completion| may be NULL. // Make sure we don't call shutdown more than once. - if (!stream_id_) { - completion->Signal(); - return; - } - - filter_->RemoveDelegate(stream_id_); - Send(new AudioInputHostMsg_CloseStream(stream_id_)); + if (stream_id_) { + filter_->RemoveDelegate(stream_id_); + Send(new AudioInputHostMsg_CloseStream(stream_id_)); - ShutDownAudioThread(); + ShutDownAudioThread(); - stream_id_ = 0; - session_id_ = 0; - pending_device_ready_ = false; + stream_id_ = 0; + session_id_ = 0; + pending_device_ready_ = false; + } - completion->Signal(); + if (completion) + completion->Signal(); } void AudioInputDevice::SetVolumeOnIOThread(double volume) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); if (stream_id_) Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); } @@ -162,7 +158,7 @@ void AudioInputDevice::OnLowLatencyCreated( base::SharedMemoryHandle handle, base::SyncSocket::Handle socket_handle, uint32 length) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); #if defined(OS_WIN) DCHECK(handle); DCHECK(socket_handle); @@ -173,7 +169,7 @@ void AudioInputDevice::OnLowLatencyCreated( DCHECK(length); DCHECK(!audio_thread_.get()); - VLOG(1) << "OnLowLatencyCreated (stream_id=" << stream_id_ << ")"; + DVLOG(1) << "OnLowLatencyCreated (stream_id=" << stream_id_ << ")"; // Takes care of the case when Stop() is called before OnLowLatencyCreated(). if (!stream_id_) { base::SharedMemory::CloseHandle(handle); @@ -200,7 +196,7 @@ void AudioInputDevice::OnVolume(double volume) { } void AudioInputDevice::OnStateChanged(AudioStreamState state) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); + DCHECK(message_loop()->BelongsToCurrentThread()); switch (state) { case kAudioStreamPaused: // TODO(xians): Should we just call ShutDownOnIOThread here instead? @@ -232,8 +228,8 @@ void AudioInputDevice::OnStateChanged(AudioStreamState state) { } void AudioInputDevice::OnDeviceReady(const std::string& device_id) { - DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); - VLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; + DCHECK(message_loop()->BelongsToCurrentThread()); + DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; // Takes care of the case when Stop() is called before OnDeviceReady(). if (!pending_device_ready_) @@ -337,3 +333,7 @@ void AudioInputDevice::ShutDownAudioThread() { audio_socket_.reset(); } } + +void AudioInputDevice::WillDestroyCurrentMessageLoop() { + ShutDownOnIOThread(NULL); +} diff --git a/content/renderer/media/audio_input_device.h b/content/renderer/media/audio_input_device.h index 96dd2ff..d93e49c 100644 --- a/content/renderer/media/audio_input_device.h +++ b/content/renderer/media/audio_input_device.h @@ -78,6 +78,7 @@ #include "base/threading/simple_thread.h" #include "content/common/content_export.h" #include "content/renderer/media/audio_input_message_filter.h" +#include "content/renderer/media/scoped_loop_observer.h" #include "media/audio/audio_parameters.h" // TODO(henrika): This class is based on the AudioDevice class and it has @@ -88,6 +89,7 @@ class CONTENT_EXPORT AudioInputDevice : public AudioInputMessageFilter::Delegate, public base::DelegateSimpleThread::Delegate, + public ScopedLoopObserver, public base::RefCountedThreadSafe<AudioInputDevice> { public: class CONTENT_EXPORT CaptureCallback { @@ -176,6 +178,10 @@ class CONTENT_EXPORT AudioInputDevice // DelegateSimpleThread::Delegate implementation. virtual void Run() OVERRIDE; + // MessageLoop::DestructionObserver implementation for the IO loop. + // If the IO loop dies before we do, we shut down the audio thread from here. + virtual void WillDestroyCurrentMessageLoop() OVERRIDE; + // Format AudioParameters audio_parameters_; diff --git a/content/renderer/media/scoped_loop_observer.cc b/content/renderer/media/scoped_loop_observer.cc new file mode 100644 index 0000000..1e02592 --- /dev/null +++ b/content/renderer/media/scoped_loop_observer.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2012 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/scoped_loop_observer.h" + +#include "base/bind.h" +#include "base/synchronization/waitable_event.h" + +ScopedLoopObserver::ScopedLoopObserver(MessageLoop* loop) + : loop_(loop->message_loop_proxy()) { + ObserveLoopDestruction(true, NULL); +} + +ScopedLoopObserver::~ScopedLoopObserver() { + ObserveLoopDestruction(false, NULL); +} + +void ScopedLoopObserver::ObserveLoopDestruction(bool enable, + base::WaitableEvent* done) { + // Note: |done| may be NULL. + if (loop_->BelongsToCurrentThread()) { + MessageLoop* loop = MessageLoop::current(); + if (enable) { + loop->AddDestructionObserver(this); + } else { + loop->RemoveDestructionObserver(this); + } + } else { + base::WaitableEvent event(false, false); + if (loop_->PostTask(FROM_HERE, + base::Bind(&ScopedLoopObserver::ObserveLoopDestruction, + base::Unretained(this), enable, &event))) { + event.Wait(); + } else { + // The message loop's thread has already terminated, so no need to wait. + } + } + + if (done) + done->Signal(); +} diff --git a/content/renderer/media/scoped_loop_observer.h b/content/renderer/media/scoped_loop_observer.h new file mode 100644 index 0000000..127d992 --- /dev/null +++ b/content/renderer/media/scoped_loop_observer.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012 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_SCOPED_LOOP_OBSERVER_H_ +#define CONTENT_RENDERER_MEDIA_SCOPED_LOOP_OBSERVER_H_ +#pragma once + +#include "base/memory/ref_counted.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" + +namespace base { +class WaitableEvent; +} + +// A common base class for AudioDevice and AudioInputDevice that manages +// a message loop pointer and monitors it for destruction. If the object goes +// out of scope before the message loop, the object will automatically remove +// itself from the message loop's list of destruction observers. +// NOTE: The class that inherits from this class must implement the +// WillDestroyCurrentMessageLoop virtual method from DestructionObserver. +class ScopedLoopObserver + : public MessageLoop::DestructionObserver { + public: + explicit ScopedLoopObserver(MessageLoop* message_loop); + virtual ~ScopedLoopObserver(); + + protected: + // Accessor to the loop that's used by the derived class. + base::MessageLoopProxy* message_loop() { return loop_; } + + private: + // Call to add or remove ourselves from the list of destruction observers for + // the message loop. + void ObserveLoopDestruction(bool enable, base::WaitableEvent* done); + + // A pointer to the message loop's proxy. In case the loop gets destroyed + // before this object goes out of scope, PostTask etc will fail but not crash. + scoped_refptr<base::MessageLoopProxy> loop_; + + DISALLOW_COPY_AND_ASSIGN(ScopedLoopObserver); +}; + +#endif // CONTENT_RENDERER_MEDIA_SCOPED_LOOP_OBSERVER_H_ diff --git a/media/audio/audio_manager_base.cc b/media/audio/audio_manager_base.cc index 9202e58..ff76b17 100644 --- a/media/audio/audio_manager_base.cc +++ b/media/audio/audio_manager_base.cc @@ -107,7 +107,7 @@ void AudioManagerBase::Shutdown() { } if (!audio_thread.get()) - return; + return; CHECK_NE(MessageLoop::current(), audio_thread->message_loop()); |