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

@ -77,6 +77,9 @@ enum spa_io_type {
* If status is SPA_STATUS_STOPPED, some error occured on the
* port.
*
* If status is SPA_STATUS_DRAINED, data from the io area was
* used to drain.
*
* Status can also be a negative errno value to indicate errors.
* such as:
* -EINVAL: buffer_id is invalid
@ -87,6 +90,7 @@ struct spa_io_buffers {
#define SPA_STATUS_NEED_DATA (1<<0)
#define SPA_STATUS_HAVE_DATA (1<<1)
#define SPA_STATUS_STOPPED (1<<2)
#define SPA_STATUS_DRAINED (1<<3)
int32_t status; /**< the status code */
uint32_t buffer_id; /**< a buffer id */
};

View file

@ -865,7 +865,7 @@ impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t buffer_id)
static int impl_node_process(void *object)
{
struct impl *this = object;
int status;
int status = 0;
spa_log_trace_fp(this->log, "%p: process convert:%u master:%d",
this, this->use_converter, this->master);
@ -875,22 +875,17 @@ static int impl_node_process(void *object)
status = spa_node_process(this->convert);
}
status = spa_node_process(this->follower);
if (status >= 0)
status = spa_node_process(this->follower);
if (this->direction == SPA_DIRECTION_OUTPUT &&
!this->master && this->use_converter) {
while (true) {
while (status >= 0) {
status = spa_node_process(this->convert);
if (status & SPA_STATUS_HAVE_DATA)
if (status & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED))
break;
if (status & SPA_STATUS_NEED_DATA) {
if (status & SPA_STATUS_NEED_DATA)
status = spa_node_process(this->follower);
if (!(status & SPA_STATUS_HAVE_DATA)) {
spa_node_call_xrun(&this->callbacks, 0, 0, NULL);
break;
}
}
}
}
spa_log_trace_fp(this->log, "%p: process status:%d", this, status);

View file

@ -1084,7 +1084,7 @@ static int impl_node_process(void *object)
if (SPA_UNLIKELY(i == 0))
res |= r & SPA_STATUS_NEED_DATA;
if (SPA_UNLIKELY(i == this->n_nodes-1))
res |= r & SPA_STATUS_HAVE_DATA;
res |= r & (SPA_STATUS_HAVE_DATA | SPA_STATUS_DRAINED);
}
if (res & SPA_STATUS_HAVE_DATA)
break;

View file

@ -864,15 +864,13 @@ static int impl_node_process(void *object)
if (SPA_UNLIKELY(outio->status == SPA_STATUS_HAVE_DATA))
return SPA_STATUS_HAVE_DATA;
if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA))
return SPA_STATUS_NEED_DATA;
/* recycle */
if (SPA_LIKELY(outio->buffer_id < outport->n_buffers)) {
recycle_buffer(this, outio->buffer_id);
outio->buffer_id = SPA_ID_INVALID;
}
if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA))
return outio->status = inio->status;
if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers))
return inio->status = -EINVAL;

View file

@ -844,7 +844,8 @@ static int impl_node_process(void *object)
outio->buffer_id = SPA_ID_INVALID;
}
if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA))
return SPA_STATUS_NEED_DATA;
return outio->status = inio->status;
if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers))
return inio->status = -EINVAL;

View file

@ -118,6 +118,7 @@ struct impl {
int mode;
unsigned int started:1;
unsigned int peaks:1;
unsigned int drained:1;
struct resample resample;
};
@ -723,6 +724,7 @@ static int impl_node_process(void *object)
void **dst_datas;
bool flush_out = false;
bool flush_in = false;
bool draining = false;
spa_return_val_if_fail(this != NULL, -EINVAL);
@ -741,15 +743,18 @@ static int impl_node_process(void *object)
if (SPA_UNLIKELY(outio->status == SPA_STATUS_HAVE_DATA))
return SPA_STATUS_HAVE_DATA;
if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA))
return SPA_STATUS_NEED_DATA;
/* recycle */
if (SPA_LIKELY(outio->buffer_id < outport->n_buffers)) {
recycle_buffer(this, outio->buffer_id);
outio->buffer_id = SPA_ID_INVALID;
}
if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA)) {
if (inio->status != SPA_STATUS_DRAINED || this->drained)
return outio->status = inio->status;
inio->buffer_id = 0;
inport->buffers[0].outbuf->datas[0].chunk->size = 0;
}
if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers))
return inio->status = -EINVAL;
@ -784,7 +789,7 @@ static int impl_node_process(void *object)
size = sb->datas[0].maxsize;
memset(sb->datas[0].data, 0, size);
inport->offset = 0;
flush_in = true;
flush_in = draining = true;
}
if (this->io_rate_match) {
@ -829,18 +834,19 @@ static int impl_node_process(void *object)
if (inport->offset >= size || flush_in) {
inio->status = SPA_STATUS_NEED_DATA;
inport->offset = 0;
SPA_FLAG_SET(res, SPA_STATUS_NEED_DATA);
spa_log_trace_fp(this->log, NAME " %p: return input buffer", this);
SPA_FLAG_SET(res, inio->status);
spa_log_trace_fp(this->log, NAME " %p: return input buffer of size %d", this, size);
}
outport->offset += out_len * sizeof(float);
if (outport->offset > 0 && (outport->offset >= maxsize || flush_out)) {
outio->status = SPA_STATUS_HAVE_DATA;
outio->buffer_id = dbuf->id;
spa_log_trace_fp(this->log, NAME " %p: have output buffer of size %d", this, outport->offset);
dequeue_buffer(this, dbuf);
outport->offset = 0;
this->drained = draining;
SPA_FLAG_SET(res, SPA_STATUS_HAVE_DATA);
spa_log_trace_fp(this->log, NAME " %p: have output buffer", this);
}
if (out_len == 0 && this->peaks) {
outio->status = SPA_STATUS_HAVE_DATA;

View file

@ -860,7 +860,7 @@ static int impl_node_process(void *object)
inio, inio->status, inio->buffer_id);
if (SPA_UNLIKELY(inio->status != SPA_STATUS_HAVE_DATA))
return SPA_STATUS_NEED_DATA;
return inio->status;
if (SPA_UNLIKELY(inio->buffer_id >= inport->n_buffers))
return inio->status = -EINVAL;