aboutsummaryrefslogtreecommitdiff
path: root/src/capture
diff options
context:
space:
mode:
Diffstat (limited to 'src/capture')
-rw-r--r--src/capture/kms.c83
-rw-r--r--src/capture/portal.c384
-rw-r--r--src/capture/xcomposite.c26
3 files changed, 434 insertions, 59 deletions
diff --git a/src/capture/kms.c b/src/capture/kms.c
index a9ce73c..e7b0b59 100644
--- a/src/capture/kms.c
+++ b/src/capture/kms.c
@@ -3,6 +3,7 @@
#include "../../include/color_conversion.h"
#include "../../kms/client/kms_client.h"
+#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
@@ -38,8 +39,8 @@ typedef struct {
gsr_monitor_rotation monitor_rotation;
- unsigned int input_texture;
- unsigned int cursor_texture;
+ unsigned int input_texture_id;
+ unsigned int cursor_texture_id;
} gsr_capture_kms;
static void gsr_capture_kms_cleanup_kms_fds(gsr_capture_kms *self) {
@@ -52,14 +53,14 @@ static void gsr_capture_kms_cleanup_kms_fds(gsr_capture_kms *self) {
}
static void gsr_capture_kms_stop(gsr_capture_kms *self) {
- if(self->input_texture) {
- self->params.egl->glDeleteTextures(1, &self->input_texture);
- self->input_texture = 0;
+ if(self->input_texture_id) {
+ self->params.egl->glDeleteTextures(1, &self->input_texture_id);
+ self->input_texture_id = 0;
}
- if(self->cursor_texture) {
- self->params.egl->glDeleteTextures(1, &self->cursor_texture);
- self->cursor_texture = 0;
+ if(self->cursor_texture_id) {
+ self->params.egl->glDeleteTextures(1, &self->cursor_texture_id);
+ self->cursor_texture_id = 0;
}
gsr_capture_kms_cleanup_kms_fds(self);
@@ -70,25 +71,25 @@ static int max_int(int a, int b) {
return a > b ? a : b;
}
-static void gsr_capture_kms_create_input_textures(gsr_capture_kms *self) {
- self->params.egl->glGenTextures(1, &self->input_texture);
- self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture);
+static void gsr_capture_kms_create_input_texture_ids(gsr_capture_kms *self) {
+ self->params.egl->glGenTextures(1, &self->input_texture_id);
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture_id);
self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
- const bool cursor_texture_is_external = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA;
- const int cursor_texture_target = cursor_texture_is_external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
+ const bool cursor_texture_id_is_external = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA;
+ const int cursor_texture_id_target = cursor_texture_id_is_external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
- self->params.egl->glGenTextures(1, &self->cursor_texture);
- self->params.egl->glBindTexture(cursor_texture_target, self->cursor_texture);
- self->params.egl->glTexParameteri(cursor_texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- self->params.egl->glTexParameteri(cursor_texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- self->params.egl->glTexParameteri(cursor_texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- self->params.egl->glTexParameteri(cursor_texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- self->params.egl->glBindTexture(cursor_texture_target, 0);
+ self->params.egl->glGenTextures(1, &self->cursor_texture_id);
+ self->params.egl->glBindTexture(cursor_texture_id_target, self->cursor_texture_id);
+ self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ self->params.egl->glBindTexture(cursor_texture_id_target, 0);
}
/* TODO: On monitor reconfiguration, find monitor x, y, width and height again. Do the same for nvfbc. */
@@ -119,7 +120,7 @@ static void monitor_callback(const gsr_monitor *monitor, void *userdata) {
static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame *frame) {
gsr_capture_kms *self = cap->priv;
- gsr_capture_kms_create_input_textures(self);
+ gsr_capture_kms_create_input_texture_ids(self);
gsr_monitor monitor;
self->monitor_id.num_connector_ids = 0;
@@ -268,7 +269,7 @@ static vec2i swap_vec2i(vec2i value) {
static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) {
gsr_capture_kms *self = cap->priv;
const bool screen_plane_use_modifiers = self->params.egl->gpu_info.vendor != GSR_GPU_VENDOR_AMD;
- const bool cursor_texture_is_external = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA;
+ const bool cursor_texture_id_is_external = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA;
//egl->glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
self->params.egl->glClear(0);
@@ -337,12 +338,12 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
// Assertion pic->display_order == pic->encode_order failed at libavcodec/vaapi_encode_h265.c:765
// kms server info: kms client shutdown, shutting down the server
intptr_t img_attr[18] = {
- EGL_LINUX_DRM_FOURCC_EXT, drm_fd->pixel_format,
- EGL_WIDTH, drm_fd->width,
- EGL_HEIGHT, drm_fd->height,
- EGL_DMA_BUF_PLANE0_FD_EXT, drm_fd->fd,
- EGL_DMA_BUF_PLANE0_OFFSET_EXT, drm_fd->offset,
- EGL_DMA_BUF_PLANE0_PITCH_EXT, drm_fd->pitch,
+ EGL_LINUX_DRM_FOURCC_EXT, drm_fd->pixel_format,
+ EGL_WIDTH, drm_fd->width,
+ EGL_HEIGHT, drm_fd->height,
+ EGL_DMA_BUF_PLANE0_FD_EXT, drm_fd->fd,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, drm_fd->offset,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, drm_fd->pitch,
};
if(screen_plane_use_modifiers) {
@@ -360,7 +361,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
}
EGLImage image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr);
- self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture);
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture_id);
self->params.egl->glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
self->params.egl->eglDestroyImage(self->params.egl->egl_display, image);
self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
@@ -374,7 +375,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
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,
+ gsr_color_conversion_draw(color_conversion, self->input_texture_id,
(vec2i){target_x, target_y}, self->capture_size,
capture_pos, self->capture_size,
texture_rotation, false);
@@ -410,20 +411,20 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
cursor_pos.y += target_y;
const intptr_t img_attr_cursor[] = {
- EGL_LINUX_DRM_FOURCC_EXT, cursor_drm_fd->pixel_format,
- EGL_WIDTH, cursor_drm_fd->width,
- EGL_HEIGHT, cursor_drm_fd->height,
- EGL_DMA_BUF_PLANE0_FD_EXT, cursor_drm_fd->fd,
- EGL_DMA_BUF_PLANE0_OFFSET_EXT, cursor_drm_fd->offset,
- EGL_DMA_BUF_PLANE0_PITCH_EXT, cursor_drm_fd->pitch,
+ EGL_LINUX_DRM_FOURCC_EXT, cursor_drm_fd->pixel_format,
+ EGL_WIDTH, cursor_drm_fd->width,
+ EGL_HEIGHT, cursor_drm_fd->height,
+ EGL_DMA_BUF_PLANE0_FD_EXT, cursor_drm_fd->fd,
+ EGL_DMA_BUF_PLANE0_OFFSET_EXT, cursor_drm_fd->offset,
+ EGL_DMA_BUF_PLANE0_PITCH_EXT, cursor_drm_fd->pitch,
EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, cursor_drm_fd->modifier & 0xFFFFFFFFULL,
EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, cursor_drm_fd->modifier >> 32ULL,
EGL_NONE
};
EGLImage cursor_image = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr_cursor);
- const int target = cursor_texture_is_external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
- self->params.egl->glBindTexture(target, self->cursor_texture);
+ const int target = cursor_texture_id_is_external ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
+ self->params.egl->glBindTexture(target, self->cursor_texture_id);
self->params.egl->glEGLImageTargetTexture2DOES(target, cursor_image);
self->params.egl->eglDestroyImage(self->params.egl->egl_display, cursor_image);
self->params.egl->glBindTexture(target, 0);
@@ -431,17 +432,15 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
self->params.egl->glEnable(GL_SCISSOR_TEST);
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,
+ gsr_color_conversion_draw(color_conversion, self->cursor_texture_id,
cursor_pos, cursor_size,
(vec2i){0, 0}, cursor_size,
- texture_rotation, cursor_texture_is_external);
+ texture_rotation, cursor_texture_id_is_external);
self->params.egl->glDisable(GL_SCISSOR_TEST);
}
self->params.egl->eglSwapBuffers(self->params.egl->egl_display, self->params.egl->egl_surface);
-
- // TODO: Do software specific video encoder conversion here
//self->params.egl->glFlush();
//self->params.egl->glFinish();
diff --git a/src/capture/portal.c b/src/capture/portal.c
new file mode 100644
index 0000000..77da206
--- /dev/null
+++ b/src/capture/portal.c
@@ -0,0 +1,384 @@
+#include "../../include/capture/portal.h"
+#include "../../include/color_conversion.h"
+#include "../../include/egl.h"
+#include "../../include/utils.h"
+#include "../../include/dbus.h"
+#include "../../include/pipewire.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <libavcodec/avcodec.h>
+
+typedef struct {
+ gsr_capture_portal_params params;
+
+ bool should_stop;
+ bool stop_is_error;
+
+ unsigned int input_texture_id;
+ unsigned int cursor_texture_id;
+
+ gsr_dbus dbus;
+ char *session_handle;
+
+ uint32_t pipewire_node;
+ int pipewire_fd;
+ gsr_pipewire pipewire;
+ vec2i capture_size;
+} gsr_capture_portal;
+
+static void gsr_capture_portal_stop(gsr_capture_portal *self) {
+ if(self->input_texture_id) {
+ self->params.egl->glDeleteTextures(1, &self->input_texture_id);
+ self->input_texture_id = 0;
+ }
+
+ if(self->cursor_texture_id) {
+ self->params.egl->glDeleteTextures(1, &self->cursor_texture_id);
+ self->cursor_texture_id = 0;
+ }
+
+ if(self->pipewire_fd > 0) {
+ close(self->pipewire_fd);
+ self->pipewire_fd = -1;
+ }
+
+ gsr_pipewire_deinit(&self->pipewire);
+
+ if(self->session_handle) {
+ free(self->session_handle);
+ self->session_handle = NULL;
+ }
+
+ gsr_dbus_deinit(&self->dbus);
+}
+
+static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) {
+ self->params.egl->glGenTextures(1, &self->input_texture_id);
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, self->input_texture_id);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
+
+ self->params.egl->glGenTextures(1, &self->cursor_texture_id);
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, self->cursor_texture_id);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
+}
+
+static void get_gpu_screen_recorder_config_directory_path(char *buffer, size_t buffer_size) {
+ const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
+ if(xdg_config_home) {
+ snprintf(buffer, buffer_size, "%s/gpu-screen-recorder", xdg_config_home);
+ } else {
+ const char *home = getenv("HOME");
+ if(!home)
+ home = "/tmp";
+ snprintf(buffer, buffer_size, "%s/.config/gpu-screen-recorder", home);
+ }
+}
+
+static void get_gpu_screen_recorder_restore_token_path(char *buffer, size_t buffer_size) {
+ const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
+ if(xdg_config_home) {
+ snprintf(buffer, buffer_size, "%s/gpu-screen-recorder/restore_token", xdg_config_home);
+ } else {
+ const char *home = getenv("HOME");
+ if(!home)
+ home = "/tmp";
+ snprintf(buffer, buffer_size, "%s/.config/gpu-screen-recorder/restore_token", home);
+ }
+}
+
+static void gsr_capture_portal_save_restore_token(const char *restore_token) {
+ char config_path[PATH_MAX];
+ config_path[0] = '\0';
+ get_gpu_screen_recorder_config_directory_path(config_path, sizeof(config_path));
+
+ if(create_directory_recursive(config_path) != 0) {
+ fprintf(stderr, "gsr warning: gsr_capture_portal_save_restore_token: failed to create directory (%s) for restore token\n", config_path);
+ return;
+ }
+
+ char restore_token_path[PATH_MAX];
+ restore_token_path[0] = '\0';
+ get_gpu_screen_recorder_restore_token_path(restore_token_path, sizeof(restore_token_path));
+
+ FILE *f = fopen(restore_token_path, "wb");
+ if(!f) {
+ fprintf(stderr, "gsr warning: gsr_capture_portal_save_restore_token: failed to create restore token file (%s)\n", restore_token_path);
+ return;
+ }
+
+ const int restore_token_len = strlen(restore_token);
+ if((long)fwrite(restore_token, 1, restore_token_len, f) != restore_token_len) {
+ fprintf(stderr, "gsr warning: gsr_capture_portal_save_restore_token: failed to write restore token to file (%s)\n", restore_token_path);
+ fclose(f);
+ return;
+ }
+
+ fprintf(stderr, "gsr info: gsr_capture_portal_save_restore_token: saved restore token to cache (%s)\n", restore_token);
+ fclose(f);
+}
+
+static void gsr_capture_portal_get_restore_token_from_cache(char *buffer, size_t buffer_size) {
+ assert(buffer_size > 0);
+ buffer[0] = '\0';
+
+ char restore_token_path[PATH_MAX];
+ restore_token_path[0] = '\0';
+ get_gpu_screen_recorder_restore_token_path(restore_token_path, sizeof(restore_token_path));
+
+ FILE *f = fopen(restore_token_path, "rb");
+ if(!f) {
+ fprintf(stderr, "gsr info: gsr_capture_portal_get_restore_token_from_cache: no restore token found in cache or failed to load (%s)\n", restore_token_path);
+ return;
+ }
+
+ fseek(f, 0, SEEK_END);
+ long file_size = ftell(f);
+ fseek(f, 0, SEEK_SET);
+
+ fprintf(stderr, "file size: %ld\n", file_size);
+
+ if(file_size > 0 && file_size < 1024 && file_size < (long)buffer_size && (long)fread(buffer, 1, file_size, f) != file_size) {
+ buffer[0] = '\0';
+ fprintf(stderr, "gsr warning: gsr_capture_portal_get_restore_token_from_cache: failed to read restore token (%s)\n", restore_token_path);
+ fclose(f);
+ return;
+ }
+
+ if(file_size > 0 && file_size < (long)buffer_size)
+ buffer[file_size] = '\0';
+
+ fprintf(stderr, "gsr info: gsr_capture_portal_get_restore_token_from_cache: read cached restore token (%s)\n", buffer);
+ fclose(f);
+}
+
+static bool gsr_capture_portal_setup_dbus(gsr_capture_portal *self) {
+ char restore_token[1024];
+ restore_token[0] = '\0';
+ if(self->params.restore_portal_session)
+ gsr_capture_portal_get_restore_token_from_cache(restore_token, sizeof(restore_token));
+
+ if(!gsr_dbus_init(&self->dbus, restore_token))
+ return false;
+
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: CreateSession\n");
+ if(!gsr_dbus_screencast_create_session(&self->dbus, &self->session_handle)) {
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: CreateSession failed\n");
+ return false;
+ }
+
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: SelectSources\n");
+ if(!gsr_dbus_screencast_select_sources(&self->dbus, self->session_handle, GSR_PORTAL_CAPTURE_TYPE_MONITOR | GSR_PORTAL_CAPTURE_TYPE_WINDOW, self->params.record_cursor ? GSR_PORTAL_CURSOR_MODE_EMBEDDED : GSR_PORTAL_CURSOR_MODE_HIDDEN)) {
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: SelectSources failed\n");
+ return false;
+ }
+
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: Start\n");
+ if(!gsr_dbus_screencast_start(&self->dbus, self->session_handle, &self->pipewire_node)) {
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: Start failed\n");
+ return false;
+ }
+
+ const char *screencast_restore_token = gsr_dbus_screencast_get_restore_token(&self->dbus);
+ if(screencast_restore_token)
+ 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)) {
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n");
+ return false;
+ }
+
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: desktop portal setup finished\n");
+ return true;
+}
+
+static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
+ gsr_pipewire_region region = {0, 0, 0, 0};
+ gsr_pipewire_region cursor_region = {0, 0, 0, 0};
+ fprintf(stderr, "gsr info: gsr_capture_portal_start: waiting for pipewire negotiation\n");
+
+ 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, &region, &cursor_region)) {
+ self->capture_size.x = region.width;
+ self->capture_size.y = region.height;
+ fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire negotiation finished\n");
+ return true;
+ }
+ usleep(30 * 1000); /* 30 milliseconds */
+ }
+
+ fprintf(stderr, "gsr info: gsr_capture_portal_start: timed out waiting for pipewire negotiation (5 seconds)\n");
+ return false;
+}
+
+static int gsr_capture_portal_start(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame *frame) {
+ gsr_capture_portal *self = cap->priv;
+
+ gsr_capture_portal_create_input_textures(self);
+
+ if(!gsr_capture_portal_setup_dbus(self)) {
+ 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_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)) {
+ gsr_capture_portal_stop(self);
+ return -1;
+ }
+
+ /* Disable vsync */
+ self->params.egl->eglSwapInterval(self->params.egl->egl_display, 0);
+
+ video_codec_context->width = FFALIGN(self->capture_size.x, 2);
+ video_codec_context->height = FFALIGN(self->capture_size.y, 2);
+
+ frame->width = video_codec_context->width;
+ frame->height = video_codec_context->height;
+ return 0;
+}
+
+static int max_int(int a, int b) {
+ return a > b ? a : b;
+}
+
+static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) {
+ (void)frame;
+ (void)color_conversion;
+ gsr_capture_portal *self = cap->priv;
+
+ //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, &region, &cursor_region)) {
+ content_size.x = region.width;
+ content_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);
+
+ gsr_color_conversion_draw(color_conversion, self->input_texture_id,
+ (vec2i){target_x, target_y}, content_size,
+ (vec2i){region.x, region.y}, content_size,
+ 0.0f, false);
+
+ const vec2i cursor_pos = {
+ target_x + cursor_region.x,
+ target_y + cursor_region.y
+ };
+
+ self->params.egl->glEnable(GL_SCISSOR_TEST);
+ self->params.egl->glScissor(target_x, target_y, content_size.x, content_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},
+ 0.0f, false);
+ self->params.egl->glDisable(GL_SCISSOR_TEST);
+
+ self->params.egl->eglSwapBuffers(self->params.egl->egl_display, self->params.egl->egl_surface);
+
+ //self->params.egl->glFlush();
+ //self->params.egl->glFinish();
+
+ return 0;
+}
+
+static bool gsr_capture_portal_should_stop(gsr_capture *cap, bool *err) {
+ gsr_capture_portal *cap_portal = cap->priv;
+ if(cap_portal->should_stop) {
+ if(err)
+ *err = cap_portal->stop_is_error;
+ return true;
+ }
+
+ if(err)
+ *err = false;
+ return false;
+}
+
+static void gsr_capture_portal_capture_end(gsr_capture *cap, AVFrame *frame) {
+ (void)cap;
+ (void)frame;
+}
+
+static gsr_source_color gsr_capture_portal_get_source_color(gsr_capture *cap) {
+ (void)cap;
+ return GSR_SOURCE_COLOR_RGB;
+}
+
+// static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
+// gsr_capture_portal *cap_portal = cap->priv;
+// return cap_portal->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA;
+// }
+
+static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) {
+ (void)video_codec_context;
+ gsr_capture_portal *cap_portal = cap->priv;
+ if(cap->priv) {
+ gsr_capture_portal_stop(cap_portal);
+ free(cap->priv);
+ cap->priv = NULL;
+ }
+ free(cap);
+}
+
+gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params) {
+ if(!params) {
+ fprintf(stderr, "gsr error: gsr_capture_portal_create params is NULL\n");
+ return NULL;
+ }
+
+ gsr_capture *cap = calloc(1, sizeof(gsr_capture));
+ if(!cap)
+ return NULL;
+
+ gsr_capture_portal *cap_portal = calloc(1, sizeof(gsr_capture_portal));
+ if(!cap_portal) {
+ free(cap);
+ return NULL;
+ }
+
+ cap_portal->params = *params;
+
+ *cap = (gsr_capture) {
+ .start = gsr_capture_portal_start,
+ .tick = NULL,
+ .should_stop = gsr_capture_portal_should_stop,
+ .capture = gsr_capture_portal_capture,
+ .capture_end = gsr_capture_portal_capture_end,
+ .get_source_color = gsr_capture_portal_get_source_color,
+ .uses_external_image = NULL,
+ .destroy = gsr_capture_portal_destroy,
+ .priv = cap_portal
+ };
+
+ return cap;
+}
diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c
index f5d2b2f..83c4800 100644
--- a/src/capture/xcomposite.c
+++ b/src/capture/xcomposite.c
@@ -351,23 +351,15 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame, gsr_
if(self->params.record_cursor && self->cursor.visible) {
gsr_cursor_tick(&self->cursor, self->window);
- const bool cursor_inside_window =
- cursor_pos.x + self->cursor.size.x >= target_x &&
- cursor_pos.x <= target_x + self->texture_size.x &&
- cursor_pos.y + self->cursor.size.y >= target_y &&
- cursor_pos.y <= target_y + self->texture_size.y;
-
- if(cursor_inside_window) {
- self->params.egl->glEnable(GL_SCISSOR_TEST);
- self->params.egl->glScissor(target_x, target_y, self->texture_size.x, self->texture_size.y);
-
- gsr_color_conversion_draw(color_conversion, self->cursor.texture_id,
- cursor_pos, self->cursor.size,
- (vec2i){0, 0}, self->cursor.size,
- 0.0f, false);
-
- self->params.egl->glDisable(GL_SCISSOR_TEST);
- }
+ self->params.egl->glEnable(GL_SCISSOR_TEST);
+ self->params.egl->glScissor(target_x, target_y, self->texture_size.x, self->texture_size.y);
+
+ gsr_color_conversion_draw(color_conversion, self->cursor.texture_id,
+ cursor_pos, self->cursor.size,
+ (vec2i){0, 0}, self->cursor.size,
+ 0.0f, false);
+
+ self->params.egl->glDisable(GL_SCISSOR_TEST);
}
self->params.egl->eglSwapBuffers(self->params.egl->egl_display, self->params.egl->egl_surface);