Chromium Code Reviews| Index: media/audio/audio_input_device.cc |
| diff --git a/media/audio/audio_input_device.cc b/media/audio/audio_input_device.cc |
| index 3b83deb10e3475b9aee2c1415b73a0e00e7f718a..bbefb2f92b5b8b109bca3e567826618fe610abee 100644 |
| --- a/media/audio/audio_input_device.cc |
| +++ b/media/audio/audio_input_device.cc |
| @@ -8,7 +8,9 @@ |
| #include <utility> |
| #include "base/bind.h" |
| +#include "base/callback_forward.h" |
| #include "base/macros.h" |
| +#include "base/metrics/histogram_macros.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/time/time.h" |
| @@ -18,11 +20,29 @@ |
| namespace media { |
| +namespace { |
| + |
| // The number of shared memory buffer segments indicated to browser process |
| // in order to avoid data overwriting. This number can be any positive number, |
| // dependent how fast the renderer process can pick up captured data from |
| // shared memory. |
| -static const int kRequestedSharedMemoryCount = 10; |
| +const int kRequestedSharedMemoryCount = 10; |
| + |
| +// The number of seconds with missing callbacks before we report a capture |
| +// error. The value is based on that the Mac audio implementation can defer |
| +// start for 5 seconds when resuming after standby, and has a startup success |
| +// check 5 seconds after actually starting, where stats is logged. We must allow |
| +// enough time for this. See AUAudioInputStream::CheckInputStartupSuccess(). |
| +const int kMissingCallbacksTimeBeforeErrorSeconds = 12; |
| + |
| +// The interval for checking missing callbacks. |
| +const int kCheckMissingCallbacksIntervalSeconds = 5; |
| + |
| +// How often AudioInputDevice::AudioThreadCallback informs that it has gotten |
| +// data from the source. |
| +const int kGotDataCallbackIntervalSeconds = 1; |
| + |
| +} // namespace |
| // Takes care of invoking the capture callback on the audio thread. |
| // An instance of this class is created for each capture stream in |
| @@ -34,7 +54,8 @@ class AudioInputDevice::AudioThreadCallback |
| base::SharedMemoryHandle memory, |
| int memory_length, |
| int total_segments, |
| - CaptureCallback* capture_callback); |
| + CaptureCallback* capture_callback, |
| + base::RepeatingClosure got_data_callback); |
| ~AudioThreadCallback() override; |
| void MapSharedMemory() override; |
| @@ -49,6 +70,14 @@ class AudioInputDevice::AudioThreadCallback |
| std::vector<std::unique_ptr<media::AudioBus>> audio_buses_; |
| CaptureCallback* capture_callback_; |
| + // Used for informing AudioInputDevice that we have gotten data, i.e. the |
| + // stream is alive. |got_data_callback_| is run every |
| + // |got_data_callback_interval_in_frames_| frames, calculated from |
| + // kGotDataCallbackIntervalSeconds. |
| + const int got_data_callback_interval_in_frames_; |
| + int frames_since_last_got_data_callback_; |
| + base::RepeatingClosure got_data_callback_; |
| + |
| DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
| }; |
| @@ -61,7 +90,8 @@ AudioInputDevice::AudioInputDevice( |
| state_(IDLE), |
| session_id_(0), |
| agc_is_enabled_(false), |
| - stopping_hack_(false) { |
| + stopping_hack_(false), |
| + missing_callbacks_detected_(false) { |
| CHECK(ipc_); |
| // The correctness of the code depends on the relative values assigned in the |
| @@ -146,12 +176,21 @@ void AudioInputDevice::OnStreamCreated( |
| DCHECK(!audio_callback_); |
| DCHECK(!audio_thread_); |
| audio_callback_.reset(new AudioInputDevice::AudioThreadCallback( |
| - audio_parameters_, handle, length, total_segments, callback_)); |
| + audio_parameters_, handle, length, total_segments, callback_, |
| + base::BindRepeating(&AudioInputDevice::SetLastCallbackTimeToNow, this))); |
| audio_thread_.reset(new AudioDeviceThread(audio_callback_.get(), |
| socket_handle, "AudioInputDevice")); |
| state_ = RECORDING; |
| ipc_->RecordStream(); |
| + |
| + // Start detecting missing callbacks. |
| + SetLastCallbackTimeToNowOnIOThread(); |
| + check_alive_timer_.Start( |
| + FROM_HERE, |
| + base::TimeDelta::FromSeconds(kCheckMissingCallbacksIntervalSeconds), this, |
| + &AudioInputDevice::CheckIfInputStreamIsAlive); |
| + DCHECK(check_alive_timer_.IsRunning()); |
| } |
| void AudioInputDevice::OnError() { |
| @@ -222,6 +261,12 @@ void AudioInputDevice::StartUpOnIOThread() { |
| void AudioInputDevice::ShutDownOnIOThread() { |
| DCHECK(task_runner()->BelongsToCurrentThread()); |
| + check_alive_timer_.Stop(); |
| + |
| + UMA_HISTOGRAM_BOOLEAN("Media.Audio.Capture.DetectedMissingCallbacks", |
| + missing_callbacks_detected_); |
| + missing_callbacks_detected_ = false; |
| + |
| // Close the stream, if we haven't already. |
| if (state_ >= CREATING_STREAM) { |
| ipc_->CloseStream(); |
| @@ -268,13 +313,33 @@ void AudioInputDevice::WillDestroyCurrentMessageLoop() { |
| ShutDownOnIOThread(); |
| } |
| +void AudioInputDevice::CheckIfInputStreamIsAlive() { |
| + DCHECK(task_runner()->BelongsToCurrentThread()); |
| + if (base::TimeTicks::Now() - last_callback_time_ > |
|
ossu-chromium
2017/05/24 15:52:00
nit: Should probably be >=, but I'm not going to e
Henrik Grunell
2017/05/24 17:18:10
Maybe so, maybe so. Everything in this mechanism i
|
| + base::TimeDelta::FromSeconds(kMissingCallbacksTimeBeforeErrorSeconds)) { |
| + callback_->OnCaptureError("No audio received from audio capture device."); |
| + missing_callbacks_detected_ = true; |
| + } |
| +} |
| + |
| +void AudioInputDevice::SetLastCallbackTimeToNow() { |
| + task_runner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&AudioInputDevice::SetLastCallbackTimeToNowOnIOThread, this)); |
| +} |
| + |
| +void AudioInputDevice::SetLastCallbackTimeToNowOnIOThread() { |
| + last_callback_time_ = base::TimeTicks::Now(); |
| +} |
| + |
| // AudioInputDevice::AudioThreadCallback |
| AudioInputDevice::AudioThreadCallback::AudioThreadCallback( |
| const AudioParameters& audio_parameters, |
| base::SharedMemoryHandle memory, |
| int memory_length, |
| int total_segments, |
| - CaptureCallback* capture_callback) |
| + CaptureCallback* capture_callback, |
| + base::RepeatingClosure got_data_callback_) |
| : AudioDeviceThread::Callback(audio_parameters, |
| memory, |
| memory_length, |
| @@ -283,7 +348,11 @@ AudioInputDevice::AudioThreadCallback::AudioThreadCallback( |
| base::Time::kMillisecondsPerSecond), |
| current_segment_id_(0), |
| last_buffer_id_(UINT32_MAX), |
| - capture_callback_(capture_callback) {} |
| + capture_callback_(capture_callback), |
| + got_data_callback_interval_in_frames_(kGotDataCallbackIntervalSeconds * |
| + audio_parameters.sample_rate()), |
| + frames_since_last_got_data_callback_(0), |
| + got_data_callback_(std::move(got_data_callback_)) {} |
| AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { |
| } |
| @@ -341,6 +410,14 @@ void AudioInputDevice::AudioThreadCallback::Process(uint32_t pending_data) { |
| // Use pre-allocated audio bus wrapping existing block of shared memory. |
| media::AudioBus* audio_bus = audio_buses_[current_segment_id_].get(); |
| + // Regularly inform that we have gotten data. |
| + frames_since_last_got_data_callback_ += audio_bus->frames(); |
| + if (frames_since_last_got_data_callback_ >= |
| + got_data_callback_interval_in_frames_) { |
| + got_data_callback_.Run(); |
| + frames_since_last_got_data_callback_ = 0; |
| + } |
| + |
| // Deliver captured data to the client in floating point format and update |
| // the audio delay measurement. |
| capture_callback_->Capture( |