From 66db43ea0e9e65b36a81b47072512be464742ba5 Mon Sep 17 00:00:00 2001 From: columbarius Date: Wed, 18 Aug 2021 22:33:27 +0200 Subject: [PATCH] screencast: introduce xdpw_frame_state and xdpw_wlr_stream_finish The enum xdpw_frame_state is used to track the state of the xdpw_frame through the screencopy callbacks. xdpw_wlr_stream_finish is used as the new endpoint of the screencopy callbacks. Here we clean up the screencopy_frame, enqueue the pipewire buffer and restart the screencopy loop if needed. --- include/screencast_common.h | 9 +++- include/wlr_screencast.h | 3 +- src/screencast/pipewire_screencast.c | 2 +- src/screencast/wlr_screencast.c | 75 +++++++++++++++++++--------- 4 files changed, 63 insertions(+), 26 deletions(-) diff --git a/include/screencast_common.h b/include/screencast_common.h index f2ed4a0..819c9e7 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -29,6 +29,13 @@ enum xdpw_chooser_types { XDPW_CHOOSER_DMENU, }; +enum xdpw_frame_state { + XDPW_FRAME_STATE_NONE, + XDPW_FRAME_STATE_RENEG, + XDPW_FRAME_STATE_FAILED, + XDPW_FRAME_STATE_SUCCESS, +}; + struct xdpw_output_chooser { enum xdpw_chooser_types type; char *cmd; @@ -89,6 +96,7 @@ struct xdpw_screencast_instance { struct xdpw_screencast_context *ctx; bool initialized; struct xdpw_frame current_frame; + enum xdpw_frame_state frame_state; // pipewire struct pw_stream *stream; @@ -108,7 +116,6 @@ struct xdpw_screencast_instance { bool with_cursor; int err; bool quit; - bool copied; // fps limit struct fps_limit_state fps_limit; diff --git a/include/wlr_screencast.h b/include/wlr_screencast.h index a1585bc..15e535f 100644 --- a/include/wlr_screencast.h +++ b/include/wlr_screencast.h @@ -24,7 +24,8 @@ struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx struct wl_output *out, uint32_t id); struct xdpw_wlr_output *xdpw_wlr_output_chooser(struct xdpw_screencast_context *ctx); -void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast); +void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast); +void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast); void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast); #endif diff --git a/src/screencast/pipewire_screencast.c b/src/screencast/pipewire_screencast.c index d07e662..b82a810 100644 --- a/src/screencast/pipewire_screencast.c +++ b/src/screencast/pipewire_screencast.c @@ -57,7 +57,7 @@ static void pwr_handle_stream_state_changed(void *data, case PW_STREAM_STATE_STREAMING: cast->pwr_stream_state = true; if (!cast->wlr_frame) { - xdpw_wlr_register_cb(cast); + xdpw_wlr_frame_start(cast); } break; default: diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index e1c8308..af57d33 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -20,10 +20,29 @@ #include "logger.h" #include "fps_limit.h" -void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) { +void wlr_frame_free(struct xdpw_screencast_instance *cast) { zwlr_screencopy_frame_v1_destroy(cast->wlr_frame); cast->wlr_frame = NULL; logprint(TRACE, "wlroots: frame destroyed"); +} + +void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast) { + logprint(TRACE, "wlroots: finish screencopy"); + + wlr_frame_free(cast); + + if (!cast->pwr_stream_state) { + return; + } + + // Check if we have a buffer + if (cast->current_frame.current_pw_buffer) { + xdpw_pwr_enqueue_buffer(cast); + } + + if (cast->frame_state == XDPW_FRAME_STATE_RENEG) { + pwr_update_stream_param(cast); + } if (cast->quit || cast->err) { // TODO: revisit the exit condition (remove quit?) @@ -34,21 +53,35 @@ void xdpw_wlr_frame_free(struct xdpw_screencast_instance *cast) { } if (cast->pwr_stream_state) { - if (cast->copied) { + if (cast->frame_state == XDPW_FRAME_STATE_SUCCESS) { uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->framerate); - cast->copied = false; if (delay_ns > 0) { xdpw_add_timer(cast->ctx->state, delay_ns, - (xdpw_event_loop_timer_func_t) xdpw_wlr_register_cb, cast); + (xdpw_event_loop_timer_func_t) xdpw_wlr_frame_start, cast); } else { - xdpw_wlr_register_cb(cast); + xdpw_wlr_frame_start(cast); } } else { - xdpw_wlr_register_cb(cast); + xdpw_wlr_frame_start(cast); } } } +void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast) { + logprint(TRACE, "wlroots: start screencopy"); + if (cast->pwr_stream_state) { + xdpw_pwr_dequeue_buffer(cast); + + if (!cast->current_frame.current_pw_buffer) { + logprint(WARN, "wlroots: failed to dequeue buffer"); + // TODO: wait for next frame + } + } + + cast->frame_state = XDPW_FRAME_STATE_NONE; + xdpw_wlr_register_cb(cast); +} + static void wlr_frame_buffer_done(void *data, struct zwlr_screencopy_frame_v1 *frame); @@ -81,13 +114,14 @@ static void wlr_frame_buffer_done(void *data, struct xdpw_screencast_instance *cast = data; logprint(TRACE, "wlroots: buffer_done event handler"); - if (cast->pwr_stream_state) { - xdpw_pwr_dequeue_buffer(cast); + if (!cast->pwr_stream_state) { + xdpw_wlr_frame_finish(cast); + return; } if (!cast->current_frame.current_pw_buffer) { - logprint(WARN, "wlroots: failed to dequeue buffer"); - xdpw_wlr_frame_free(cast); + logprint(WARN, "wlroots: no current buffer"); + xdpw_wlr_frame_finish(cast); return; } @@ -97,12 +131,8 @@ static void wlr_frame_buffer_done(void *data, cast->pwr_format.size.width != cast->screencopy_frame.width || cast->pwr_format.size.height != cast->screencopy_frame.height) { logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream"); - - xdpw_pwr_enqueue_buffer(cast); - if (cast->pwr_stream_state) { - pwr_update_stream_param(cast); - } - xdpw_wlr_frame_free(cast); + cast->frame_state = XDPW_FRAME_STATE_RENEG; + xdpw_wlr_frame_finish(cast); return; } @@ -110,8 +140,8 @@ static void wlr_frame_buffer_done(void *data, if (cast->current_frame.size != cast->screencopy_frame.size || cast->current_frame.stride != cast->screencopy_frame.stride) { logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions"); - xdpw_pwr_enqueue_buffer(cast); - xdpw_wlr_frame_free(cast); + cast->frame_state = XDPW_FRAME_STATE_FAILED; + xdpw_wlr_frame_finish(cast); return; } @@ -154,11 +184,10 @@ static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, cast->current_frame.tv_sec = ((((uint64_t)tv_sec_hi) << 32) | tv_sec_lo); cast->current_frame.tv_nsec = tv_nsec; - cast->copied = true; - xdpw_pwr_enqueue_buffer(cast); + cast->frame_state = XDPW_FRAME_STATE_SUCCESS; - xdpw_wlr_frame_free(cast); + xdpw_wlr_frame_finish(cast); } static void wlr_frame_failed(void *data, @@ -168,9 +197,9 @@ static void wlr_frame_failed(void *data, logprint(TRACE, "wlroots: failed event handler"); cast->err = true; - xdpw_pwr_enqueue_buffer(cast); + cast->frame_state = XDPW_FRAME_STATE_FAILED; - xdpw_wlr_frame_free(cast); + xdpw_wlr_frame_finish(cast); } static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = {