Index: media/audio/audio_input_device.cc |
diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc |
index 5c5bcf756f31a113b41a99d1236606c43e77de81..08eed35f16eb4dd7ddf9cdbedaf30c071162d038 100644 |
--- a/media/audio/audio_input_device.cc |
+++ b/media/audio/audio_input_device.cc |
@@ -4,6 +4,7 @@ |
#include "media/audio/audio_input_device.h" |
+#include "base/basictypes.h" |
#include "base/bind.h" |
#include "base/message_loop.h" |
#include "base/threading/thread_restrictions.h" |
@@ -37,17 +38,25 @@ class AudioInputDevice::AudioThreadCallback |
}; |
AudioInputDevice::AudioInputDevice( |
- AudioInputIPC* ipc, |
+ scoped_ptr<AudioInputIPC> ipc, |
const scoped_refptr<base::MessageLoopProxy>& io_loop) |
: ScopedLoopObserver(io_loop), |
callback_(NULL), |
event_handler_(NULL), |
- ipc_(ipc), |
- stream_id_(0), |
+ ipc_(ipc.Pass()), |
+ state_(IDLE), |
session_id_(0), |
- pending_device_ready_(false), |
- agc_is_enabled_(false) { |
+ agc_is_enabled_(false), |
+ stopping_hack_(false) { |
CHECK(ipc_); |
+ |
+ // The correctness of the code depends on the relative values assigned in the |
+ // State enum. |
+ COMPILE_ASSERT(IPC_CLOSED < IDLE, invalid_enum_value_assignment_0); |
+ COMPILE_ASSERT(IDLE < STARTING_DEVICE, invalid_enum_value_assignment_1); |
+ COMPILE_ASSERT(STARTING_DEVICE < CREATING_STREAM, |
+ invalid_enum_value_assignment_2); |
+ COMPILE_ASSERT(CREATING_STREAM < RECORDING, invalid_enum_value_assignment_3); |
} |
void AudioInputDevice::Initialize(const AudioParameters& params, |
@@ -55,6 +64,7 @@ void AudioInputDevice::Initialize(const AudioParameters& params, |
CaptureEventHandler* event_handler) { |
DCHECK(!callback_); |
DCHECK(!event_handler_); |
+ DCHECK(params.IsValid()); |
audio_parameters_ = params; |
callback_ = callback; |
event_handler_ = event_handler; |
@@ -67,9 +77,10 @@ void AudioInputDevice::SetDevice(int session_id) { |
} |
void AudioInputDevice::Start() { |
+ DCHECK(callback_) << "Initialize hasn't been called"; |
DVLOG(1) << "Start()"; |
message_loop()->PostTask(FROM_HERE, |
- base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); |
+ base::Bind(&AudioInputDevice::StartUpOnIOThread, this)); |
} |
void AudioInputDevice::Stop() { |
@@ -78,6 +89,7 @@ void AudioInputDevice::Stop() { |
{ |
base::AutoLock auto_lock(audio_thread_lock_); |
audio_thread_.Stop(MessageLoop::current()); |
+ stopping_hack_ = true; |
} |
message_loop()->PostTask(FROM_HERE, |
@@ -113,17 +125,17 @@ void AudioInputDevice::OnStreamCreated( |
DCHECK_GE(handle.fd, 0); |
DCHECK_GE(socket_handle, 0); |
#endif |
- DCHECK(length); |
- DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")"; |
+ DCHECK_GT(length, 0); |
- // We should only get this callback if stream_id_ is valid. If it is not, |
- // the IPC layer should have closed the shared memory and socket handles |
- // for us and not invoked the callback. The basic assertion is that when |
- // stream_id_ is 0 the AudioInputDevice instance is not registered as a |
- // delegate and hence it should not receive callbacks. |
- DCHECK(stream_id_); |
+ if (state_ != CREATING_STREAM) |
+ return; |
base::AutoLock auto_lock(audio_thread_lock_); |
+ // TODO(miu): See TODO in OnStreamCreated method for AudioOutputDevice. |
+ // Interface changes need to be made; likely, after AudioInputDevice is merged |
+ // into AudioOutputDevice (http://crbug.com/179597). |
+ if (stopping_hack_) |
+ return; |
DCHECK(audio_thread_.IsStopped()); |
audio_callback_.reset( |
@@ -131,8 +143,8 @@ void AudioInputDevice::OnStreamCreated( |
length, callback_)); |
audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); |
- MessageLoop::current()->PostTask(FROM_HERE, |
- base::Bind(&AudioInputDevice::StartOnIOThread, this)); |
+ state_ = RECORDING; |
+ ipc_->RecordStream(); |
} |
void AudioInputDevice::OnVolume(double volume) { |
@@ -144,22 +156,16 @@ void AudioInputDevice::OnStateChanged( |
DCHECK(message_loop()->BelongsToCurrentThread()); |
// Do nothing if the stream has been closed. |
- if (!stream_id_) |
+ if (state_ < STARTING_DEVICE) |
return; |
+ // TODO(miu): Clean-up inconsistent and incomplete handling here. |
+ // http://crbug.com/180640 |
switch (state) { |
case AudioInputIPCDelegate::kStopped: |
- // TODO(xians): Should we just call ShutDownOnIOThread here instead? |
- ipc_->RemoveDelegate(stream_id_); |
- |
- audio_thread_.Stop(MessageLoop::current()); |
- audio_callback_.reset(); |
- |
if (event_handler_) |
event_handler_->OnDeviceStopped(); |
- |
- stream_id_ = 0; |
- pending_device_ready_ = false; |
+ ShutDownOnIOThread(); |
break; |
case AudioInputIPCDelegate::kRecording: |
NOTIMPLEMENTED(); |
@@ -186,79 +192,70 @@ void AudioInputDevice::OnDeviceReady(const std::string& device_id) { |
DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; |
// Takes care of the case when Stop() is called before OnDeviceReady(). |
- if (!pending_device_ready_) |
+ if (state_ != STARTING_DEVICE) |
return; |
// If AudioInputDeviceManager returns an empty string, it means no device |
// is ready for start. |
if (device_id.empty()) { |
- ipc_->RemoveDelegate(stream_id_); |
- stream_id_ = 0; |
+ ipc_->CloseStream(); |
+ state_ = IDLE; |
} else { |
- ipc_->CreateStream(stream_id_, audio_parameters_, device_id, |
- agc_is_enabled_); |
+ state_ = CREATING_STREAM; |
+ ipc_->CreateStream(this, audio_parameters_, device_id, agc_is_enabled_); |
} |
- pending_device_ready_ = false; |
// Notify the client that the device has been started. |
if (event_handler_) |
event_handler_->OnDeviceStarted(device_id); |
} |
void AudioInputDevice::OnIPCClosed() { |
- ipc_ = NULL; |
+ DCHECK(message_loop()->BelongsToCurrentThread()); |
+ state_ = IPC_CLOSED; |
+ ipc_.reset(); |
} |
AudioInputDevice::~AudioInputDevice() { |
// TODO(henrika): The current design requires that the user calls |
// Stop before deleting this class. |
- CHECK_EQ(0, stream_id_); |
+ DCHECK(audio_thread_.IsStopped()); |
} |
-void AudioInputDevice::InitializeOnIOThread() { |
+void AudioInputDevice::StartUpOnIOThread() { |
DCHECK(message_loop()->BelongsToCurrentThread()); |
+ |
// Make sure we don't call Start() more than once. |
- DCHECK_EQ(0, stream_id_); |
- if (stream_id_) |
+ if (state_ != IDLE) |
return; |
- stream_id_ = ipc_->AddDelegate(this); |
- // If |session_id_| is not specified, it will directly create the stream; |
- // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser |
- // and create the stream when getting a OnDeviceReady() callback. |
+ // If |session_id_| is not specified, directly create the stream. Otherwise, |
+ // send a AudioInputHostMsg_StartDevice msg to the browser and create the |
+ // stream after receiving a reply via the OnDeviceReady() callback. |
if (!session_id_) { |
- ipc_->CreateStream(stream_id_, audio_parameters_, |
- AudioManagerBase::kDefaultDeviceId, agc_is_enabled_); |
+ state_ = CREATING_STREAM; |
+ ipc_->CreateStream(this, audio_parameters_, |
+ AudioManagerBase::kDefaultDeviceId, agc_is_enabled_); |
} else { |
- ipc_->StartDevice(stream_id_, session_id_); |
- pending_device_ready_ = true; |
+ state_ = STARTING_DEVICE; |
+ ipc_->StartDevice(this, session_id_); |
} |
} |
void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { |
DCHECK(message_loop()->BelongsToCurrentThread()); |
+ DCHECK_EQ(state_, IDLE); |
session_id_ = session_id; |
} |
-void AudioInputDevice::StartOnIOThread() { |
- DCHECK(message_loop()->BelongsToCurrentThread()); |
- if (stream_id_) |
- ipc_->RecordStream(stream_id_); |
-} |
- |
void AudioInputDevice::ShutDownOnIOThread() { |
DCHECK(message_loop()->BelongsToCurrentThread()); |
- // NOTE: |completion| may be NULL. |
- // Make sure we don't call shutdown more than once. |
- if (stream_id_) { |
- if (ipc_) { |
- ipc_->CloseStream(stream_id_); |
- ipc_->RemoveDelegate(stream_id_); |
- } |
- |
- stream_id_ = 0; |
+ |
+ // Close the stream, if we haven't already. |
+ if (state_ >= STARTING_DEVICE) { |
+ ipc_->CloseStream(); |
+ state_ = IDLE; |
session_id_ = 0; |
- pending_device_ready_ = false; |
agc_is_enabled_ = false; |
} |
@@ -266,26 +263,30 @@ void AudioInputDevice::ShutDownOnIOThread() { |
// OnStreamCreated is called in cases where Start/Stop are called before we |
// get the OnStreamCreated callback. To handle that corner case, we call |
// Stop(). In most cases, the thread will already be stopped. |
+ // |
// Another situation is when the IO thread goes away before Stop() is called |
// in which case, we cannot use the message loop to close the thread handle |
// and can't not rely on the main thread existing either. |
+ base::AutoLock auto_lock_(audio_thread_lock_); |
base::ThreadRestrictions::ScopedAllowIO allow_io; |
audio_thread_.Stop(NULL); |
audio_callback_.reset(); |
+ stopping_hack_ = false; |
} |
void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
DCHECK(message_loop()->BelongsToCurrentThread()); |
- if (stream_id_) |
- ipc_->SetVolume(stream_id_, volume); |
+ if (state_ >= CREATING_STREAM) |
+ ipc_->SetVolume(volume); |
} |
void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { |
DCHECK(message_loop()->BelongsToCurrentThread()); |
- DCHECK_EQ(0, stream_id_) << |
- "The AGC state can not be modified while capturing is active."; |
- if (stream_id_) |
+ |
+ if (state_ >= STARTING_DEVICE) { |
+ DLOG(WARNING) << "The AGC state can not be modified after starting."; |
return; |
+ } |
// We simply store the new AGC setting here. This value will be used when |
// a new stream is initialized and by GetAutomaticGainControl(). |