mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-31 22:25:35 -04:00
pcm: ioplug: Implement proper drain behavior
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 error happens during draining, it drops the stream, for a safety reason. Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
d3d42f60a6
commit
ce2095c41f
1 changed files with 68 additions and 10 deletions
|
|
@ -47,6 +47,11 @@ typedef struct snd_pcm_ioplug_priv {
|
||||||
snd_htimestamp_t trigger_tstamp;
|
snd_htimestamp_t trigger_tstamp;
|
||||||
} ioplug_priv_t;
|
} 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 */
|
/* update the hw pointer */
|
||||||
/* called in lock */
|
/* called in lock */
|
||||||
static void snd_pcm_ioplug_hw_ptr_update(snd_pcm_t *pcm)
|
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);
|
hw = io->data->callback->pointer(io->data);
|
||||||
if (hw >= 0) {
|
if (hw >= 0) {
|
||||||
snd_pcm_uframes_t delta;
|
snd_pcm_uframes_t delta;
|
||||||
|
snd_pcm_uframes_t avail;
|
||||||
|
|
||||||
if ((snd_pcm_uframes_t)hw >= io->last_hw)
|
if ((snd_pcm_uframes_t)hw >= io->last_hw)
|
||||||
delta = 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;
|
delta = wrap_point + hw - io->last_hw;
|
||||||
}
|
}
|
||||||
snd_pcm_mmap_hw_forward(io->data->pcm, delta);
|
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;
|
io->last_hw = (snd_pcm_uframes_t)hw;
|
||||||
} else
|
} else {
|
||||||
io->data->state = SNDRV_PCM_STATE_XRUN;
|
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)
|
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;
|
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 */
|
/* need own locking */
|
||||||
static int snd_pcm_ioplug_drain(snd_pcm_t *pcm)
|
static int snd_pcm_ioplug_drain(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
ioplug_priv_t *io = pcm->private_data;
|
ioplug_priv_t *io = pcm->private_data;
|
||||||
int err;
|
int err = 0;
|
||||||
|
|
||||||
if (io->data->state == SND_PCM_STATE_OPEN)
|
|
||||||
return -EBADFD;
|
|
||||||
|
|
||||||
io->data->state = SND_PCM_STATE_DRAINING;
|
|
||||||
if (io->data->callback->drain)
|
|
||||||
io->data->callback->drain(io->data);
|
|
||||||
snd_pcm_lock(pcm);
|
snd_pcm_lock(pcm);
|
||||||
err = snd_pcm_ioplug_drop(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
snd_pcm_unlock(pcm);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue