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/strings/string_number_conversions.h" 9#include "base/strings/stringprintf.h" 10#include "base/threading/thread_restrictions.h" 11#include "base/time/time.h" 12#include "media/audio/audio_parameters.h" 13#include "media/base/scoped_histogram_timer.h" 14#include "media/base/user_input_monitor.h" 15 16using base::TimeDelta; 17 18namespace { 19 20const int kMaxInputChannels = 3; 21 22// TODO(henrika): remove usage of timers and add support for proper 23// notification of when the input device is removed. This was originally added 24// to resolve http://crbug.com/79936 for Windows platforms. This then caused 25// breakage (very hard to repro bugs!) on other platforms: See 26// http://crbug.com/226327 and http://crbug.com/230972. 27// See also that the timer has been disabled on Mac now due to 28// crbug.com/357501. 29const int kTimerResetIntervalSeconds = 1; 30// We have received reports that the timer can be too trigger happy on some 31// Mac devices and the initial timer interval has therefore been increased 32// from 1 second to 5 seconds. 33const int kTimerInitialIntervalSeconds = 5; 34 35#if defined(AUDIO_POWER_MONITORING) 36// Time constant for AudioPowerMonitor. 37// The utilized smoothing factor (alpha) in the exponential filter is given 38// by 1-exp(-1/(fs*ts)), where fs is the sample rate in Hz and ts is the time 39// constant given by |kPowerMeasurementTimeConstantMilliseconds|. 40// Example: fs=44100, ts=10e-3 => alpha~0.022420 41// fs=44100, ts=20e-3 => alpha~0.165903 42// A large smoothing factor corresponds to a faster filter response to input 43// changes since y(n)=alpha*x(n)+(1-alpha)*y(n-1), where x(n) is the input 44// and y(n) is the output. 45const int kPowerMeasurementTimeConstantMilliseconds = 10; 46 47// Time in seconds between two successive measurements of audio power levels. 48const int kPowerMonitorLogIntervalSeconds = 15; 49 50// A warning will be logged when the microphone audio volume is below this 51// threshold. 52const int kLowLevelMicrophoneLevelPercent = 10; 53 54// Logs if the user has enabled the microphone mute or not. This is normally 55// done by marking a checkbox in an audio-settings UI which is unique for each 56// platform. Elements in this enum should not be added, deleted or rearranged. 57enum MicrophoneMuteResult { 58 MICROPHONE_IS_MUTED = 0, 59 MICROPHONE_IS_NOT_MUTED = 1, 60 MICROPHONE_MUTE_MAX = MICROPHONE_IS_NOT_MUTED 61}; 62 63void LogMicrophoneMuteResult(MicrophoneMuteResult result) { 64 UMA_HISTOGRAM_ENUMERATION("Media.MicrophoneMuted", 65 result, 66 MICROPHONE_MUTE_MAX + 1); 67} 68#endif 69} 70 71// Used to log the result of capture startup. 72// This was previously logged as a boolean with only the no callback and OK 73// options. The enum order is kept to ensure backwards compatibility. 74// Elements in this enum should not be deleted or rearranged; the only 75// permitted operation is to add new elements before CAPTURE_STARTUP_RESULT_MAX 76// and update CAPTURE_STARTUP_RESULT_MAX. 77enum CaptureStartupResult { 78 CAPTURE_STARTUP_NO_DATA_CALLBACK = 0, 79 CAPTURE_STARTUP_OK = 1, 80 CAPTURE_STARTUP_CREATE_STREAM_FAILED = 2, 81 CAPTURE_STARTUP_OPEN_STREAM_FAILED = 3, 82 CAPTURE_STARTUP_RESULT_MAX = CAPTURE_STARTUP_OPEN_STREAM_FAILED 83}; 84 85void LogCaptureStartupResult(CaptureStartupResult result) { 86 UMA_HISTOGRAM_ENUMERATION("Media.AudioInputControllerCaptureStartupSuccess", 87 result, 88 CAPTURE_STARTUP_RESULT_MAX + 1); 89 90} 91 92namespace media { 93 94// static 95AudioInputController::Factory* AudioInputController::factory_ = NULL; 96 97AudioInputController::AudioInputController(EventHandler* handler, 98 SyncWriter* sync_writer, 99 UserInputMonitor* user_input_monitor) 100 : creator_task_runner_(base::MessageLoopProxy::current()), 101 handler_(handler), 102 stream_(NULL), 103 data_is_active_(false), 104 state_(CLOSED), 105 sync_writer_(sync_writer), 106 max_volume_(0.0), 107 user_input_monitor_(user_input_monitor), 108#if defined(AUDIO_POWER_MONITORING) 109 log_silence_state_(false), 110 silence_state_(SILENCE_STATE_NO_MEASUREMENT), 111#endif 112 prev_key_down_count_(0) { 113 DCHECK(creator_task_runner_.get()); 114} 115 116AudioInputController::~AudioInputController() { 117 DCHECK_EQ(state_, CLOSED); 118} 119 120// static 121scoped_refptr<AudioInputController> AudioInputController::Create( 122 AudioManager* audio_manager, 123 EventHandler* event_handler, 124 const AudioParameters& params, 125 const std::string& device_id, 126 UserInputMonitor* user_input_monitor) { 127 DCHECK(audio_manager); 128 129 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) 130 return NULL; 131 132 if (factory_) { 133 return factory_->Create( 134 audio_manager, event_handler, params, user_input_monitor); 135 } 136 scoped_refptr<AudioInputController> controller( 137 new AudioInputController(event_handler, NULL, user_input_monitor)); 138 139 controller->task_runner_ = audio_manager->GetTaskRunner(); 140 141 // Create and open a new audio input stream from the existing 142 // audio-device thread. 143 if (!controller->task_runner_->PostTask( 144 FROM_HERE, 145 base::Bind(&AudioInputController::DoCreate, 146 controller, 147 base::Unretained(audio_manager), 148 params, 149 device_id))) { 150 controller = NULL; 151 } 152 153 return controller; 154} 155 156// static 157scoped_refptr<AudioInputController> AudioInputController::CreateLowLatency( 158 AudioManager* audio_manager, 159 EventHandler* event_handler, 160 const AudioParameters& params, 161 const std::string& device_id, 162 SyncWriter* sync_writer, 163 UserInputMonitor* user_input_monitor) { 164 DCHECK(audio_manager); 165 DCHECK(sync_writer); 166 167 if (!params.IsValid() || (params.channels() > kMaxInputChannels)) 168 return NULL; 169 170 // Create the AudioInputController object and ensure that it runs on 171 // the audio-manager thread. 172 scoped_refptr<AudioInputController> controller( 173 new AudioInputController(event_handler, sync_writer, user_input_monitor)); 174 controller->task_runner_ = audio_manager->GetTaskRunner(); 175 176 // Create and open a new audio input stream from the existing 177 // audio-device thread. Use the provided audio-input device. 178 if (!controller->task_runner_->PostTask( 179 FROM_HERE, 180 base::Bind(&AudioInputController::DoCreateForLowLatency, 181 controller, 182 base::Unretained(audio_manager), 183 params, 184 device_id))) { 185 controller = NULL; 186 } 187 188 return controller; 189} 190 191// static 192scoped_refptr<AudioInputController> AudioInputController::CreateForStream( 193 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, 194 EventHandler* event_handler, 195 AudioInputStream* stream, 196 SyncWriter* sync_writer, 197 UserInputMonitor* user_input_monitor) { 198 DCHECK(sync_writer); 199 DCHECK(stream); 200 201 // Create the AudioInputController object and ensure that it runs on 202 // the audio-manager thread. 203 scoped_refptr<AudioInputController> controller( 204 new AudioInputController(event_handler, sync_writer, user_input_monitor)); 205 controller->task_runner_ = task_runner; 206 207 // TODO(miu): See TODO at top of file. Until that's resolved, we need to 208 // disable the error auto-detection here (since the audio mirroring 209 // implementation will reliably report error and close events). Note, of 210 // course, that we're assuming CreateForStream() has been called for the audio 211 // mirroring use case only. 212 if (!controller->task_runner_->PostTask( 213 FROM_HERE, 214 base::Bind(&AudioInputController::DoCreateForStream, 215 controller, 216 stream))) { 217 controller = NULL; 218 } 219 220 return controller; 221} 222 223void AudioInputController::Record() { 224 task_runner_->PostTask(FROM_HERE, base::Bind( 225 &AudioInputController::DoRecord, this)); 226} 227 228void AudioInputController::Close(const base::Closure& closed_task) { 229 DCHECK(!closed_task.is_null()); 230 DCHECK(creator_task_runner_->BelongsToCurrentThread()); 231 232 task_runner_->PostTaskAndReply( 233 FROM_HERE, base::Bind(&AudioInputController::DoClose, this), closed_task); 234} 235 236void AudioInputController::SetVolume(double volume) { 237 task_runner_->PostTask(FROM_HERE, base::Bind( 238 &AudioInputController::DoSetVolume, this, volume)); 239} 240 241void AudioInputController::SetAutomaticGainControl(bool enabled) { 242 task_runner_->PostTask(FROM_HERE, base::Bind( 243 &AudioInputController::DoSetAutomaticGainControl, this, enabled)); 244} 245 246void AudioInputController::DoCreate(AudioManager* audio_manager, 247 const AudioParameters& params, 248 const std::string& device_id) { 249 DCHECK(task_runner_->BelongsToCurrentThread()); 250 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); 251 if (handler_) 252 handler_->OnLog(this, "AIC::DoCreate"); 253 254#if defined(AUDIO_POWER_MONITORING) 255 // Create the audio (power) level meter given the provided audio parameters. 256 // An AudioBus is also needed to wrap the raw data buffer from the native 257 // layer to match AudioPowerMonitor::Scan(). 258 // TODO(henrika): Remove use of extra AudioBus. See http://crbug.com/375155. 259 last_audio_level_log_time_ = base::TimeTicks::Now(); 260 audio_level_.reset(new media::AudioPowerMonitor( 261 params.sample_rate(), 262 TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMilliseconds))); 263 audio_params_ = params; 264 silence_state_ = SILENCE_STATE_NO_MEASUREMENT; 265#endif 266 267 // TODO(miu): See TODO at top of file. Until that's resolved, assume all 268 // platform audio input requires the |no_data_timer_| be used to auto-detect 269 // errors. In reality, probably only Windows needs to be treated as 270 // unreliable here. 271 DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id)); 272} 273 274void AudioInputController::DoCreateForLowLatency(AudioManager* audio_manager, 275 const AudioParameters& params, 276 const std::string& device_id) { 277 DCHECK(task_runner_->BelongsToCurrentThread()); 278 279#if defined(AUDIO_POWER_MONITORING) 280 // We only log silence state UMA stats for low latency mode and if we use a 281 // real device. 282 if (params.format() != AudioParameters::AUDIO_FAKE) 283 log_silence_state_ = true; 284#endif 285 286 low_latency_create_time_ = base::TimeTicks::Now(); 287 DoCreate(audio_manager, params, device_id); 288} 289 290void AudioInputController::DoCreateForStream( 291 AudioInputStream* stream_to_control) { 292 DCHECK(task_runner_->BelongsToCurrentThread()); 293 294 DCHECK(!stream_); 295 stream_ = stream_to_control; 296 297 if (!stream_) { 298 if (handler_) 299 handler_->OnError(this, STREAM_CREATE_ERROR); 300 LogCaptureStartupResult(CAPTURE_STARTUP_CREATE_STREAM_FAILED); 301 return; 302 } 303 304 if (stream_ && !stream_->Open()) { 305 stream_->Close(); 306 stream_ = NULL; 307 if (handler_) 308 handler_->OnError(this, STREAM_OPEN_ERROR); 309 LogCaptureStartupResult(CAPTURE_STARTUP_OPEN_STREAM_FAILED); 310 return; 311 } 312 313 DCHECK(!no_data_timer_.get()); 314 315 // Create the data timer which will call FirstCheckForNoData(). The timer 316 // is started in DoRecord() and restarted in each DoCheckForNoData() 317 // callback. 318 // The timer is enabled for logging purposes. The NO_DATA_ERROR triggered 319 // from the timer must be ignored by the EventHandler. 320 // TODO(henrika): remove usage of timer when it has been verified on Canary 321 // that we are safe doing so. Goal is to get rid of |no_data_timer_| and 322 // everything that is tied to it. crbug.com/357569. 323 no_data_timer_.reset(new base::Timer( 324 FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds), 325 base::Bind(&AudioInputController::FirstCheckForNoData, 326 base::Unretained(this)), false)); 327 328 state_ = CREATED; 329 if (handler_) 330 handler_->OnCreated(this); 331 332 if (user_input_monitor_) { 333 user_input_monitor_->EnableKeyPressMonitoring(); 334 prev_key_down_count_ = user_input_monitor_->GetKeyPressCount(); 335 } 336} 337 338void AudioInputController::DoRecord() { 339 DCHECK(task_runner_->BelongsToCurrentThread()); 340 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.RecordTime"); 341 342 if (state_ != CREATED) 343 return; 344 345 { 346 base::AutoLock auto_lock(lock_); 347 state_ = RECORDING; 348 } 349 350 if (handler_) 351 handler_->OnLog(this, "AIC::DoRecord"); 352 353 if (no_data_timer_) { 354 // Start the data timer. Once |kTimerResetIntervalSeconds| have passed, 355 // a callback to FirstCheckForNoData() is made. 356 no_data_timer_->Reset(); 357 } 358 359 stream_->Start(this); 360 if (handler_) 361 handler_->OnRecording(this); 362} 363 364void AudioInputController::DoClose() { 365 DCHECK(task_runner_->BelongsToCurrentThread()); 366 SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CloseTime"); 367 368 if (state_ == CLOSED) 369 return; 370 371 // If this is a low-latency stream, log the total duration (since DoCreate) 372 // and add it to a UMA histogram. 373 if (!low_latency_create_time_.is_null()) { 374 base::TimeDelta duration = 375 base::TimeTicks::Now() - low_latency_create_time_; 376 UMA_HISTOGRAM_LONG_TIMES("Media.InputStreamDuration", duration); 377 if (handler_) { 378 std::string log_string = 379 base::StringPrintf("AIC::DoClose: stream duration="); 380 log_string += base::Int64ToString(duration.InSeconds()); 381 log_string += " seconds"; 382 handler_->OnLog(this, log_string); 383 } 384 } 385 386 // Delete the timer on the same thread that created it. 387 no_data_timer_.reset(); 388 389 DoStopCloseAndClearStream(); 390 SetDataIsActive(false); 391 392 if (SharedMemoryAndSyncSocketMode()) 393 sync_writer_->Close(); 394 395 if (user_input_monitor_) 396 user_input_monitor_->DisableKeyPressMonitoring(); 397 398#if defined(AUDIO_POWER_MONITORING) 399 // Send UMA stats if enabled. 400 if (log_silence_state_) 401 LogSilenceState(silence_state_); 402 log_silence_state_ = false; 403#endif 404 405 state_ = CLOSED; 406} 407 408void AudioInputController::DoReportError() { 409 DCHECK(task_runner_->BelongsToCurrentThread()); 410 if (handler_) 411 handler_->OnError(this, STREAM_ERROR); 412} 413 414void AudioInputController::DoSetVolume(double volume) { 415 DCHECK(task_runner_->BelongsToCurrentThread()); 416 DCHECK_GE(volume, 0); 417 DCHECK_LE(volume, 1.0); 418 419 if (state_ != CREATED && state_ != RECORDING) 420 return; 421 422 // Only ask for the maximum volume at first call and use cached value 423 // for remaining function calls. 424 if (!max_volume_) { 425 max_volume_ = stream_->GetMaxVolume(); 426 } 427 428 if (max_volume_ == 0.0) { 429 DLOG(WARNING) << "Failed to access input volume control"; 430 return; 431 } 432 433 // Set the stream volume and scale to a range matched to the platform. 434 stream_->SetVolume(max_volume_ * volume); 435} 436 437void AudioInputController::DoSetAutomaticGainControl(bool enabled) { 438 DCHECK(task_runner_->BelongsToCurrentThread()); 439 DCHECK_NE(state_, RECORDING); 440 441 // Ensure that the AGC state only can be modified before streaming starts. 442 if (state_ != CREATED) 443 return; 444 445 stream_->SetAutomaticGainControl(enabled); 446} 447 448void AudioInputController::FirstCheckForNoData() { 449 DCHECK(task_runner_->BelongsToCurrentThread()); 450 LogCaptureStartupResult(GetDataIsActive() ? 451 CAPTURE_STARTUP_OK : 452 CAPTURE_STARTUP_NO_DATA_CALLBACK); 453 if (handler_) { 454 handler_->OnLog(this, GetDataIsActive() ? 455 "AIC::FirstCheckForNoData => data is active" : 456 "AIC::FirstCheckForNoData => data is NOT active"); 457 } 458 DoCheckForNoData(); 459} 460 461void AudioInputController::DoCheckForNoData() { 462 DCHECK(task_runner_->BelongsToCurrentThread()); 463 464 if (!GetDataIsActive()) { 465 // The data-is-active marker will be false only if it has been more than 466 // one second since a data packet was recorded. This can happen if a 467 // capture device has been removed or disabled. 468 if (handler_) 469 handler_->OnError(this, NO_DATA_ERROR); 470 } 471 472 // Mark data as non-active. The flag will be re-enabled in OnData() each 473 // time a data packet is received. Hence, under normal conditions, the 474 // flag will only be disabled during a very short period. 475 SetDataIsActive(false); 476 477 // Restart the timer to ensure that we check the flag again in 478 // |kTimerResetIntervalSeconds|. 479 no_data_timer_->Start( 480 FROM_HERE, base::TimeDelta::FromSeconds(kTimerResetIntervalSeconds), 481 base::Bind(&AudioInputController::DoCheckForNoData, 482 base::Unretained(this))); 483} 484 485void AudioInputController::OnData(AudioInputStream* stream, 486 const AudioBus* source, 487 uint32 hardware_delay_bytes, 488 double volume) { 489 // Mark data as active to ensure that the periodic calls to 490 // DoCheckForNoData() does not report an error to the event handler. 491 SetDataIsActive(true); 492 493 { 494 base::AutoLock auto_lock(lock_); 495 if (state_ != RECORDING) 496 return; 497 } 498 499 bool key_pressed = false; 500 if (user_input_monitor_) { 501 size_t current_count = user_input_monitor_->GetKeyPressCount(); 502 key_pressed = current_count != prev_key_down_count_; 503 prev_key_down_count_ = current_count; 504 DVLOG_IF(6, key_pressed) << "Detected keypress."; 505 } 506 507 // Use SharedMemory and SyncSocket if the client has created a SyncWriter. 508 // Used by all low-latency clients except WebSpeech. 509 if (SharedMemoryAndSyncSocketMode()) { 510 sync_writer_->Write(source, volume, key_pressed); 511 sync_writer_->UpdateRecordedBytes(hardware_delay_bytes); 512 513#if defined(AUDIO_POWER_MONITORING) 514 // Only do power-level measurements if an AudioPowerMonitor object has 515 // been created. Done in DoCreate() but not DoCreateForStream(), hence 516 // logging will mainly be done for WebRTC and WebSpeech clients. 517 if (!audio_level_) 518 return; 519 520 // Perform periodic audio (power) level measurements. 521 if ((base::TimeTicks::Now() - last_audio_level_log_time_).InSeconds() > 522 kPowerMonitorLogIntervalSeconds) { 523 // Wrap data into an AudioBus to match AudioPowerMonitor::Scan. 524 // TODO(henrika): remove this section when capture side uses AudioBus. 525 // See http://crbug.com/375155 for details. 526 audio_level_->Scan(*source, source->frames()); 527 528 // Get current average power level and add it to the log. 529 // Possible range is given by [-inf, 0] dBFS. 530 std::pair<float, bool> result = audio_level_->ReadCurrentPowerAndClip(); 531 532 // Add current microphone volume to log and UMA histogram. 533 const int mic_volume_percent = static_cast<int>(100.0 * volume); 534 535 // Use event handler on the audio thread to relay a message to the ARIH 536 // in content which does the actual logging on the IO thread. 537 task_runner_->PostTask(FROM_HERE, 538 base::Bind(&AudioInputController::DoLogAudioLevels, 539 this, 540 result.first, 541 mic_volume_percent)); 542 543 last_audio_level_log_time_ = base::TimeTicks::Now(); 544 545 // Reset the average power level (since we don't log continuously). 546 audio_level_->Reset(); 547 } 548#endif 549 return; 550 } 551 552 // TODO(henrika): Investigate if we can avoid the extra copy here. 553 // (see http://crbug.com/249316 for details). AFAIK, this scope is only 554 // active for WebSpeech clients. 555 scoped_ptr<AudioBus> audio_data = 556 AudioBus::Create(source->channels(), source->frames()); 557 source->CopyTo(audio_data.get()); 558 559 // Ownership of the audio buffer will be with the callback until it is run, 560 // when ownership is passed to the callback function. 561 task_runner_->PostTask( 562 FROM_HERE, 563 base::Bind( 564 &AudioInputController::DoOnData, this, base::Passed(&audio_data))); 565} 566 567void AudioInputController::DoOnData(scoped_ptr<AudioBus> data) { 568 DCHECK(task_runner_->BelongsToCurrentThread()); 569 if (handler_) 570 handler_->OnData(this, data.get()); 571} 572 573void AudioInputController::DoLogAudioLevels(float level_dbfs, 574 int microphone_volume_percent) { 575#if defined(AUDIO_POWER_MONITORING) 576 DCHECK(task_runner_->BelongsToCurrentThread()); 577 if (!handler_) 578 return; 579 580 // Detect if the user has enabled hardware mute by pressing the mute 581 // button in audio settings for the selected microphone. 582 const bool microphone_is_muted = stream_->IsMuted(); 583 if (microphone_is_muted) { 584 LogMicrophoneMuteResult(MICROPHONE_IS_MUTED); 585 handler_->OnLog(this, "AIC::OnData: microphone is muted!"); 586 // Return early if microphone is muted. No need to adding logs and UMA stats 587 // of audio levels if we know that the micropone is muted. 588 return; 589 } 590 591 LogMicrophoneMuteResult(MICROPHONE_IS_NOT_MUTED); 592 593 std::string log_string = base::StringPrintf( 594 "AIC::OnData: average audio level=%.2f dBFS", level_dbfs); 595 static const float kSilenceThresholdDBFS = -72.24719896f; 596 if (level_dbfs < kSilenceThresholdDBFS) 597 log_string += " <=> low audio input level!"; 598 handler_->OnLog(this, log_string); 599 600 UpdateSilenceState(level_dbfs < kSilenceThresholdDBFS); 601 602 UMA_HISTOGRAM_PERCENTAGE("Media.MicrophoneVolume", microphone_volume_percent); 603 log_string = base::StringPrintf( 604 "AIC::OnData: microphone volume=%d%%", microphone_volume_percent); 605 if (microphone_volume_percent < kLowLevelMicrophoneLevelPercent) 606 log_string += " <=> low microphone level!"; 607 handler_->OnLog(this, log_string); 608#endif 609} 610 611void AudioInputController::OnError(AudioInputStream* stream) { 612 // Handle error on the audio-manager thread. 613 task_runner_->PostTask(FROM_HERE, base::Bind( 614 &AudioInputController::DoReportError, this)); 615} 616 617void AudioInputController::DoStopCloseAndClearStream() { 618 DCHECK(task_runner_->BelongsToCurrentThread()); 619 620 // Allow calling unconditionally and bail if we don't have a stream to close. 621 if (stream_ != NULL) { 622 stream_->Stop(); 623 stream_->Close(); 624 stream_ = NULL; 625 } 626 627 // The event handler should not be touched after the stream has been closed. 628 handler_ = NULL; 629} 630 631void AudioInputController::SetDataIsActive(bool enabled) { 632 base::subtle::Release_Store(&data_is_active_, enabled); 633} 634 635bool AudioInputController::GetDataIsActive() { 636 return (base::subtle::Acquire_Load(&data_is_active_) != false); 637} 638 639#if defined(AUDIO_POWER_MONITORING) 640void AudioInputController::UpdateSilenceState(bool silence) { 641 if (silence) { 642 if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) { 643 silence_state_ = SILENCE_STATE_ONLY_SILENCE; 644 } else if (silence_state_ == SILENCE_STATE_ONLY_AUDIO) { 645 silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE; 646 } else { 647 DCHECK(silence_state_ == SILENCE_STATE_ONLY_SILENCE || 648 silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE); 649 } 650 } else { 651 if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) { 652 silence_state_ = SILENCE_STATE_ONLY_AUDIO; 653 } else if (silence_state_ == SILENCE_STATE_ONLY_SILENCE) { 654 silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE; 655 } else { 656 DCHECK(silence_state_ == SILENCE_STATE_ONLY_AUDIO || 657 silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE); 658 } 659 } 660} 661 662void AudioInputController::LogSilenceState(SilenceState value) { 663 UMA_HISTOGRAM_ENUMERATION("Media.AudioInputControllerSessionSilenceReport", 664 value, 665 SILENCE_STATE_MAX + 1); 666} 667#endif 668 669} // namespace media 670