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/strings/stringprintf.h" | |
| 8 #include "base/threading/thread_restrictions.h" | 9 #include "base/threading/thread_restrictions.h" |
| 10 #include "base/time/time.h" | |
| 9 #include "media/base/limits.h" | 11 #include "media/base/limits.h" |
| 10 #include "media/base/scoped_histogram_timer.h" | 12 #include "media/base/scoped_histogram_timer.h" |
| 11 #include "media/base/user_input_monitor.h" | 13 #include "media/base/user_input_monitor.h" |
| 12 | 14 |
| 15 using base::TimeDelta; | |
| 16 | |
| 13 namespace { | 17 namespace { |
| 14 const int kMaxInputChannels = 3; | 18 const int kMaxInputChannels = 3; |
| 15 | 19 |
| 16 // TODO(henrika): remove usage of timers and add support for proper | 20 // TODO(henrika): remove usage of timers and add support for proper |
| 17 // notification of when the input device is removed. This was originally added | 21 // notification of when the input device is removed. This was originally added |
| 18 // to resolve http://crbug.com/79936 for Windows platforms. This then caused | 22 // to resolve http://crbug.com/79936 for Windows platforms. This then caused |
| 19 // breakage (very hard to repro bugs!) on other platforms: See | 23 // breakage (very hard to repro bugs!) on other platforms: See |
| 20 // http://crbug.com/226327 and http://crbug.com/230972. | 24 // http://crbug.com/226327 and http://crbug.com/230972. |
| 21 // See also that the timer has been disabled on Mac now due to | 25 // See also that the timer has been disabled on Mac now due to |
| 22 // crbug.com/357501. | 26 // crbug.com/357501. |
| 23 const int kTimerResetIntervalSeconds = 1; | 27 const int kTimerResetIntervalSeconds = 1; |
| 24 // We have received reports that the timer can be too trigger happy on some | 28 // We have received reports that the timer can be too trigger happy on some |
| 25 // Mac devices and the initial timer interval has therefore been increased | 29 // Mac devices and the initial timer interval has therefore been increased |
| 26 // from 1 second to 5 seconds. | 30 // from 1 second to 5 seconds. |
| 27 const int kTimerInitialIntervalSeconds = 5; | 31 const int kTimerInitialIntervalSeconds = 5; |
| 32 | |
| 33 // Time constant for AudioPowerMonitor. | |
| 34 // The utilized smoothing factor (alpha) in the exponential filter is given | |
| 35 // by 1-exp(-1/(fs*ts)), where fs is the sample rate in Hz and ts is the time | |
| 36 // constant given by |kPowerMeasurementTimeConstantMilliseconds|. | |
| 37 // Example: fs=44100, ts=10e-3 => alpha~0.022420 | |
| 38 // fs=44100, ts=20e-3 => alpha~0.165903 | |
| 39 // A large smoothing factor corresponds to a faster filter response to input | |
| 40 // changes since y(n)=alpha*x(n)+(1-alpha)*y(n-1), where x(n) is the input | |
| 41 // and y(n) is the output. | |
| 42 const int kPowerMeasurementTimeConstantMilliseconds = 10; | |
| 43 | |
| 44 // Time in seconds between two successive measurements of audio power levels. | |
| 45 const int kPowerMonitorLogIntervalSeconds = 5; | |
| 28 } | 46 } |
| 29 | 47 |
| 30 namespace media { | 48 namespace media { |
| 31 | 49 |
| 32 // static | 50 // static |
| 33 AudioInputController::Factory* AudioInputController::factory_ = NULL; | 51 AudioInputController::Factory* AudioInputController::factory_ = NULL; |
| 34 | 52 |
| 35 AudioInputController::AudioInputController(EventHandler* handler, | 53 AudioInputController::AudioInputController(EventHandler* handler, |
| 36 SyncWriter* sync_writer, | 54 SyncWriter* sync_writer, |
| 37 UserInputMonitor* user_input_monitor) | 55 UserInputMonitor* user_input_monitor) |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 52 } | 70 } |
| 53 | 71 |
| 54 // static | 72 // static |
| 55 scoped_refptr<AudioInputController> AudioInputController::Create( | 73 scoped_refptr<AudioInputController> AudioInputController::Create( |
| 56 AudioManager* audio_manager, | 74 AudioManager* audio_manager, |
| 57 EventHandler* event_handler, | 75 EventHandler* event_handler, |
| 58 const AudioParameters& params, | 76 const AudioParameters& params, |
| 59 const std::string& device_id, | 77 const std::string& device_id, |
| 60 UserInputMonitor* user_input_monitor) { | 78 UserInputMonitor* user_input_monitor) { |
| 61 DCHECK(audio_manager); | 79 DCHECK(audio_manager); |
| 80 DVLOG(1) << "AudioInputController::Create"; | |
| 62 | 81 |
| 63 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) | 82 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) |
| 64 return NULL; | 83 return NULL; |
| 65 | 84 |
| 66 if (factory_) { | 85 if (factory_) { |
| 67 return factory_->Create( | 86 return factory_->Create( |
| 68 audio_manager, event_handler, params, user_input_monitor); | 87 audio_manager, event_handler, params, user_input_monitor); |
| 69 } | 88 } |
| 70 scoped_refptr<AudioInputController> controller( | 89 scoped_refptr<AudioInputController> controller( |
| 71 new AudioInputController(event_handler, NULL, user_input_monitor)); | 90 new AudioInputController(event_handler, NULL, user_input_monitor)); |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 86 // static | 105 // static |
| 87 scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency( | 106 scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency( |
| 88 AudioManager* audio_manager, | 107 AudioManager* audio_manager, |
| 89 EventHandler* event_handler, | 108 EventHandler* event_handler, |
| 90 const AudioParameters& params, | 109 const AudioParameters& params, |
| 91 const std::string& device_id, | 110 const std::string& device_id, |
| 92 SyncWriter* sync_writer, | 111 SyncWriter* sync_writer, |
| 93 UserInputMonitor* user_input_monitor) { | 112 UserInputMonitor* user_input_monitor) { |
| 94 DCHECK(audio_manager); | 113 DCHECK(audio_manager); |
| 95 DCHECK(sync_writer); | 114 DCHECK(sync_writer); |
| 115 DVLOG(1) << "AudioInputController::CreateLowLatency"; | |
| 96 | 116 |
| 97 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) | 117 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) |
| 98 return NULL; | 118 return NULL; |
| 99 | 119 |
| 100 // Create the AudioInputController object and ensure that it runs on | 120 // Create the AudioInputController object and ensure that it runs on |
| 101 // the audio-manager thread. | 121 // the audio-manager thread. |
| 102 scoped_refptr<AudioInputController> controller( | 122 scoped_refptr<AudioInputController> controller( |
| 103 new AudioInputController(event_handler, sync_writer, user_input_monitor)); | 123 new AudioInputController(event_handler, sync_writer, user_input_monitor)); |
| 104 controller->task_runner_ = audio_manager->GetTaskRunner(); | 124 controller->task_runner_ = audio_manager->GetTaskRunner(); |
| 105 | 125 |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 166 void AudioInputController::SetAutomaticGainControl(bool enabled) { | 186 void AudioInputController::SetAutomaticGainControl(bool enabled) { |
| 167 task_runner_->PostTask(FROM_HERE, base::Bind( | 187 task_runner_->PostTask(FROM_HERE, base::Bind( |
| 168 &AudioInputController::DoSetAutomaticGainControl, this, enabled)); | 188 &AudioInputController::DoSetAutomaticGainControl, this, enabled)); |
| 169 } | 189 } |
| 170 | 190 |
| 171 void AudioInputController::DoCreate(AudioManager* audio_manager, | 191 void AudioInputController::DoCreate(AudioManager* audio_manager, |
| 172 const AudioParameters& params, | 192 const AudioParameters& params, |
| 173 const std::string& device_id) { | 193 const std::string& device_id) { |
| 174 DCHECK(task_runner_->BelongsToCurrentThread()); | 194 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 175 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); | 195 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); |
| 196 DVLOG(1) << "AudioInputController::DoCreate"; | |
| 197 | |
| 198 #if defined(AUDIO_POWER_MONITORING) | |
| 199 // Create the audio (power) level meter given the provided audio parameters. | |
| 200 // An AudioBus is also needed to wrap the raw data buffer from the native | |
| 201 // layer to match AudioPowerMonitor::Scan(). | |
| 202 // TODO(henrika): Remove use of extra AudioBus. See http://crbug.com/375155. | |
| 203 audio_level_.reset(new media::AudioPowerMonitor( | |
| 204 params.sample_rate(), | |
| 205 TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMilliseconds))); | |
| 206 audio_bus_ = AudioBus::Create(params); | |
| 207 audio_params_ = params; | |
| 208 #endif | |
| 209 | |
| 176 // TODO(miu): See TODO at top of file. Until that's resolved, assume all | 210 // TODO(miu): See TODO at top of file. Until that's resolved, assume all |
| 177 // platform audio input requires the |no_data_timer_| be used to auto-detect | 211 // platform audio input requires the |no_data_timer_| be used to auto-detect |
| 178 // errors. In reality, probably only Windows needs to be treated as | 212 // errors. In reality, probably only Windows needs to be treated as |
| 179 // unreliable here. | 213 // unreliable here. |
| 180 DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id), | 214 DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id), |
| 181 true); | 215 true); |
| 182 } | 216 } |
| 183 | 217 |
| 184 void AudioInputController::DoCreateForStream( | 218 void AudioInputController::DoCreateForStream( |
| 185 AudioInputStream* stream_to_control, bool enable_nodata_timer) { | 219 AudioInputStream* stream_to_control, bool enable_nodata_timer) { |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 259 void AudioInputController::DoClose() { | 293 void AudioInputController::DoClose() { |
| 260 DCHECK(task_runner_->BelongsToCurrentThread()); | 294 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 261 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CloseTime"); | 295 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CloseTime"); |
| 262 | 296 |
| 263 if (state_ == CLOSED) | 297 if (state_ == CLOSED) |
| 264 return; | 298 return; |
| 265 | 299 |
| 266 // Delete the timer on the same thread that created it. | 300 // Delete the timer on the same thread that created it. |
| 267 no_data_timer_.reset(); | 301 no_data_timer_.reset(); |
| 268 | 302 |
| 303 #if defined(AUDIO_POWER_MONITORING) | |
| 304 std::string log_string("AIC::DoClose: closing input stream"); | |
|
no longer working on chromium
2014/05/26 14:20:16
nit, const
henrika (OOO until Aug 14)
2014/05/26 15:01:17
Removed.
| |
| 305 if (handler_) | |
| 306 handler_->OnLog(this, log_string); | |
| 307 if (audio_level_) | |
| 308 audio_level_->Reset(); | |
| 309 #endif | |
| 310 | |
| 269 DoStopCloseAndClearStream(); | 311 DoStopCloseAndClearStream(); |
| 270 SetDataIsActive(false); | 312 SetDataIsActive(false); |
| 271 | 313 |
| 272 if (SharedMemoryAndSyncSocketMode()) | 314 if (SharedMemoryAndSyncSocketMode()) |
| 273 sync_writer_->Close(); | 315 sync_writer_->Close(); |
| 274 | 316 |
| 275 if (user_input_monitor_) | 317 if (user_input_monitor_) |
| 276 user_input_monitor_->DisableKeyPressMonitoring(); | 318 user_input_monitor_->DisableKeyPressMonitoring(); |
| 277 | 319 |
| 278 state_ = CLOSED; | 320 state_ = CLOSED; |
| (...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 370 key_pressed = current_count != prev_key_down_count_; | 412 key_pressed = current_count != prev_key_down_count_; |
| 371 prev_key_down_count_ = current_count; | 413 prev_key_down_count_ = current_count; |
| 372 DVLOG_IF(6, key_pressed) << "Detected keypress."; | 414 DVLOG_IF(6, key_pressed) << "Detected keypress."; |
| 373 } | 415 } |
| 374 | 416 |
| 375 // Use SharedMemory and SyncSocket if the client has created a SyncWriter. | 417 // Use SharedMemory and SyncSocket if the client has created a SyncWriter. |
| 376 // Used by all low-latency clients except WebSpeech. | 418 // Used by all low-latency clients except WebSpeech. |
| 377 if (SharedMemoryAndSyncSocketMode()) { | 419 if (SharedMemoryAndSyncSocketMode()) { |
| 378 sync_writer_->Write(data, size, volume, key_pressed); | 420 sync_writer_->Write(data, size, volume, key_pressed); |
| 379 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); | 421 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); |
| 422 | |
| 423 #if defined(AUDIO_POWER_MONITORING) | |
| 424 { | |
| 425 // Only do power-level measurements if an AudioPowerMonitor object has | |
| 426 // been created. Done in DoCreate() but not DoCreateForStream(), hence | |
| 427 // logging will mainly be done for WebRTC and WebSpeech clients. | |
| 428 base::AutoLock auto_lock(lock_); | |
| 429 if (!audio_level_) | |
| 430 return; | |
| 431 } | |
| 432 | |
| 433 // Perform periodic audio (power) level measurements. | |
| 434 if ((base::TimeTicks::Now() - last_audio_level_log_time_).InSeconds() > | |
| 435 kPowerMonitorLogIntervalSeconds) { | |
| 436 // Wrap data into an AudioBus to match AudioPowerMonitor::Scan. | |
| 437 // TODO(henrika): remove this section when capture side uses AudioBus. | |
| 438 // See http://crbug.com/375155 for details. | |
| 439 audio_bus_->FromInterleaved( | |
| 440 data, audio_bus_->frames(), audio_params_.bits_per_sample() / 8); | |
| 441 audio_level_->Scan(*audio_bus_, audio_bus_->frames()); | |
| 442 | |
| 443 task_runner_->PostTask( | |
| 444 FROM_HERE, base::Bind(&AudioInputController::DoLogAudioLevel, this)); | |
| 445 | |
| 446 last_audio_level_log_time_ = base::TimeTicks::Now(); | |
| 447 } | |
| 448 #endif | |
| 449 | |
| 380 return; | 450 return; |
| 381 } | 451 } |
| 382 | 452 |
| 383 // TODO(henrika): Investigate if we can avoid the extra copy here. | 453 // TODO(henrika): Investigate if we can avoid the extra copy here. |
| 384 // (see http://crbug.com/249316 for details). AFAIK, this scope is only | 454 // (see http://crbug.com/249316 for details). AFAIK, this scope is only |
| 385 // active for WebSpeech clients. | 455 // active for WebSpeech clients. |
| 386 scoped_ptr<uint8[]> audio_data(new uint8[size]); | 456 scoped_ptr<uint8[]> audio_data(new uint8[size]); |
| 387 memcpy(audio_data.get(), data, size); | 457 memcpy(audio_data.get(), data, size); |
| 388 | 458 |
| 389 // Ownership of the audio buffer will be with the callback until it is run, | 459 // Ownership of the audio buffer will be with the callback until it is run, |
| 390 // when ownership is passed to the callback function. | 460 // when ownership is passed to the callback function. |
| 391 task_runner_->PostTask(FROM_HERE, base::Bind( | 461 task_runner_->PostTask(FROM_HERE, base::Bind( |
| 392 &AudioInputController::DoOnData, this, base::Passed(&audio_data), size)); | 462 &AudioInputController::DoOnData, this, base::Passed(&audio_data), size)); |
| 393 } | 463 } |
| 394 | 464 |
| 395 void AudioInputController::DoOnData(scoped_ptr<uint8[]> data, uint32 size) { | 465 void AudioInputController::DoOnData(scoped_ptr<uint8[]> data, uint32 size) { |
| 396 DCHECK(task_runner_->BelongsToCurrentThread()); | 466 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 397 if (handler_) | 467 if (handler_) |
| 398 handler_->OnData(this, data.get(), size); | 468 handler_->OnData(this, data.get(), size); |
| 399 } | 469 } |
| 400 | 470 |
| 471 void AudioInputController::DoLogAudioLevel() { | |
| 472 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 473 | |
| 474 // Get current average power level and add it to the log. | |
| 475 // Possible range is given by [-inf, 0] dBFS. | |
| 476 std::pair<float, bool> result = audio_level_->ReadCurrentPowerAndClip(); | |
|
no longer working on chromium
2014/05/26 14:20:16
this introduces a race here. audio_level_ is chang
henrika (OOO until Aug 14)
2014/05/26 15:01:17
Will fix.
| |
| 477 std::string log_string = base::StringPrintf( | |
| 478 "AIC::OnData: average audio level=%.2f dBFS", result.first); | |
| 479 static const float kSilenceThresholdDBFS = -72.24719896f; | |
| 480 if (result.first < kSilenceThresholdDBFS) | |
| 481 log_string += " <=> no audio input!"; | |
| 482 if (handler_) | |
| 483 handler_->OnLog(this, log_string); | |
| 484 | |
| 485 // Reset the average power level (since we don't log continuously). | |
| 486 audio_level_->Reset(); | |
| 487 } | |
| 488 | |
| 401 void AudioInputController::OnError(AudioInputStream* stream) { | 489 void AudioInputController::OnError(AudioInputStream* stream) { |
| 402 // Handle error on the audio-manager thread. | 490 // Handle error on the audio-manager thread. |
| 403 task_runner_->PostTask(FROM_HERE, base::Bind( | 491 task_runner_->PostTask(FROM_HERE, base::Bind( |
| 404 &AudioInputController::DoReportError, this)); | 492 &AudioInputController::DoReportError, this)); |
| 405 } | 493 } |
| 406 | 494 |
| 407 void AudioInputController::DoStopCloseAndClearStream() { | 495 void AudioInputController::DoStopCloseAndClearStream() { |
| 408 DCHECK(task_runner_->BelongsToCurrentThread()); | 496 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 409 | 497 |
| 410 // Allow calling unconditionally and bail if we don't have a stream to close. | 498 // Allow calling unconditionally and bail if we don't have a stream to close. |
| 411 if (stream_ != NULL) { | 499 if (stream_ != NULL) { |
| 412 stream_->Stop(); | 500 stream_->Stop(); |
| 413 stream_->Close(); | 501 stream_->Close(); |
| 414 stream_ = NULL; | 502 stream_ = NULL; |
| 415 } | 503 } |
| 416 | 504 |
| 417 // The event handler should not be touched after the stream has been closed. | 505 // The event handler should not be touched after the stream has been closed. |
| 418 handler_ = NULL; | 506 handler_ = NULL; |
| 419 } | 507 } |
| 420 | 508 |
| 421 void AudioInputController::SetDataIsActive(bool enabled) { | 509 void AudioInputController::SetDataIsActive(bool enabled) { |
| 422 base::subtle::Release_Store(&data_is_active_, enabled); | 510 base::subtle::Release_Store(&data_is_active_, enabled); |
| 423 } | 511 } |
| 424 | 512 |
| 425 bool AudioInputController::GetDataIsActive() { | 513 bool AudioInputController::GetDataIsActive() { |
| 426 return (base::subtle::Acquire_Load(&data_is_active_) != false); | 514 return (base::subtle::Acquire_Load(&data_is_active_) != false); |
| 427 } | 515 } |
| 428 | 516 |
| 429 } // namespace media | 517 } // namespace media |
| OLD | NEW |