summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/content_renderer.gypi2
-rw-r--r--content/renderer/media/audio_device.cc55
-rw-r--r--content/renderer/media/audio_device.h14
-rw-r--r--content/renderer/media/audio_input_device.cc88
-rw-r--r--content/renderer/media/audio_input_device.h6
-rw-r--r--content/renderer/media/scoped_loop_observer.cc42
-rw-r--r--content/renderer/media/scoped_loop_observer.h45
-rw-r--r--media/audio/audio_manager_base.cc2
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());