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 #include "media/base/scoped_histogram_timer.h" | 10 #include "media/base/scoped_histogram_timer.h" |
| 11 #include "media/base/user_input_monitor.h" |
11 | 12 |
12 namespace { | 13 namespace { |
13 const int kMaxInputChannels = 2; | 14 const int kMaxInputChannels = 2; |
14 | 15 |
15 // TODO(henrika): remove usage of timers and add support for proper | 16 // TODO(henrika): remove usage of timers and add support for proper |
16 // notification of when the input device is removed. This was originally added | 17 // notification of when the input device is removed. This was originally added |
17 // to resolve http://crbug.com/79936 for Windows platforms. This then caused | 18 // to resolve http://crbug.com/79936 for Windows platforms. This then caused |
18 // breakage (very hard to repro bugs!) on other platforms: See | 19 // breakage (very hard to repro bugs!) on other platforms: See |
19 // http://crbug.com/226327 and http://crbug.com/230972. | 20 // http://crbug.com/226327 and http://crbug.com/230972. |
20 const int kTimerResetIntervalSeconds = 1; | 21 const int kTimerResetIntervalSeconds = 1; |
(...skipping 18 matching lines...) Expand all Loading... |
39 SyncWriter* sync_writer, | 40 SyncWriter* sync_writer, |
40 UserInputMonitor* user_input_monitor) | 41 UserInputMonitor* user_input_monitor) |
41 : creator_loop_(base::MessageLoopProxy::current()), | 42 : creator_loop_(base::MessageLoopProxy::current()), |
42 handler_(handler), | 43 handler_(handler), |
43 stream_(NULL), | 44 stream_(NULL), |
44 data_is_active_(false), | 45 data_is_active_(false), |
45 state_(kEmpty), | 46 state_(kEmpty), |
46 sync_writer_(sync_writer), | 47 sync_writer_(sync_writer), |
47 max_volume_(0.0), | 48 max_volume_(0.0), |
48 user_input_monitor_(user_input_monitor), | 49 user_input_monitor_(user_input_monitor), |
49 key_pressed_(false) { | 50 prev_key_down_count_(0) { |
50 DCHECK(creator_loop_.get()); | 51 DCHECK(creator_loop_.get()); |
51 } | 52 } |
52 | 53 |
53 AudioInputController::~AudioInputController() { | 54 AudioInputController::~AudioInputController() { |
54 DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_); | 55 DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_); |
55 } | 56 } |
56 | 57 |
57 // static | 58 // static |
58 scoped_refptr<AudioInputController> AudioInputController::Create( | 59 scoped_refptr<AudioInputController> AudioInputController::Create( |
59 AudioManager* audio_manager, | 60 AudioManager* audio_manager, |
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
211 no_data_timer_.reset(new base::Timer( | 212 no_data_timer_.reset(new base::Timer( |
212 FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds), | 213 FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds), |
213 base::Bind(&AudioInputController::DoCheckForNoData, | 214 base::Bind(&AudioInputController::DoCheckForNoData, |
214 base::Unretained(this)), false)); | 215 base::Unretained(this)), false)); |
215 } else { | 216 } else { |
216 DVLOG(1) << "Disabled: timer check for no data."; | 217 DVLOG(1) << "Disabled: timer check for no data."; |
217 } | 218 } |
218 | 219 |
219 state_ = kCreated; | 220 state_ = kCreated; |
220 handler_->OnCreated(this); | 221 handler_->OnCreated(this); |
| 222 |
| 223 if (user_input_monitor_) { |
| 224 user_input_monitor_->EnableKeyPressMonitoring(); |
| 225 prev_key_down_count_ = user_input_monitor_->GetKeyPressCount(); |
| 226 } |
221 } | 227 } |
222 | 228 |
223 void AudioInputController::DoRecord() { | 229 void AudioInputController::DoRecord() { |
224 DCHECK(message_loop_->BelongsToCurrentThread()); | 230 DCHECK(message_loop_->BelongsToCurrentThread()); |
225 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.RecordTime"); | 231 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.RecordTime"); |
226 | 232 |
227 if (state_ != kCreated) | 233 if (state_ != kCreated) |
228 return; | 234 return; |
229 | 235 |
230 { | 236 { |
231 base::AutoLock auto_lock(lock_); | 237 base::AutoLock auto_lock(lock_); |
232 state_ = kRecording; | 238 state_ = kRecording; |
233 } | 239 } |
234 | 240 |
235 if (no_data_timer_) { | 241 if (no_data_timer_) { |
236 // Start the data timer. Once |kTimerResetIntervalSeconds| have passed, | 242 // Start the data timer. Once |kTimerResetIntervalSeconds| have passed, |
237 // a callback to DoCheckForNoData() is made. | 243 // a callback to DoCheckForNoData() is made. |
238 no_data_timer_->Reset(); | 244 no_data_timer_->Reset(); |
239 } | 245 } |
240 | 246 |
241 stream_->Start(this); | 247 stream_->Start(this); |
242 handler_->OnRecording(this); | 248 handler_->OnRecording(this); |
243 | |
244 if (user_input_monitor_) | |
245 user_input_monitor_->AddKeyStrokeListener(this); | |
246 } | 249 } |
247 | 250 |
248 void AudioInputController::DoClose() { | 251 void AudioInputController::DoClose() { |
249 DCHECK(message_loop_->BelongsToCurrentThread()); | 252 DCHECK(message_loop_->BelongsToCurrentThread()); |
250 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CloseTime"); | 253 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CloseTime"); |
251 | 254 |
252 // Delete the timer on the same thread that created it. | 255 // Delete the timer on the same thread that created it. |
253 no_data_timer_.reset(); | 256 no_data_timer_.reset(); |
254 | 257 |
255 if (state_ != kClosed) { | 258 if (state_ != kClosed) { |
256 DoStopCloseAndClearStream(NULL); | 259 DoStopCloseAndClearStream(NULL); |
257 SetDataIsActive(false); | 260 SetDataIsActive(false); |
258 | 261 |
259 if (LowLatencyMode()) { | 262 if (LowLatencyMode()) { |
260 sync_writer_->Close(); | 263 sync_writer_->Close(); |
261 } | 264 } |
262 | 265 |
263 state_ = kClosed; | 266 state_ = kClosed; |
264 | 267 |
265 if (user_input_monitor_) | 268 if (user_input_monitor_) |
266 user_input_monitor_->RemoveKeyStrokeListener(this); | 269 user_input_monitor_->DisableKeyPressMonitoring(); |
267 } | 270 } |
268 } | 271 } |
269 | 272 |
270 void AudioInputController::DoReportError() { | 273 void AudioInputController::DoReportError() { |
271 DCHECK(message_loop_->BelongsToCurrentThread()); | 274 DCHECK(message_loop_->BelongsToCurrentThread()); |
272 handler_->OnError(this); | 275 handler_->OnError(this); |
273 } | 276 } |
274 | 277 |
275 void AudioInputController::DoSetVolume(double volume) { | 278 void AudioInputController::DoSetVolume(double volume) { |
276 DCHECK(message_loop_->BelongsToCurrentThread()); | 279 DCHECK(message_loop_->BelongsToCurrentThread()); |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
323 SetDataIsActive(false); | 326 SetDataIsActive(false); |
324 | 327 |
325 // Restart the timer to ensure that we check the flag again in | 328 // Restart the timer to ensure that we check the flag again in |
326 // |kTimerResetIntervalSeconds|. | 329 // |kTimerResetIntervalSeconds|. |
327 no_data_timer_->Start( | 330 no_data_timer_->Start( |
328 FROM_HERE, base::TimeDelta::FromSeconds(kTimerResetIntervalSeconds), | 331 FROM_HERE, base::TimeDelta::FromSeconds(kTimerResetIntervalSeconds), |
329 base::Bind(&AudioInputController::DoCheckForNoData, | 332 base::Bind(&AudioInputController::DoCheckForNoData, |
330 base::Unretained(this))); | 333 base::Unretained(this))); |
331 } | 334 } |
332 | 335 |
333 void AudioInputController::OnData(AudioInputStream* stream, const uint8* data, | 336 void AudioInputController::OnData(AudioInputStream* stream, |
334 uint32 size, uint32 hardware_delay_bytes, | 337 const uint8* data, |
| 338 uint32 size, |
| 339 uint32 hardware_delay_bytes, |
335 double volume) { | 340 double volume) { |
336 bool key_pressed = false; | |
337 { | 341 { |
338 base::AutoLock auto_lock(lock_); | 342 base::AutoLock auto_lock(lock_); |
339 if (state_ != kRecording) | 343 if (state_ != kRecording) |
340 return; | 344 return; |
| 345 } |
341 | 346 |
342 std::swap(key_pressed, key_pressed_); | 347 bool key_pressed = false; |
| 348 if (user_input_monitor_) { |
| 349 size_t current_count = user_input_monitor_->GetKeyPressCount(); |
| 350 key_pressed = current_count != prev_key_down_count_; |
| 351 prev_key_down_count_ = current_count; |
| 352 DVLOG_IF(6, key_pressed) << "Detected keypress."; |
343 } | 353 } |
344 | 354 |
345 // Mark data as active to ensure that the periodic calls to | 355 // Mark data as active to ensure that the periodic calls to |
346 // DoCheckForNoData() does not report an error to the event handler. | 356 // DoCheckForNoData() does not report an error to the event handler. |
347 SetDataIsActive(true); | 357 SetDataIsActive(true); |
348 | 358 |
349 // Use SyncSocket if we are in a low-latency mode. | 359 // Use SyncSocket if we are in a low-latency mode. |
350 if (LowLatencyMode()) { | 360 if (LowLatencyMode()) { |
351 sync_writer_->Write(data, size, volume, key_pressed); | 361 sync_writer_->Write(data, size, volume, key_pressed); |
352 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); | 362 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); |
353 return; | 363 return; |
354 } | 364 } |
355 | 365 |
356 handler_->OnData(this, data, size); | 366 handler_->OnData(this, data, size); |
357 } | 367 } |
358 | 368 |
359 void AudioInputController::OnClose(AudioInputStream* stream) { | 369 void AudioInputController::OnClose(AudioInputStream* stream) { |
360 DVLOG(1) << "AudioInputController::OnClose()"; | 370 DVLOG(1) << "AudioInputController::OnClose()"; |
361 // TODO(satish): Sometimes the device driver closes the input stream without | 371 // TODO(satish): Sometimes the device driver closes the input stream without |
362 // us asking for it (may be if the device was unplugged?). Check how to handle | 372 // us asking for it (may be if the device was unplugged?). Check how to handle |
363 // such cases here. | 373 // such cases here. |
364 } | 374 } |
365 | 375 |
366 void AudioInputController::OnError(AudioInputStream* stream) { | 376 void AudioInputController::OnError(AudioInputStream* stream) { |
367 // Handle error on the audio-manager thread. | 377 // Handle error on the audio-manager thread. |
368 message_loop_->PostTask(FROM_HERE, base::Bind( | 378 message_loop_->PostTask(FROM_HERE, base::Bind( |
369 &AudioInputController::DoReportError, this)); | 379 &AudioInputController::DoReportError, this)); |
370 } | 380 } |
371 | 381 |
372 void AudioInputController::OnKeyStroke() { | |
373 base::AutoLock auto_lock(lock_); | |
374 key_pressed_ = true; | |
375 } | |
376 | |
377 void AudioInputController::DoStopCloseAndClearStream( | 382 void AudioInputController::DoStopCloseAndClearStream( |
378 base::WaitableEvent* done) { | 383 base::WaitableEvent* done) { |
379 DCHECK(message_loop_->BelongsToCurrentThread()); | 384 DCHECK(message_loop_->BelongsToCurrentThread()); |
380 | 385 |
381 // Allow calling unconditionally and bail if we don't have a stream to close. | 386 // Allow calling unconditionally and bail if we don't have a stream to close. |
382 if (stream_ != NULL) { | 387 if (stream_ != NULL) { |
383 stream_->Stop(); | 388 stream_->Stop(); |
384 stream_->Close(); | 389 stream_->Close(); |
385 stream_ = NULL; | 390 stream_ = NULL; |
386 } | 391 } |
387 | 392 |
388 // Should be last in the method, do not touch "this" from here on. | 393 // Should be last in the method, do not touch "this" from here on. |
389 if (done != NULL) | 394 if (done != NULL) |
390 done->Signal(); | 395 done->Signal(); |
391 } | 396 } |
392 | 397 |
393 void AudioInputController::SetDataIsActive(bool enabled) { | 398 void AudioInputController::SetDataIsActive(bool enabled) { |
394 base::subtle::Release_Store(&data_is_active_, enabled); | 399 base::subtle::Release_Store(&data_is_active_, enabled); |
395 } | 400 } |
396 | 401 |
397 bool AudioInputController::GetDataIsActive() { | 402 bool AudioInputController::GetDataIsActive() { |
398 return (base::subtle::Acquire_Load(&data_is_active_) != false); | 403 return (base::subtle::Acquire_Load(&data_is_active_) != false); |
399 } | 404 } |
400 | 405 |
401 } // namespace media | 406 } // namespace media |
OLD | NEW |