aboutsummaryrefslogtreecommitdiff
path: root/src/capture
diff options
context:
space:
mode:
Diffstat (limited to 'src/capture')
-rw-r--r--src/capture/kms.c13
-rw-r--r--src/capture/nvfbc.c6
-rw-r--r--src/capture/portal.c150
3 files changed, 91 insertions, 78 deletions
diff --git a/src/capture/kms.c b/src/capture/kms.c
index 18858f2..36a5355 100644
--- a/src/capture/kms.c
+++ b/src/capture/kms.c
@@ -12,7 +12,7 @@
#include <fcntl.h>
#include <xf86drm.h>
-#include <libdrm/drm_fourcc.h>
+#include <drm_fourcc.h>
#include <libavutil/mastering_display_metadata.h>
@@ -108,22 +108,14 @@ static int max_int(int a, int b) {
}
static void gsr_capture_kms_create_input_texture_ids(gsr_capture_kms *self) {
- const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
-
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_BORDER);
- self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
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->external_input_texture_id);
self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->external_input_texture_id);
- self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameterfv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_BORDER_COLOR, border_color);
self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
@@ -133,9 +125,6 @@ static void gsr_capture_kms_create_input_texture_ids(gsr_capture_kms *self) {
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_BORDER);
- self->params.egl->glTexParameteri(cursor_texture_id_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameterfv(cursor_texture_id_target, GL_TEXTURE_BORDER_COLOR, border_color);
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);
diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c
index b92bd41..13b46c3 100644
--- a/src/capture/nvfbc.c
+++ b/src/capture/nvfbc.c
@@ -289,12 +289,12 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
int driver_major_version = 0;
int driver_minor_version = 0;
if(self->params.direct_capture && get_driver_version(&driver_major_version, &driver_minor_version)) {
- fprintf(stderr, "Info: detected nvidia version: %d.%d\n", driver_major_version, driver_minor_version);
+ fprintf(stderr, "gsr info: detected nvidia version: %d.%d\n", driver_major_version, driver_minor_version);
// TODO:
if(version_at_least(driver_major_version, driver_minor_version, 515, 57) && version_less_than(driver_major_version, driver_minor_version, 520, 56)) {
self->params.direct_capture = false;
- fprintf(stderr, "Warning: \"screen-direct\" has temporary been disabled as it causes stuttering with driver versions >= 515.57 and < 520.56. Please update your driver if possible. Capturing \"screen\" instead.\n");
+ fprintf(stderr, "gsr warning: \"screen-direct\" has temporary been disabled as it causes stuttering with driver versions >= 515.57 and < 520.56. Please update your driver if possible. Capturing \"screen\" instead.\n");
}
// TODO:
@@ -304,7 +304,7 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
if(version_at_least(driver_major_version, driver_minor_version, 515, 57))
self->supports_direct_cursor = true;
else
- fprintf(stderr, "Info: capturing \"screen-direct\" but driver version appears to be less than 515.57. Disabling capture of cursor. Please update your driver if you want to capture your cursor or record \"screen\" instead.\n");
+ fprintf(stderr, "gsr info: capturing \"screen-direct\" but driver version appears to be less than 515.57. Disabling capture of cursor. Please update your driver if you want to capture your cursor or record \"screen\" instead.\n");
}
*/
}
diff --git a/src/capture/portal.c b/src/capture/portal.c
index 27f514f..d2217d1 100644
--- a/src/capture/portal.c
+++ b/src/capture/portal.c
@@ -11,6 +11,15 @@
#include <limits.h>
#include <assert.h>
+#define PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE 60
+
+typedef enum {
+ PORTAL_CAPTURE_SETUP_IDLE,
+ PORTAL_CAPTURE_SETUP_IN_PROGRESS,
+ PORTAL_CAPTURE_SETUP_FINISHED,
+ PORTAL_CAPTURE_SETUP_FAILED
+} gsr_portal_capture_setup_state;
+
typedef struct {
gsr_capture_portal_params params;
@@ -23,6 +32,15 @@ typedef struct {
vec2i capture_size;
gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
int num_dmabuf_data;
+
+ gsr_pipewire_video_region region;
+ gsr_pipewire_video_region cursor_region;
+ uint32_t pipewire_fourcc;
+ uint64_t pipewire_modifiers;
+ bool using_external_image;
+
+ bool should_stop;
+ bool stop_is_error;
} gsr_capture_portal;
static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) {
@@ -52,43 +70,25 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) {
}
gsr_capture_portal_cleanup_plane_fds(self);
-
gsr_pipewire_video_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) {
- const float border_color[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
-
self->params.egl->glGenTextures(1, &self->texture_map.texture_id);
self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_map.texture_id);
- self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
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->texture_map.external_texture_id);
self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, self->texture_map.external_texture_id);
- self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameterfv(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_BORDER_COLOR, border_color);
self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
self->params.egl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
self->params.egl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
self->params.egl->glGenTextures(1, &self->texture_map.cursor_texture_id);
self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_map.cursor_texture_id);
- self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
- self->params.egl->glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
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);
@@ -234,19 +234,13 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire
}
static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
- gsr_pipewire_video_region region = {0, 0, 0, 0};
- gsr_pipewire_video_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) {
- bool uses_external_image = false;
- uint32_t fourcc = 0;
- uint64_t modifiers = 0;
- if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &region, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &fourcc, &modifiers, &uses_external_image)) {
- gsr_capture_portal_cleanup_plane_fds(self);
- self->capture_size.x = region.width;
- self->capture_size.y = region.height;
+ if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &self->region, &self->cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &self->pipewire_fourcc, &self->pipewire_modifiers, &self->using_external_image)) {
+ self->capture_size.x = self->region.width;
+ self->capture_size.y = self->region.height;
fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire negotiation finished\n");
return true;
}
@@ -257,45 +251,51 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
return false;
}
-static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) {
- gsr_capture_portal *self = cap->priv;
-
+static int gsr_capture_portal_setup(gsr_capture_portal *self, int fps) {
gsr_capture_portal_create_input_textures(self);
int pipewire_fd = 0;
uint32_t pipewire_node = 0;
const int response_status = gsr_capture_portal_setup_dbus(self, &pipewire_fd, &pipewire_node);
if(response_status != 0) {
- gsr_capture_portal_stop(self);
// Response status values:
// 0: Success, the request is carried out
// 1: The user cancelled the interaction
// 2: The user interaction was ended in some other way
// Response status value 2 happens usually if there was some kind of error in the desktop portal on the system
if(response_status == 2) {
- fprintf(stderr, "gsr error: gsr_capture_portal_start: desktop portal capture failed. Either you Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system\n");
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup: desktop portal capture failed. Either you Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system\n");
return 50;
} else if(response_status == 1) {
- fprintf(stderr, "gsr error: gsr_capture_portal_start: desktop portal capture failed. It seems like desktop portal capture was canceled by the user.\n");
- return 60;
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup: desktop portal capture failed. It seems like desktop portal capture was canceled by the user.\n");
+ return PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE;
} else {
return -1;
}
}
- fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n");
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup: setting up pipewire\n");
/* TODO: support hdr when pipewire supports it */
/* gsr_pipewire closes the pipewire fd, even on failure */
- if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, capture_metadata->fps, 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);
+ if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, fps, self->params.record_cursor, self->params.egl)) {
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
return -1;
}
- fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire setup finished\n");
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup: pipewire setup finished\n");
- if(!gsr_capture_portal_get_frame_dimensions(self)) {
- gsr_capture_portal_stop(self);
+ if(!gsr_capture_portal_get_frame_dimensions(self))
return -1;
+
+ return 0;
+}
+
+static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) {
+ gsr_capture_portal *self = cap->priv;
+
+ const int result = gsr_capture_portal_setup(self, capture_metadata->fps);
+ if(result != 0) {
+ gsr_capture_portal_stop(self);
+ return result;
}
if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) {
@@ -314,24 +314,40 @@ static int max_int(int a, int b) {
return a > b ? a : b;
}
+static bool gsr_capture_portal_capture_has_synchronous_task(gsr_capture *cap) {
+ gsr_capture_portal *self = cap->priv;
+ return gsr_pipewire_video_should_restart(&self->pipewire);
+}
+
static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) {
(void)color_conversion;
gsr_capture_portal *self = cap->priv;
- /* TODO: Handle formats other than RGB(a) */
- gsr_pipewire_video_region region = {0, 0, 0, 0};
- gsr_pipewire_video_region cursor_region = {0, 0, 0, 0};
- uint32_t pipewire_fourcc = 0;
- uint64_t pipewire_modifiers = 0;
- bool using_external_image = false;
- if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &region, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &pipewire_fourcc, &pipewire_modifiers, &using_external_image)) {
- if(region.width != self->capture_size.x || region.height != self->capture_size.y) {
- self->capture_size.x = region.width;
- self->capture_size.y = region.height;
- gsr_color_conversion_clear(color_conversion);
+ if(self->should_stop)
+ return -1;
+
+ if(gsr_pipewire_video_should_restart(&self->pipewire)) {
+ fprintf(stderr, "gsr info: gsr_capture_portal_capture: pipewire capture was paused, trying to start capture again\n");
+ gsr_capture_portal_stop(self);
+ const int result = gsr_capture_portal_setup(self, capture_metadata->fps);
+ if(result != 0) {
+ self->stop_is_error = result != PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE;
+ self->should_stop = true;
+ }
+ return -1;
+ }
+
+ /* TODO: Handle formats other than RGB(A) */
+ if(self->num_dmabuf_data == 0) {
+ if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &self->region, &self->cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &self->pipewire_fourcc, &self->pipewire_modifiers, &self->using_external_image)) {
+ if(self->region.width != self->capture_size.x || self->region.height != self->capture_size.y) {
+ self->capture_size.x = self->region.width;
+ self->capture_size.y = self->region.height;
+ gsr_color_conversion_clear(color_conversion);
+ }
+ } else {
+ return -1;
}
- } else {
- return 0;
}
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
@@ -345,27 +361,27 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
// TODO: Handle region crop
- gsr_color_conversion_draw(color_conversion, using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id,
+ gsr_color_conversion_draw(color_conversion, self->using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id,
target_pos, output_size,
- (vec2i){region.x, region.y}, self->capture_size, self->capture_size,
- GSR_ROT_0, GSR_SOURCE_COLOR_RGB, using_external_image, false);
+ (vec2i){self->region.x, self->region.y}, self->capture_size, self->capture_size,
+ GSR_ROT_0, GSR_SOURCE_COLOR_RGB, self->using_external_image, false);
- if(self->params.record_cursor && self->texture_map.cursor_texture_id > 0 && cursor_region.width > 0) {
+ if(self->params.record_cursor && self->texture_map.cursor_texture_id > 0 && self->cursor_region.width > 0) {
const vec2d scale = {
self->capture_size.x == 0 ? 0 : (double)output_size.x / (double)self->capture_size.x,
self->capture_size.y == 0 ? 0 : (double)output_size.y / (double)self->capture_size.y
};
const vec2i cursor_pos = {
- target_pos.x + (cursor_region.x * scale.x),
- target_pos.y + (cursor_region.y * scale.y)
+ target_pos.x + (self->cursor_region.x * scale.x),
+ target_pos.y + (self->cursor_region.y * scale.y)
};
self->params.egl->glEnable(GL_SCISSOR_TEST);
self->params.egl->glScissor(target_pos.x, target_pos.y, output_size.x, output_size.y);
gsr_color_conversion_draw(color_conversion, self->texture_map.cursor_texture_id,
- (vec2i){cursor_pos.x, cursor_pos.y}, (vec2i){cursor_region.width * scale.x, cursor_region.height * scale.y},
- (vec2i){0, 0}, (vec2i){cursor_region.width, cursor_region.height}, (vec2i){cursor_region.width, cursor_region.height},
+ (vec2i){cursor_pos.x, cursor_pos.y}, (vec2i){self->cursor_region.width * scale.x, self->cursor_region.height * scale.y},
+ (vec2i){0, 0}, (vec2i){self->cursor_region.width, self->cursor_region.height}, (vec2i){self->cursor_region.width, self->cursor_region.height},
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
self->params.egl->glDisable(GL_SCISSOR_TEST);
}
@@ -383,6 +399,13 @@ static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
return true;
}
+static bool gsr_capture_portal_should_stop(gsr_capture *cap, bool *err) {
+ gsr_capture_portal *self = cap->priv;
+ if(err)
+ *err = self->stop_is_error;
+ return self->should_stop;
+}
+
static bool gsr_capture_portal_is_damaged(gsr_capture *cap) {
gsr_capture_portal *self = cap->priv;
return gsr_pipewire_video_is_damaged(&self->pipewire);
@@ -424,7 +447,8 @@ gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params)
*cap = (gsr_capture) {
.start = gsr_capture_portal_start,
.tick = NULL,
- .should_stop = NULL,
+ .should_stop = gsr_capture_portal_should_stop,
+ .capture_has_synchronous_task = gsr_capture_portal_capture_has_synchronous_task,
.capture = gsr_capture_portal_capture,
.uses_external_image = gsr_capture_portal_uses_external_image,
.is_damaged = gsr_capture_portal_is_damaged,