Commit 9e590696 authored by Raman Mazurkevich's avatar Raman Mazurkevich Committed by Commit Bot

[Chromecast] Add recovering from suspended state after PcmWritei

 - For devices, which support power management ALSA can switch audio
 outputs to suspended state. In this case PcmWritei is returning -EBADFD
 error.

Bug: internal b/129408282

Change-Id: I5840eb943b0ff54e577084502771b0e2d97a8cfa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1562074Reviewed-by: default avatarKenneth MacKay <kmackay@chromium.org>
Reviewed-by: default avatarMax Morin <maxmorin@chromium.org>
Commit-Queue: Steven Zhu <jz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#649852}
parent af203e12
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "chromecast/base/chromecast_switches.h" #include "chromecast/base/chromecast_switches.h"
#include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h"
...@@ -104,6 +105,12 @@ constexpr unsigned int kFallbackSampleRateHiRes = 96000; ...@@ -104,6 +105,12 @@ constexpr unsigned int kFallbackSampleRateHiRes = 96000;
// the direction explicitly. // the direction explicitly.
constexpr int* kAlsaDirDontCare = nullptr; constexpr int* kAlsaDirDontCare = nullptr;
// The snd_pcm_resume function can return EAGAIN error code, so call should be
// retried. Below constants define retries params.
constexpr int kRestoreAfterSuspensionAttempts = 10;
constexpr base::TimeDelta kRestoreAfterSuspensionAttemptDelay =
base::TimeDelta::FromMilliseconds(20);
// These sample formats will be tried in order. 32 bit samples is ideal, but // These sample formats will be tried in order. 32 bit samples is ideal, but
// some devices do not support 32 bit samples. // some devices do not support 32 bit samples.
constexpr snd_pcm_format_t kPreferredSampleFormats[] = { constexpr snd_pcm_format_t kPreferredSampleFormats[] = {
...@@ -226,6 +233,11 @@ bool MixerOutputStreamAlsa::Write(const float* data, ...@@ -226,6 +233,11 @@ bool MixerOutputStreamAlsa::Write(const float* data,
while ((frames_or_error = while ((frames_or_error =
alsa_->PcmWritei(pcm_, output_data, frames_left)) < 0) { alsa_->PcmWritei(pcm_, output_data, frames_left)) < 0) {
*out_playback_interrupted = true; *out_playback_interrupted = true;
if (frames_or_error == -EBADFD &&
MaybeRecoverDeviceFromSuspendedState()) {
// Write data again, if recovered.
continue;
}
RETURN_FALSE_ON_ERROR(PcmRecover, pcm_, frames_or_error, RETURN_FALSE_ON_ERROR(PcmRecover, pcm_, frames_or_error,
kPcmRecoverIsSilent); kPcmRecoverIsSilent);
} }
...@@ -267,8 +279,8 @@ void MixerOutputStreamAlsa::Stop() { ...@@ -267,8 +279,8 @@ void MixerOutputStreamAlsa::Stop() {
LOG(INFO) << "snd_pcm_close: handle=" << pcm_; LOG(INFO) << "snd_pcm_close: handle=" << pcm_;
int err = alsa_->PcmClose(pcm_); int err = alsa_->PcmClose(pcm_);
if (err < 0) { if (err < 0) {
LOG(ERROR) << "snd_pcm_close error, leaking handle: " LOG(ERROR) << "snd_pcm_close error, leaking handle: "
<< alsa_->StrError(err); << alsa_->StrError(err);
} }
pcm_ = nullptr; pcm_ = nullptr;
} }
...@@ -493,5 +505,33 @@ void MixerOutputStreamAlsa::UpdateRenderingDelay() { ...@@ -493,5 +505,33 @@ void MixerOutputStreamAlsa::UpdateRenderingDelay() {
sample_rate_; sample_rate_;
} }
bool MixerOutputStreamAlsa::MaybeRecoverDeviceFromSuspendedState() {
if (alsa_->PcmState(pcm_) != SND_PCM_STATE_SUSPENDED) {
LOG(WARNING) << "Alsa output is not suspended";
return false;
}
if (alsa_->PcmHwParamsCanResume(pcm_hw_params_)) {
LOG(INFO) << "Trying to resume output";
for (int attempt = 0; attempt < kRestoreAfterSuspensionAttempts;
++attempt) {
int err = alsa_->PcmResume(pcm_);
if (err == 0) {
LOG(INFO) << "ALSA output is resumed from suspended state";
return true;
}
if (err != -EAGAIN) {
// If PcmResume failed or device doesn't support resume, try to use
// PcmPrepare.
err = alsa_->PcmPrepare(pcm_);
LOG_IF(INFO, err == 0)
<< "ALSA output is recovered from suspended state";
return err == 0;
}
base::PlatformThread::Sleep(kRestoreAfterSuspensionAttemptDelay);
}
}
return false;
}
} // namespace media } // namespace media
} // namespace chromecast } // namespace chromecast
...@@ -56,6 +56,11 @@ class MixerOutputStreamAlsa : public MixerOutputStream { ...@@ -56,6 +56,11 @@ class MixerOutputStreamAlsa : public MixerOutputStream {
void UpdateRenderingDelay(); void UpdateRenderingDelay();
// Checks ALSA output for current state and if it's suspended, tries to
// recover.
// Returns true if ALSA device is recovered successfully.
bool MaybeRecoverDeviceFromSuspendedState();
std::unique_ptr<AlsaWrapper> alsa_; std::unique_ptr<AlsaWrapper> alsa_;
snd_pcm_t* pcm_ = nullptr; snd_pcm_t* pcm_ = nullptr;
......
...@@ -51,6 +51,10 @@ int AlsaWrapper::PcmDelay(snd_pcm_t* handle, snd_pcm_sframes_t* delay) { ...@@ -51,6 +51,10 @@ int AlsaWrapper::PcmDelay(snd_pcm_t* handle, snd_pcm_sframes_t* delay) {
return snd_pcm_delay(handle, delay); return snd_pcm_delay(handle, delay);
} }
int AlsaWrapper::PcmResume(snd_pcm_t* handle) {
return snd_pcm_resume(handle);
}
snd_pcm_sframes_t AlsaWrapper::PcmWritei(snd_pcm_t* handle, snd_pcm_sframes_t AlsaWrapper::PcmWritei(snd_pcm_t* handle,
const void* buffer, const void* buffer,
snd_pcm_uframes_t size) { snd_pcm_uframes_t size) {
...@@ -98,6 +102,10 @@ int AlsaWrapper::PcmHwParamsAny(snd_pcm_t* handle, ...@@ -98,6 +102,10 @@ int AlsaWrapper::PcmHwParamsAny(snd_pcm_t* handle,
return snd_pcm_hw_params_any(handle, hw_params); return snd_pcm_hw_params_any(handle, hw_params);
} }
int AlsaWrapper::PcmHwParamsCanResume(snd_pcm_hw_params_t* hw_params) {
return snd_pcm_hw_params_can_resume(hw_params);
}
int AlsaWrapper::PcmHwParamsSetRateResample(snd_pcm_t* handle, int AlsaWrapper::PcmHwParamsSetRateResample(snd_pcm_t* handle,
snd_pcm_hw_params_t* hw_params, snd_pcm_hw_params_t* hw_params,
unsigned int value) { unsigned int value) {
......
...@@ -33,6 +33,7 @@ class MEDIA_EXPORT AlsaWrapper { ...@@ -33,6 +33,7 @@ class MEDIA_EXPORT AlsaWrapper {
virtual int PcmDrain(snd_pcm_t* handle); virtual int PcmDrain(snd_pcm_t* handle);
virtual int PcmDrop(snd_pcm_t* handle); virtual int PcmDrop(snd_pcm_t* handle);
virtual int PcmDelay(snd_pcm_t* handle, snd_pcm_sframes_t* delay); virtual int PcmDelay(snd_pcm_t* handle, snd_pcm_sframes_t* delay);
virtual int PcmResume(snd_pcm_t* handle);
virtual snd_pcm_sframes_t PcmWritei(snd_pcm_t* handle, virtual snd_pcm_sframes_t PcmWritei(snd_pcm_t* handle,
const void* buffer, const void* buffer,
snd_pcm_uframes_t size); snd_pcm_uframes_t size);
...@@ -48,6 +49,7 @@ class MEDIA_EXPORT AlsaWrapper { ...@@ -48,6 +49,7 @@ class MEDIA_EXPORT AlsaWrapper {
snd_pcm_uframes_t* period_size); snd_pcm_uframes_t* period_size);
virtual int PcmHwParamsMalloc(snd_pcm_hw_params_t** hw_params); virtual int PcmHwParamsMalloc(snd_pcm_hw_params_t** hw_params);
virtual int PcmHwParamsAny(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params); virtual int PcmHwParamsAny(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params);
virtual int PcmHwParamsCanResume(snd_pcm_hw_params_t* hw_params);
virtual int PcmHwParamsSetRateResample(snd_pcm_t* handle, virtual int PcmHwParamsSetRateResample(snd_pcm_t* handle,
snd_pcm_hw_params_t* hw_params, snd_pcm_hw_params_t* hw_params,
unsigned int value); unsigned int value);
......
...@@ -31,6 +31,7 @@ class MockAlsaWrapper : public AlsaWrapper { ...@@ -31,6 +31,7 @@ class MockAlsaWrapper : public AlsaWrapper {
MOCK_METHOD1(PcmDrain, int(snd_pcm_t* handle)); MOCK_METHOD1(PcmDrain, int(snd_pcm_t* handle));
MOCK_METHOD1(PcmDrop, int(snd_pcm_t* handle)); MOCK_METHOD1(PcmDrop, int(snd_pcm_t* handle));
MOCK_METHOD2(PcmDelay, int(snd_pcm_t* handle, snd_pcm_sframes_t* delay)); MOCK_METHOD2(PcmDelay, int(snd_pcm_t* handle, snd_pcm_sframes_t* delay));
MOCK_METHOD1(PcmResume, int(snd_pcm_t* handle));
MOCK_METHOD3(PcmWritei, MOCK_METHOD3(PcmWritei,
snd_pcm_sframes_t(snd_pcm_t* handle, snd_pcm_sframes_t(snd_pcm_t* handle,
const void* buffer, const void* buffer,
...@@ -55,6 +56,8 @@ class MockAlsaWrapper : public AlsaWrapper { ...@@ -55,6 +56,8 @@ class MockAlsaWrapper : public AlsaWrapper {
MOCK_METHOD1(PcmHwParamsMalloc, int(snd_pcm_hw_params_t** hw_params)); MOCK_METHOD1(PcmHwParamsMalloc, int(snd_pcm_hw_params_t** hw_params));
MOCK_METHOD2(PcmHwParamsAny, MOCK_METHOD2(PcmHwParamsAny,
int(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params)); int(snd_pcm_t* handle, snd_pcm_hw_params_t* hw_params));
MOCK_METHOD1(PcmHwParamsCanResume, int(snd_pcm_hw_params_t* hw_params));
MOCK_METHOD3(PcmHwParamsSetRateResample, MOCK_METHOD3(PcmHwParamsSetRateResample,
int(snd_pcm_t* handle, int(snd_pcm_t* handle,
snd_pcm_hw_params_t* hw_params, snd_pcm_hw_params_t* hw_params,
......
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