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 @@
#include "base/command_line.h"
#include "base/stl_util.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "chromecast/base/chromecast_switches.h"
#include "chromecast/media/cma/backend/alsa/alsa_wrapper.h"
......@@ -104,6 +105,12 @@ constexpr unsigned int kFallbackSampleRateHiRes = 96000;
// the direction explicitly.
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
// some devices do not support 32 bit samples.
constexpr snd_pcm_format_t kPreferredSampleFormats[] = {
......@@ -226,6 +233,11 @@ bool MixerOutputStreamAlsa::Write(const float* data,
while ((frames_or_error =
alsa_->PcmWritei(pcm_, output_data, frames_left)) < 0) {
*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,
kPcmRecoverIsSilent);
}
......@@ -267,8 +279,8 @@ void MixerOutputStreamAlsa::Stop() {
LOG(INFO) << "snd_pcm_close: handle=" << pcm_;
int err = alsa_->PcmClose(pcm_);
if (err < 0) {
LOG(ERROR) << "snd_pcm_close error, leaking handle: "
<< alsa_->StrError(err);
LOG(ERROR) << "snd_pcm_close error, leaking handle: "
<< alsa_->StrError(err);
}
pcm_ = nullptr;
}
......@@ -493,5 +505,33 @@ void MixerOutputStreamAlsa::UpdateRenderingDelay() {
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 chromecast
......@@ -56,6 +56,11 @@ class MixerOutputStreamAlsa : public MixerOutputStream {
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_;
snd_pcm_t* pcm_ = nullptr;
......
......@@ -51,6 +51,10 @@ int AlsaWrapper::PcmDelay(snd_pcm_t* handle, snd_pcm_sframes_t* 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,
const void* buffer,
snd_pcm_uframes_t size) {
......@@ -98,6 +102,10 @@ int AlsaWrapper::PcmHwParamsAny(snd_pcm_t* handle,
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,
snd_pcm_hw_params_t* hw_params,
unsigned int value) {
......
......@@ -33,6 +33,7 @@ class MEDIA_EXPORT AlsaWrapper {
virtual int PcmDrain(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 PcmResume(snd_pcm_t* handle);
virtual snd_pcm_sframes_t PcmWritei(snd_pcm_t* handle,
const void* buffer,
snd_pcm_uframes_t size);
......@@ -48,6 +49,7 @@ class MEDIA_EXPORT AlsaWrapper {
snd_pcm_uframes_t* period_size);
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 PcmHwParamsCanResume(snd_pcm_hw_params_t* hw_params);
virtual int PcmHwParamsSetRateResample(snd_pcm_t* handle,
snd_pcm_hw_params_t* hw_params,
unsigned int value);
......
......@@ -31,6 +31,7 @@ class MockAlsaWrapper : public AlsaWrapper {
MOCK_METHOD1(PcmDrain, 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_METHOD1(PcmResume, int(snd_pcm_t* handle));
MOCK_METHOD3(PcmWritei,
snd_pcm_sframes_t(snd_pcm_t* handle,
const void* buffer,
......@@ -55,6 +56,8 @@ class MockAlsaWrapper : public AlsaWrapper {
MOCK_METHOD1(PcmHwParamsMalloc, int(snd_pcm_hw_params_t** hw_params));
MOCK_METHOD2(PcmHwParamsAny,
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,
int(snd_pcm_t* handle,
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