From 463c1d61f0f4cc3b02eebf69bedfde578a8ad128 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 17 Jul 2024 20:54:37 +0200 Subject: Portal: make resizing not glitched, clear background on resize, proper handling of multithreaded plane fd --- src/capture/portal.c | 68 +++++++++++++++++++++++++++++++++------------------- src/pipewire.c | 23 ++++++++++++++---- 2 files changed, 62 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/capture/portal.c b/src/capture/portal.c index a6066a9..5af5163 100644 --- a/src/capture/portal.c +++ b/src/capture/portal.c @@ -24,10 +24,9 @@ typedef struct { gsr_dbus dbus; char *session_handle; - uint32_t pipewire_node; - int pipewire_fd; gsr_pipewire pipewire; vec2i capture_size; + int plane_fd; } gsr_capture_portal; static void gsr_capture_portal_stop(gsr_capture_portal *self) { @@ -41,9 +40,9 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) { self->cursor_texture_id = 0; } - if(self->pipewire_fd > 0) { - close(self->pipewire_fd); - self->pipewire_fd = -1; + if(self->plane_fd > 0) { + close(self->plane_fd); + self->plane_fd = 0; } gsr_pipewire_deinit(&self->pipewire); @@ -163,7 +162,10 @@ static void gsr_capture_portal_get_restore_token_from_cache(char *buffer, size_t fclose(f); } -static bool gsr_capture_portal_setup_dbus(gsr_capture_portal *self) { +static bool gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire_fd, uint32_t *pipewire_node) { + *pipewire_fd = 0; + *pipewire_node = 0; + char restore_token[1024]; restore_token[0] = '\0'; if(self->params.restore_portal_session) @@ -185,7 +187,7 @@ static bool gsr_capture_portal_setup_dbus(gsr_capture_portal *self) { } fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: Start\n"); - if(!gsr_dbus_screencast_start(&self->dbus, self->session_handle, &self->pipewire_node)) { + if(!gsr_dbus_screencast_start(&self->dbus, self->session_handle, pipewire_node)) { fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: Start failed\n"); return false; } @@ -195,7 +197,7 @@ static bool gsr_capture_portal_setup_dbus(gsr_capture_portal *self) { gsr_capture_portal_save_restore_token(screencast_restore_token); fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: OpenPipeWireRemote\n"); - if(!gsr_dbus_screencast_open_pipewire_remote(&self->dbus, self->session_handle, &self->pipewire_fd)) { + if(!gsr_dbus_screencast_open_pipewire_remote(&self->dbus, self->session_handle, pipewire_fd)) { fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n"); return false; } @@ -211,7 +213,13 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) { const double start_time = clock_get_monotonic_seconds(); while(clock_get_monotonic_seconds() - start_time < 5.0) { - if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region)) { + int plane_fd = 0; + if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region, &plane_fd)) { + if(plane_fd > 0) { + close(plane_fd); + plane_fd = 0; + } + self->capture_size.x = region.width; self->capture_size.y = region.height; fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire negotiation finished\n"); @@ -229,19 +237,21 @@ static int gsr_capture_portal_start(gsr_capture *cap, AVCodecContext *video_code gsr_capture_portal_create_input_textures(self); - if(!gsr_capture_portal_setup_dbus(self)) { + int pipewire_fd = 0; + uint32_t pipewire_node = 0; + if(!gsr_capture_portal_setup_dbus(self, &pipewire_fd, &pipewire_node)) { gsr_capture_portal_stop(self); return -1; } fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n"); /* TODO: support hdr when pipewire supports it */ - if(!gsr_pipewire_init(&self->pipewire, self->pipewire_fd, self->pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) { - fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", self->pipewire_fd, self->pipewire_node); + /* gsr_pipewire closes the pipewire fd, even on failure */ + if(!gsr_pipewire_init(&self->pipewire, pipewire_fd, pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) { + fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node); gsr_capture_portal_stop(self); return -1; } - self->pipewire_fd = -1; fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire setup finished\n"); if(!gsr_capture_portal_get_frame_dimensions(self)) { @@ -269,25 +279,31 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo (void)color_conversion; gsr_capture_portal *self = cap->priv; + if(self->plane_fd > 0) { + close(self->plane_fd); + self->plane_fd = 0; + } + //egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f); self->params.egl->glClear(0); - vec2i content_size = self->capture_size; - /* TODO: Handle formats other than RGB(a) */ gsr_pipewire_region region = {0, 0, 0, 0}; gsr_pipewire_region cursor_region = {0, 0, 0, 0}; - if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region)) { - content_size.x = region.width; - content_size.y = region.height; + if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, ®ion, &cursor_region, &self->plane_fd)) { + if(region.width != self->capture_size.x || region.height != self->capture_size.y) { + gsr_color_conversion_clear(color_conversion); + self->capture_size.x = region.width; + self->capture_size.y = region.height; + } } - const int target_x = max_int(0, frame->width / 2 - content_size.x / 2); - const int target_y = max_int(0, frame->height / 2 - content_size.y / 2); + const int target_x = max_int(0, frame->width / 2 - self->capture_size.x / 2); + const int target_y = max_int(0, frame->height / 2 - self->capture_size.y / 2); gsr_color_conversion_draw(color_conversion, self->input_texture_id, - (vec2i){target_x, target_y}, content_size, - (vec2i){region.x, region.y}, content_size, + (vec2i){target_x, target_y}, self->capture_size, + (vec2i){region.x, region.y}, self->capture_size, 0.0f, false); const vec2i cursor_pos = { @@ -296,7 +312,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo }; self->params.egl->glEnable(GL_SCISSOR_TEST); - self->params.egl->glScissor(target_x, target_y, content_size.x, content_size.y); + self->params.egl->glScissor(target_x, target_y, self->capture_size.x, self->capture_size.y); gsr_color_conversion_draw(color_conversion, self->cursor_texture_id, (vec2i){cursor_pos.x, cursor_pos.y}, (vec2i){cursor_region.width, cursor_region.height}, (vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height}, @@ -325,8 +341,12 @@ static bool gsr_capture_portal_should_stop(gsr_capture *cap, bool *err) { } static void gsr_capture_portal_capture_end(gsr_capture *cap, AVFrame *frame) { - (void)cap; (void)frame; + gsr_capture_portal *self = cap->priv; + if(self->plane_fd > 0) { + close(self->plane_fd); + self->plane_fd = 0; + } } static gsr_source_color gsr_capture_portal_get_source_color(gsr_capture *cap) { diff --git a/src/pipewire.c b/src/pipewire.c index 981d2bc..985a80a 100644 --- a/src/pipewire.c +++ b/src/pipewire.c @@ -109,8 +109,13 @@ static void on_process_cb(void *user_data) { pthread_mutex_lock(&self->mutex); if(buffer->datas[0].type == SPA_DATA_DmaBuf) { + if(self->dmabuf_data.fd > 0) { + close(self->dmabuf_data.fd); + self->dmabuf_data.fd = -1; + } + if(buffer->n_datas > 0) { - self->dmabuf_data.fd = buffer->datas[0].fd; + self->dmabuf_data.fd = dup(buffer->datas[0].fd); self->dmabuf_data.offset = buffer->datas[0].chunk->offset; self->dmabuf_data.stride = buffer->datas[0].chunk->stride; } else { @@ -525,7 +530,12 @@ void gsr_pipewire_deinit(gsr_pipewire *self) { if(self->fd > 0) { close(self->fd); - self->fd = 0; + self->fd = -1; + } + + if(self->dmabuf_data.fd > 0) { + close(self->dmabuf_data.fd); + self->dmabuf_data.fd = -1; } self->negotiated = false; @@ -548,8 +558,8 @@ void gsr_pipewire_deinit(gsr_pipewire *self) { } } -/* TODO: Do this in the thread instead, otherwise this is not guaranteed to always work and may produce glitched output (happens now when resizing the captured window) */ -bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsigned int cursor_texture_id, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region) { +bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsigned int cursor_texture_id, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, int *plane_fd) { + *plane_fd = -1; pthread_mutex_lock(&self->mutex); if(!self->negotiated || self->dmabuf_data.fd <= 0) { @@ -557,12 +567,15 @@ bool gsr_pipewire_map_texture(gsr_pipewire *self, unsigned int texture_id, unsig return false; } + *plane_fd = self->dmabuf_data.fd; + self->dmabuf_data.fd = -1; + /* TODO: Support multiple planes */ const intptr_t img_attr[] = { EGL_LINUX_DRM_FOURCC_EXT, spa_video_format_to_drm_format(self->format.info.raw.format), EGL_WIDTH, self->format.info.raw.size.width, EGL_HEIGHT, self->format.info.raw.size.height, - EGL_DMA_BUF_PLANE0_FD_EXT, self->dmabuf_data.fd, + EGL_DMA_BUF_PLANE0_FD_EXT, *plane_fd, EGL_DMA_BUF_PLANE0_OFFSET_EXT, self->dmabuf_data.offset, EGL_DMA_BUF_PLANE0_PITCH_EXT, self->dmabuf_data.stride, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, self->format.info.raw.modifier & 0xFFFFFFFFULL, -- cgit v1.2.3