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..66966381cdcc46ab9f264d3ac2db7ad854bf4d90 100644 |
--- a/media/audio/audio_input_device.cc |
+++ b/media/audio/audio_input_device.cc |
@@ -8,21 +8,41 @@ |
#include <utility> |
#include "base/bind.h" |
+#include "base/callback_forward.h" |
#include "base/macros.h" |
+#include "base/memory/ptr_util.h" |
+#include "base/metrics/histogram_macros.h" |
#include "base/strings/stringprintf.h" |
#include "base/threading/thread_restrictions.h" |
-#include "base/time/time.h" |
#include "build/build_config.h" |
#include "media/audio/audio_manager_base.h" |
#include "media/base/audio_bus.h" |
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,23 @@ 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(); |
+ DCHECK(!check_alive_timer_); |
+ check_alive_timer_ = base::MakeUnique<base::RepeatingTimer>(); |
+ check_alive_timer_->Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(kCheckMissingCallbacksIntervalSeconds), this, |
+ &AudioInputDevice::CheckIfInputStreamIsAlive); |
+ DCHECK(check_alive_timer_->IsRunning()); |
} |
void AudioInputDevice::OnError() { |
@@ -222,6 +263,15 @@ void AudioInputDevice::StartUpOnIOThread() { |
void AudioInputDevice::ShutDownOnIOThread() { |
DCHECK(task_runner()->BelongsToCurrentThread()); |
+ if (check_alive_timer_) { |
+ check_alive_timer_->Stop(); |
+ check_alive_timer_.reset(); |
+ } |
+ |
+ 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 +318,33 @@ void AudioInputDevice::WillDestroyCurrentMessageLoop() { |
ShutDownOnIOThread(); |
} |
+void AudioInputDevice::CheckIfInputStreamIsAlive() { |
+ DCHECK(task_runner()->BelongsToCurrentThread()); |
+ if (base::TimeTicks::Now() - last_callback_time_ > |
+ 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 +353,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 +415,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( |