summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2016-08-09 16:35:33 +0200
committerwm4 <wm4@nowhere>2016-08-09 17:09:29 +0200
commit2ded41d2beff54de26ab1c4d16ab4d0bd14eeba0 (patch)
tree71a651db16e0cde39096b4cd09c5cab70d48e787
parent062349ff5b9b062aff581e96f3770e6cbd419491 (diff)
downloadmpv-2ded41d2beff54de26ab1c4d16ab4d0bd14eeba0.tar.bz2
mpv-2ded41d2beff54de26ab1c4d16ab4d0bd14eeba0.tar.xz
ao_alsa: handle --audio-stream-silence
push.c does not handle this automatically, and AOs using push.c have to handle it themselves. Also, ALSA is low-level enough that it needs explicit support in user code. At least I haven't found any option that does this. We still can get away relatively cheaply by abusing underflow-handling for this. ao_alsa.c already configures ALSA to handle underflows by playing silence. So we purposely induce an underflow when opening the device, as well as when pausing or resetting the device. This introduces minor misbehavior: it doesn't account for the additional delay the initial silence adds, unless the device has fully played the fragment of silence when the player starts sending data to it. But nobody cares.
-rw-r--r--audio/out/ao_alsa.c60
1 files changed, 51 insertions, 9 deletions
diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c
index 4a55d19656..28281482ee 100644
--- a/audio/out/ao_alsa.c
+++ b/audio/out/ao_alsa.c
@@ -53,6 +53,7 @@ struct priv {
bool device_lost;
snd_pcm_format_t alsa_fmt;
bool can_pause;
+ bool pretend_pause;
snd_pcm_sframes_t prepause_frames;
double delay_before_pause;
snd_pcm_uframes_t buffersize;
@@ -785,6 +786,14 @@ static int init_device(struct ao *ao, int mode)
MP_VERBOSE(ao, "buffersize: %d samples\n", (int)p->buffersize);
MP_VERBOSE(ao, "period size: %d samples\n", (int)p->outburst);
+ ao->device_buffer = p->buffersize;
+
+ // ao_alsa implements this by relying on underrun behavior (no data means
+ // underrun, during which silence is played). Trigger by playing some
+ // initial silence.
+ if (ao->stream_silence)
+ ao_play_silence(ao, p->outburst);
+
return 0;
alsa_error:
@@ -874,7 +883,7 @@ static double get_delay(struct ao *ao)
struct priv *p = ao->priv;
snd_pcm_sframes_t delay;
- if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED)
+ if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED || p->pretend_pause)
return p->delay_before_pause;
if (snd_pcm_delay(p->alsa, &delay) < 0)
@@ -888,12 +897,34 @@ static double get_delay(struct ao *ao)
return delay / (double)ao->samplerate;
}
+// For stream-silence mode: replace remaining buffer with silence.
+// Tries to cause an instant buffer underrun.
+static void soft_reset(struct ao *ao)
+{
+ struct priv *p = ao->priv;
+ snd_pcm_sframes_t frames = snd_pcm_rewindable(p->alsa);
+ if (frames > 0 && snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
+ frames = snd_pcm_rewind(p->alsa, frames);
+ if (frames < 0) {
+ int err = frames;
+ CHECK_ALSA_WARN("pcm rewind error");
+ }
+ }
+}
+
static void audio_pause(struct ao *ao)
{
struct priv *p = ao->priv;
int err;
- if (p->can_pause) {
+ if (ao->stream_silence) {
+ if (snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
+ p->delay_before_pause = get_delay(ao);
+ p->prepause_frames = p->delay_before_pause * ao->samplerate;
+ soft_reset(ao);
+ p->pretend_pause = true;
+ }
+ } else if (p->can_pause) {
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_RUNNING) {
p->delay_before_pause = get_delay(ao);
err = snd_pcm_pause(p->alsa, 1);
@@ -932,7 +963,12 @@ static void audio_resume(struct ao *ao)
resume_device(ao);
- if (p->can_pause) {
+ if (ao->stream_silence) {
+ p->pretend_pause = false;
+ get_delay(ao); // recovers from underrun (as a side-effect)
+ if (p->prepause_frames)
+ ao_play_silence(ao, p->prepause_frames);
+ } else if (p->can_pause) {
if (snd_pcm_state(p->alsa) == SND_PCM_STATE_PAUSED) {
err = snd_pcm_pause(p->alsa, 0);
CHECK_ALSA_ERROR("pcm resume error");
@@ -953,12 +989,18 @@ static void reset(struct ao *ao)
struct priv *p = ao->priv;
int err;
- p->prepause_frames = 0;
- p->delay_before_pause = 0;
- err = snd_pcm_drop(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
- err = snd_pcm_prepare(p->alsa);
- CHECK_ALSA_ERROR("pcm prepare error");
+ p->pretend_pause = false;
+
+ if (ao->stream_silence) {
+ soft_reset(ao);
+ } else {
+ p->prepause_frames = 0;
+ p->delay_before_pause = 0;
+ err = snd_pcm_drop(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ err = snd_pcm_prepare(p->alsa);
+ CHECK_ALSA_ERROR("pcm prepare error");
+ }
alsa_error: ;
}