Commit 128d4999 authored by kmackay's avatar kmackay Committed by Commit bot

[Chromecast] Fade in after a mixer input stream has skipped

This reduces audio glitches after a stream has skipped (due to
the stream not having enough data).

BUG= internal b/30747742

Review-Url: https://codereview.chromium.org/2344993003
Cr-Commit-Position: refs/heads/master@{#419084}
parent 77b028cf
...@@ -785,6 +785,7 @@ bool StreamMixerAlsa::TryWriteFrames() { ...@@ -785,6 +785,7 @@ bool StreamMixerAlsa::TryWriteFrames() {
frames_in_buffer < min_frames_in_buffer) { frames_in_buffer < min_frames_in_buffer) {
// If there has been (or soon will be) an underrun, continue without the // If there has been (or soon will be) an underrun, continue without the
// empty primary input stream. // empty primary input stream.
input->OnSkipped();
continue; continue;
} }
...@@ -793,16 +794,13 @@ bool StreamMixerAlsa::TryWriteFrames() { ...@@ -793,16 +794,13 @@ bool StreamMixerAlsa::TryWriteFrames() {
FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2), FROM_HERE, base::TimeDelta::FromMilliseconds(kMinBufferedDataMs / 2),
base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this)));
return false; return false;
} else {
input->OnSkipped();
} }
} }
if (active_inputs.empty()) { if (active_inputs.empty()) {
// No inputs have any data to provide. // No inputs have any data to provide. Fill with silence to avoid underrun.
if (!inputs_.empty()) {
return false; // If there are some inputs, don't fill with silence.
}
// If we have no inputs, fill with silence to avoid underrun.
chunk_size = kPreventUnderrunChunkSize; chunk_size = kPreventUnderrunChunkSize;
if (!mixed_ || mixed_->frames() < chunk_size) { if (!mixed_ || mixed_->frames() < chunk_size) {
mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size);
......
...@@ -93,6 +93,11 @@ class StreamMixerAlsa { ...@@ -93,6 +93,11 @@ class StreamMixerAlsa {
// MaxReadSize(), and |dest->frames()| shall be >= |frames|. // MaxReadSize(), and |dest->frames()| shall be >= |frames|.
virtual void GetResampledData(::media::AudioBus* dest, int frames) = 0; virtual void GetResampledData(::media::AudioBus* dest, int frames) = 0;
// Called when this input has been skipped for output due to not having any
// data available. This indicates that there will be a gap in the playback
// from this stream.
virtual void OnSkipped() = 0;
// This is called for every InputQueue when the mixer writes data to ALSA // This is called for every InputQueue when the mixer writes data to ALSA
// for any of its input streams. // for any of its input streams.
virtual void AfterWriteFrames( virtual void AfterWriteFrames(
......
...@@ -235,6 +235,14 @@ void StreamMixerAlsaInputImpl::DidQueueData(bool end_of_stream) { ...@@ -235,6 +235,14 @@ void StreamMixerAlsaInputImpl::DidQueueData(bool end_of_stream) {
mixer_->OnFramesQueued(); mixer_->OnFramesQueued();
} }
void StreamMixerAlsaInputImpl::OnSkipped() {
DCHECK(mixer_task_runner_->BelongsToCurrentThread());
if (state_ == kStateNormalPlayback) {
// Fade in once this input starts providing data again.
fade_frames_remaining_ = NormalFadeFrames();
}
}
void StreamMixerAlsaInputImpl::AfterWriteFrames( void StreamMixerAlsaInputImpl::AfterWriteFrames(
const MediaPipelineBackendAlsa::RenderingDelay& mixer_rendering_delay) { const MediaPipelineBackendAlsa::RenderingDelay& mixer_rendering_delay) {
DCHECK(mixer_task_runner_->BelongsToCurrentThread()); DCHECK(mixer_task_runner_->BelongsToCurrentThread());
......
...@@ -120,6 +120,7 @@ class StreamMixerAlsaInputImpl : public StreamMixerAlsa::InputQueue { ...@@ -120,6 +120,7 @@ class StreamMixerAlsaInputImpl : public StreamMixerAlsa::InputQueue {
mixer_rendering_delay) override; mixer_rendering_delay) override;
int MaxReadSize() override; int MaxReadSize() override;
void GetResampledData(::media::AudioBus* dest, int frames) override; void GetResampledData(::media::AudioBus* dest, int frames) override;
void OnSkipped() override;
void AfterWriteFrames(const MediaPipelineBackendAlsa::RenderingDelay& void AfterWriteFrames(const MediaPipelineBackendAlsa::RenderingDelay&
mixer_rendering_delay) override; mixer_rendering_delay) override;
void SignalError(StreamMixerAlsaInput::MixerError error) override; void SignalError(StreamMixerAlsaInput::MixerError error) override;
......
...@@ -151,6 +151,7 @@ class MockInputQueue : public StreamMixerAlsa::InputQueue { ...@@ -151,6 +151,7 @@ class MockInputQueue : public StreamMixerAlsa::InputQueue {
mixer_rendering_delay)); mixer_rendering_delay));
int MaxReadSize() override { return max_read_size_; } int MaxReadSize() override { return max_read_size_; }
MOCK_METHOD2(GetResampledData, void(::media::AudioBus* dest, int frames)); MOCK_METHOD2(GetResampledData, void(::media::AudioBus* dest, int frames));
MOCK_METHOD0(OnSkipped, void());
MOCK_METHOD1(AfterWriteFrames, MOCK_METHOD1(AfterWriteFrames,
void(const MediaPipelineBackendAlsa::RenderingDelay& void(const MediaPipelineBackendAlsa::RenderingDelay&
mixer_rendering_delay)); mixer_rendering_delay));
...@@ -376,6 +377,7 @@ TEST_F(StreamMixerAlsaTest, WriteFrames) { ...@@ -376,6 +377,7 @@ TEST_F(StreamMixerAlsaTest, WriteFrames) {
// to ALSA. // to ALSA.
inputs[1]->SetPrimary(false); inputs[1]->SetPrimary(false);
inputs[1]->SetMaxReadSize(0); inputs[1]->SetMaxReadSize(0);
EXPECT_CALL(*inputs[1], OnSkipped());
inputs[2]->SetPrimary(false); inputs[2]->SetPrimary(false);
for (auto* input : inputs) { for (auto* input : inputs) {
if (input != inputs[1]) if (input != inputs[1])
...@@ -654,6 +656,7 @@ TEST_F(StreamMixerAlsaTest, StuckStreamWithUnderrun) { ...@@ -654,6 +656,7 @@ TEST_F(StreamMixerAlsaTest, StuckStreamWithUnderrun) {
EXPECT_CALL(*inputs[0], GetResampledData(_, kNumFrames)); EXPECT_CALL(*inputs[0], GetResampledData(_, kNumFrames));
EXPECT_CALL(*inputs[0], AfterWriteFrames(_)); EXPECT_CALL(*inputs[0], AfterWriteFrames(_));
EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0); EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0);
EXPECT_CALL(*inputs[1], OnSkipped());
EXPECT_CALL(*inputs[1], AfterWriteFrames(_)); EXPECT_CALL(*inputs[1], AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1); EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1);
...@@ -688,6 +691,7 @@ TEST_F(StreamMixerAlsaTest, StuckStreamWithLowBuffer) { ...@@ -688,6 +691,7 @@ TEST_F(StreamMixerAlsaTest, StuckStreamWithLowBuffer) {
EXPECT_CALL(*inputs[0], GetResampledData(_, kNumFrames)); EXPECT_CALL(*inputs[0], GetResampledData(_, kNumFrames));
EXPECT_CALL(*inputs[0], AfterWriteFrames(_)); EXPECT_CALL(*inputs[0], AfterWriteFrames(_));
EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0); EXPECT_CALL(*inputs[1], GetResampledData(_, _)).Times(0);
EXPECT_CALL(*inputs[1], OnSkipped());
EXPECT_CALL(*inputs[1], AfterWriteFrames(_)); EXPECT_CALL(*inputs[1], AfterWriteFrames(_));
EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1); EXPECT_CALL(*mock_alsa(), PcmWritei(_, _, kNumFrames)).Times(1);
......
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