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);