mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -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;
|
||||
} 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)
|
||||
return -EBADFD;
|
||||
|
||||
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);
|
||||
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);
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue