spa: improve draining

Make a new DRAINED status.
Place the DRAINED status on an input IO when a stream is out of
buffers and draining.
All nodes that don't have HAVE_DATA on the input io need to copy
it to the output io and return the status. This makes sure the
DRAINED is forwarded and nodes return DRAINED from _process()
DRAINED on the resampler flushes out the last queued samples and then
forwards the DRAINED in the next iteration.
Emit a new drained signal from the context when a node returns
DRAINED. Use this to trigger the drained signal in the stream.
This commit is contained in:
Wim Taymans 2020-04-07 17:58:43 +02:00
parent 029f431418
commit b18dacde9a
10 changed files with 46 additions and 42 deletions

View file

@ -906,6 +906,9 @@ static inline int process_node(void *data)
} else {
resume_node(this, status);
}
if (status & SPA_STATUS_DRAINED) {
pw_context_driver_emit_drained(this->context, this);
}
return 0;
}

View file

@ -235,6 +235,7 @@ pw_core_resource_errorf(struct pw_resource *resource, uint32_t id, int seq,
#define pw_context_driver_emit_xrun(c,n) pw_context_driver_emit(c, xrun, 0, n)
#define pw_context_driver_emit_incomplete(c,n) pw_context_driver_emit(c, incomplete, 0, n)
#define pw_context_driver_emit_timeout(c,n) pw_context_driver_emit(c, timeout, 0, n)
#define pw_context_driver_emit_drained(c,n) pw_context_driver_emit(c, drained, 0, n)
struct pw_context_driver_events {
#define PW_VERSION_CONTEXT_DRIVER_EVENTS 0
@ -248,6 +249,8 @@ struct pw_context_driver_events {
void (*incomplete) (void *data, struct pw_impl_node *node);
/** The driver got a sync timeout */
void (*timeout) (void *data, struct pw_impl_node *node);
/** a node drained */
void (*drained) (void *data, struct pw_impl_node *node);
};
#define pw_registry_resource(r,m,v,...) pw_resource_call(r, struct pw_registry_events,m,v,##__VA_ARGS__)

View file

@ -331,7 +331,6 @@ do_call_drained(struct spa_loop *loop,
struct pw_stream *stream = &impl->this;
pw_log_trace(NAME" %p: drained", stream);
pw_stream_emit_drained(stream);
impl->draining = false;
return 0;
}
@ -765,8 +764,7 @@ again:
pw_log_trace(NAME" %p: process out status:%d id:%d ticks:%"PRIu64" delay:%"PRIi64, stream,
io->status, io->buffer_id, impl->time.ticks, impl->time.delay);
res = 0;
if (io->status != SPA_STATUS_HAVE_DATA) {
if ((res = io->status) != SPA_STATUS_HAVE_DATA) {
/* recycle old buffer */
if ((b = get_buffer(stream, io->buffer_id)) != NULL) {
pw_log_trace(NAME" %p: recycle buffer %d", stream, b->id);
@ -776,20 +774,17 @@ again:
/* pop new buffer */
if ((b = pop_queue(impl, &impl->queued)) != NULL) {
io->buffer_id = b->id;
io->status = SPA_STATUS_HAVE_DATA;
res = io->status = SPA_STATUS_HAVE_DATA;
pw_log_trace(NAME" %p: pop %d %p", stream, b->id, io);
} else if (impl->draining) {
impl->drained = true;
io->buffer_id = SPA_ID_INVALID;
res = io->status = SPA_STATUS_DRAINED;
pw_log_trace(NAME" %p: draining", stream);
} else {
io->buffer_id = SPA_ID_INVALID;
io->status = SPA_STATUS_NEED_DATA;
res = io->status = SPA_STATUS_NEED_DATA;
pw_log_trace(NAME" %p: no more buffers %p", stream, io);
if (impl->draining && !impl->drained) {
b = pop_queue(impl, &impl->dequeued);
io->buffer_id = b->id;
io->status = SPA_STATUS_HAVE_DATA;
b->this.buffer->datas[0].chunk->size = 0;
pw_log_trace(NAME" %p: drain buffer %d", stream, b->id);
impl->drained = true;
}
}
}
@ -803,7 +798,6 @@ again:
}
copy_position(impl, impl->queued.outcount);
res = io->status;
pw_log_trace(NAME" %p: res %d", stream, res);
return res;
@ -1044,7 +1038,7 @@ static const struct pw_core_events core_events = {
.error = on_core_error,
};
static void context_xrun(void *data, struct pw_impl_node *node)
static void context_drained(void *data, struct pw_impl_node *node)
{
struct stream *impl = data;
if (impl->node != node)
@ -1055,7 +1049,7 @@ static void context_xrun(void *data, struct pw_impl_node *node)
static const struct pw_context_driver_events context_events = {
PW_VERSION_CONTEXT_DRIVER_EVENTS,
.xrun = context_xrun,
.drained = context_drained,
};
static struct stream *