diff options
Diffstat (limited to 'audio')
-rw-r--r-- | audio/out/ao_alsa.c | 60 |
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: ; } |