diff --git a/spa/plugins/alsa/alsa-pcm-sink.c b/spa/plugins/alsa/alsa-pcm-sink.c index 8df1a54dd..43285862e 100644 --- a/spa/plugins/alsa/alsa-pcm-sink.c +++ b/spa/plugins/alsa/alsa-pcm-sink.c @@ -638,11 +638,13 @@ static int impl_node_process(void *object) spa_return_val_if_fail(input != NULL, -EIO); spa_log_trace_fp(this->log, NAME " %p: process %d %d/%d", this, input->status, - input->buffer_id, - this->n_buffers); + input->buffer_id, this->n_buffers); + if (this->position && this->position->clock.flags & SPA_IO_CLOCK_FLAG_FREEWHEEL) { input->status = SPA_STATUS_NEED_DATA; - } else if (input->status == SPA_STATUS_HAVE_DATA && + return SPA_STATUS_HAVE_DATA; + } + if (input->status == SPA_STATUS_HAVE_DATA && input->buffer_id < this->n_buffers) { struct buffer *b = &this->buffers[input->buffer_id]; diff --git a/spa/plugins/alsa/alsa-pcm-source.c b/spa/plugins/alsa/alsa-pcm-source.c index dd9c8ecd4..81fd610e5 100644 --- a/spa/plugins/alsa/alsa-pcm-source.c +++ b/spa/plugins/alsa/alsa-pcm-source.c @@ -666,16 +666,13 @@ static int impl_node_process(void *object) spa_alsa_recycle_buffer(this, io->buffer_id); io->buffer_id = SPA_ID_INVALID; } - if (this->position && this->position->clock.flags & SPA_IO_CLOCK_FLAG_FREEWHEEL) { - spa_log_info(this->log, NAME " %p:freewheel", this); - io->status = SPA_STATUS_HAVE_DATA; - io->buffer_id = 0; - return SPA_STATUS_HAVE_DATA; + + if (spa_list_is_empty(&this->ready) && this->following) { + if (this->freewheel) + spa_alsa_skip(this); + else + spa_alsa_read(this); } - - if (spa_list_is_empty(&this->ready) && this->following) - spa_alsa_read(this, 0); - if (spa_list_is_empty(&this->ready) || !this->following) return SPA_STATUS_OK; diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c index 423210d1d..f398fe885 100644 --- a/spa/plugins/alsa/alsa-pcm.c +++ b/spa/plugins/alsa/alsa-pcm.c @@ -1158,7 +1158,7 @@ push_frames(struct state *state, } -int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence) +int spa_alsa_read(struct state *state) { snd_pcm_t *hndl = state->hndl; snd_pcm_uframes_t total_read = 0, to_read; @@ -1260,6 +1260,40 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence) return 0; } +int spa_alsa_skip(struct state *state) +{ + struct buffer *b; + struct spa_data *d; + uint32_t i, avail, total_frames, n_bytes, frames; + + if (spa_list_is_empty(&state->free)) { + spa_log_warn(state->log, NAME" %s: no more buffers", state->props.device); + return -EPIPE; + } + + frames = state->read_size; + + b = spa_list_first(&state->free, struct buffer, link); + spa_list_remove(&b->link); + + d = b->buf->datas; + + avail = d[0].maxsize / state->frame_size; + total_frames = SPA_MIN(avail, frames); + n_bytes = total_frames * state->frame_size; + + for (i = 0; i < b->buf->n_datas; i++) { + memset(d[i].data, 0, n_bytes); + d[i].chunk->offset = 0; + d[i].chunk->size = n_bytes; + d[i].chunk->stride = state->frame_size; + } + spa_list_append(&state->ready, &b->link); + + return 0; +} + + static int handle_play(struct state *state, uint64_t nsec, snd_pcm_uframes_t delay, snd_pcm_uframes_t target) { @@ -1305,7 +1339,7 @@ static int handle_capture(struct state *state, uint64_t nsec, if (SPA_UNLIKELY(res = update_time(state, nsec, delay, target, false)) < 0) return res; - if ((res = spa_alsa_read(state, target)) < 0) + if ((res = spa_alsa_read(state)) < 0) return res; if (spa_list_is_empty(&state->ready)) @@ -1508,7 +1542,7 @@ static int do_reassign_follower(struct spa_loop *loop, int spa_alsa_reassign_follower(struct state *state) { - bool following; + bool following, freewheel; if (!state->started) return 0; @@ -1520,6 +1554,17 @@ int spa_alsa_reassign_follower(struct state *state) spa_loop_invoke(state->data_loop, do_reassign_follower, 0, NULL, 0, true, state); } setup_matching(state); + + freewheel = state->position && + SPA_FLAG_IS_SET(state->position->clock.flags, SPA_IO_CLOCK_FLAG_FREEWHEEL); + + if (state->freewheel != freewheel) { + state->freewheel = freewheel; + if (freewheel) + snd_pcm_pause(state->hndl, 1); + else + snd_pcm_pause(state->hndl, 0); + } return 0; } diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h index 118f405b2..6803b4eb7 100644 --- a/spa/plugins/alsa/alsa-pcm.h +++ b/spa/plugins/alsa/alsa-pcm.h @@ -167,6 +167,7 @@ struct state { unsigned int resample:1; unsigned int use_mmap:1; unsigned int planar:1; + unsigned int freewheel:1; int64_t sample_count; @@ -195,7 +196,8 @@ int spa_alsa_pause(struct state *state); int spa_alsa_close(struct state *state); int spa_alsa_write(struct state *state); -int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence); +int spa_alsa_read(struct state *state); +int spa_alsa_skip(struct state *state); void spa_alsa_recycle_buffer(struct state *state, uint32_t buffer_id);