| 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_controller.h" | 5 #include "media/audio/audio_input_controller.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/threading/thread_restrictions.h" | 8 #include "base/threading/thread_restrictions.h" |
| 9 #include "media/base/limits.h" | 9 #include "media/base/limits.h" |
| 10 | 10 |
| 11 namespace { | 11 namespace { |
| 12 const int kMaxInputChannels = 2; | 12 const int kMaxInputChannels = 2; |
| 13 const int kTimerResetInterval = 1; // One second. | 13 const int kTimerResetInterval = 1; // One second. |
| 14 } | 14 } |
| 15 | 15 |
| 16 namespace media { | 16 namespace media { |
| 17 | 17 |
| 18 // static | 18 // static |
| 19 AudioInputController::Factory* AudioInputController::factory_ = NULL; | 19 AudioInputController::Factory* AudioInputController::factory_ = NULL; |
| 20 | 20 |
| 21 AudioInputController::AudioInputController(EventHandler* handler, | 21 AudioInputController::AudioInputController(EventHandler* handler, |
| 22 SyncWriter* sync_writer) | 22 SyncWriter* sync_writer) |
| 23 : creator_loop_(base::MessageLoopProxy::current()), | 23 : creator_loop_(base::MessageLoopProxy::current()), |
| 24 handler_(handler), | 24 handler_(handler), |
| 25 stream_(NULL), | 25 stream_(NULL), |
| 26 data_is_active_(false), |
| 26 state_(kEmpty), | 27 state_(kEmpty), |
| 27 sync_writer_(sync_writer), | 28 sync_writer_(sync_writer), |
| 28 max_volume_(0.0) { | 29 max_volume_(0.0) { |
| 29 DCHECK(creator_loop_); | 30 DCHECK(creator_loop_); |
| 30 no_data_timer_.reset(new base::DelayTimer<AudioInputController>(FROM_HERE, | |
| 31 base::TimeDelta::FromSeconds(kTimerResetInterval), | |
| 32 this, | |
| 33 &AudioInputController::DoReportNoDataError)); | |
| 34 } | 31 } |
| 35 | 32 |
| 36 AudioInputController::~AudioInputController() { | 33 AudioInputController::~AudioInputController() { |
| 37 DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_); | 34 DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_); |
| 38 } | 35 } |
| 39 | 36 |
| 40 // static | 37 // static |
| 41 scoped_refptr<AudioInputController> AudioInputController::Create( | 38 scoped_refptr<AudioInputController> AudioInputController::Create( |
| 42 AudioManager* audio_manager, | 39 AudioManager* audio_manager, |
| 43 EventHandler* event_handler, | 40 EventHandler* event_handler, |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 98 } | 95 } |
| 99 | 96 |
| 100 void AudioInputController::Record() { | 97 void AudioInputController::Record() { |
| 101 message_loop_->PostTask(FROM_HERE, base::Bind( | 98 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 102 &AudioInputController::DoRecord, this)); | 99 &AudioInputController::DoRecord, this)); |
| 103 } | 100 } |
| 104 | 101 |
| 105 void AudioInputController::Close(const base::Closure& closed_task) { | 102 void AudioInputController::Close(const base::Closure& closed_task) { |
| 106 DCHECK(!closed_task.is_null()); | 103 DCHECK(!closed_task.is_null()); |
| 107 DCHECK(creator_loop_->BelongsToCurrentThread()); | 104 DCHECK(creator_loop_->BelongsToCurrentThread()); |
| 108 // See crbug.com/119783: Deleting the timer now to avoid disaster if | 105 |
| 109 // AudioInputController is destructed on a thread other than the creator | |
| 110 // thread. | |
| 111 no_data_timer_.reset(); | |
| 112 message_loop_->PostTaskAndReply( | 106 message_loop_->PostTaskAndReply( |
| 113 FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task); | 107 FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task); |
| 114 } | 108 } |
| 115 | 109 |
| 116 void AudioInputController::SetVolume(double volume) { | 110 void AudioInputController::SetVolume(double volume) { |
| 117 message_loop_->PostTask(FROM_HERE, base::Bind( | 111 message_loop_->PostTask(FROM_HERE, base::Bind( |
| 118 &AudioInputController::DoSetVolume, this, volume)); | 112 &AudioInputController::DoSetVolume, this, volume)); |
| 119 } | 113 } |
| 120 | 114 |
| 121 void AudioInputController::SetAutomaticGainControl(bool enabled) { | 115 void AudioInputController::SetAutomaticGainControl(bool enabled) { |
| (...skipping 15 matching lines...) Expand all Loading... |
| 137 } | 131 } |
| 138 | 132 |
| 139 if (stream_ && !stream_->Open()) { | 133 if (stream_ && !stream_->Open()) { |
| 140 stream_->Close(); | 134 stream_->Close(); |
| 141 stream_ = NULL; | 135 stream_ = NULL; |
| 142 // TODO(satish): Define error types. | 136 // TODO(satish): Define error types. |
| 143 handler_->OnError(this, 0); | 137 handler_->OnError(this, 0); |
| 144 return; | 138 return; |
| 145 } | 139 } |
| 146 | 140 |
| 147 creator_loop_->PostTask(FROM_HERE, base::Bind( | 141 DCHECK(!no_data_timer_.get()); |
| 148 &AudioInputController::DoResetNoDataTimer, this)); | 142 // Create the data timer which will call DoCheckForNoData() after a delay |
| 143 // of |kTimerResetInterval| seconds. The timer is started in DoRecord() |
| 144 // and restarted in each DoCheckForNoData() callback. |
| 145 no_data_timer_.reset(new base::DelayTimer<AudioInputController>(FROM_HERE, |
| 146 base::TimeDelta::FromSeconds(kTimerResetInterval), |
| 147 this, |
| 148 &AudioInputController::DoCheckForNoData)); |
| 149 | 149 |
| 150 state_ = kCreated; | 150 state_ = kCreated; |
| 151 handler_->OnCreated(this); | 151 handler_->OnCreated(this); |
| 152 } | 152 } |
| 153 | 153 |
| 154 void AudioInputController::DoRecord() { | 154 void AudioInputController::DoRecord() { |
| 155 DCHECK(message_loop_->BelongsToCurrentThread()); | 155 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 156 | 156 |
| 157 if (state_ != kCreated) | 157 if (state_ != kCreated) |
| 158 return; | 158 return; |
| 159 | 159 |
| 160 { | 160 { |
| 161 base::AutoLock auto_lock(lock_); | 161 base::AutoLock auto_lock(lock_); |
| 162 state_ = kRecording; | 162 state_ = kRecording; |
| 163 } | 163 } |
| 164 | 164 |
| 165 // Start the data timer. Once |kTimerResetInterval| seconds have passed, |
| 166 // a callback to DoCheckForNoData() is made. |
| 167 no_data_timer_->Reset(); |
| 168 |
| 165 stream_->Start(this); | 169 stream_->Start(this); |
| 166 handler_->OnRecording(this); | 170 handler_->OnRecording(this); |
| 167 } | 171 } |
| 168 | 172 |
| 169 void AudioInputController::DoClose() { | 173 void AudioInputController::DoClose() { |
| 170 DCHECK(message_loop_->BelongsToCurrentThread()); | 174 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 171 | 175 |
| 176 // Delete the timer on the same thread that created it. |
| 177 no_data_timer_.reset(); |
| 178 |
| 172 if (state_ != kClosed) { | 179 if (state_ != kClosed) { |
| 173 DoStopCloseAndClearStream(NULL); | 180 DoStopCloseAndClearStream(NULL); |
| 181 SetDataIsActive(false); |
| 174 | 182 |
| 175 if (LowLatencyMode()) { | 183 if (LowLatencyMode()) { |
| 176 sync_writer_->Close(); | 184 sync_writer_->Close(); |
| 177 } | 185 } |
| 178 | 186 |
| 179 state_ = kClosed; | 187 state_ = kClosed; |
| 180 } | 188 } |
| 181 } | 189 } |
| 182 | 190 |
| 183 void AudioInputController::DoReportError(int code) { | 191 void AudioInputController::DoReportError(int code) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 212 DCHECK(message_loop_->BelongsToCurrentThread()); | 220 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 213 DCHECK_NE(state_, kRecording); | 221 DCHECK_NE(state_, kRecording); |
| 214 | 222 |
| 215 // Ensure that the AGC state only can be modified before streaming starts. | 223 // Ensure that the AGC state only can be modified before streaming starts. |
| 216 if (state_ != kCreated || state_ == kRecording) | 224 if (state_ != kCreated || state_ == kRecording) |
| 217 return; | 225 return; |
| 218 | 226 |
| 219 stream_->SetAutomaticGainControl(enabled); | 227 stream_->SetAutomaticGainControl(enabled); |
| 220 } | 228 } |
| 221 | 229 |
| 222 void AudioInputController::DoReportNoDataError() { | 230 void AudioInputController::DoCheckForNoData() { |
| 223 DCHECK(creator_loop_->BelongsToCurrentThread()); | 231 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 224 | 232 |
| 225 // Error notifications should be sent on the audio-manager thread. | 233 if (!GetDataIsActive()) { |
| 226 int code = 0; | 234 // The data-is-active marker will be false only if it has been more than |
| 227 message_loop_->PostTask(FROM_HERE, base::Bind( | 235 // one second since a data packet was recorded. This can happen if a |
| 228 &AudioInputController::DoReportError, this, code)); | 236 // capture device has been removed or disabled. |
| 229 } | 237 handler_->OnError(this, 0); |
| 238 return; |
| 239 } |
| 230 | 240 |
| 231 void AudioInputController::DoResetNoDataTimer() { | 241 // Mark data as non-active. The flag will be re-enabled in OnData() each |
| 232 DCHECK(creator_loop_->BelongsToCurrentThread()); | 242 // time a data packet is received. Hence, under normal conditions, the |
| 233 if (no_data_timer_.get()) | 243 // flag will only be disabled during a very short period. |
| 234 no_data_timer_->Reset(); | 244 SetDataIsActive(false); |
| 245 |
| 246 // Restart the timer to ensure that we check the flag in one second again. |
| 247 no_data_timer_->Reset(); |
| 235 } | 248 } |
| 236 | 249 |
| 237 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, | 250 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, |
| 238 uint32 size, uint32 hardware_delay_bytes, | 251 uint32 size, uint32 hardware_delay_bytes, |
| 239 double volume) { | 252 double volume) { |
| 240 { | 253 { |
| 241 base::AutoLock auto_lock(lock_); | 254 base::AutoLock auto_lock(lock_); |
| 242 if (state_ != kRecording) | 255 if (state_ != kRecording) |
| 243 return; | 256 return; |
| 244 } | 257 } |
| 245 | 258 |
| 246 creator_loop_->PostTask(FROM_HERE, base::Bind( | 259 // Mark data as active to ensure that the periodic calls to |
| 247 &AudioInputController::DoResetNoDataTimer, this)); | 260 // DoCheckForNoData() does not report an error to the event handler. |
| 261 SetDataIsActive(true); |
| 248 | 262 |
| 249 // Use SyncSocket if we are in a low-latency mode. | 263 // Use SyncSocket if we are in a low-latency mode. |
| 250 if (LowLatencyMode()) { | 264 if (LowLatencyMode()) { |
| 251 sync_writer_->Write(data, size, volume); | 265 sync_writer_->Write(data, size, volume); |
| 252 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); | 266 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); |
| 253 return; | 267 return; |
| 254 } | 268 } |
| 255 | 269 |
| 256 handler_->OnData(this, data, size); | 270 handler_->OnData(this, data, size); |
| 257 } | 271 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 278 stream_->Stop(); | 292 stream_->Stop(); |
| 279 stream_->Close(); | 293 stream_->Close(); |
| 280 stream_ = NULL; | 294 stream_ = NULL; |
| 281 } | 295 } |
| 282 | 296 |
| 283 // Should be last in the method, do not touch "this" from here on. | 297 // Should be last in the method, do not touch "this" from here on. |
| 284 if (done != NULL) | 298 if (done != NULL) |
| 285 done->Signal(); | 299 done->Signal(); |
| 286 } | 300 } |
| 287 | 301 |
| 302 void AudioInputController::SetDataIsActive(bool enabled) { |
| 303 base::subtle::Release_Store(&data_is_active_, enabled); |
| 304 } |
| 305 |
| 306 bool AudioInputController::GetDataIsActive() { |
| 307 return (base::subtle::Acquire_Load(&data_is_active_) != false); |
| 308 } |
| 309 |
| 288 } // namespace media | 310 } // namespace media |
| OLD | NEW |