[alsa-devel] [PATCH] pcm: ioplug: Implement proper drain behavior
Takashi Iwai
tiwai at suse.de
Thu Mar 29 10:02:11 CEST 2018
This patch fixes the draining behavior of ioplug in the following
ways:
- When no draining ioplug callback is defined, implement the draining
loop using snd_pcm_wait*() and sync with the drain finishes.
This is equivalent with the implementation in the kernel write().
Similarly as in kernel code, for non-blocking mode, it returns
immediately after setting DRAINING state.
- The hw_ptr update function checks the PCM state and stops the stream
if the draining finishes.
- When draining ioplug callback is defined, leave the whole draining
operation to it. The callback is supposed to return -EAGAIN for
non-blocking case, too.
- When an unexpected error happens during draining, it drops the
stream, for a safety reason.
Signed-off-by: Takashi Iwai <tiwai at suse.de>
---
The patch was slightly changed from the draft version.
e.g. hw_ptr_update() is called at the beginning of the loop, so that
it's updated for non-blocking mode, too. And, the stream is dropped
in case of unexpected errors.
Also, this patch requires the previous fix ("pcm: Skip avail_min check
during draining").
src/pcm/pcm_ioplug.c | 76 +++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 67 insertions(+), 9 deletions(-)
diff --git a/src/pcm/pcm_ioplug.c b/src/pcm/pcm_ioplug.c
index 8c0ed4836365..db64853b3136 100644
--- a/src/pcm/pcm_ioplug.c
+++ b/src/pcm/pcm_ioplug.c
@@ -47,6 +47,11 @@ typedef struct snd_pcm_ioplug_priv {
snd_htimestamp_t trigger_tstamp;
} ioplug_priv_t;
+static int snd_pcm_ioplug_drop(snd_pcm_t *pcm);
+static int snd_pcm_ioplug_poll_descriptors_count(snd_pcm_t *pcm);
+static int snd_pcm_ioplug_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
+static int snd_pcm_ioplug_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
+
/* update the hw pointer */
/* called in lock */
static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
@@ -57,6 +62,7 @@ static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
hw = io->data->callback->pointer(io->data);
if (hw >= 0) {
snd_pcm_uframes_t delta;
+ snd_pcm_uframes_t avail;
if ((snd_pcm_uframes_t)hw >= io->last_hw)
delta = hw - io->last_hw;
@@ -67,9 +73,19 @@ static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
delta = wrap_point + hw - io->last_hw;
}
snd_pcm_mmap_hw_forward(io->data->pcm, delta);
+ /* stop the stream if all samples are drained */
+ if (io->data->state == SND_PCM_STATE_DRAINING) {
+ avail = snd_pcm_mmap_avail(pcm);
+ if (avail >= pcm->buffer_size)
+ snd_pcm_ioplug_drop(pcm);
+ }
io->last_hw = (snd_pcm_uframes_t)hw;
- } else
- io->data->state = SNDRV_PCM_STATE_XRUN;
+ } else {
+ if (io->data->state == SND_PCM_STATE_DRAINING)
+ snd_pcm_ioplug_drop(pcm);
+ else
+ io->data->state = SNDRV_PCM_STATE_XRUN;
+ }
}
static int snd_pcm_ioplug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
@@ -488,20 +504,62 @@ static int snd_pcm_ioplug_drop(snd_pcm_t *pcm)
return 0;
}
+static int ioplug_drain_via_poll(snd_pcm_t *pcm)
+{
+ ioplug_priv_t *io = pcm->private_data;
+
+ while (io->data->state == SND_PCM_STATE_DRAINING) {
+ snd_pcm_ioplug_hw_ptr_update(pcm);
+ if (io->data->state != SND_PCM_STATE_DRAINING)
+ break;
+ /* in non-blocking mode, let application to poll() by itself */
+ if (io->data->nonblock)
+ return -EAGAIN;
+ if (snd_pcm_wait_nocheck(pcm, -1) < 0)
+ break;
+ }
+
+ return 0; /* force to drop at error */
+}
+
/* need own locking */
static int snd_pcm_ioplug_drain(snd_pcm_t *pcm)
{
ioplug_priv_t *io = pcm->private_data;
- int err;
+ int err = 0;
- if (io->data->state == SND_PCM_STATE_OPEN)
+ snd_pcm_lock(pcm);
+ switch (io->data->state) {
+ case SND_PCM_STATE_OPEN:
+ case SND_PCM_STATE_DISCONNECTED:
+ case SND_PCM_STATE_SUSPENDED:
return -EBADFD;
+ case SND_PCM_STATE_PREPARED:
+ if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
+ err = snd_pcm_ioplug_start(pcm);
+ if (err < 0)
+ goto unlock;
+ io->data->state = SND_PCM_STATE_DRAINING;
+ }
+ break;
+ case SND_PCM_STATE_RUNNING:
+ io->data->state = SND_PCM_STATE_DRAINING;
+ break;
+ }
- io->data->state = SND_PCM_STATE_DRAINING;
- if (io->data->callback->drain)
- io->data->callback->drain(io->data);
- snd_pcm_lock(pcm);
- err = snd_pcm_ioplug_drop(pcm);
+ if (io->data->state == SND_PCM_STATE_DRAINING) {
+ if (io->data->callback->drain) {
+ snd_pcm_unlock(pcm); /* let plugin own locking */
+ err = io->data->callback->drain(io->data);
+ snd_pcm_lock(pcm);
+ } else {
+ err = ioplug_drain_via_poll(pcm);
+ }
+ }
+
+ unlock:
+ if (!err && io->data->state != SND_PCM_STATE_SETUP)
+ snd_pcm_ioplug_drop(pcm);
snd_pcm_unlock(pcm);
return err;
}
--
2.16.2
More information about the Alsa-devel
mailing list