mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-11-22 14:35:57 +01:00
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:
parent
058bd97815
commit
074e62d0c3
4 changed files with 58 additions and 35 deletions
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue