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..3209691529741a7108ad6b3c0b14631ef3009e03 100644 |
--- a/media/audio/audio_input_device.cc |
+++ b/media/audio/audio_input_device.cc |
@@ -8,21 +8,36 @@ |
#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" |
#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 has a restart |
+// mechanism that triggers after 10-15 seconds of missing callbacks. We must |
+// allow enough time for that restart. See |
+// AUAudioInputStream::CheckIfInputStreamIsAlive(). |
+const int kMissingCallbacksTimeBeforeErrorSeconds = 20; |
+ |
+// The interval for checking missing callbacks. |
+const int kCheckMissingCallbacksIntervalSeconds = 5; |
+ |
+} // namespace |
// Takes care of invoking the capture callback on the audio thread. |
// An instance of this class is created for each capture stream in |
@@ -30,11 +45,15 @@ static const int kRequestedSharedMemoryCount = 10; |
class AudioInputDevice::AudioThreadCallback |
: public AudioDeviceThread::Callback { |
public: |
+ using GotDataCallback = |
+ base::RepeatingCallback<void(base::TimeTicks got_data_time)>; |
+ |
AudioThreadCallback(const AudioParameters& audio_parameters, |
base::SharedMemoryHandle memory, |
int memory_length, |
int total_segments, |
- CaptureCallback* capture_callback); |
+ CaptureCallback* capture_callback, |
+ GotDataCallback got_data_callback); |
~AudioThreadCallback() override; |
void MapSharedMemory() override; |
@@ -48,6 +67,7 @@ class AudioInputDevice::AudioThreadCallback |
uint32_t last_buffer_id_; |
std::vector<std::unique_ptr<media::AudioBus>> audio_buses_; |
CaptureCallback* capture_callback_; |
+ GotDataCallback got_data_callback_; |
DISALLOW_COPY_AND_ASSIGN(AudioThreadCallback); |
}; |
@@ -61,7 +81,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 +167,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::SetLastCallbackTime, this))); |
audio_thread_.reset(new AudioDeviceThread(audio_callback_.get(), |
socket_handle, "AudioInputDevice")); |
state_ = RECORDING; |
ipc_->RecordStream(); |
+ |
+ // Start detecting missing callbacks. |
+ last_callback_time_ = base::TimeTicks::Now(); |
+ check_alive_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromSeconds(kCheckMissingCallbacksIntervalSeconds), this, |
+ &AudioInputDevice::CheckIfInputStreamIsAlive); |
+ DCHECK(check_alive_timer_.IsRunning()); |
} |
void AudioInputDevice::OnError() { |
@@ -222,6 +252,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 +304,37 @@ void AudioInputDevice::WillDestroyCurrentMessageLoop() { |
ShutDownOnIOThread(); |
} |
+void AudioInputDevice::CheckIfInputStreamIsAlive() { |
+ DCHECK(task_runner()->BelongsToCurrentThread()); |
+ base::TimeDelta time_since_last_callback = |
+ base::TimeTicks::Now() - last_callback_time_; |
+ if (time_since_last_callback > |
+ base::TimeDelta::FromSeconds(kMissingCallbacksTimeBeforeErrorSeconds)) { |
+ callback_->OnCaptureError("No audio received from audio capture device."); |
+ missing_callbacks_detected_ = true; |
+ } |
+} |
+ |
+void AudioInputDevice::SetLastCallbackTime(base::TimeTicks last_callback_time) { |
+ task_runner()->PostTask( |
+ FROM_HERE, base::Bind(&AudioInputDevice::SetLastCallbackTimeOnIOThread, |
+ this, last_callback_time)); |
+} |
+ |
+void AudioInputDevice::SetLastCallbackTimeOnIOThread( |
+ base::TimeTicks last_callback_time) { |
+ DCHECK(task_runner()->BelongsToCurrentThread()); |
+ last_callback_time_ = last_callback_time; |
+} |
+ |
// AudioInputDevice::AudioThreadCallback |
AudioInputDevice::AudioThreadCallback::AudioThreadCallback( |
const AudioParameters& audio_parameters, |
base::SharedMemoryHandle memory, |
int memory_length, |
int total_segments, |
- CaptureCallback* capture_callback) |
+ CaptureCallback* capture_callback, |
+ GotDataCallback got_data_callback_) |
: AudioDeviceThread::Callback(audio_parameters, |
memory, |
memory_length, |
@@ -283,7 +343,8 @@ 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_(std::move(got_data_callback_)) {} |
AudioInputDevice::AudioThreadCallback::~AudioThreadCallback() { |
} |
@@ -341,6 +402,9 @@ 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(); |
+ // Inform that we have gotten data. |
+ got_data_callback_.Run(base::TimeTicks::Now()); |
+ |
// Deliver captured data to the client in floating point format and update |
// the audio delay measurement. |
capture_callback_->Capture( |