| 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 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 45 callback_(callback), | 45 callback_(callback), |
| 46 event_handler_(event_handler), | 46 event_handler_(event_handler), |
| 47 volume_(1.0), | 47 volume_(1.0), |
| 48 stream_id_(0), | 48 stream_id_(0), |
| 49 session_id_(0), | 49 session_id_(0), |
| 50 pending_device_ready_(false), | 50 pending_device_ready_(false), |
| 51 agc_is_enabled_(false) { | 51 agc_is_enabled_(false) { |
| 52 filter_ = RenderThreadImpl::current()->audio_input_message_filter(); | 52 filter_ = RenderThreadImpl::current()->audio_input_message_filter(); |
| 53 } | 53 } |
| 54 | 54 |
| 55 AudioInputDevice::~AudioInputDevice() { | 55 void AudioInputDevice::SetDevice(int session_id) { |
| 56 // TODO(henrika): The current design requires that the user calls | 56 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; |
| 57 // Stop before deleting this class. | 57 message_loop()->PostTask(FROM_HERE, |
| 58 CHECK_EQ(0, stream_id_); | 58 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); |
| 59 } | 59 } |
| 60 | 60 |
| 61 void AudioInputDevice::Start() { | 61 void AudioInputDevice::Start() { |
| 62 DVLOG(1) << "Start()"; | 62 DVLOG(1) << "Start()"; |
| 63 message_loop()->PostTask(FROM_HERE, | 63 message_loop()->PostTask(FROM_HERE, |
| 64 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); | 64 base::Bind(&AudioInputDevice::InitializeOnIOThread, this)); |
| 65 } | 65 } |
| 66 | 66 |
| 67 void AudioInputDevice::SetDevice(int session_id) { | |
| 68 DVLOG(1) << "SetDevice (session_id=" << session_id << ")"; | |
| 69 message_loop()->PostTask(FROM_HERE, | |
| 70 base::Bind(&AudioInputDevice::SetSessionIdOnIOThread, this, session_id)); | |
| 71 } | |
| 72 | |
| 73 void AudioInputDevice::Stop() { | 67 void AudioInputDevice::Stop() { |
| 74 DVLOG(1) << "Stop()"; | 68 DVLOG(1) << "Stop()"; |
| 75 | 69 |
| 76 { | 70 { |
| 77 base::AutoLock auto_lock(audio_thread_lock_); | 71 base::AutoLock auto_lock(audio_thread_lock_); |
| 78 audio_thread_.Stop(MessageLoop::current()); | 72 audio_thread_.Stop(MessageLoop::current()); |
| 79 } | 73 } |
| 80 | 74 |
| 81 message_loop()->PostTask(FROM_HERE, | 75 message_loop()->PostTask(FROM_HERE, |
| 82 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); | 76 base::Bind(&AudioInputDevice::ShutDownOnIOThread, this)); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 97 return false; | 91 return false; |
| 98 } | 92 } |
| 99 | 93 |
| 100 void AudioInputDevice::SetAutomaticGainControl(bool enabled) { | 94 void AudioInputDevice::SetAutomaticGainControl(bool enabled) { |
| 101 DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")"; | 95 DVLOG(1) << "SetAutomaticGainControl(enabled=" << enabled << ")"; |
| 102 message_loop()->PostTask(FROM_HERE, | 96 message_loop()->PostTask(FROM_HERE, |
| 103 base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread, | 97 base::Bind(&AudioInputDevice::SetAutomaticGainControlOnIOThread, |
| 104 this, enabled)); | 98 this, enabled)); |
| 105 } | 99 } |
| 106 | 100 |
| 107 void AudioInputDevice::InitializeOnIOThread() { | |
| 108 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 109 // Make sure we don't call Start() more than once. | |
| 110 DCHECK_EQ(0, stream_id_); | |
| 111 if (stream_id_) | |
| 112 return; | |
| 113 | |
| 114 stream_id_ = filter_->AddDelegate(this); | |
| 115 // If |session_id_| is not specified, it will directly create the stream; | |
| 116 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser | |
| 117 // and create the stream when getting a OnDeviceReady() callback. | |
| 118 if (!session_id_) { | |
| 119 Send(new AudioInputHostMsg_CreateStream( | |
| 120 stream_id_, audio_parameters_, | |
| 121 media::AudioManagerBase::kDefaultDeviceId, | |
| 122 agc_is_enabled_)); | |
| 123 } else { | |
| 124 Send(new AudioInputHostMsg_StartDevice(stream_id_, session_id_)); | |
| 125 pending_device_ready_ = true; | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { | |
| 130 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 131 session_id_ = session_id; | |
| 132 } | |
| 133 | |
| 134 void AudioInputDevice::StartOnIOThread() { | |
| 135 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 136 if (stream_id_) | |
| 137 Send(new AudioInputHostMsg_RecordStream(stream_id_)); | |
| 138 } | |
| 139 | |
| 140 void AudioInputDevice::ShutDownOnIOThread() { | |
| 141 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 142 // NOTE: |completion| may be NULL. | |
| 143 // Make sure we don't call shutdown more than once. | |
| 144 if (stream_id_) { | |
| 145 filter_->RemoveDelegate(stream_id_); | |
| 146 Send(new AudioInputHostMsg_CloseStream(stream_id_)); | |
| 147 | |
| 148 stream_id_ = 0; | |
| 149 session_id_ = 0; | |
| 150 pending_device_ready_ = false; | |
| 151 agc_is_enabled_ = false; | |
| 152 } | |
| 153 | |
| 154 // We can run into an issue where ShutDownOnIOThread is called right after | |
| 155 // OnStreamCreated is called in cases where Start/Stop are called before we | |
| 156 // get the OnStreamCreated callback. To handle that corner case, we call | |
| 157 // Stop(). In most cases, the thread will already be stopped. | |
| 158 // Another situation is when the IO thread goes away before Stop() is called | |
| 159 // in which case, we cannot use the message loop to close the thread handle | |
| 160 // and can't not rely on the main thread existing either. | |
| 161 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 162 audio_thread_.Stop(NULL); | |
| 163 audio_callback_.reset(); | |
| 164 } | |
| 165 | |
| 166 void AudioInputDevice::SetVolumeOnIOThread(double volume) { | |
| 167 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 168 if (stream_id_) | |
| 169 Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); | |
| 170 } | |
| 171 | |
| 172 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { | |
| 173 DCHECK(message_loop()->BelongsToCurrentThread()); | |
| 174 DCHECK_EQ(0, stream_id_) << | |
| 175 "The AGC state can not be modified while capturing is active."; | |
| 176 if (stream_id_) | |
| 177 return; | |
| 178 | |
| 179 // We simply store the new AGC setting here. This value will be used when | |
| 180 // a new stream is initialized and by GetAutomaticGainControl(). | |
| 181 agc_is_enabled_ = enabled; | |
| 182 } | |
| 183 | |
| 184 void AudioInputDevice::OnStreamCreated( | 101 void AudioInputDevice::OnStreamCreated( |
| 185 base::SharedMemoryHandle handle, | 102 base::SharedMemoryHandle handle, |
| 186 base::SyncSocket::Handle socket_handle, | 103 base::SyncSocket::Handle socket_handle, |
| 187 uint32 length) { | 104 uint32 length) { |
| 188 DCHECK(message_loop()->BelongsToCurrentThread()); | 105 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 189 #if defined(OS_WIN) | 106 #if defined(OS_WIN) |
| 190 DCHECK(handle); | 107 DCHECK(handle); |
| 191 DCHECK(socket_handle); | 108 DCHECK(socket_handle); |
| 192 #else | 109 #else |
| 193 DCHECK_GE(handle.fd, 0); | 110 DCHECK_GE(handle.fd, 0); |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 280 Send(new AudioInputHostMsg_CreateStream(stream_id_, audio_parameters_, | 197 Send(new AudioInputHostMsg_CreateStream(stream_id_, audio_parameters_, |
| 281 device_id, agc_is_enabled_)); | 198 device_id, agc_is_enabled_)); |
| 282 } | 199 } |
| 283 | 200 |
| 284 pending_device_ready_ = false; | 201 pending_device_ready_ = false; |
| 285 // Notify the client that the device has been started. | 202 // Notify the client that the device has been started. |
| 286 if (event_handler_) | 203 if (event_handler_) |
| 287 event_handler_->OnDeviceStarted(device_id); | 204 event_handler_->OnDeviceStarted(device_id); |
| 288 } | 205 } |
| 289 | 206 |
| 207 AudioInputDevice::~AudioInputDevice() { |
| 208 // TODO(henrika): The current design requires that the user calls |
| 209 // Stop before deleting this class. |
| 210 CHECK_EQ(0, stream_id_); |
| 211 } |
| 212 |
| 213 void AudioInputDevice::InitializeOnIOThread() { |
| 214 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 215 // Make sure we don't call Start() more than once. |
| 216 DCHECK_EQ(0, stream_id_); |
| 217 if (stream_id_) |
| 218 return; |
| 219 |
| 220 stream_id_ = filter_->AddDelegate(this); |
| 221 // If |session_id_| is not specified, it will directly create the stream; |
| 222 // otherwise it will send a AudioInputHostMsg_StartDevice msg to the browser |
| 223 // and create the stream when getting a OnDeviceReady() callback. |
| 224 if (!session_id_) { |
| 225 Send(new AudioInputHostMsg_CreateStream( |
| 226 stream_id_, audio_parameters_, |
| 227 media::AudioManagerBase::kDefaultDeviceId, |
| 228 agc_is_enabled_)); |
| 229 } else { |
| 230 Send(new AudioInputHostMsg_StartDevice(stream_id_, session_id_)); |
| 231 pending_device_ready_ = true; |
| 232 } |
| 233 } |
| 234 |
| 235 void AudioInputDevice::SetSessionIdOnIOThread(int session_id) { |
| 236 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 237 session_id_ = session_id; |
| 238 } |
| 239 |
| 240 void AudioInputDevice::StartOnIOThread() { |
| 241 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 242 if (stream_id_) |
| 243 Send(new AudioInputHostMsg_RecordStream(stream_id_)); |
| 244 } |
| 245 |
| 246 void AudioInputDevice::ShutDownOnIOThread() { |
| 247 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 248 // NOTE: |completion| may be NULL. |
| 249 // Make sure we don't call shutdown more than once. |
| 250 if (stream_id_) { |
| 251 filter_->RemoveDelegate(stream_id_); |
| 252 Send(new AudioInputHostMsg_CloseStream(stream_id_)); |
| 253 |
| 254 stream_id_ = 0; |
| 255 session_id_ = 0; |
| 256 pending_device_ready_ = false; |
| 257 agc_is_enabled_ = false; |
| 258 } |
| 259 |
| 260 // We can run into an issue where ShutDownOnIOThread is called right after |
| 261 // OnStreamCreated is called in cases where Start/Stop are called before we |
| 262 // get the OnStreamCreated callback. To handle that corner case, we call |
| 263 // Stop(). In most cases, the thread will already be stopped. |
| 264 // Another situation is when the IO thread goes away before Stop() is called |
| 265 // in which case, we cannot use the message loop to close the thread handle |
| 266 // and can't not rely on the main thread existing either. |
| 267 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| 268 audio_thread_.Stop(NULL); |
| 269 audio_callback_.reset(); |
| 270 } |
| 271 |
| 272 void AudioInputDevice::SetVolumeOnIOThread(double volume) { |
| 273 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 274 if (stream_id_) |
| 275 Send(new AudioInputHostMsg_SetVolume(stream_id_, volume)); |
| 276 } |
| 277 |
| 278 void AudioInputDevice::SetAutomaticGainControlOnIOThread(bool enabled) { |
| 279 DCHECK(message_loop()->BelongsToCurrentThread()); |
| 280 DCHECK_EQ(0, stream_id_) << |
| 281 "The AGC state can not be modified while capturing is active."; |
| 282 if (stream_id_) |
| 283 return; |
| 284 |
| 285 // We simply store the new AGC setting here. This value will be used when |
| 286 // a new stream is initialized and by GetAutomaticGainControl(). |
| 287 agc_is_enabled_ = enabled; |
| 288 } |
| 289 |
| 290 void AudioInputDevice::Send(IPC::Message* message) { | 290 void AudioInputDevice::Send(IPC::Message* message) { |
| 291 filter_->Send(message); | 291 filter_->Send(message); |
| 292 } | 292 } |
| 293 | 293 |
| 294 void AudioInputDevice::WillDestroyCurrentMessageLoop() { | 294 void AudioInputDevice::WillDestroyCurrentMessageLoop() { |
| 295 LOG(ERROR) << "IO loop going away before the input device has been stopped"; | 295 LOG(ERROR) << "IO loop going away before the input device has been stopped"; |
| 296 ShutDownOnIOThread(); | 296 ShutDownOnIOThread(); |
| 297 } | 297 } |
| 298 | 298 |
| 299 // AudioInputDevice::AudioThreadCallback | 299 // AudioInputDevice::AudioThreadCallback |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 channel_index, | 338 channel_index, |
| 339 bytes_per_sample, | 339 bytes_per_sample, |
| 340 number_of_frames); | 340 number_of_frames); |
| 341 } | 341 } |
| 342 | 342 |
| 343 // Deliver captured data to the client in floating point format | 343 // Deliver captured data to the client in floating point format |
| 344 // and update the audio-delay measurement. | 344 // and update the audio-delay measurement. |
| 345 capture_callback_->Capture(audio_data_, number_of_frames, | 345 capture_callback_->Capture(audio_data_, number_of_frames, |
| 346 audio_delay_milliseconds, volume); | 346 audio_delay_milliseconds, volume); |
| 347 } | 347 } |
| OLD | NEW |