From 058bd97815709bf8374960c380fcdad0c31330d4 Mon Sep 17 00:00:00 2001 From: columbarius Date: Wed, 2 Mar 2022 01:57:11 +0100 Subject: [PATCH] screencast: introduce xdpw_buffer It showed that handling self contained buffers is much easier then have the metadata of the buffer seperated from the actual buffer attached to the screencast instance. The goal of the following changes is to separate the meta informations like requested buffer attributes and wlr_screencast data from the actual buffers. This enables us to: * Simplify the flow between the PipeWire loop and the wlroots one * Track and apply damage to each used buffer (ext_screencopy) --- include/screencast_common.h | 26 +++++++--- src/screencast/pipewire_screencast.c | 73 +++++++++++----------------- src/screencast/screencast.c | 2 + src/screencast/screencast_common.c | 46 +++++++++++++++++- src/screencast/wlr_screencast.c | 10 ++-- 5 files changed, 99 insertions(+), 58 deletions(-) diff --git a/include/screencast_common.h b/include/screencast_common.h index e78b523..933cfd2 100644 --- a/include/screencast_common.h +++ b/include/screencast_common.h @@ -50,13 +50,11 @@ struct xdpw_frame_damage { }; struct xdpw_frame { - uint32_t size; - uint32_t stride; bool y_invert; uint64_t tv_sec; uint32_t tv_nsec; struct xdpw_frame_damage damage; - struct wl_buffer *buffer; + struct xdpw_buffer *xdpw_buffer; struct pw_buffer *current_pw_buffer; }; @@ -68,6 +66,21 @@ struct xdpw_screencopy_frame_info { enum wl_shm_format format; }; +struct xdpw_buffer { + struct wl_list link; + + uint32_t width; + uint32_t height; + enum wl_shm_format format; + + int fd; + uint32_t size; + uint32_t stride; + uint32_t offset; + + struct wl_buffer *buffer; +}; + struct xdpw_screencast_context { // xdpw @@ -98,6 +111,7 @@ struct xdpw_screencast_instance { bool initialized; struct xdpw_frame current_frame; enum xdpw_frame_state frame_state; + struct wl_list buffer_list; // pipewire struct pw_stream *stream; @@ -136,9 +150,9 @@ struct xdpw_wlr_output { }; void randname(char *buf); -int anonymous_shm_open(void); -struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, int fd, - enum wl_shm_format fmt, int width, int height, int stride); +struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, + struct xdpw_screencopy_frame_info *frame_info); +void xdpw_buffer_destroy(struct xdpw_buffer *buffer); enum spa_video_format xdpw_format_pw_from_wl_shm(enum wl_shm_format format); enum spa_video_format xdpw_format_pw_strip_alpha(enum spa_video_format format); diff --git a/src/screencast/pipewire_screencast.c b/src/screencast/pipewire_screencast.c index 1e834ee..0b4d804 100644 --- a/src/screencast/pipewire_screencast.c +++ b/src/screencast/pipewire_screencast.c @@ -118,35 +118,24 @@ static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) { } logprint(TRACE, "pipewire: selected buffertype %u", d[0].type); - // Prepare buffer for choosen type - if (d[0].type == SPA_DATA_MemFd) { - d[0].maxsize = cast->screencopy_frame_info.size; - d[0].mapoffset = 0; - d[0].chunk->size = cast->screencopy_frame_info.size; - d[0].chunk->stride = cast->screencopy_frame_info.stride; - d[0].chunk->offset = 0; - d[0].flags = 0; - d[0].fd = anonymous_shm_open(); - d[0].data = NULL; - if (d[0].fd == -1) { - logprint(ERROR, "pipewire: unable to create anonymous filedescriptor"); - cast->err = 1; - return; - } - - if (ftruncate(d[0].fd, d[0].maxsize) < 0) { - logprint(ERROR, "pipewire: unable to truncate filedescriptor"); - close(d[0].fd); - d[0].fd = -1; - cast->err = 1; - return; - } - - // create wl_buffer - buffer->user_data = import_wl_shm_buffer(cast, d[0].fd, cast->screencopy_frame_info.format, - cast->screencopy_frame_info.width, cast->screencopy_frame_info.height, cast->screencopy_frame_info.stride); + struct xdpw_buffer *xdpw_buffer = xdpw_buffer_create(cast, &cast->screencopy_frame_info); + if (xdpw_buffer == NULL) { + logprint(ERROR, "pipewire: failed to create xdpw buffer"); + cast->err = 1; + return; } + wl_list_insert(&cast->buffer_list, &xdpw_buffer->link); + buffer->user_data = xdpw_buffer; + + d[0].maxsize = xdpw_buffer->size; + d[0].mapoffset = 0; + d[0].chunk->size = xdpw_buffer->size; + d[0].chunk->stride = xdpw_buffer->stride; + d[0].chunk->offset = xdpw_buffer->offset; + d[0].flags = 0; + d[0].fd = xdpw_buffer->fd; + d[0].data = NULL; } static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer) { @@ -154,20 +143,18 @@ static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer logprint(TRACE, "pipewire: remove buffer event handle"); - struct spa_data *d = buffer->buffer->datas; 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.buffer = NULL; + cast->current_frame.xdpw_buffer = NULL; } - switch (d[0].type) { - case SPA_DATA_MemFd: - wl_buffer_destroy(buffer->user_data); - close(d[0].fd); - break; - default: - break; + struct xdpw_buffer *xdpw_buffer = buffer->user_data; + if (xdpw_buffer) { + xdpw_buffer_destroy(xdpw_buffer); + buffer->user_data = NULL; } + buffer->buffer->datas[0].fd = -1; + buffer->user_data = NULL; } static const struct pw_stream_events pwr_stream_events = { @@ -184,15 +171,11 @@ void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast) { assert(cast->current_frame.current_pw_buffer == NULL); if ((cast->current_frame.current_pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) { logprint(WARN, "pipewire: out of buffers"); - cast->current_frame.buffer = NULL; + cast->current_frame.xdpw_buffer = NULL; return; } - struct spa_buffer *spa_buf = cast->current_frame.current_pw_buffer->buffer; - struct spa_data *d = spa_buf->datas; - cast->current_frame.size = d[0].chunk->size; - cast->current_frame.stride = d[0].chunk->stride; - cast->current_frame.buffer = cast->current_frame.current_pw_buffer->user_data; + cast->current_frame.xdpw_buffer = cast->current_frame.current_pw_buffer->user_data; } void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) { @@ -230,15 +213,15 @@ void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) { logprint(TRACE, "pipewire: fd %u", d[0].fd); logprint(TRACE, "pipewire: size %d", d[0].maxsize); logprint(TRACE, "pipewire: stride %d", d[0].chunk->stride); - logprint(TRACE, "pipewire: width %d", cast->screencopy_frame_info.width); - logprint(TRACE, "pipewire: height %d", cast->screencopy_frame_info.height); + logprint(TRACE, "pipewire: width %d", cast->current_frame.xdpw_buffer->width); + logprint(TRACE, "pipewire: height %d", cast->current_frame.xdpw_buffer->height); logprint(TRACE, "pipewire: y_invert %d", cast->current_frame.y_invert); logprint(TRACE, "********************"); pw_stream_queue_buffer(cast->stream, pw_buf); cast->current_frame.current_pw_buffer = NULL; - cast->current_frame.buffer = NULL; + cast->current_frame.xdpw_buffer = NULL; } void pwr_update_stream_param(struct xdpw_screencast_instance *cast) { diff --git a/src/screencast/screencast.c b/src/screencast/screencast.c index 0972b86..9fcc7de 100644 --- a/src/screencast/screencast.c +++ b/src/screencast/screencast.c @@ -70,6 +70,7 @@ void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx, cast->with_cursor = with_cursor; cast->refcount = 1; cast->node_id = SPA_ID_INVALID; + wl_list_init(&cast->buffer_list); logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount); wl_list_insert(&ctx->screencast_instances, &cast->link); logprint(INFO, "xdpw: %d active screencast instances", @@ -91,6 +92,7 @@ void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast) { wl_list_remove(&cast->link); xdpw_pwr_stream_destroy(cast); + assert(wl_list_length(&cast->buffer_list) == 0); free(cast); } diff --git a/src/screencast/screencast_common.c b/src/screencast/screencast_common.c index 4589b8c..a15a132 100644 --- a/src/screencast/screencast_common.c +++ b/src/screencast/screencast_common.c @@ -19,7 +19,7 @@ void randname(char *buf) { } } -int anonymous_shm_open(void) { +static int anonymous_shm_open(void) { char name[] = "/xdpw-shm-XXXXXX"; int retries = 100; @@ -38,7 +38,7 @@ int anonymous_shm_open(void) { return -1; } -struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, int fd, +static struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, int fd, enum wl_shm_format fmt, int width, int height, int stride) { struct xdpw_screencast_context *ctx = cast->ctx; int size = stride * height; @@ -55,6 +55,48 @@ struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, in return buffer; } +struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, + struct xdpw_screencopy_frame_info *frame_info) { + struct xdpw_buffer *buffer = calloc(1, sizeof(struct xdpw_buffer)); + buffer->width = frame_info->width; + buffer->height = frame_info->height; + buffer->format = frame_info->format; + buffer->size = frame_info->size; + buffer->stride = frame_info->stride; + buffer->offset = 0; + buffer->fd = anonymous_shm_open(); + if (buffer->fd == -1) { + logprint(ERROR, "xdpw: unable to create anonymous filedescriptor"); + free(buffer); + return NULL; + } + + if (ftruncate(buffer->fd, buffer->size) < 0) { + logprint(ERROR, "xdpw: unable to truncate filedescriptor"); + close(buffer->fd); + free(buffer); + return NULL; + } + + buffer->buffer = import_wl_shm_buffer(cast, buffer->fd, frame_info->format, + frame_info->width, frame_info->height, frame_info->stride); + if (buffer->buffer == NULL) { + logprint(ERROR, "xdpw: unable to create wl_buffer"); + close(buffer->fd); + free(buffer); + return NULL; + } + + return buffer; +} + +void xdpw_buffer_destroy(struct xdpw_buffer *buffer) { + wl_buffer_destroy(buffer->buffer); + close(buffer->fd); + wl_list_remove(&buffer->link); + free(buffer); +} + enum spa_video_format xdpw_format_pw_from_wl_shm(enum wl_shm_format format) { switch (format) { case WL_SHM_FORMAT_ARGB8888: diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c index baf46bd..37f7dcc 100644 --- a/src/screencast/wlr_screencast.c +++ b/src/screencast/wlr_screencast.c @@ -134,18 +134,18 @@ static void wlr_frame_buffer_done(void *data, return; } + assert(cast->current_frame.xdpw_buffer); + // Check if dequeued buffer is compatible with announced buffer - if (cast->current_frame.size != cast->screencopy_frame_info.size || - cast->current_frame.stride != cast->screencopy_frame_info.stride) { + if (cast->current_frame.xdpw_buffer->size != cast->screencopy_frame_info.size || + cast->current_frame.xdpw_buffer->stride != cast->screencopy_frame_info.stride) { logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions"); cast->frame_state = XDPW_FRAME_STATE_FAILED; xdpw_wlr_frame_finish(cast); return; } - assert(cast->current_frame.buffer); - - zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->current_frame.buffer); + zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->current_frame.xdpw_buffer->buffer); logprint(TRACE, "wlroots: frame copied"); fps_limit_measure_start(&cast->fps_limit, cast->framerate);