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 "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 if (!no_data_timer_.get()) { |
|
tommi (sloooow) - chröme
2012/04/17 13:12:49
sorry, what I meant was this:
DCHECK(!no_data_tim
henrika (OOO until Aug 14)
2012/04/17 13:38:31
Done.
| |
| 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 |
| 150 state_ = kCreated; | 151 state_ = kCreated; |
| 151 handler_->OnCreated(this); | 152 handler_->OnCreated(this); |
| 152 } | 153 } |
| 153 | 154 |
| 154 void AudioInputController::DoRecord() { | 155 void AudioInputController::DoRecord() { |
| 155 DCHECK(message_loop_->BelongsToCurrentThread()); | 156 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 156 | 157 |
| 157 if (state_ != kCreated) | 158 if (state_ != kCreated) |
| 158 return; | 159 return; |
| 159 | 160 |
| 160 { | 161 { |
| 161 base::AutoLock auto_lock(lock_); | 162 base::AutoLock auto_lock(lock_); |
| 162 state_ = kRecording; | 163 state_ = kRecording; |
| 163 } | 164 } |
| 164 | 165 |
| 166 // Start the data timer. Once |kTimerResetInterval| seconds have passed, | |
| 167 // a callback to DoCheckForNoData() made. | |
| 168 no_data_timer_->Reset(); | |
| 169 | |
| 165 stream_->Start(this); | 170 stream_->Start(this); |
| 166 handler_->OnRecording(this); | 171 handler_->OnRecording(this); |
| 167 } | 172 } |
| 168 | 173 |
| 169 void AudioInputController::DoClose() { | 174 void AudioInputController::DoClose() { |
| 170 DCHECK(message_loop_->BelongsToCurrentThread()); | 175 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 171 | 176 |
| 177 if (no_data_timer_.get()) { | |
| 178 // Delete the timer on the same thread that created it. | |
| 179 no_data_timer_.reset(); | |
| 180 } | |
| 181 | |
| 172 if (state_ != kClosed) { | 182 if (state_ != kClosed) { |
| 173 DoStopCloseAndClearStream(NULL); | 183 DoStopCloseAndClearStream(NULL); |
| 184 SetDataIsActive(false); | |
| 174 | 185 |
| 175 if (LowLatencyMode()) { | 186 if (LowLatencyMode()) { |
| 176 sync_writer_->Close(); | 187 sync_writer_->Close(); |
| 177 } | 188 } |
| 178 | 189 |
| 179 state_ = kClosed; | 190 state_ = kClosed; |
| 180 } | 191 } |
| 181 } | 192 } |
| 182 | 193 |
| 183 void AudioInputController::DoReportError(int code) { | 194 void AudioInputController::DoReportError(int code) { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 212 DCHECK(message_loop_->BelongsToCurrentThread()); | 223 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 213 DCHECK_NE(state_, kRecording); | 224 DCHECK_NE(state_, kRecording); |
| 214 | 225 |
| 215 // Ensure that the AGC state only can be modified before streaming starts. | 226 // Ensure that the AGC state only can be modified before streaming starts. |
| 216 if (state_ != kCreated || state_ == kRecording) | 227 if (state_ != kCreated || state_ == kRecording) |
| 217 return; | 228 return; |
| 218 | 229 |
| 219 stream_->SetAutomaticGainControl(enabled); | 230 stream_->SetAutomaticGainControl(enabled); |
| 220 } | 231 } |
| 221 | 232 |
| 222 void AudioInputController::DoReportNoDataError() { | 233 void AudioInputController::DoCheckForNoData() { |
| 223 DCHECK(creator_loop_->BelongsToCurrentThread()); | 234 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 224 | 235 |
| 225 // Error notifications should be sent on the audio-manager thread. | 236 if (!GetDataIsActive()) { |
| 226 int code = 0; | 237 // The data-is-active marker will be false only if it has been more than |
| 227 message_loop_->PostTask(FROM_HERE, base::Bind( | 238 // one second since a data packet was recorded. This can happen if a |
| 228 &AudioInputController::DoReportError, this, code)); | 239 // capture device has been removed or disabled. |
| 229 } | 240 handler_->OnError(this, 0); |
| 241 return; | |
| 242 } | |
| 230 | 243 |
| 231 void AudioInputController::DoResetNoDataTimer() { | 244 // Mark data as non-active. The flag will be re-enabled in OnData() each |
| 232 DCHECK(creator_loop_->BelongsToCurrentThread()); | 245 // time a data packet is received. Hence, under normal conditions, the |
| 233 if (no_data_timer_.get()) | 246 // flag will only be disabled during a very short period. |
| 234 no_data_timer_->Reset(); | 247 SetDataIsActive(false); |
| 248 | |
| 249 // Restart the timer to ensure that we check the flag in one second again. | |
| 250 no_data_timer_->Reset(); | |
| 235 } | 251 } |
| 236 | 252 |
| 237 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, | 253 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, |
| 238 uint32 size, uint32 hardware_delay_bytes, | 254 uint32 size, uint32 hardware_delay_bytes, |
| 239 double volume) { | 255 double volume) { |
| 240 { | 256 { |
| 241 base::AutoLock auto_lock(lock_); | 257 base::AutoLock auto_lock(lock_); |
| 242 if (state_ != kRecording) | 258 if (state_ != kRecording) |
| 243 return; | 259 return; |
| 244 } | 260 } |
| 245 | 261 |
| 246 creator_loop_->PostTask(FROM_HERE, base::Bind( | 262 // Mark data as active to ensure that the periodic calls to |
| 247 &AudioInputController::DoResetNoDataTimer, this)); | 263 // DoCheckForNoData() does not report an error to the event handler. |
| 264 SetDataIsActive(true); | |
| 248 | 265 |
| 249 // Use SyncSocket if we are in a low-latency mode. | 266 // Use SyncSocket if we are in a low-latency mode. |
| 250 if (LowLatencyMode()) { | 267 if (LowLatencyMode()) { |
| 251 sync_writer_->Write(data, size, volume); | 268 sync_writer_->Write(data, size, volume); |
| 252 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); | 269 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); |
| 253 return; | 270 return; |
| 254 } | 271 } |
| 255 | 272 |
| 256 handler_->OnData(this, data, size); | 273 handler_->OnData(this, data, size); |
| 257 } | 274 } |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 278 stream_->Stop(); | 295 stream_->Stop(); |
| 279 stream_->Close(); | 296 stream_->Close(); |
| 280 stream_ = NULL; | 297 stream_ = NULL; |
| 281 } | 298 } |
| 282 | 299 |
| 283 // Should be last in the method, do not touch "this" from here on. | 300 // Should be last in the method, do not touch "this" from here on. |
| 284 if (done != NULL) | 301 if (done != NULL) |
| 285 done->Signal(); | 302 done->Signal(); |
| 286 } | 303 } |
| 287 | 304 |
| 305 void AudioInputController::SetDataIsActive(bool enabled) { | |
| 306 base::subtle::Release_Store(&data_is_active_, enabled); | |
| 307 } | |
| 308 | |
| 309 bool AudioInputController::GetDataIsActive() { | |
| 310 return (base::subtle::Acquire_Load(&data_is_active_) != false); | |
| 311 } | |
| 312 | |
| 288 } // namespace media | 313 } // namespace media |
| OLD | NEW |