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.
This commit is contained in:
columbarius 2021-08-18 22:33:27 +02:00
parent b986020050
commit 66db43ea0e
4 changed files with 63 additions and 26 deletions

View File

@ -29,6 +29,13 @@ enum xdpw_chooser_types {
XDPW_CHOOSER_DMENU, 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 { struct xdpw_output_chooser {
enum xdpw_chooser_types type; enum xdpw_chooser_types type;
char *cmd; char *cmd;
@ -89,6 +96,7 @@ struct xdpw_screencast_instance {
struct xdpw_screencast_context *ctx; struct xdpw_screencast_context *ctx;
bool initialized; bool initialized;
struct xdpw_frame current_frame; struct xdpw_frame current_frame;
enum xdpw_frame_state frame_state;
// pipewire // pipewire
struct pw_stream *stream; struct pw_stream *stream;
@ -108,7 +116,6 @@ struct xdpw_screencast_instance {
bool with_cursor; bool with_cursor;
int err; int err;
bool quit; bool quit;
bool copied;
// fps limit // fps limit
struct fps_limit_state fps_limit; struct fps_limit_state fps_limit;

View File

@ -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 wl_output *out, uint32_t id);
struct xdpw_wlr_output *xdpw_wlr_output_chooser(struct xdpw_screencast_context *ctx); 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); void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast);
#endif #endif

View File

@ -57,7 +57,7 @@ static void pwr_handle_stream_state_changed(void *data,
case PW_STREAM_STATE_STREAMING: case PW_STREAM_STATE_STREAMING:
cast->pwr_stream_state = true; cast->pwr_stream_state = true;
if (!cast->wlr_frame) { if (!cast->wlr_frame) {
xdpw_wlr_register_cb(cast); xdpw_wlr_frame_start(cast);
} }
break; break;
default: default:

View File

@ -20,10 +20,29 @@
#include "logger.h" #include "logger.h"
#include "fps_limit.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); zwlr_screencopy_frame_v1_destroy(cast->wlr_frame);
cast->wlr_frame = NULL; cast->wlr_frame = NULL;
logprint(TRACE, "wlroots: frame destroyed"); 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) { if (cast->quit || cast->err) {
// TODO: revisit the exit condition (remove quit?) // 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->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); uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->framerate);
cast->copied = false;
if (delay_ns > 0) { if (delay_ns > 0) {
xdpw_add_timer(cast->ctx->state, delay_ns, 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 { } else {
xdpw_wlr_register_cb(cast); xdpw_wlr_frame_start(cast);
} }
} else { } 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, static void wlr_frame_buffer_done(void *data,
struct zwlr_screencopy_frame_v1 *frame); struct zwlr_screencopy_frame_v1 *frame);
@ -81,13 +114,14 @@ static void wlr_frame_buffer_done(void *data,
struct xdpw_screencast_instance *cast = data; struct xdpw_screencast_instance *cast = data;
logprint(TRACE, "wlroots: buffer_done event handler"); logprint(TRACE, "wlroots: buffer_done event handler");
if (cast->pwr_stream_state) { if (!cast->pwr_stream_state) {
xdpw_pwr_dequeue_buffer(cast); xdpw_wlr_frame_finish(cast);
return;
} }
if (!cast->current_frame.current_pw_buffer) { if (!cast->current_frame.current_pw_buffer) {
logprint(WARN, "wlroots: failed to dequeue buffer"); logprint(WARN, "wlroots: no current buffer");
xdpw_wlr_frame_free(cast); xdpw_wlr_frame_finish(cast);
return; 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.width != cast->screencopy_frame.width ||
cast->pwr_format.size.height != cast->screencopy_frame.height) { cast->pwr_format.size.height != cast->screencopy_frame.height) {
logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream"); logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream");
cast->frame_state = XDPW_FRAME_STATE_RENEG;
xdpw_pwr_enqueue_buffer(cast); xdpw_wlr_frame_finish(cast);
if (cast->pwr_stream_state) {
pwr_update_stream_param(cast);
}
xdpw_wlr_frame_free(cast);
return; return;
} }
@ -110,8 +140,8 @@ static void wlr_frame_buffer_done(void *data,
if (cast->current_frame.size != cast->screencopy_frame.size || if (cast->current_frame.size != cast->screencopy_frame.size ||
cast->current_frame.stride != cast->screencopy_frame.stride) { cast->current_frame.stride != cast->screencopy_frame.stride) {
logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions"); logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions");
xdpw_pwr_enqueue_buffer(cast); cast->frame_state = XDPW_FRAME_STATE_FAILED;
xdpw_wlr_frame_free(cast); xdpw_wlr_frame_finish(cast);
return; 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_sec = ((((uint64_t)tv_sec_hi) << 32) | tv_sec_lo);
cast->current_frame.tv_nsec = tv_nsec; 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, static void wlr_frame_failed(void *data,
@ -168,9 +197,9 @@ static void wlr_frame_failed(void *data,
logprint(TRACE, "wlroots: failed event handler"); logprint(TRACE, "wlroots: failed event handler");
cast->err = true; 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 = { static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = {