| 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..24d67b8a4d5319145ec11413849026889427e141 100644
|
| --- a/media/audio/audio_input_device.cc
|
| +++ b/media/audio/audio_input_device.cc
|
| @@ -8,21 +8,40 @@
|
| #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 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 +53,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 +69,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 +89,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 +175,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 +260,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 +312,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 +347,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 +409,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(
|
|
|