Chromium Code Reviews| 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 "content/renderer/media/audio_input_device.h" | 5 #include "content/renderer/media/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 27 matching lines...) Expand all Loading... | |
| 38 }; | 38 }; |
| 39 | 39 |
| 40 AudioInputDevice::AudioInputDevice(const media::AudioParameters& params, | 40 AudioInputDevice::AudioInputDevice(const media::AudioParameters& params, |
| 41 CaptureCallback* callback, | 41 CaptureCallback* callback, |
| 42 CaptureEventHandler* event_handler) | 42 CaptureEventHandler* event_handler) |
| 43 : ScopedLoopObserver( | 43 : ScopedLoopObserver( |
| 44 ChildProcess::current()->io_message_loop()->message_loop_proxy()), | 44 ChildProcess::current()->io_message_loop()->message_loop_proxy()), |
| 45 audio_parameters_(params), | 45 audio_parameters_(params), |
| 46 callback_(callback), | 46 callback_(callback), |
| 47 event_handler_(event_handler), | 47 event_handler_(event_handler), |
| 48 volume_(1.0), | |
| 49 stream_id_(0), | 48 stream_id_(0), |
| 50 session_id_(0), | 49 session_id_(0), |
| 51 pending_device_ready_(false), | 50 pending_device_ready_(false), |
| 52 agc_is_enabled_(false) { | 51 agc_is_enabled_(false) { |
| 53 filter_ = RenderThreadImpl::current()->audio_input_message_filter(); | 52 // TODO(tommi): This needs to be updated to match AudioDevice. |
| 53 ipc_ = RenderThreadImpl::current()->audio_input_message_filter(); | |
| 54 } | 54 } |
| 55 | 55 |
| 56 void AudioInputDevice::SetDevice(int session_id) { | 56 void AudioInputDevice::SetDevice(int session_id) { |
| 57 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; | 57 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; |
| 58 message_loop()->PostTask(FROM_HERE, | 58 message_loop()->PostTask(FROM_HERE, |
| 59 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); | 59 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); |
| 60 } | 60 } |
| 61 | 61 |
| 62 void AudioInputDevice::Start() { | 62 void AudioInputDevice::Start() { |
| 63 DVLOG(1) << "Start()"; | 63 DVLOG(1) << "Start()"; |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 126 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); | 126 audio_thread_.Start(audio_callback_.get(), socket_handle, "AudioInputDevice"); |
| 127 | 127 |
| 128 MessageLoop::current()->PostTask(FROM_HERE, | 128 MessageLoop::current()->PostTask(FROM_HERE, |
| 129 base::Bind(&AudioInputDevice::StartOnIOThread, this)); | 129 base::Bind(&AudioInputDevice::StartOnIOThread, this)); |
| 130 } | 130 } |
| 131 | 131 |
| 132 void AudioInputDevice::OnVolume(double volume) { | 132 void AudioInputDevice::OnVolume(double volume) { |
| 133 NOTIMPLEMENTED(); | 133 NOTIMPLEMENTED(); |
| 134 } | 134 } |
| 135 | 135 |
| 136 void AudioInputDevice::OnStateChanged(AudioStreamState state) { | 136 void AudioInputDevice::OnStateChanged( |
| 137 media::AudioInputDeviceIPCDelegate::State state) { | |
| 137 DCHECK(message_loop()->BelongsToCurrentThread()); | 138 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 138 | 139 |
| 139 // Do nothing if the stream has been closed. | 140 // Do nothing if the stream has been closed. |
| 140 if (!stream_id_) | 141 if (!stream_id_) |
| 141 return; | 142 return; |
| 142 | 143 |
| 143 switch (state) { | 144 switch (state) { |
| 144 // TODO(xians): This should really be kAudioStreamStopped since the stream | 145 case media::AudioInputDeviceIPCDelegate::kStopped: |
| 145 // has been closed at this point. | |
| 146 case kAudioStreamPaused: | |
| 147 // TODO(xians): Should we just call ShutDownOnIOThread here instead? | 146 // TODO(xians): Should we just call ShutDownOnIOThread here instead? |
| 148 filter_->RemoveDelegate(stream_id_); | 147 ipc_->RemoveDelegate(stream_id_); |
| 149 | 148 |
| 150 audio_thread_.Stop(MessageLoop::current()); | 149 audio_thread_.Stop(MessageLoop::current()); |
| 151 audio_callback_.reset(); | 150 audio_callback_.reset(); |
| 152 | 151 |
| 153 if (event_handler_) | 152 if (event_handler_) |
| 154 event_handler_->OnDeviceStopped(); | 153 event_handler_->OnDeviceStopped(); |
| 155 | 154 |
| 156 stream_id_ = 0; | 155 stream_id_ = 0; |
| 157 pending_device_ready_ = false; | 156 pending_device_ready_ = false; |
| 158 break; | 157 break; |
| 159 case kAudioStreamPlaying: | 158 case media::AudioInputDeviceIPCDelegate::kRecording: |
| 160 NOTIMPLEMENTED(); | 159 NOTIMPLEMENTED(); |
| 161 break; | 160 break; |
| 162 case kAudioStreamError: | 161 case media::AudioInputDeviceIPCDelegate::kError: |
| 163 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; | 162 DLOG(WARNING) << "AudioInputDevice::OnStateChanged(kError)"; |
| 164 // Don't dereference the callback object if the audio thread | 163 // Don't dereference the callback object if the audio thread |
| 165 // is stopped or stopping. That could mean that the callback | 164 // is stopped or stopping. That could mean that the callback |
| 166 // object has been deleted. | 165 // object has been deleted. |
| 167 // TODO(tommi): Add an explicit contract for clearing the callback | 166 // TODO(tommi): Add an explicit contract for clearing the callback |
| 168 // object. Possibly require calling Initialize again or provide | 167 // object. Possibly require calling Initialize again or provide |
| 169 // a callback object via Start() and clear it in Stop(). | 168 // a callback object via Start() and clear it in Stop(). |
| 170 if (!audio_thread_.IsStopped()) | 169 if (!audio_thread_.IsStopped()) |
| 171 callback_->OnCaptureError(); | 170 callback_->OnCaptureError(); |
| 172 break; | 171 break; |
| 173 default: | 172 default: |
| 174 NOTREACHED(); | 173 NOTREACHED(); |
| 175 break; | 174 break; |
| 176 } | 175 } |
| 177 } | 176 } |
| 178 | 177 |
| 179 void AudioInputDevice::OnDeviceReady(const std::string& device_id) { | 178 void AudioInputDevice::OnDeviceReady(const std::string& device_id) { |
| 180 DCHECK(message_loop()->BelongsToCurrentThread()); | 179 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 181 DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; | 180 DVLOG(1) << "OnDeviceReady (device_id=" << device_id << ")"; |
| 182 | 181 |
| 183 // Takes care of the case when Stop() is called before OnDeviceReady(). | 182 // Takes care of the case when Stop() is called before OnDeviceReady(). |
| 184 if (!pending_device_ready_) | 183 if (!pending_device_ready_) |
| 185 return; | 184 return; |
| 186 | 185 |
| 187 // If AudioInputDeviceManager returns an empty string, it means no device | 186 // If AudioInputDeviceManager returns an empty string, it means no device |
| 188 // is ready for start. | 187 // is ready for start. |
| 189 if (device_id.empty()) { | 188 if (device_id.empty()) { |
| 190 filter_->RemoveDelegate(stream_id_); | 189 ipc_->RemoveDelegate(stream_id_); |
| 191 stream_id_ = 0; | 190 stream_id_ = 0; |
| 192 } else { | 191 } else { |
| 193 Send(new AudioInputHostMsg_CreateStream(stream_id_, audio_parameters_, | 192 ipc_->CreateStream(stream_id_, audio_parameters_, device_id, |
| 194 device_id, agc_is_enabled_)); | 193 agc_is_enabled_); |
|
scherkus (not reviewing)
2012/07/24 17:57:48
indenting
tommi (sloooow) - chröme
2012/07/25 13:46:17
Done.
| |
| 195 } | 194 } |
| 196 | 195 |
| 197 pending_device_ready_ = false; | 196 pending_device_ready_ = false; |
| 198 // Notify the client that the device has been started. | 197 // Notify the client that the device has been started. |
| 199 if (event_handler_) | 198 if (event_handler_) |
| 200 event_handler_->OnDeviceStarted(device_id); | 199 event_handler_->OnDeviceStarted(device_id); |
| 201 } | 200 } |
| 202 | 201 |
| 202 void AudioInputDevice::OnIPCClosed() { | |
| 203 ipc_ = NULL; | |
|
scherkus (not reviewing)
2012/07/24 17:57:48
FYI we seem to guard against calling ipc_ with str
tommi (sloooow) - chröme
2012/07/25 13:46:17
The stream_id_ checks are not really meant to guar
| |
| 204 } | |
| 205 | |
| 203 AudioInputDevice::~AudioInputDevice() { | 206 AudioInputDevice::~AudioInputDevice() { |
| 204 // TODO(henrika): The current design requires that the user calls | 207 // TODO(henrika): The current design requires that the user calls |
| 205 // Stop before deleting this class. | 208 // Stop before deleting this class. |
| 206 CHECK_EQ(0, stream_id_); | 209 CHECK_EQ(0, stream_id_); |
| 207 } | 210 } |
| 208 | 211 |
| 209 void AudioInputDevice::InitializeOnIOThread() { | 212 void AudioInputDevice::InitializeOnIOThread() { |
| 210 DCHECK(message_loop()->BelongsToCurrentThread()); | 213 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 211 // Make sure we don't call Start() more than once. | 214 // Make sure we don't call Start() more than once. |
| 212 DCHECK_EQ(0, stream_id_); | 215 DCHECK_EQ(0, stream_id_); |
| 213 if (stream_id_) | 216 if (stream_id_) |
| 214 return; | 217 return; |
| 215 | 218 |
| 216 stream_id_ = filter_->AddDelegate(this); | 219 stream_id_ = ipc_->AddDelegate(this); |
| 217 // If |session_id_| is not specified, it will directly create the stream; | 220 // If |session_id_| is not specified, it will directly create the stream; |
| 218 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser | 221 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser |
| 219 // and create the stream when getting a OnDeviceReady() callback. | 222 // and create the stream when getting a OnDeviceReady() callback. |
| 220 if (!session_id_) { | 223 if (!session_id_) { |
| 221 Send(new AudioInputHostMsg_CreateStream( | 224 ipc_->CreateStream(stream_id_, audio_parameters_, |
| 222 stream_id_, audio_parameters_, | 225 media::AudioManagerBase::kDefaultDeviceId, agc_is_enabled_); |
| 223 media::AudioManagerBase::kDefaultDeviceId, | |
| 224 agc_is_enabled_)); | |
| 225 } else { | 226 } else { |
| 226 Send(new AudioInputHostMsg_StartDevice(stream_id_, session_id_)); | 227 ipc_->StartDevice(stream_id_, session_id_); |
| 227 pending_device_ready_ = true; | 228 pending_device_ready_ = true; |
| 228 } | 229 } |
| 229 } | 230 } |
| 230 | 231 |
| 231 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { | 232 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { |
| 232 DCHECK(message_loop()->BelongsToCurrentThread()); | 233 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 233 session_id_ = session_id; | 234 session_id_ = session_id; |
| 234 } | 235 } |
| 235 | 236 |
| 236 void AudioInputDevice::StartOnIOThread() { | 237 void AudioInputDevice::StartOnIOThread() { |
| 237 DCHECK(message_loop()->BelongsToCurrentThread()); | 238 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 238 if (stream_id_) | 239 if (stream_id_) |
| 239 Send(new AudioInputHostMsg_RecordStream(stream_id_)); | 240 ipc_->RecordStream(stream_id_); |
| 240 } | 241 } |
| 241 | 242 |
| 242 void AudioInputDevice::ShutDownOnIOThread() { | 243 void AudioInputDevice::ShutDownOnIOThread() { |
| 243 DCHECK(message_loop()->BelongsToCurrentThread()); | 244 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 244 // NOTE: |completion| may be NULL. | 245 // NOTE: |completion| may be NULL. |
| 245 // Make sure we don't call shutdown more than once. | 246 // Make sure we don't call shutdown more than once. |
| 246 if (stream_id_) { | 247 if (stream_id_) { |
| 247 filter_->RemoveDelegate(stream_id_); | 248 ipc_->CloseStream(stream_id_); |
| 248 Send(new AudioInputHostMsg_CloseStream(stream_id_)); | 249 ipc_->RemoveDelegate(stream_id_); |
| 249 | 250 |
| 250 stream_id_ = 0; | 251 stream_id_ = 0; |
| 251 session_id_ = 0; | 252 session_id_ = 0; |
| 252 pending_device_ready_ = false; | 253 pending_device_ready_ = false; |
| 253 agc_is_enabled_ = false; | 254 agc_is_enabled_ = false; |
| 254 } | 255 } |
| 255 | 256 |
| 256 // We can run into an issue where ShutDownOnIOThread is called right after | 257 // We can run into an issue where ShutDownOnIOThread is called right after |
| 257 // OnStreamCreated is called in cases where Start/Stop are called before we | 258 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 258 // get the OnStreamCreated callback. To handle that corner case, we call | 259 // get the OnStreamCreated callback. To handle that corner case, we call |
| 259 // Stop(). In most cases, the thread will already be stopped. | 260 // Stop(). In most cases, the thread will already be stopped. |
| 260 // Another situation is when the IO thread goes away before Stop() is called | 261 // Another situation is when the IO thread goes away before Stop() is called |
| 261 // in which case, we cannot use the message loop to close the thread handle | 262 // in which case, we cannot use the message loop to close the thread handle |
| 262 // and can't not rely on the main thread existing either. | 263 // and can't not rely on the main thread existing either. |
| 263 base::ThreadRestrictions::ScopedAllowIO allow_io; | 264 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 264 audio_thread_.Stop(NULL); | 265 audio_thread_.Stop(NULL); |
| 265 audio_callback_.reset(); | 266 audio_callback_.reset(); |
| 266 } | 267 } |
| 267 | 268 |
| 268 void AudioInputDevice::SetVolumeOnIOThread(double volume) { | 269 void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
| 269 DCHECK(message_loop()->BelongsToCurrentThread()); | 270 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 270 if (stream_id_) | 271 if (stream_id_) |
| 271 Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); | 272 ipc_->SetVolume(stream_id_, volume); |
| 272 } | 273 } |
| 273 | 274 |
| 274 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { | 275 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { |
| 275 DCHECK(message_loop()->BelongsToCurrentThread()); | 276 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 276 DCHECK_EQ(0, stream_id_) << | 277 DCHECK_EQ(0, stream_id_) << |
| 277 "The AGC state can not be modified while capturing is active."; | 278 "The AGC state can not be modified while capturing is active."; |
| 278 if (stream_id_) | 279 if (stream_id_) |
| 279 return; | 280 return; |
| 280 | 281 |
| 281 // We simply store the new AGC setting here. This value will be used when | 282 // We simply store the new AGC setting here. This value will be used when |
| 282 // a new stream is initialized and by GetAutomaticGainControl(). | 283 // a new stream is initialized and by GetAutomaticGainControl(). |
| 283 agc_is_enabled_ = enabled; | 284 agc_is_enabled_ = enabled; |
| 284 } | 285 } |
| 285 | 286 |
| 286 void AudioInputDevice::Send(IPC::Message* message) { | |
| 287 filter_->Send(message); | |
| 288 } | |
| 289 | |
| 290 void AudioInputDevice::WillDestroyCurrentMessageLoop() { | 287 void AudioInputDevice::WillDestroyCurrentMessageLoop() { |
| 291 LOG(ERROR) << "IO loop going away before the input device has been stopped"; | 288 LOG(ERROR) << "IO loop going away before the input device has been stopped"; |
| 292 ShutDownOnIOThread(); | 289 ShutDownOnIOThread(); |
| 293 } | 290 } |
| 294 | 291 |
| 295 // AudioInputDevice::AudioThreadCallback | 292 // AudioInputDevice::AudioThreadCallback |
| 296 AudioInputDevice::AudioThreadCallback::AudioThreadCallback( | 293 AudioInputDevice::AudioThreadCallback::AudioThreadCallback( |
| 297 const media::AudioParameters& audio_parameters, | 294 const media::AudioParameters& audio_parameters, |
| 298 base::SharedMemoryHandle memory, | 295 base::SharedMemoryHandle memory, |
| 299 int memory_length, | 296 int memory_length, |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 334 channel_index, | 331 channel_index, |
| 335 bytes_per_sample, | 332 bytes_per_sample, |
| 336 number_of_frames); | 333 number_of_frames); |
| 337 } | 334 } |
| 338 | 335 |
| 339 // Deliver captured data to the client in floating point format | 336 // Deliver captured data to the client in floating point format |
| 340 // and update the audio-delay measurement. | 337 // and update the audio-delay measurement. |
| 341 capture_callback_->Capture(audio_data_, number_of_frames, | 338 capture_callback_->Capture(audio_data_, number_of_frames, |
| 342 audio_delay_milliseconds, volume); | 339 audio_delay_milliseconds, volume); |
| 343 } | 340 } |
| OLD | NEW |