Portal: make resizing not glitched, clear background on resize, proper handling of multithreaded plane fd

This commit is contained in:
dec05eba
2024-07-17 20:54:37 +02:00
parent 0558779638
commit 463c1d61f0
4 changed files with 66 additions and 33 deletions

View File

@@ -78,6 +78,6 @@ typedef struct {
bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl); bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl);
void gsr_pipewire_deinit(gsr_pipewire *self); void gsr_pipewire_deinit(gsr_pipewire *self);
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);
#endif /* GSR_PIPEWIRE_H */ #endif /* GSR_PIPEWIRE_H */

View File

@@ -329,12 +329,12 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
} }
void cleanup_socket(gsr_kms_client *self, bool kill_server) { void cleanup_socket(gsr_kms_client *self, bool kill_server) {
if(self->initial_client_fd != -1) { if(self->initial_client_fd > 0) {
close(self->initial_client_fd); close(self->initial_client_fd);
self->initial_client_fd = -1; self->initial_client_fd = -1;
} }
if(self->initial_socket_fd != -1) { if(self->initial_socket_fd > 0) {
close(self->initial_socket_fd); close(self->initial_socket_fd);
self->initial_socket_fd = -1; self->initial_socket_fd = -1;
} }
@@ -348,7 +348,7 @@ void cleanup_socket(gsr_kms_client *self, bool kill_server) {
} }
} }
if(kill_server && self->kms_server_pid != -1) { if(kill_server && self->kms_server_pid > 0) {
kill(self->kms_server_pid, SIGKILL); kill(self->kms_server_pid, SIGKILL);
//int status; //int status;
//waitpid(self->kms_server_pid, &status, 0); //waitpid(self->kms_server_pid, &status, 0);

View File

@@ -24,10 +24,9 @@ typedef struct {
gsr_dbus dbus; gsr_dbus dbus;
char *session_handle; char *session_handle;
uint32_t pipewire_node;
int pipewire_fd;
gsr_pipewire pipewire; gsr_pipewire pipewire;
vec2i capture_size; vec2i capture_size;
int plane_fd;
} gsr_capture_portal; } gsr_capture_portal;
static void gsr_capture_portal_stop(gsr_capture_portal *self) { 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; self->cursor_texture_id = 0;
} }
if(self->pipewire_fd > 0) { if(self->plane_fd > 0) {
close(self->pipewire_fd); close(self->plane_fd);
self->pipewire_fd = -1; self->plane_fd = 0;
} }
gsr_pipewire_deinit(&self->pipewire); 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); 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]; char restore_token[1024];
restore_token[0] = '\0'; restore_token[0] = '\0';
if(self->params.restore_portal_session) 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"); 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"); fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: Start failed\n");
return false; 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); gsr_capture_portal_save_restore_token(screencast_restore_token);
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: OpenPipeWireRemote\n"); 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"); fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n");
return false; 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(); const double start_time = clock_get_monotonic_seconds();
while(clock_get_monotonic_seconds() - start_time < 5.0) { while(clock_get_monotonic_seconds() - start_time < 5.0) {
if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, &region, &cursor_region)) { int plane_fd = 0;
if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, &region, &cursor_region, &plane_fd)) {
if(plane_fd > 0) {
close(plane_fd);
plane_fd = 0;
}
self->capture_size.x = region.width; self->capture_size.x = region.width;
self->capture_size.y = region.height; self->capture_size.y = region.height;
fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire negotiation finished\n"); 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); 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); gsr_capture_portal_stop(self);
return -1; return -1;
} }
fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n"); fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n");
/* TODO: support hdr when pipewire supports it */ /* 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)) { /* gsr_pipewire closes the pipewire fd, even on failure */
fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", self->pipewire_fd, self->pipewire_node); 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); gsr_capture_portal_stop(self);
return -1; return -1;
} }
self->pipewire_fd = -1;
fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire setup finished\n"); fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire setup finished\n");
if(!gsr_capture_portal_get_frame_dimensions(self)) { 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; (void)color_conversion;
gsr_capture_portal *self = cap->priv; 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); //egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
self->params.egl->glClear(0); self->params.egl->glClear(0);
vec2i content_size = self->capture_size;
/* TODO: Handle formats other than RGB(a) */ /* TODO: Handle formats other than RGB(a) */
gsr_pipewire_region region = {0, 0, 0, 0}; gsr_pipewire_region region = {0, 0, 0, 0};
gsr_pipewire_region cursor_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, &region, &cursor_region)) { if(gsr_pipewire_map_texture(&self->pipewire, self->input_texture_id, self->cursor_texture_id, &region, &cursor_region, &self->plane_fd)) {
content_size.x = region.width; if(region.width != self->capture_size.x || region.height != self->capture_size.y) {
content_size.y = region.height; 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_x = max_int(0, frame->width / 2 - self->capture_size.x / 2);
const int target_y = max_int(0, frame->height / 2 - content_size.y / 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, gsr_color_conversion_draw(color_conversion, self->input_texture_id,
(vec2i){target_x, target_y}, content_size, (vec2i){target_x, target_y}, self->capture_size,
(vec2i){region.x, region.y}, content_size, (vec2i){region.x, region.y}, self->capture_size,
0.0f, false); 0.0f, false);
const vec2i cursor_pos = { 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->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, 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){cursor_pos.x, cursor_pos.y}, (vec2i){cursor_region.width, cursor_region.height},
(vec2i){0, 0}, (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) { static void gsr_capture_portal_capture_end(gsr_capture *cap, AVFrame *frame) {
(void)cap;
(void)frame; (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) { static gsr_source_color gsr_capture_portal_get_source_color(gsr_capture *cap) {

View File

@@ -109,8 +109,13 @@ static void on_process_cb(void *user_data) {
pthread_mutex_lock(&self->mutex); pthread_mutex_lock(&self->mutex);
if(buffer->datas[0].type == SPA_DATA_DmaBuf) { 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) { 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.offset = buffer->datas[0].chunk->offset;
self->dmabuf_data.stride = buffer->datas[0].chunk->stride; self->dmabuf_data.stride = buffer->datas[0].chunk->stride;
} else { } else {
@@ -525,7 +530,12 @@ void gsr_pipewire_deinit(gsr_pipewire *self) {
if(self->fd > 0) { if(self->fd > 0) {
close(self->fd); 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; 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, int *plane_fd) {
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) { *plane_fd = -1;
pthread_mutex_lock(&self->mutex); pthread_mutex_lock(&self->mutex);
if(!self->negotiated || self->dmabuf_data.fd <= 0) { 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; return false;
} }
*plane_fd = self->dmabuf_data.fd;
self->dmabuf_data.fd = -1;
/* TODO: Support multiple planes */ /* TODO: Support multiple planes */
const intptr_t img_attr[] = { const intptr_t img_attr[] = {
EGL_LINUX_DRM_FOURCC_EXT, spa_video_format_to_drm_format(self->format.info.raw.format), 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_WIDTH, self->format.info.raw.size.width,
EGL_HEIGHT, self->format.info.raw.size.height, 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_OFFSET_EXT, self->dmabuf_data.offset,
EGL_DMA_BUF_PLANE0_PITCH_EXT, self->dmabuf_data.stride, EGL_DMA_BUF_PLANE0_PITCH_EXT, self->dmabuf_data.stride,
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, self->format.info.raw.modifier & 0xFFFFFFFFULL, EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, self->format.info.raw.modifier & 0xFFFFFFFFULL,