| 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(
|
|
|