| 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 #include "media/audio/mac/audio_low_latency_input_mac.h" | 4 #include "media/audio/mac/audio_low_latency_input_mac.h" |
| 5 | 5 |
| 6 #include <CoreServices/CoreServices.h> | 6 #include <CoreServices/CoreServices.h> |
| 7 #include <mach/mach.h> | 7 #include <mach/mach.h> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 | 22 |
| 23 namespace media { | 23 namespace media { |
| 24 | 24 |
| 25 // Number of blocks of buffers used in the |fifo_|. | 25 // Number of blocks of buffers used in the |fifo_|. |
| 26 const int kNumberOfBlocksBufferInFifo = 2; | 26 const int kNumberOfBlocksBufferInFifo = 2; |
| 27 | 27 |
| 28 // Max length of sequence of TooManyFramesToProcessError errors. | 28 // Max length of sequence of TooManyFramesToProcessError errors. |
| 29 // The stream will be stopped as soon as this time limit is passed. | 29 // The stream will be stopped as soon as this time limit is passed. |
| 30 const int kMaxErrorTimeoutInSeconds = 1; | 30 const int kMaxErrorTimeoutInSeconds = 1; |
| 31 | 31 |
| 32 // A repeating timer is created and started in Start() and it triggers calls | |
| 33 // to CheckIfInputStreamIsAlive() where we do periodic checks to see if the | |
| 34 // input data callback sequence is active or not. If the stream seems dead, | |
| 35 // up to |kMaxNumberOfRestartAttempts| restart attempts tries to bring the | |
| 36 // stream back to life. | |
| 37 const int kCheckInputIsAliveTimeInSeconds = 5; | |
| 38 | |
| 39 // Number of restart indications required to actually trigger a restart | |
| 40 // attempt. | |
| 41 const int kNumberOfIndicationsToTriggerRestart = 1; | |
| 42 | |
| 43 // Max number of times we try to restart a stream when it has been categorized | |
| 44 // as dead. Note that we can do many restarts during an audio session and this | |
| 45 // limitation is per detected problem. Once a restart has succeeded, a new | |
| 46 // sequence of |kMaxNumberOfRestartAttempts| number of restart attempts can be | |
| 47 // done. | |
| 48 const int kMaxNumberOfRestartAttempts = 1; | |
| 49 | |
| 50 // A one-shot timer is created and started in Start() and it triggers | 32 // A one-shot timer is created and started in Start() and it triggers |
| 51 // CheckInputStartupSuccess() after this amount of time. UMA stats marked | 33 // CheckInputStartupSuccess() after this amount of time. UMA stats marked |
| 52 // Media.Audio.InputStartupSuccessMac is then updated where true is added | 34 // Media.Audio.InputStartupSuccessMac is then updated where true is added |
| 53 // if input callbacks have started, and false otherwise. Note that the value | 35 // if input callbacks have started, and false otherwise. |
| 54 // is larger than |kCheckInputIsAliveTimeInSeconds| to ensure that at least one | 36 const int kInputCallbackStartTimeoutInSeconds = 5; |
| 55 // restart attempt can be done before storing the result. | |
| 56 const int kInputCallbackStartTimeoutInSeconds = | |
| 57 kCheckInputIsAliveTimeInSeconds + 3; | |
| 58 | 37 |
| 59 // Returns true if the format flags in |format_flags| has the "non-interleaved" | 38 // Returns true if the format flags in |format_flags| has the "non-interleaved" |
| 60 // flag (kAudioFormatFlagIsNonInterleaved) cleared (set to 0). | 39 // flag (kAudioFormatFlagIsNonInterleaved) cleared (set to 0). |
| 61 static bool FormatIsInterleaved(UInt32 format_flags) { | 40 static bool FormatIsInterleaved(UInt32 format_flags) { |
| 62 return !(format_flags & kAudioFormatFlagIsNonInterleaved); | 41 return !(format_flags & kAudioFormatFlagIsNonInterleaved); |
| 63 } | 42 } |
| 64 | 43 |
| 65 // Converts the 32-bit non-terminated 4 byte string into an std::string. | 44 // Converts the 32-bit non-terminated 4 byte string into an std::string. |
| 66 // Example: code=1735354734 <=> 'goin' <=> kAudioDevicePropertyDeviceIsRunning. | 45 // Example: code=1735354734 <=> 'goin' <=> kAudioDevicePropertyDeviceIsRunning. |
| 67 static std::string FourCharFormatCodeToString(UInt32 code) { | 46 static std::string FourCharFormatCodeToString(UInt32 code) { |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 248 number_of_frames_provided_(0), | 227 number_of_frames_provided_(0), |
| 249 io_buffer_frame_size_(0), | 228 io_buffer_frame_size_(0), |
| 250 sink_(nullptr), | 229 sink_(nullptr), |
| 251 audio_unit_(0), | 230 audio_unit_(0), |
| 252 input_device_id_(audio_device_id), | 231 input_device_id_(audio_device_id), |
| 253 hardware_latency_frames_(0), | 232 hardware_latency_frames_(0), |
| 254 number_of_channels_in_frame_(0), | 233 number_of_channels_in_frame_(0), |
| 255 fifo_(input_params.channels(), | 234 fifo_(input_params.channels(), |
| 256 number_of_frames_, | 235 number_of_frames_, |
| 257 kNumberOfBlocksBufferInFifo), | 236 kNumberOfBlocksBufferInFifo), |
| 237 got_input_callback_(false), |
| 258 input_callback_is_active_(false), | 238 input_callback_is_active_(false), |
| 259 start_was_deferred_(false), | 239 start_was_deferred_(false), |
| 260 buffer_size_was_changed_(false), | 240 buffer_size_was_changed_(false), |
| 261 audio_unit_render_has_worked_(false), | 241 audio_unit_render_has_worked_(false), |
| 262 device_listener_is_active_(false), | 242 device_listener_is_active_(false), |
| 263 last_sample_time_(0.0), | 243 last_sample_time_(0.0), |
| 264 last_number_of_frames_(0), | 244 last_number_of_frames_(0), |
| 265 total_lost_frames_(0), | 245 total_lost_frames_(0), |
| 266 largest_glitch_frames_(0), | 246 largest_glitch_frames_(0), |
| 267 glitches_detected_(0), | 247 glitches_detected_(0), |
| 268 number_of_restart_indications_(0), | |
| 269 number_of_restart_attempts_(0), | |
| 270 total_number_of_restart_attempts_(0), | |
| 271 log_callback_(log_callback), | 248 log_callback_(log_callback), |
| 272 weak_factory_(this) { | 249 weak_factory_(this) { |
| 273 DCHECK(manager_); | 250 DCHECK(manager_); |
| 274 CHECK(!log_callback_.Equals(AudioManager::LogCallback())); | 251 CHECK(!log_callback_.Equals(AudioManager::LogCallback())); |
| 275 | 252 |
| 276 // Set up the desired (output) format specified by the client. | 253 // Set up the desired (output) format specified by the client. |
| 277 format_.mSampleRate = input_params.sample_rate(); | 254 format_.mSampleRate = input_params.sample_rate(); |
| 278 format_.mFormatID = kAudioFormatLinearPCM; | 255 format_.mFormatID = kAudioFormatLinearPCM; |
| 279 format_.mFormatFlags = | 256 format_.mFormatFlags = |
| 280 kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; | 257 kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; |
| (...skipping 255 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 536 base::Unretained(this), callback)); | 513 base::Unretained(this), callback)); |
| 537 manager_->GetTaskRunner()->PostDelayedTask( | 514 manager_->GetTaskRunner()->PostDelayedTask( |
| 538 FROM_HERE, deferred_start_cb_.callback(), | 515 FROM_HERE, deferred_start_cb_.callback(), |
| 539 base::TimeDelta::FromSeconds( | 516 base::TimeDelta::FromSeconds( |
| 540 AudioManagerMac::kStartDelayInSecsForPowerEvents)); | 517 AudioManagerMac::kStartDelayInSecsForPowerEvents)); |
| 541 return; | 518 return; |
| 542 } | 519 } |
| 543 | 520 |
| 544 sink_ = callback; | 521 sink_ = callback; |
| 545 last_success_time_ = base::TimeTicks::Now(); | 522 last_success_time_ = base::TimeTicks::Now(); |
| 546 last_callback_time_ = base::TimeTicks::Now(); | |
| 547 audio_unit_render_has_worked_ = false; | 523 audio_unit_render_has_worked_ = false; |
| 548 StartAgc(); | 524 StartAgc(); |
| 549 OSStatus result = AudioOutputUnitStart(audio_unit_); | 525 OSStatus result = AudioOutputUnitStart(audio_unit_); |
| 550 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 526 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
| 551 << "Failed to start acquiring data"; | 527 << "Failed to start acquiring data"; |
| 552 if (result != noErr) { | 528 if (result != noErr) { |
| 553 Stop(); | 529 Stop(); |
| 554 return; | 530 return; |
| 555 } | 531 } |
| 556 DCHECK(IsRunning()) << "Audio unit started OK but is not yet running"; | 532 DCHECK(IsRunning()) << "Audio unit started OK but is not yet running"; |
| 557 | 533 |
| 558 // For UMA stat purposes, start a one-shot timer which detects when input | 534 // For UMA stat purposes, start a one-shot timer which detects when input |
| 559 // callbacks starts indicating if input audio recording starts as intended. | 535 // callbacks starts indicating if input audio recording starts as intended. |
| 560 // CheckInputStartupSuccess() will check if |input_callback_is_active_| is | 536 // CheckInputStartupSuccess() will check if |input_callback_is_active_| is |
| 561 // true when the timer expires. | 537 // true when the timer expires. |
| 562 input_callback_timer_.reset(new base::OneShotTimer()); | 538 input_callback_timer_.reset(new base::OneShotTimer()); |
| 563 input_callback_timer_->Start( | 539 input_callback_timer_->Start( |
| 564 FROM_HERE, | 540 FROM_HERE, |
| 565 base::TimeDelta::FromSeconds(kInputCallbackStartTimeoutInSeconds), this, | 541 base::TimeDelta::FromSeconds(kInputCallbackStartTimeoutInSeconds), this, |
| 566 &AUAudioInputStream::CheckInputStartupSuccess); | 542 &AUAudioInputStream::CheckInputStartupSuccess); |
| 567 DCHECK(input_callback_timer_->IsRunning()); | 543 DCHECK(input_callback_timer_->IsRunning()); |
| 568 | |
| 569 // Also create and start a timer that provides periodic callbacks used to | |
| 570 // monitor if the input stream is alive or not. | |
| 571 check_alive_timer_.reset(new base::RepeatingTimer()); | |
| 572 check_alive_timer_->Start( | |
| 573 FROM_HERE, base::TimeDelta::FromSeconds(kCheckInputIsAliveTimeInSeconds), | |
| 574 this, &AUAudioInputStream::CheckIfInputStreamIsAlive); | |
| 575 DCHECK(check_alive_timer_->IsRunning()); | |
| 576 } | 544 } |
| 577 | 545 |
| 578 void AUAudioInputStream::Stop() { | 546 void AUAudioInputStream::Stop() { |
| 579 DCHECK(thread_checker_.CalledOnValidThread()); | 547 DCHECK(thread_checker_.CalledOnValidThread()); |
| 580 deferred_start_cb_.Cancel(); | 548 deferred_start_cb_.Cancel(); |
| 581 DVLOG(1) << "Stop"; | 549 DVLOG(1) << "Stop"; |
| 582 StopAgc(); | 550 StopAgc(); |
| 583 if (check_alive_timer_ != nullptr) { | |
| 584 check_alive_timer_->Stop(); | |
| 585 check_alive_timer_.reset(); | |
| 586 } | |
| 587 if (input_callback_timer_ != nullptr) { | 551 if (input_callback_timer_ != nullptr) { |
| 588 input_callback_timer_->Stop(); | 552 input_callback_timer_->Stop(); |
| 589 input_callback_timer_.reset(); | 553 input_callback_timer_.reset(); |
| 590 } | 554 } |
| 591 | 555 |
| 592 if (audio_unit_ != nullptr) { | 556 if (audio_unit_ != nullptr) { |
| 593 // Stop the I/O audio unit. | 557 // Stop the I/O audio unit. |
| 594 OSStatus result = AudioOutputUnitStop(audio_unit_); | 558 OSStatus result = AudioOutputUnitStop(audio_unit_); |
| 595 DCHECK_EQ(result, noErr); | 559 DCHECK_EQ(result, noErr); |
| 596 // Add a DCHECK here just in case. AFAIK, the call to AudioOutputUnitStop() | 560 // Add a DCHECK here just in case. AFAIK, the call to AudioOutputUnitStop() |
| 597 // seems to set this state synchronously, hence it should always report | 561 // seems to set this state synchronously, hence it should always report |
| 598 // false after a successful call. | 562 // false after a successful call. |
| 599 DCHECK(!IsRunning()) << "Audio unit is stopped but still running"; | 563 DCHECK(!IsRunning()) << "Audio unit is stopped but still running"; |
| 600 | 564 |
| 601 // Reset the audio unit’s render state. This function clears memory. | 565 // Reset the audio unit’s render state. This function clears memory. |
| 602 // It does not allocate or free memory resources. | 566 // It does not allocate or free memory resources. |
| 603 result = AudioUnitReset(audio_unit_, kAudioUnitScope_Global, 0); | 567 result = AudioUnitReset(audio_unit_, kAudioUnitScope_Global, 0); |
| 604 DCHECK_EQ(result, noErr); | 568 DCHECK_EQ(result, noErr); |
| 605 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 569 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
| 606 << "Failed to stop acquiring data"; | 570 << "Failed to stop acquiring data"; |
| 607 } | 571 } |
| 608 | 572 |
| 609 SetInputCallbackIsActive(false); | 573 SetInputCallbackIsActive(false); |
| 610 ReportAndResetStats(); | 574 ReportAndResetStats(); |
| 611 sink_ = nullptr; | 575 sink_ = nullptr; |
| 612 fifo_.Clear(); | 576 fifo_.Clear(); |
| 613 io_buffer_frame_size_ = 0; | 577 io_buffer_frame_size_ = 0; |
| 578 got_input_callback_ = false; |
| 614 } | 579 } |
| 615 | 580 |
| 616 void AUAudioInputStream::Close() { | 581 void AUAudioInputStream::Close() { |
| 617 DCHECK(thread_checker_.CalledOnValidThread()); | 582 DCHECK(thread_checker_.CalledOnValidThread()); |
| 618 DVLOG(1) << "Close"; | 583 DVLOG(1) << "Close"; |
| 584 |
| 619 // It is valid to call Close() before calling open or Start(). | 585 // It is valid to call Close() before calling open or Start(). |
| 620 // It is also valid to call Close() after Start() has been called. | 586 // It is also valid to call Close() after Start() has been called. |
| 621 Stop(); | 587 Stop(); |
| 588 |
| 622 // Uninitialize and dispose the audio unit. | 589 // Uninitialize and dispose the audio unit. |
| 623 CloseAudioUnit(); | 590 CloseAudioUnit(); |
| 591 |
| 624 // Disable the listener for device property changes. | 592 // Disable the listener for device property changes. |
| 625 DeRegisterDeviceChangeListener(); | 593 DeRegisterDeviceChangeListener(); |
| 594 |
| 626 // Add more UMA stats but only if AGC was activated, i.e. for e.g. WebRTC | 595 // Add more UMA stats but only if AGC was activated, i.e. for e.g. WebRTC |
| 627 // audio input streams. | 596 // audio input streams. |
| 628 if (GetAutomaticGainControl()) { | 597 if (GetAutomaticGainControl()) { |
| 629 // Check if any device property changes are added by filtering out a | 598 // Check if any device property changes are added by filtering out a |
| 630 // selected set of the |device_property_changes_map_| map. Add UMA stats | 599 // selected set of the |device_property_changes_map_| map. Add UMA stats |
| 631 // if valuable data is found. | 600 // if valuable data is found. |
| 632 AddDevicePropertyChangesToUMA(false); | 601 AddDevicePropertyChangesToUMA(false); |
| 633 // Log whether call to Start() was deferred or not. To be compared with | 602 // Log whether call to Start() was deferred or not. To be compared with |
| 634 // Media.Audio.InputStartWasDeferredMac which logs the same value but only | 603 // Media.Audio.InputStartWasDeferredMac which logs the same value but only |
| 635 // when input audio fails to start. | 604 // when input audio fails to start. |
| 636 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredAudioWorkedMac", | 605 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredAudioWorkedMac", |
| 637 start_was_deferred_); | 606 start_was_deferred_); |
| 638 // Log if a change of I/O buffer size was required. To be compared with | 607 // Log if a change of I/O buffer size was required. To be compared with |
| 639 // Media.Audio.InputBufferSizeWasChangedMac which logs the same value but | 608 // Media.Audio.InputBufferSizeWasChangedMac which logs the same value but |
| 640 // only when input audio fails to start. | 609 // only when input audio fails to start. |
| 641 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedAudioWorkedMac", | 610 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedAudioWorkedMac", |
| 642 buffer_size_was_changed_); | 611 buffer_size_was_changed_); |
| 643 // Logs the total number of times RestartAudio() has been called. | |
| 644 DVLOG(1) << "Total number of restart attempts: " | |
| 645 << total_number_of_restart_attempts_; | |
| 646 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.InputRestartAttemptsMac", | |
| 647 total_number_of_restart_attempts_); | |
| 648 // TODO(henrika): possibly add more values here... | |
| 649 } | 612 } |
| 613 |
| 650 // Inform the audio manager that we have been closed. This will cause our | 614 // Inform the audio manager that we have been closed. This will cause our |
| 651 // destruction. | 615 // destruction. |
| 652 manager_->ReleaseInputStream(this); | 616 manager_->ReleaseInputStream(this); |
| 653 } | 617 } |
| 654 | 618 |
| 655 double AUAudioInputStream::GetMaxVolume() { | 619 double AUAudioInputStream::GetMaxVolume() { |
| 656 // Verify that we have a valid device. | 620 // Verify that we have a valid device. |
| 657 if (input_device_id_ == kAudioObjectUnknown) { | 621 if (input_device_id_ == kAudioObjectUnknown) { |
| 658 NOTREACHED() << "Device ID is unknown"; | 622 NOTREACHED() << "Device ID is unknown"; |
| 659 return 0.0; | 623 return 0.0; |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 811 return self->OnDataIsAvailable(flags, time_stamp, bus_number, | 775 return self->OnDataIsAvailable(flags, time_stamp, bus_number, |
| 812 number_of_frames); | 776 number_of_frames); |
| 813 } | 777 } |
| 814 | 778 |
| 815 OSStatus AUAudioInputStream::OnDataIsAvailable( | 779 OSStatus AUAudioInputStream::OnDataIsAvailable( |
| 816 AudioUnitRenderActionFlags* flags, | 780 AudioUnitRenderActionFlags* flags, |
| 817 const AudioTimeStamp* time_stamp, | 781 const AudioTimeStamp* time_stamp, |
| 818 UInt32 bus_number, | 782 UInt32 bus_number, |
| 819 UInt32 number_of_frames) { | 783 UInt32 number_of_frames) { |
| 820 TRACE_EVENT0("audio", "AUAudioInputStream::OnDataIsAvailable"); | 784 TRACE_EVENT0("audio", "AUAudioInputStream::OnDataIsAvailable"); |
| 821 // Update |last_callback_time_| on the main browser thread. Its value is used | |
| 822 // by CheckIfInputStreamIsAlive() to detect if the stream is dead or alive. | |
| 823 manager_->GetTaskRunner()->PostTask( | |
| 824 FROM_HERE, | |
| 825 base::Bind(&AUAudioInputStream::UpdateDataCallbackTimeOnMainThread, | |
| 826 weak_factory_.GetWeakPtr(), base::TimeTicks::Now())); | |
| 827 | 785 |
| 828 // Indicate that input callbacks have started on the internal AUHAL IO | 786 // Indicate that input callbacks have started. |
| 829 // thread. The |input_callback_is_active_| member is read from the creating | 787 if (!got_input_callback_) { |
| 830 // thread when a timer fires once and set to false in Stop() on the same | 788 got_input_callback_ = true; |
| 831 // thread. It means that this thread is the only writer of | 789 SetInputCallbackIsActive(true); |
| 832 // |input_callback_is_active_| once the tread starts and it should therefore | 790 } |
| 833 // be safe to modify. | |
| 834 SetInputCallbackIsActive(true); | |
| 835 | 791 |
| 836 // Update the |mDataByteSize| value in the audio_buffer_list() since | 792 // Update the |mDataByteSize| value in the audio_buffer_list() since |
| 837 // |number_of_frames| can be changed on the fly. | 793 // |number_of_frames| can be changed on the fly. |
| 838 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|, | 794 // |mDataByteSize| needs to be exactly mapping to |number_of_frames|, |
| 839 // otherwise it will put CoreAudio into bad state and results in | 795 // otherwise it will put CoreAudio into bad state and results in |
| 840 // AudioUnitRender() returning -50 for the new created stream. | 796 // AudioUnitRender() returning -50 for the new created stream. |
| 841 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and | 797 // We have also seen kAudioUnitErr_TooManyFramesToProcess (-10874) and |
| 842 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes. | 798 // kAudioUnitErr_CannotDoInCurrentContext (-10863) as error codes. |
| 843 // See crbug/428706 for details. | 799 // See crbug/428706 for details. |
| 844 UInt32 new_size = number_of_frames * format_.mBytesPerFrame; | 800 UInt32 new_size = number_of_frames * format_.mBytesPerFrame; |
| (...skipping 375 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1220 input_callback_is_active); | 1176 input_callback_is_active); |
| 1221 DVLOG(1) << "input_callback_is_active: " << input_callback_is_active; | 1177 DVLOG(1) << "input_callback_is_active: " << input_callback_is_active; |
| 1222 if (!input_callback_is_active) { | 1178 if (!input_callback_is_active) { |
| 1223 // Now when we know that startup has failed for some reason, add extra | 1179 // Now when we know that startup has failed for some reason, add extra |
| 1224 // UMA stats in an attempt to figure out the exact reason. | 1180 // UMA stats in an attempt to figure out the exact reason. |
| 1225 AddHistogramsForFailedStartup(); | 1181 AddHistogramsForFailedStartup(); |
| 1226 } | 1182 } |
| 1227 } | 1183 } |
| 1228 } | 1184 } |
| 1229 | 1185 |
| 1230 void AUAudioInputStream::UpdateDataCallbackTimeOnMainThread( | |
| 1231 base::TimeTicks now_tick) { | |
| 1232 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1233 last_callback_time_ = now_tick; | |
| 1234 } | |
| 1235 | |
| 1236 void AUAudioInputStream::CheckIfInputStreamIsAlive() { | |
| 1237 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1238 // Avoid checking stream state if we are suspended. | |
| 1239 if (manager_->IsSuspending()) | |
| 1240 return; | |
| 1241 // Restrict usage of the restart mechanism to "AGC streams" only. | |
| 1242 // TODO(henrika): if the restart scheme works well, we might include it | |
| 1243 // for all types of input streams. | |
| 1244 if (!GetAutomaticGainControl()) | |
| 1245 return; | |
| 1246 | |
| 1247 // Clear this counter here when audio is active instead of in Start(), | |
| 1248 // otherwise it would be cleared at each restart attempt and that would break | |
| 1249 // the current design where only a certain number of restart attempts is | |
| 1250 // allowed. | |
| 1251 if (GetInputCallbackIsActive()) | |
| 1252 number_of_restart_attempts_ = 0; | |
| 1253 | |
| 1254 // Measure time since last callback. |last_callback_time_| is set the first | |
| 1255 // time in Start() and then updated in each data callback, hence if | |
| 1256 // |time_since_last_callback| is large (>1) and growing for each check, the | |
| 1257 // callback sequence has stopped. A typical value under normal/working | |
| 1258 // conditions is a few milliseconds. | |
| 1259 base::TimeDelta time_since_last_callback = | |
| 1260 base::TimeTicks::Now() - last_callback_time_; | |
| 1261 DVLOG(2) << "time since last callback: " | |
| 1262 << time_since_last_callback.InSecondsF(); | |
| 1263 | |
| 1264 // Increase a counter if it has been too long since the last data callback. | |
| 1265 // A non-zero value of this counter is a strong indication of a "dead" input | |
| 1266 // stream. Reset the same counter if the stream is alive. | |
| 1267 if (time_since_last_callback.InSecondsF() > | |
| 1268 0.5 * kCheckInputIsAliveTimeInSeconds) { | |
| 1269 ++number_of_restart_indications_; | |
| 1270 } else { | |
| 1271 number_of_restart_indications_ = 0; | |
| 1272 } | |
| 1273 | |
| 1274 // Restart the audio stream if two conditions are met. First, the number of | |
| 1275 // restart indicators must be larger than a threshold, and secondly, only a | |
| 1276 // fixed number of restart attempts is allowed. | |
| 1277 // Note that, the threshold is different depending on if the stream is seen | |
| 1278 // as dead directly at the first call to Start() (i.e. it has never started) | |
| 1279 // or if the stream has started OK at least once but then stopped for some | |
| 1280 // reason (e.g by placing the device in sleep mode). One restart indication | |
| 1281 // is sufficient for the first case (threshold is zero), while a larger value | |
| 1282 // (threshold > 0) is required for the second case to avoid false alarms when | |
| 1283 // e.g. resuming from a suspended state. | |
| 1284 const size_t restart_threshold = | |
| 1285 GetInputCallbackIsActive() ? kNumberOfIndicationsToTriggerRestart : 0; | |
| 1286 if (number_of_restart_indications_ > restart_threshold && | |
| 1287 number_of_restart_attempts_ < kMaxNumberOfRestartAttempts) { | |
| 1288 SetInputCallbackIsActive(false); | |
| 1289 ++total_number_of_restart_attempts_; | |
| 1290 ++number_of_restart_attempts_; | |
| 1291 number_of_restart_indications_ = 0; | |
| 1292 RestartAudio(); | |
| 1293 } | |
| 1294 } | |
| 1295 | |
| 1296 void AUAudioInputStream::CloseAudioUnit() { | 1186 void AUAudioInputStream::CloseAudioUnit() { |
| 1297 DCHECK(thread_checker_.CalledOnValidThread()); | 1187 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1298 DVLOG(1) << "CloseAudioUnit"; | 1188 DVLOG(1) << "CloseAudioUnit"; |
| 1299 if (!audio_unit_) | 1189 if (!audio_unit_) |
| 1300 return; | 1190 return; |
| 1301 OSStatus result = AudioUnitUninitialize(audio_unit_); | 1191 OSStatus result = AudioUnitUninitialize(audio_unit_); |
| 1302 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 1192 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
| 1303 << "AudioUnitUninitialize() failed."; | 1193 << "AudioUnitUninitialize() failed."; |
| 1304 result = AudioComponentInstanceDispose(audio_unit_); | 1194 result = AudioComponentInstanceDispose(audio_unit_); |
| 1305 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) | 1195 OSSTATUS_DLOG_IF(ERROR, result != noErr, result) |
| 1306 << "AudioComponentInstanceDispose() failed."; | 1196 << "AudioComponentInstanceDispose() failed."; |
| 1307 audio_unit_ = 0; | 1197 audio_unit_ = 0; |
| 1308 } | 1198 } |
| 1309 | 1199 |
| 1310 void AUAudioInputStream::RestartAudio() { | |
| 1311 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1312 DVLOG(1) << "RestartAudio"; | |
| 1313 LOG_IF(ERROR, IsRunning()) | |
| 1314 << "Stream is reported dead but actually seems alive"; | |
| 1315 if (!audio_unit_) | |
| 1316 return; | |
| 1317 | |
| 1318 // Store the existing callback instance for upcoming call to Start(). | |
| 1319 AudioInputCallback* sink = sink_; | |
| 1320 // Do a best-effort attempt to restart the presumably dead input audio stream. | |
| 1321 // TODO(henrika): initial tests shows that the scheme below works well but | |
| 1322 // there might be corner cases that I have missed. | |
| 1323 Stop(); | |
| 1324 CloseAudioUnit(); | |
| 1325 DeRegisterDeviceChangeListener(); | |
| 1326 Open(); | |
| 1327 Start(sink); | |
| 1328 } | |
| 1329 | |
| 1330 void AUAudioInputStream::AddHistogramsForFailedStartup() { | 1200 void AUAudioInputStream::AddHistogramsForFailedStartup() { |
| 1331 DCHECK(thread_checker_.CalledOnValidThread()); | 1201 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1332 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredMac", | 1202 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputStartWasDeferredMac", |
| 1333 start_was_deferred_); | 1203 start_was_deferred_); |
| 1334 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac", | 1204 UMA_HISTOGRAM_BOOLEAN("Media.Audio.InputBufferSizeWasChangedMac", |
| 1335 buffer_size_was_changed_); | 1205 buffer_size_was_changed_); |
| 1336 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfOutputStreamsMac", | 1206 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfOutputStreamsMac", |
| 1337 manager_->output_streams()); | 1207 manager_->output_streams()); |
| 1338 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfLowLatencyInputStreamsMac", | 1208 UMA_HISTOGRAM_COUNTS_1000("Media.Audio.NumberOfLowLatencyInputStreamsMac", |
| 1339 manager_->low_latency_input_streams()); | 1209 manager_->low_latency_input_streams()); |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1668 | 1538 |
| 1669 number_of_frames_provided_ = 0; | 1539 number_of_frames_provided_ = 0; |
| 1670 glitches_detected_ = 0; | 1540 glitches_detected_ = 0; |
| 1671 last_sample_time_ = 0; | 1541 last_sample_time_ = 0; |
| 1672 last_number_of_frames_ = 0; | 1542 last_number_of_frames_ = 0; |
| 1673 total_lost_frames_ = 0; | 1543 total_lost_frames_ = 0; |
| 1674 largest_glitch_frames_ = 0; | 1544 largest_glitch_frames_ = 0; |
| 1675 } | 1545 } |
| 1676 | 1546 |
| 1677 } // namespace media | 1547 } // namespace media |
| OLD | NEW |