Chromium Code Reviews| 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..9d43375aec4c62c10044922387ff6ace98e08c9f 100644 |
| --- a/media/audio/audio_input_device.cc |
| +++ b/media/audio/audio_input_device.cc |
| @@ -37,16 +37,16 @@ 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_); |
| } |
| @@ -55,6 +55,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 +68,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 +80,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 +116,16 @@ void AudioInputDevice::OnStreamCreated( |
| DCHECK_GE(handle.fd, 0); |
| DCHECK_GE(socket_handle, 0); |
| #endif |
| - DCHECK(length); |
| - DVLOG(1) << "OnStreamCreated (stream_id=" << stream_id_ << ")"; |
| + DCHECK_LT(0, length); |
|
DaleCurtis
2013/03/05 23:29:54
/fist-shake.
miu
2013/03/06 22:36:52
With a cabasa, maybe?
|
| - // 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. Same |
| + // shit applies here. |
|
palmer
2013/03/05 21:09:32
This would be the first naughty word I've seen in
DaleCurtis
2013/03/05 23:29:54
Damn straight.
miu
2013/03/06 22:36:52
Whoops. I meant to, um, *elaborate* on this. ;-)
|
| + if (stopping_hack_) |
| + return; |
| DCHECK(audio_thread_.IsStopped()); |
| audio_callback_.reset( |
| @@ -131,8 +133,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 +146,14 @@ void AudioInputDevice::OnStateChanged( |
| DCHECK(message_loop()->BelongsToCurrentThread()); |
| // Do nothing if the stream has been closed. |
| - if (!stream_id_) |
| + if (state_ < STARTING_DEVICE) |
| return; |
| 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 +180,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; |
|
no longer working on chromium
2013/03/05 18:33:10
why do we need this IPC_CLOSED state? Can we simpl
miu
2013/03/06 19:25:41
See header file comments for the enum. IDLE indic
|
| + 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(IDLE, state_); |
| 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 +251,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(). |