Commit 21b62413 authored by Marcin Radomski's avatar Marcin Radomski Committed by Commit Bot

Make StreamMixer report input/output underruns back to audio source

- Add MixerUnderrun proto for reporting mixer input/output underruns
  to OutputStreamConnections
- Add MixerInput::Source::OnOutputUnderrun virtual method, called on
  every mixer input whenever StreamMixer detects an output underrun
- Add OutputStreamConnection::Delegate::OnMixerUnderrun virtual method,
  called in response to MixerUnderrun proto

      simulated poor network conditions

Bug: b/142084721
Test: manual verification of values logged from RecordUnderruns() in
Merge-With: eureka-internal/350603
Change-Id: I6ad4dcdd573a5a6b9d89baf5f15abac0a109ee92
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1992068Reviewed-by: default avatarKenneth MacKay <kmackay@chromium.org>
Commit-Queue: Marcin Radomski <dextero@google.com>
Cr-Commit-Position: refs/heads/master@{#732432}
parent 6cb0b3c0
...@@ -208,6 +208,17 @@ message Error { ...@@ -208,6 +208,17 @@ message Error {
optional string message = 2; optional string message = 2;
} }
message MixerUnderrun {
// Keep in sync with OutputStreamConnection::Delegate::MixerUnderrunType
enum Type {
// An underrun was detected on mixer input.
INPUT_UNDERRUN = 0;
// An underrun was detected on mixer output.
OUTPUT_UNDERRUN = 1;
};
optional Type type = 1;
}
message Generic { message Generic {
optional OutputStreamParams output_stream_params = 1; optional OutputStreamParams output_stream_params = 1;
optional SetStartTimestamp set_start_timestamp = 2; optional SetStartTimestamp set_start_timestamp = 2;
...@@ -232,4 +243,5 @@ message Generic { ...@@ -232,4 +243,5 @@ message Generic {
optional NumOutputChannels set_num_output_channels = 21; optional NumOutputChannels set_num_output_channels = 21;
optional StreamInterruption stream_interruption = 22; optional StreamInterruption stream_interruption = 22;
optional SetAudioClockRate set_audio_clock_rate = 23; optional SetAudioClockRate set_audio_clock_rate = 23;
optional MixerUnderrun mixer_underrun = 24;
} }
...@@ -198,6 +198,11 @@ bool OutputStreamConnection::HandleMetadata(const Generic& message) { ...@@ -198,6 +198,11 @@ bool OutputStreamConnection::HandleMetadata(const Generic& message) {
if (message.has_error()) { if (message.has_error()) {
delegate_->OnMixerError(); delegate_->OnMixerError();
} }
if (message.has_mixer_underrun()) {
delegate_->OnMixerUnderrun(static_cast<Delegate::MixerUnderrunType>(
message.mixer_underrun().type()));
}
return true; return true;
} }
......
...@@ -29,6 +29,14 @@ class OutputStreamConnection : public MixerConnection, ...@@ -29,6 +29,14 @@ class OutputStreamConnection : public MixerConnection,
public: public:
class Delegate { class Delegate {
public: public:
// Keep in sync with mixer_service.proto:MixerUnderrun.Type
enum class MixerUnderrunType {
// An underrun was detected on mixer input.
kStream = 0,
// An underrun was detected on mixer output.
kMixer = 1,
};
// Called to fill more audio data. The implementation should write up to // Called to fill more audio data. The implementation should write up to
// |frames| frames of audio data into |buffer|, and then call // |frames| frames of audio data into |buffer|, and then call
// SendNextBuffer() with the actual number of frames that were filled (or // SendNextBuffer() with the actual number of frames that were filled (or
...@@ -52,6 +60,9 @@ class OutputStreamConnection : public MixerConnection, ...@@ -52,6 +60,9 @@ class OutputStreamConnection : public MixerConnection,
// longer be played out. // longer be played out.
virtual void OnMixerError() {} virtual void OnMixerError() {}
// Called when an underrun happens on mixer input/output.
virtual void OnMixerUnderrun(MixerUnderrunType type) {}
protected: protected:
virtual ~Delegate() = default; virtual ~Delegate() = default;
}; };
......
...@@ -78,6 +78,9 @@ class MixerInput { ...@@ -78,6 +78,9 @@ class MixerInput {
// source. // source.
virtual void OnAudioPlaybackError(MixerError error) = 0; virtual void OnAudioPlaybackError(MixerError error) = 0;
// Called when an underrun error occurs on mixer output.
virtual void OnOutputUnderrun() {}
// Called when the mixer has finished removing this input. The source may be // Called when the mixer has finished removing this input. The source may be
// deleted at this point. // deleted at this point.
virtual void FinalizeAudioPlayback() = 0; virtual void FinalizeAudioPlayback() = 0;
......
...@@ -209,6 +209,8 @@ MixerInputConnection::MixerInputConnection( ...@@ -209,6 +209,8 @@ MixerInputConnection::MixerInputConnection(
eos_task_ = base::BindRepeating(&MixerInputConnection::PostEos, weak_this_); eos_task_ = base::BindRepeating(&MixerInputConnection::PostEos, weak_this_);
ready_for_playback_task_ = base::BindRepeating( ready_for_playback_task_ = base::BindRepeating(
&MixerInputConnection::PostAudioReadyForPlayback, weak_this_); &MixerInputConnection::PostAudioReadyForPlayback, weak_this_);
post_stream_underrun_task_ = base::BindRepeating(
&MixerInputConnection::PostStreamUnderrun, weak_this_);
CreateBufferPool(fill_size_); CreateBufferPool(fill_size_);
mixer_->AddInput(this); mixer_->AddInput(this);
...@@ -755,6 +757,7 @@ int MixerInputConnection::FillAudioPlaybackFrames( ...@@ -755,6 +757,7 @@ int MixerInputConnection::FillAudioPlaybackFrames(
int filled = 0; int filled = 0;
bool queued_more_data = false; bool queued_more_data = false;
bool signal_eos = false; bool signal_eos = false;
bool underrun = false;
bool remove_self = false; bool remove_self = false;
{ {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
...@@ -778,6 +781,7 @@ int MixerInputConnection::FillAudioPlaybackFrames( ...@@ -778,6 +781,7 @@ int MixerInputConnection::FillAudioPlaybackFrames(
if (state_ == State::kNormalPlayback && !can_complete_fill) { if (state_ == State::kNormalPlayback && !can_complete_fill) {
LOG_IF(INFO, !zero_fader_frames_) << "Stream underrun for " << this; LOG_IF(INFO, !zero_fader_frames_) << "Stream underrun for " << this;
zero_fader_frames_ = true; zero_fader_frames_ = true;
underrun = true;
} else { } else {
LOG_IF(INFO, started_ && zero_fader_frames_) LOG_IF(INFO, started_ && zero_fader_frames_)
<< "Stream underrun recovered for " << this; << "Stream underrun recovered for " << this;
...@@ -839,6 +843,9 @@ int MixerInputConnection::FillAudioPlaybackFrames( ...@@ -839,6 +843,9 @@ int MixerInputConnection::FillAudioPlaybackFrames(
if (signal_eos) { if (signal_eos) {
io_task_runner_->PostTask(FROM_HERE, eos_task_); io_task_runner_->PostTask(FROM_HERE, eos_task_);
} }
if (underrun) {
io_task_runner_->PostTask(FROM_HERE, post_stream_underrun_task_);
}
if (remove_self) { if (remove_self) {
mixer_->RemoveInput(this); mixer_->RemoveInput(this);
...@@ -1012,6 +1019,22 @@ void MixerInputConnection::PostAudioReadyForPlayback() { ...@@ -1012,6 +1019,22 @@ void MixerInputConnection::PostAudioReadyForPlayback() {
audio_ready_for_playback_fired_ = true; audio_ready_for_playback_fired_ = true;
} }
void MixerInputConnection::PostStreamUnderrun() {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
mixer_service::Generic message;
message.mutable_mixer_underrun()->set_type(
mixer_service::MixerUnderrun::INPUT_UNDERRUN);
socket_->SendProto(message);
}
void MixerInputConnection::PostOutputUnderrun() {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
mixer_service::Generic message;
message.mutable_mixer_underrun()->set_type(
mixer_service::MixerUnderrun::OUTPUT_UNDERRUN);
socket_->SendProto(message);
}
void MixerInputConnection::OnAudioPlaybackError(MixerError error) { void MixerInputConnection::OnAudioPlaybackError(MixerError error) {
if (error == MixerError::kInputIgnored) { if (error == MixerError::kInputIgnored) {
LOG(INFO) << "Mixer input " << this LOG(INFO) << "Mixer input " << this
...@@ -1029,6 +1052,12 @@ void MixerInputConnection::OnAudioPlaybackError(MixerError error) { ...@@ -1029,6 +1052,12 @@ void MixerInputConnection::OnAudioPlaybackError(MixerError error) {
} }
} }
void MixerInputConnection::OnOutputUnderrun() {
io_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&MixerInputConnection::PostOutputUnderrun, weak_this_));
}
void MixerInputConnection::PostError(MixerError error) { void MixerInputConnection::PostError(MixerError error) {
DCHECK(io_task_runner_->RunsTasksInCurrentSequence()); DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
mixer_service::Generic message; mixer_service::Generic message;
......
...@@ -111,6 +111,7 @@ class MixerInputConnection : public mixer_service::MixerSocket::Delegate, ...@@ -111,6 +111,7 @@ class MixerInputConnection : public mixer_service::MixerSocket::Delegate,
RenderingDelay rendering_delay, RenderingDelay rendering_delay,
::media::AudioBus* buffer) override; ::media::AudioBus* buffer) override;
void OnAudioPlaybackError(MixerError error) override; void OnAudioPlaybackError(MixerError error) override;
void OnOutputUnderrun() override;
void FinalizeAudioPlayback() override; void FinalizeAudioPlayback() override;
// AudioProvider implementation: // AudioProvider implementation:
...@@ -131,6 +132,8 @@ class MixerInputConnection : public mixer_service::MixerSocket::Delegate, ...@@ -131,6 +132,8 @@ class MixerInputConnection : public mixer_service::MixerSocket::Delegate,
void PostPcmCompletion(); void PostPcmCompletion();
void PostEos(); void PostEos();
void PostError(MixerError error); void PostError(MixerError error);
void PostStreamUnderrun();
void PostOutputUnderrun();
void PostAudioReadyForPlayback(); void PostAudioReadyForPlayback();
void DropAudio(int64_t frames) EXCLUSIVE_LOCKS_REQUIRED(lock_); void DropAudio(int64_t frames) EXCLUSIVE_LOCKS_REQUIRED(lock_);
void CheckAndStartPlaybackIfNecessary(int num_frames, void CheckAndStartPlaybackIfNecessary(int num_frames,
...@@ -208,6 +211,7 @@ class MixerInputConnection : public mixer_service::MixerSocket::Delegate, ...@@ -208,6 +211,7 @@ class MixerInputConnection : public mixer_service::MixerSocket::Delegate,
base::RepeatingClosure pcm_completion_task_; base::RepeatingClosure pcm_completion_task_;
base::RepeatingClosure eos_task_; base::RepeatingClosure eos_task_;
base::RepeatingClosure ready_for_playback_task_; base::RepeatingClosure ready_for_playback_task_;
base::RepeatingClosure post_stream_underrun_task_;
base::WeakPtr<MixerInputConnection> weak_this_; base::WeakPtr<MixerInputConnection> weak_this_;
base::WeakPtrFactory<MixerInputConnection> weak_factory_; base::WeakPtrFactory<MixerInputConnection> weak_factory_;
......
...@@ -824,6 +824,10 @@ void StreamMixer::WriteMixedPcm(int frames, int64_t expected_playback_time) { ...@@ -824,6 +824,10 @@ void StreamMixer::WriteMixedPcm(int frames, int64_t expected_playback_time) {
if (playback_interrupted) { if (playback_interrupted) {
loopback_handler_->SendInterrupt(LoopbackInterruptReason::kUnderrun); loopback_handler_->SendInterrupt(LoopbackInterruptReason::kUnderrun);
for (const auto& input : inputs_) {
input.first->OnOutputUnderrun();
}
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment