audio_input_controller.cc revision 424c4d7b64af9d0d8fd9624f381f469654d5e3d2
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 3// found in the LICENSE file. 4 5#include "media/audio/audio_input_controller.h" 6 7#include "base/bind.h" 8#include "base/threading/thread_restrictions.h" 9#include "media/base/limits.h" 10#include "media/base/scoped_histogram_timer.h" 11#include "media/base/user_input_monitor.h" 12 13namespace { 14const int kMaxInputChannels = 2; 15 16// TODO(henrika): remove usage of timers and add support for proper 17// 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 19// breakage (very hard to repro bugs!) on other platforms: See 20// http://crbug.com/226327 and http://crbug.com/230972. 21const int kTimerResetIntervalSeconds = 1; 22#if defined(OS_IOS) 23// The first callback on iOS is received after the current background 24// audio has faded away. 25const int kTimerInitialIntervalSeconds = 4; 26#else 27// We have received reports that the timer can be too trigger happy on some 28// Mac devices and the initial timer interval has therefore been increased 29// from 1 second to 5 seconds. 30const int kTimerInitialIntervalSeconds = 5; 31#endif // defined(OS_IOS) 32} 33 34namespace media { 35 36// static 37AudioInputController::Factory* AudioInputController::factory_ = NULL; 38 39AudioInputController::AudioInputController(EventHandler* handler, 40 SyncWriter* sync_writer, 41 UserInputMonitor* user_input_monitor) 42 : creator_loop_(base::MessageLoopProxy::current()), 43 handler_(handler), 44 stream_(NULL), 45 data_is_active_(false), 46 state_(kEmpty), 47 sync_writer_(sync_writer), 48 max_volume_(0.0), 49 user_input_monitor_(user_input_monitor), 50 prev_key_down_count_(0) { 51 DCHECK(creator_loop_.get()); 52} 53 54AudioInputController::~AudioInputController() { 55 DCHECK(kClosed == state_ || kCreated == state_ || kEmpty == state_); 56} 57 58// static 59scoped_refptr<AudioInputController> AudioInputController::Create( 60 AudioManager* audio_manager, 61 EventHandler* event_handler, 62 const AudioParameters& params, 63 const std::string& device_id, 64 UserInputMonitor* user_input_monitor) { 65 DCHECK(audio_manager); 66 67 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) 68 return NULL; 69 70 if (factory_) { 71 return factory_->Create( 72 audio_manager, event_handler, params, user_input_monitor); 73 } 74 scoped_refptr<AudioInputController> controller( 75 new AudioInputController(event_handler, NULL, user_input_monitor)); 76 77 controller->message_loop_ = audio_manager->GetMessageLoop(); 78 79 // Create and open a new audio input stream from the existing 80 // audio-device thread. 81 if (!controller->message_loop_->PostTask(FROM_HERE, 82 base::Bind(&AudioInputController::DoCreate, controller, 83 base::Unretained(audio_manager), params, device_id))) { 84 controller = NULL; 85 } 86 87 return controller; 88} 89 90// static 91scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency( 92 AudioManager* audio_manager, 93 EventHandler* event_handler, 94 const AudioParameters& params, 95 const std::string& device_id, 96 SyncWriter* sync_writer, 97 UserInputMonitor* user_input_monitor) { 98 DCHECK(audio_manager); 99 DCHECK(sync_writer); 100 101 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) 102 return NULL; 103 104 // Create the AudioInputController object and ensure that it runs on 105 // the audio-manager thread. 106 scoped_refptr<AudioInputController> controller( 107 new AudioInputController(event_handler, sync_writer, user_input_monitor)); 108 controller->message_loop_ = audio_manager->GetMessageLoop(); 109 110 // Create and open a new audio input stream from the existing 111 // audio-device thread. Use the provided audio-input device. 112 if (!controller->message_loop_->PostTask(FROM_HERE, 113 base::Bind(&AudioInputController::DoCreate, controller, 114 base::Unretained(audio_manager), params, device_id))) { 115 controller = NULL; 116 } 117 118 return controller; 119} 120 121// static 122scoped_refptr<AudioInputController> AudioInputController::CreateForStream( 123 const scoped_refptr<base::MessageLoopProxy>& message_loop, 124 EventHandler* event_handler, 125 AudioInputStream* stream, 126 SyncWriter* sync_writer, 127 UserInputMonitor* user_input_monitor) { 128 DCHECK(sync_writer); 129 DCHECK(stream); 130 131 // Create the AudioInputController object and ensure that it runs on 132 // the audio-manager thread. 133 scoped_refptr<AudioInputController> controller( 134 new AudioInputController(event_handler, sync_writer, user_input_monitor)); 135 controller->message_loop_ = message_loop; 136 137 // TODO(miu): See TODO at top of file. Until that's resolved, we need to 138 // disable the error auto-detection here (since the audio mirroring 139 // implementation will reliably report error and close events). Note, of 140 // course, that we're assuming CreateForStream() has been called for the audio 141 // mirroring use case only. 142 if (!controller->message_loop_->PostTask( 143 FROM_HERE, 144 base::Bind(&AudioInputController::DoCreateForStream, controller, 145 stream, false))) { 146 controller = NULL; 147 } 148 149 return controller; 150} 151 152void AudioInputController::Record() { 153 message_loop_->PostTask(FROM_HERE, base::Bind( 154 &AudioInputController::DoRecord, this)); 155} 156 157void AudioInputController::Close(const base::Closure& closed_task) { 158 DCHECK(!closed_task.is_null()); 159 DCHECK(creator_loop_->BelongsToCurrentThread()); 160 161 message_loop_->PostTaskAndReply( 162 FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task); 163} 164 165void AudioInputController::SetVolume(double volume) { 166 message_loop_->PostTask(FROM_HERE, base::Bind( 167 &AudioInputController::DoSetVolume, this, volume)); 168} 169 170void AudioInputController::SetAutomaticGainControl(bool enabled) { 171 message_loop_->PostTask(FROM_HERE, base::Bind( 172 &AudioInputController::DoSetAutomaticGainControl, this, enabled)); 173} 174 175void AudioInputController::DoCreate(AudioManager* audio_manager, 176 const AudioParameters& params, 177 const std::string& device_id) { 178 DCHECK(message_loop_->BelongsToCurrentThread()); 179 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); 180 // TODO(miu): See TODO at top of file. Until that's resolved, assume all 181 // platform audio input requires the |no_data_timer_| be used to auto-detect 182 // errors. In reality, probably only Windows and IOS need to be treated as 183 // unreliable here. 184 DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id), 185 true); 186} 187 188void AudioInputController::DoCreateForStream( 189 AudioInputStream* stream_to_control, bool enable_nodata_timer) { 190 DCHECK(message_loop_->BelongsToCurrentThread()); 191 192 DCHECK(!stream_); 193 stream_ = stream_to_control; 194 195 if (!stream_) { 196 handler_->OnError(this); 197 return; 198 } 199 200 if (stream_ && !stream_->Open()) { 201 stream_->Close(); 202 stream_ = NULL; 203 handler_->OnError(this); 204 return; 205 } 206 207 DCHECK(!no_data_timer_.get()); 208 if (enable_nodata_timer) { 209 // Create the data timer which will call DoCheckForNoData(). The timer 210 // is started in DoRecord() and restarted in each DoCheckForNoData() 211 // callback. 212 no_data_timer_.reset(new base::Timer( 213 FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds), 214 base::Bind(&AudioInputController::DoCheckForNoData, 215 base::Unretained(this)), false)); 216 } else { 217 DVLOG(1) << "Disabled: timer check for no data."; 218 } 219 220 state_ = kCreated; 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 } 227} 228 229void AudioInputController::DoRecord() { 230 DCHECK(message_loop_->BelongsToCurrentThread()); 231 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.RecordTime"); 232 233 if (state_ != kCreated) 234 return; 235 236 { 237 base::AutoLock auto_lock(lock_); 238 state_ = kRecording; 239 } 240 241 if (no_data_timer_) { 242 // Start the data timer. Once |kTimerResetIntervalSeconds| have passed, 243 // a callback to DoCheckForNoData() is made. 244 no_data_timer_->Reset(); 245 } 246 247 stream_->Start(this); 248 handler_->OnRecording(this); 249} 250 251void AudioInputController::DoClose() { 252 DCHECK(message_loop_->BelongsToCurrentThread()); 253 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CloseTime"); 254 255 // Delete the timer on the same thread that created it. 256 no_data_timer_.reset(); 257 258 if (state_ != kClosed) { 259 DoStopCloseAndClearStream(NULL); 260 SetDataIsActive(false); 261 262 if (LowLatencyMode()) { 263 sync_writer_->Close(); 264 } 265 266 state_ = kClosed; 267 268 if (user_input_monitor_) 269 user_input_monitor_->DisableKeyPressMonitoring(); 270 } 271} 272 273void AudioInputController::DoReportError() { 274 DCHECK(message_loop_->BelongsToCurrentThread()); 275 handler_->OnError(this); 276} 277 278void AudioInputController::DoSetVolume(double volume) { 279 DCHECK(message_loop_->BelongsToCurrentThread()); 280 DCHECK_GE(volume, 0); 281 DCHECK_LE(volume, 1.0); 282 283 if (state_ != kCreated && state_ != kRecording) 284 return; 285 286 // Only ask for the maximum volume at first call and use cached value 287 // for remaining function calls. 288 if (!max_volume_) { 289 max_volume_ = stream_->GetMaxVolume(); 290 } 291 292 if (max_volume_ == 0.0) { 293 DLOG(WARNING) << "Failed to access input volume control"; 294 return; 295 } 296 297 // Set the stream volume and scale to a range matched to the platform. 298 stream_->SetVolume(max_volume_ * volume); 299} 300 301void AudioInputController::DoSetAutomaticGainControl(bool enabled) { 302 DCHECK(message_loop_->BelongsToCurrentThread()); 303 DCHECK_NE(state_, kRecording); 304 305 // Ensure that the AGC state only can be modified before streaming starts. 306 if (state_ != kCreated || state_ == kRecording) 307 return; 308 309 stream_->SetAutomaticGainControl(enabled); 310} 311 312void AudioInputController::DoCheckForNoData() { 313 DCHECK(message_loop_->BelongsToCurrentThread()); 314 315 if (!GetDataIsActive()) { 316 // The data-is-active marker will be false only if it has been more than 317 // one second since a data packet was recorded. This can happen if a 318 // capture device has been removed or disabled. 319 handler_->OnError(this); 320 return; 321 } 322 323 // Mark data as non-active. The flag will be re-enabled in OnData() each 324 // time a data packet is received. Hence, under normal conditions, the 325 // flag will only be disabled during a very short period. 326 SetDataIsActive(false); 327 328 // Restart the timer to ensure that we check the flag again in 329 // |kTimerResetIntervalSeconds|. 330 no_data_timer_->Start( 331 FROM_HERE, base::TimeDelta::FromSeconds(kTimerResetIntervalSeconds), 332 base::Bind(&AudioInputController::DoCheckForNoData, 333 base::Unretained(this))); 334} 335 336void AudioInputController::OnData(AudioInputStream* stream, 337 const uint8* data, 338 uint32 size, 339 uint32 hardware_delay_bytes, 340 double volume) { 341 { 342 base::AutoLock auto_lock(lock_); 343 if (state_ != kRecording) 344 return; 345 } 346 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."; 353 } 354 355 // Mark data as active to ensure that the periodic calls to 356 // DoCheckForNoData() does not report an error to the event handler. 357 SetDataIsActive(true); 358 359 // Use SyncSocket if we are in a low-latency mode. 360 if (LowLatencyMode()) { 361 sync_writer_->Write(data, size, volume, key_pressed); 362 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); 363 return; 364 } 365 366 handler_->OnData(this, data, size); 367} 368 369void AudioInputController::OnClose(AudioInputStream* stream) { 370 DVLOG(1) << "AudioInputController::OnClose()"; 371 // TODO(satish): Sometimes the device driver closes the input stream without 372 // us asking for it (may be if the device was unplugged?). Check how to handle 373 // such cases here. 374} 375 376void AudioInputController::OnError(AudioInputStream* stream) { 377 // Handle error on the audio-manager thread. 378 message_loop_->PostTask(FROM_HERE, base::Bind( 379 &AudioInputController::DoReportError, this)); 380} 381 382void AudioInputController::DoStopCloseAndClearStream( 383 base::WaitableEvent* done) { 384 DCHECK(message_loop_->BelongsToCurrentThread()); 385 386 // Allow calling unconditionally and bail if we don't have a stream to close. 387 if (stream_ != NULL) { 388 stream_->Stop(); 389 stream_->Close(); 390 stream_ = NULL; 391 } 392 393 // Should be last in the method, do not touch "this" from here on. 394 if (done != NULL) 395 done->Signal(); 396} 397 398void AudioInputController::SetDataIsActive(bool enabled) { 399 base::subtle::Release_Store(&data_is_active_, enabled); 400} 401 402bool AudioInputController::GetDataIsActive() { 403 return (base::subtle::Acquire_Load(&data_is_active_) != false); 404} 405 406} // namespace media 407