From 074e62d0c3a3ceb071987cbb6e8816b0d1db1783 Mon Sep 17 00:00:00 2001 From: columbarius Date: Wed, 2 Mar 2022 17:57:07 +0100 Subject: [PATCH] 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. --- include/pipewire_screencast.h | 4 +- include/screencast_common.h | 2 +- src/screencast/pipewire_screencast.c | 55 +++++++++++++++++++--------- src/screencast/wlr_screencast.c | 32 ++++++++-------- 4 files changed, 58 insertions(+), 35 deletions(-) diff --git a/include/pipewire_screencast.h b/include/pipewire_screencast.h index 2fb6aaa..6ad5f41 100644 --- a/include/pipewire_screencast.h +++ b/include/pipewire_screencast.h @@ -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); diff --git a/include/screencast_common.h b/include/screencast_common.h index 933cfd2..90f6ef1 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -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 { diff --git a/src/screencast/pipewire_screencast.c b/src/screencast/pipewire_screencast.c index 0b4d804..8384d85 100644 --- a/src/screencast/pipewire_screencast.c +++ b/src/screencast/pipewire_screencast.c @@ -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) { diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index 37f7dcc..0003a27 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -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