Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(603)

Unified Diff: media/audio/audio_input_device.cc

Issue 2888383002: Stop source and fire MediaStreamTrack ended event if missing audio input callbacks are detected. (Closed)
Patch Set: Code review (ossu@ and tommi@). Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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(

Powered by Google App Engine
This is Rietveld 408576698