screencast: restructure the screencopy flow

The previous implemented way to use wlr_screencopy events to cicle the
screencast had issues, like halting the stream if it was paused and
resumed before PipeWire triggered a recreation of buffers. This came
from not returning a dequeued buffer. This is now mititgated by
enqueuing the current pw_buffer imidiately on a paused event and trying
to dequeue a buffer just before requesting a screencopy if none is
present.
This commit is contained in:
columbarius 2022-03-02 17:57:07 +01:00
parent 058bd97815
commit 074e62d0c3
4 changed files with 58 additions and 35 deletions

View File

@ -3,11 +3,13 @@
#include "screencast_common.h"
#define XDPW_PWR_BUFFERS 4
#define XDPW_PWR_BUFFERS 2
#define XDPW_PWR_BUFFERS_MIN 2
#define XDPW_PWR_ALIGN 16
void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast);
void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast);
void xdpw_pwr_swap_buffer(struct xdpw_screencast_instance *cast);
void pwr_update_stream_param(struct xdpw_screencast_instance *cast);
void xdpw_pwr_stream_create(struct xdpw_screencast_instance *cast);
void xdpw_pwr_stream_destroy(struct xdpw_screencast_instance *cast);

View File

@ -55,7 +55,7 @@ struct xdpw_frame {
uint32_t tv_nsec;
struct xdpw_frame_damage damage;
struct xdpw_buffer *xdpw_buffer;
struct pw_buffer *current_pw_buffer;
struct pw_buffer *pw_buffer;
};
struct xdpw_screencopy_frame_info {

View File

@ -60,6 +60,11 @@ static void pwr_handle_stream_state_changed(void *data,
xdpw_wlr_frame_start(cast);
}
break;
case PW_STREAM_STATE_PAUSED:
if (old == PW_STREAM_STATE_STREAMING) {
xdpw_pwr_enqueue_buffer(cast);
}
// fall through
default:
cast->pwr_stream_state = false;
break;
@ -83,9 +88,10 @@ static void pwr_handle_stream_param_changed(void *data, uint32_t id,
spa_format_video_raw_parse(param, &cast->pwr_format);
cast->framerate = (uint32_t)(cast->pwr_format.max_framerate.num / cast->pwr_format.max_framerate.denom);
params[0] = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(XDPW_PWR_BUFFERS, 1, 32),
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(XDPW_PWR_BUFFERS, XDPW_PWR_BUFFERS_MIN, 32),
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
SPA_PARAM_BUFFERS_size, SPA_POD_Int(cast->screencopy_frame_info.size),
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(cast->screencopy_frame_info.stride),
@ -143,15 +149,12 @@ static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer
logprint(TRACE, "pipewire: remove buffer event handle");
if (cast->current_frame.current_pw_buffer == buffer) {
logprint(TRACE, "pipewire: remove buffer currently in use");
cast->current_frame.current_pw_buffer = NULL;
cast->current_frame.xdpw_buffer = NULL;
}
struct xdpw_buffer *xdpw_buffer = buffer->user_data;
if (xdpw_buffer) {
xdpw_buffer_destroy(xdpw_buffer);
buffer->user_data = NULL;
}
if (cast->current_frame.pw_buffer == buffer) {
cast->current_frame.pw_buffer = NULL;
}
buffer->buffer->datas[0].fd = -1;
buffer->user_data = NULL;
@ -168,27 +171,28 @@ static const struct pw_stream_events pwr_stream_events = {
void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast) {
logprint(TRACE, "pipewire: dequeueing buffer");
assert(cast->current_frame.current_pw_buffer == NULL);
if ((cast->current_frame.current_pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) {
assert(!cast->current_frame.pw_buffer);
if ((cast->current_frame.pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) {
logprint(WARN, "pipewire: out of buffers");
cast->current_frame.xdpw_buffer = NULL;
return;
}
cast->current_frame.xdpw_buffer = cast->current_frame.current_pw_buffer->user_data;
cast->current_frame.xdpw_buffer = cast->current_frame.pw_buffer->user_data;
}
void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
logprint(TRACE, "pipewire: exporting buffer");
struct pw_buffer *pw_buf = cast->current_frame.current_pw_buffer;
bool buffer_corrupt = cast->frame_state != XDPW_FRAME_STATE_SUCCESS;
assert(pw_buf);
logprint(TRACE, "pipewire: enqueueing buffer");
if (!cast->current_frame.pw_buffer) {
logprint(WARN, "pipewire: no buffer to queue");
goto done;
}
struct pw_buffer *pw_buf = cast->current_frame.pw_buffer;
struct spa_buffer *spa_buf = pw_buf->buffer;
struct spa_data *d = spa_buf->datas;
bool buffer_corrupt = cast->frame_state != XDPW_FRAME_STATE_SUCCESS;
if (cast->current_frame.y_invert) {
//TODO: Flip buffer or set stride negative
buffer_corrupt = true;
@ -220,8 +224,23 @@ void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) {
pw_stream_queue_buffer(cast->stream, pw_buf);
cast->current_frame.current_pw_buffer = NULL;
done:
cast->current_frame.xdpw_buffer = NULL;
cast->current_frame.pw_buffer = NULL;
}
void xdpw_pwr_swap_buffer(struct xdpw_screencast_instance *cast) {
logprint(TRACE, "pipewire: swapping buffers");
if (!cast->current_frame.pw_buffer) {
goto dequeue_buffer;
}
xdpw_pwr_enqueue_buffer(cast);
dequeue_buffer:
assert(!cast->current_frame.pw_buffer);
xdpw_pwr_dequeue_buffer(cast);
}
void pwr_update_stream_param(struct xdpw_screencast_instance *cast) {

View File

@ -40,11 +40,6 @@ void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast) {
return;
}
// Check if we have a buffer
if (cast->current_frame.current_pw_buffer) {
xdpw_pwr_enqueue_buffer(cast);
}
if (!cast->pwr_stream_state) {
cast->frame_state = XDPW_FRAME_STATE_NONE;
return;
@ -55,6 +50,7 @@ void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast) {
}
if (cast->frame_state == XDPW_FRAME_STATE_SUCCESS) {
xdpw_pwr_swap_buffer(cast);
uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->framerate);
if (delay_ns > 0) {
xdpw_add_timer(cast->ctx->state, delay_ns,
@ -67,18 +63,14 @@ void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast) {
void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast) {
logprint(TRACE, "wlroots: start screencopy");
if (cast->err) {
logprint(ERROR, "wlroots: nonrecoverable error has happened. shutting down instance");
if (cast->quit || cast->err) {
xdpw_screencast_instance_destroy(cast);
return;
}
if (cast->pwr_stream_state) {
xdpw_pwr_dequeue_buffer(cast);
if (!cast->current_frame.current_pw_buffer) {
logprint(WARN, "wlroots: failed to dequeue buffer");
}
if (cast->initialized && !cast->pwr_stream_state) {
cast->frame_state = XDPW_FRAME_STATE_NONE;
return;
}
cast->frame_state = XDPW_FRAME_STATE_STARTED;
@ -117,8 +109,8 @@ static void wlr_frame_buffer_done(void *data,
struct xdpw_screencast_instance *cast = data;
logprint(TRACE, "wlroots: buffer_done event handler");
if (!cast->current_frame.current_pw_buffer) {
logprint(WARN, "wlroots: no current buffer");
if (!cast->initialized) {
xdpw_wlr_frame_finish(cast);
return;
}
@ -134,6 +126,16 @@ static void wlr_frame_buffer_done(void *data,
return;
}
if (!cast->current_frame.xdpw_buffer) {
xdpw_pwr_dequeue_buffer(cast);
}
if (!cast->current_frame.xdpw_buffer) {
logprint(WARN, "wlroots: no current buffer");
xdpw_wlr_frame_finish(cast);
return;
}
assert(cast->current_frame.xdpw_buffer);
// Check if dequeued buffer is compatible with announced buffer