| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/audio/audio_input_device.h" | 5 #include "media/audio/audio_input_device.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/message_loop.h" | 8 #include "base/message_loop.h" |
| 9 #include "base/threading/thread_restrictions.h" | 9 #include "base/threading/thread_restrictions.h" |
| 10 #include "base/time.h" | 10 #include "base/time.h" |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 42 CaptureCallback* capture_callback_; | 42 CaptureCallback* capture_callback_; |
| 43 scoped_ptr<AudioBus> audio_bus_; | 43 scoped_ptr<AudioBus> audio_bus_; |
| 44 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); | 44 DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
| 45 }; | 45 }; |
| 46 | 46 |
| 47 AudioInputDevice::AudioInputDevice( | 47 AudioInputDevice::AudioInputDevice( |
| 48 AudioInputIPC* ipc, | 48 AudioInputIPC* ipc, |
| 49 const scoped_refptr<base::MessageLoopProxy>& io_loop) | 49 const scoped_refptr<base::MessageLoopProxy>& io_loop) |
| 50 : ScopedLoopObserver(io_loop), | 50 : ScopedLoopObserver(io_loop), |
| 51 callback_(NULL), | 51 callback_(NULL), |
| 52 event_handler_(NULL), | |
| 53 ipc_(ipc), | 52 ipc_(ipc), |
| 54 stream_id_(0), | 53 stream_id_(0), |
| 55 session_id_(0), | 54 session_id_(0), |
| 56 pending_device_ready_(false), | |
| 57 agc_is_enabled_(false) { | 55 agc_is_enabled_(false) { |
| 58 CHECK(ipc_); | 56 CHECK(ipc_); |
| 59 } | 57 } |
| 60 | 58 |
| 61 void AudioInputDevice::Initialize(const AudioParameters& params, | 59 void AudioInputDevice::Initialize(const AudioParameters& params, |
| 62 CaptureCallback* callback, | 60 CaptureCallback* callback, |
| 63 CaptureEventHandler* event_handler) { | 61 int session_id) { |
| 64 DCHECK(!callback_); | 62 DCHECK(!callback_); |
| 65 DCHECK(!event_handler_); | 63 DCHECK_EQ(0, session_id_); |
| 66 audio_parameters_ = params; | 64 audio_parameters_ = params; |
| 67 callback_ = callback; | 65 callback_ = callback; |
| 68 event_handler_ = event_handler; | 66 session_id_ = session_id; |
| 69 } | |
| 70 | |
| 71 void AudioInputDevice::SetDevice(int session_id) { | |
| 72 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; | |
| 73 message_loop()->PostTask(FROM_HERE, | |
| 74 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); | |
| 75 } | 67 } |
| 76 | 68 |
| 77 void AudioInputDevice::Start() { | 69 void AudioInputDevice::Start() { |
| 78 DVLOG(1) << "Start()"; | 70 DVLOG(1) << "Start()"; |
| 79 message_loop()->PostTask(FROM_HERE, | 71 message_loop()->PostTask(FROM_HERE, |
| 80 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); | 72 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); |
| 81 } | 73 } |
| 82 | 74 |
| 83 void AudioInputDevice::Stop() { | 75 void AudioInputDevice::Stop() { |
| 84 DVLOG(1) << "Stop()"; | 76 DVLOG(1) << "Stop()"; |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 157 return; | 149 return; |
| 158 | 150 |
| 159 switch (state) { | 151 switch (state) { |
| 160 case AudioInputIPCDelegate::kStopped: | 152 case AudioInputIPCDelegate::kStopped: |
| 161 // TODO(xians): Should we just call ShutDownOnIOThread here instead? | 153 // TODO(xians): Should we just call ShutDownOnIOThread here instead? |
| 162 ipc_->RemoveDelegate(stream_id_); | 154 ipc_->RemoveDelegate(stream_id_); |
| 163 | 155 |
| 164 audio_thread_.Stop(MessageLoop::current()); | 156 audio_thread_.Stop(MessageLoop::current()); |
| 165 audio_callback_.reset(); | 157 audio_callback_.reset(); |
| 166 | 158 |
| 167 if (event_handler_) | |
| 168 event_handler_->OnDeviceStopped(); | |
| 169 | |
| 170 stream_id_ = 0; | 159 stream_id_ = 0; |
| 171 pending_device_ready_ = false; | |
| 172 break; | 160 break; |
| 173 case AudioInputIPCDelegate::kRecording: | 161 case AudioInputIPCDelegate::kRecording: |
| 174 NOTIMPLEMENTED(); | 162 NOTIMPLEMENTED(); |
| 175 break; | 163 break; |
| 176 case AudioInputIPCDelegate::kError: | 164 case AudioInputIPCDelegate::kError: |
| 177 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; | 165 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; |
| 178 // Don't dereference the callback object if the audio thread | 166 // Don't dereference the callback object if the audio thread |
| 179 // is stopped or stopping. That could mean that the callback | 167 // is stopped or stopping. That could mean that the callback |
| 180 // object has been deleted. | 168 // object has been deleted. |
| 181 // TODO(tommi): Add an explicit contract for clearing the callback | 169 // TODO(tommi): Add an explicit contract for clearing the callback |
| 182 // object. Possibly require calling Initialize again or provide | 170 // object. Possibly require calling Initialize again or provide |
| 183 // a callback object via Start() and clear it in Stop(). | 171 // a callback object via Start() and clear it in Stop(). |
| 184 if (!audio_thread_.IsStopped()) | 172 if (!audio_thread_.IsStopped()) |
| 185 callback_->OnCaptureError(); | 173 callback_->OnCaptureError(); |
| 186 break; | 174 break; |
| 187 default: | 175 default: |
| 188 NOTREACHED(); | 176 NOTREACHED(); |
| 189 break; | 177 break; |
| 190 } | 178 } |
| 191 } | 179 } |
| 192 | 180 |
| 193 void AudioInputDevice::OnDeviceReady(const std::string& device_id) { | |
| 194 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 195 DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; | |
| 196 | |
| 197 // Takes care of the case when Stop() is called before OnDeviceReady(). | |
| 198 if (!pending_device_ready_) | |
| 199 return; | |
| 200 | |
| 201 // If AudioInputDeviceManager returns an empty string, it means no device | |
| 202 // is ready for start. | |
| 203 if (device_id.empty()) { | |
| 204 ipc_->RemoveDelegate(stream_id_); | |
| 205 stream_id_ = 0; | |
| 206 } else { | |
| 207 ipc_->CreateStream(stream_id_, audio_parameters_, device_id, | |
| 208 agc_is_enabled_, kRequestedSharedMemoryCount); | |
| 209 } | |
| 210 | |
| 211 pending_device_ready_ = false; | |
| 212 // Notify the client that the device has been started. | |
| 213 if (event_handler_) | |
| 214 event_handler_->OnDeviceStarted(device_id); | |
| 215 } | |
| 216 | |
| 217 void AudioInputDevice::OnIPCClosed() { | 181 void AudioInputDevice::OnIPCClosed() { |
| 218 ipc_ = NULL; | 182 ipc_ = NULL; |
| 219 } | 183 } |
| 220 | 184 |
| 221 AudioInputDevice::~AudioInputDevice() { | 185 AudioInputDevice::~AudioInputDevice() { |
| 222 // TODO(henrika): The current design requires that the user calls | 186 // TODO(henrika): The current design requires that the user calls |
| 223 // Stop before deleting this class. | 187 // Stop before deleting this class. |
| 224 CHECK_EQ(0, stream_id_); | 188 CHECK_EQ(0, stream_id_); |
| 225 } | 189 } |
| 226 | 190 |
| 227 void AudioInputDevice::InitializeOnIOThread() { | 191 void AudioInputDevice::InitializeOnIOThread() { |
| 228 DCHECK(message_loop()->BelongsToCurrentThread()); | 192 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 229 // Make sure we don't call Start() more than once. | 193 // Make sure we don't call Start() more than once. |
| 230 DCHECK_EQ(0, stream_id_); | 194 DCHECK_EQ(0, stream_id_); |
| 231 if (stream_id_) | 195 if (stream_id_) |
| 232 return; | 196 return; |
| 233 | 197 |
| 198 if (session_id_ <= 0) { |
| 199 DLOG(WARNING) << "Invalid session id for the input stream " << session_id_; |
| 200 return; |
| 201 } |
| 202 |
| 234 stream_id_ = ipc_->AddDelegate(this); | 203 stream_id_ = ipc_->AddDelegate(this); |
| 235 // If |session_id_| is not specified, it will directly create the stream; | 204 ipc_->CreateStream(stream_id_, session_id_, audio_parameters_, |
| 236 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser | 205 agc_is_enabled_, kRequestedSharedMemoryCount); |
| 237 // and create the stream when getting a OnDeviceReady() callback. | |
| 238 if (!session_id_) { | |
| 239 ipc_->CreateStream(stream_id_, audio_parameters_, | |
| 240 AudioManagerBase::kDefaultDeviceId, agc_is_enabled_, | |
| 241 kRequestedSharedMemoryCount); | |
| 242 } else { | |
| 243 ipc_->StartDevice(stream_id_, session_id_); | |
| 244 pending_device_ready_ = true; | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { | |
| 249 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 250 session_id_ = session_id; | |
| 251 } | 206 } |
| 252 | 207 |
| 253 void AudioInputDevice::StartOnIOThread() { | 208 void AudioInputDevice::StartOnIOThread() { |
| 254 DCHECK(message_loop()->BelongsToCurrentThread()); | 209 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 255 if (stream_id_) | 210 if (stream_id_) |
| 256 ipc_->RecordStream(stream_id_); | 211 ipc_->RecordStream(stream_id_); |
| 257 } | 212 } |
| 258 | 213 |
| 259 void AudioInputDevice::ShutDownOnIOThread() { | 214 void AudioInputDevice::ShutDownOnIOThread() { |
| 260 DCHECK(message_loop()->BelongsToCurrentThread()); | 215 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 261 // NOTE: |completion| may be NULL. | 216 // NOTE: |completion| may be NULL. |
| 262 // Make sure we don't call shutdown more than once. | 217 // Make sure we don't call shutdown more than once. |
| 263 if (stream_id_) { | 218 if (stream_id_) { |
| 264 if (ipc_) { | 219 if (ipc_) { |
| 265 ipc_->CloseStream(stream_id_); | 220 ipc_->CloseStream(stream_id_); |
| 266 ipc_->RemoveDelegate(stream_id_); | 221 ipc_->RemoveDelegate(stream_id_); |
| 267 } | 222 } |
| 268 | 223 |
| 269 stream_id_ = 0; | 224 stream_id_ = 0; |
| 270 session_id_ = 0; | |
| 271 pending_device_ready_ = false; | |
| 272 agc_is_enabled_ = false; | 225 agc_is_enabled_ = false; |
| 273 } | 226 } |
| 274 | 227 |
| 275 // We can run into an issue where ShutDownOnIOThread is called right after | 228 // We can run into an issue where ShutDownOnIOThread is called right after |
| 276 // OnStreamCreated is called in cases where Start/Stop are called before we | 229 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 277 // get the OnStreamCreated callback. To handle that corner case, we call | 230 // get the OnStreamCreated callback. To handle that corner case, we call |
| 278 // Stop(). In most cases, the thread will already be stopped. | 231 // Stop(). In most cases, the thread will already be stopped. |
| 279 // Another situation is when the IO thread goes away before Stop() is called | 232 // Another situation is when the IO thread goes away before Stop() is called |
| 280 // in which case, we cannot use the message loop to close the thread handle | 233 // in which case, we cannot use the message loop to close the thread handle |
| 281 // and can't not rely on the main thread existing either. | 234 // and can't not rely on the main thread existing either. |
| 282 base::ThreadRestrictions::ScopedAllowIO allow_io; | 235 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 283 audio_thread_.Stop(NULL); | 236 audio_thread_.Stop(NULL); |
| 284 audio_callback_.reset(); | 237 audio_callback_.reset(); |
| 238 session_id_ = 0; |
| 285 } | 239 } |
| 286 | 240 |
| 287 void AudioInputDevice::SetVolumeOnIOThread(double volume) { | 241 void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
| 288 DCHECK(message_loop()->BelongsToCurrentThread()); | 242 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 289 if (stream_id_) | 243 if (stream_id_) |
| 290 ipc_->SetVolume(stream_id_, volume); | 244 ipc_->SetVolume(stream_id_, volume); |
| 291 } | 245 } |
| 292 | 246 |
| 293 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { | 247 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { |
| 294 DCHECK(message_loop()->BelongsToCurrentThread()); | 248 DCHECK(message_loop()->BelongsToCurrentThread()); |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 350 // with nominal range -1.0 -> +1.0. | 304 // with nominal range -1.0 -> +1.0. |
| 351 audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); | 305 audio_bus_->FromInterleaved(memory, audio_bus_->frames(), bytes_per_sample); |
| 352 | 306 |
| 353 // Deliver captured data to the client in floating point format | 307 // Deliver captured data to the client in floating point format |
| 354 // and update the audio-delay measurement. | 308 // and update the audio-delay measurement. |
| 355 capture_callback_->Capture(audio_bus_.get(), | 309 capture_callback_->Capture(audio_bus_.get(), |
| 356 audio_delay_milliseconds, volume); | 310 audio_delay_milliseconds, volume); |
| 357 } | 311 } |
| 358 | 312 |
| 359 } // namespace media | 313 } // namespace media |
| OLD | NEW |