aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/capture/kms.h4
-rw-r--r--include/capture/nvfbc.h2
-rw-r--r--include/capture/portal.h2
-rw-r--r--include/capture/xcomposite.h2
-rw-r--r--include/capture/ximage.h18
-rw-r--r--include/egl.h1
-rw-r--r--include/image_writer.h8
-rw-r--r--include/utils.h2
-rw-r--r--meson.build1
-rw-r--r--src/capture/kms.c2
-rw-r--r--src/capture/nvfbc.c1
-rw-r--r--src/capture/portal.c3
-rw-r--r--src/capture/xcomposite.c3
-rw-r--r--src/capture/ximage.c247
-rw-r--r--src/egl.c1
-rw-r--r--src/encoder/video/nvenc.c17
-rw-r--r--src/encoder/video/software.c18
-rw-r--r--src/encoder/video/vulkan.c18
-rw-r--r--src/image_writer.c77
-rw-r--r--src/main.cpp107
-rw-r--r--src/utils.c15
21 files changed, 403 insertions, 146 deletions
diff --git a/include/capture/kms.h b/include/capture/kms.h
index f359783..ce09817 100644
--- a/include/capture/kms.h
+++ b/include/capture/kms.h
@@ -5,9 +5,7 @@
typedef struct {
gsr_egl *egl;
- const char *display_to_capture; /* if this is "screen", then the first monitor is captured. A copy is made of this */
- gsr_color_depth color_depth;
- gsr_color_range color_range;
+ const char *display_to_capture; /* A copy is made of this */
bool hdr;
bool record_cursor;
int fps;
diff --git a/include/capture/nvfbc.h b/include/capture/nvfbc.h
index f93fdc0..07cc42f 100644
--- a/include/capture/nvfbc.h
+++ b/include/capture/nvfbc.h
@@ -11,8 +11,6 @@ typedef struct {
vec2i pos;
vec2i size;
bool direct_capture;
- gsr_color_depth color_depth;
- gsr_color_range color_range;
bool record_cursor;
vec2i output_resolution;
vec2i region_size;
diff --git a/include/capture/portal.h b/include/capture/portal.h
index 3989b98..74cdba9 100644
--- a/include/capture/portal.h
+++ b/include/capture/portal.h
@@ -5,8 +5,6 @@
typedef struct {
gsr_egl *egl;
- gsr_color_depth color_depth;
- gsr_color_range color_range;
bool record_cursor;
bool restore_portal_session;
/* If this is set to NULL then this defaults to $XDG_CONFIG_HOME/gpu-screen-recorder/restore_token ($XDG_CONFIG_HOME defaults to $HOME/.config) */
diff --git a/include/capture/xcomposite.h b/include/capture/xcomposite.h
index 45eb481..bf6532e 100644
--- a/include/capture/xcomposite.h
+++ b/include/capture/xcomposite.h
@@ -8,9 +8,7 @@ typedef struct {
gsr_egl *egl;
unsigned long window;
bool follow_focused; /* If this is set then |window| is ignored */
- gsr_color_range color_range;
bool record_cursor;
- gsr_color_depth color_depth;
vec2i output_resolution;
} gsr_capture_xcomposite_params;
diff --git a/include/capture/ximage.h b/include/capture/ximage.h
new file mode 100644
index 0000000..e6c3607
--- /dev/null
+++ b/include/capture/ximage.h
@@ -0,0 +1,18 @@
+#ifndef GSR_CAPTURE_XIMAGE_H
+#define GSR_CAPTURE_XIMAGE_H
+
+#include "capture.h"
+#include "../vec2.h"
+
+typedef struct {
+ gsr_egl *egl;
+ const char *display_to_capture; /* A copy is made of this */
+ bool record_cursor;
+ vec2i output_resolution;
+ vec2i region_size;
+ vec2i region_position;
+} gsr_capture_ximage_params;
+
+gsr_capture* gsr_capture_ximage_create(const gsr_capture_ximage_params *params);
+
+#endif /* GSR_CAPTURE_XIMAGE_H */
diff --git a/include/egl.h b/include/egl.h
index 8caf89a..0d08270 100644
--- a/include/egl.h
+++ b/include/egl.h
@@ -235,6 +235,7 @@ struct gsr_egl {
void (*glTexParameteriv)(unsigned int target, unsigned int pname, const int *params);
void (*glGetTexLevelParameteriv)(unsigned int target, int level, unsigned int pname, int *params);
void (*glTexImage2D)(unsigned int target, int level, int internalFormat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels);
+ void (*glTexSubImage2D)(unsigned int target, int level, int xoffset, int yoffset, int width, int height, unsigned format, unsigned type, const void *pixels);
void (*glGetTexImage)(unsigned int target, int level, unsigned int format, unsigned int type, void *pixels);
void (*glGenFramebuffers)(int n, unsigned int *framebuffers);
void (*glBindFramebuffer)(unsigned int target, unsigned int framebuffer);
diff --git a/include/image_writer.h b/include/image_writer.h
index 79f549e..400edd0 100644
--- a/include/image_writer.h
+++ b/include/image_writer.h
@@ -11,7 +11,8 @@ typedef enum {
} gsr_image_format;
typedef enum {
- GSR_IMAGE_WRITER_SOURCE_OPENGL
+ GSR_IMAGE_WRITER_SOURCE_OPENGL,
+ GSR_IMAGE_WRITER_SOURCE_MEMORY
} gsr_image_writer_source;
typedef struct {
@@ -20,9 +21,12 @@ typedef struct {
int width;
int height;
unsigned int texture;
+ const void *memory; /* Reference */
} gsr_image_writer;
-bool gsr_image_writer_init(gsr_image_writer *self, gsr_image_writer_source source, gsr_egl *egl, int width, int height);
+bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height);
+/* |memory| is taken as a reference */
+bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height);
void gsr_image_writer_deinit(gsr_image_writer *self);
/* Quality is between 1 and 100 where 100 is the max quality. Quality doesn't apply to lossless formats */
diff --git a/include/utils.h b/include/utils.h
index 22bc4cf..fd340e8 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -69,4 +69,6 @@ bool vaapi_copy_egl_image_to_video_surface(gsr_egl *egl, EGLImage image, vec2i s
vec2i scale_keep_aspect_ratio(vec2i from, vec2i to);
+unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter);
+
#endif /* GSR_UTILS_H */
diff --git a/meson.build b/meson.build
index 28d3b24..0991728 100644
--- a/meson.build
+++ b/meson.build
@@ -12,6 +12,7 @@ src = [
'src/capture/capture.c',
'src/capture/nvfbc.c',
'src/capture/xcomposite.c',
+ 'src/capture/ximage.c',
'src/capture/kms.c',
'src/encoder/video/video.c',
'src/encoder/video/nvenc.c',
diff --git a/src/capture/kms.c b/src/capture/kms.c
index 9693c58..578fded 100644
--- a/src/capture/kms.c
+++ b/src/capture/kms.c
@@ -14,9 +14,7 @@
#include <xf86drm.h>
#include <libdrm/drm_fourcc.h>
-#include <libavcodec/avcodec.h>
#include <libavutil/mastering_display_metadata.h>
-#include <libavformat/avformat.h>
#define FIND_CRTC_BY_NAME_TIMEOUT_SECONDS 2.0
diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c
index a0c8d29..188b321 100644
--- a/src/capture/nvfbc.c
+++ b/src/capture/nvfbc.c
@@ -13,7 +13,6 @@
#include <assert.h>
#include <X11/Xlib.h>
-#include <libavcodec/avcodec.h>
typedef struct {
gsr_capture_nvfbc_params params;
diff --git a/src/capture/portal.c b/src/capture/portal.c
index 893c07a..e065f02 100644
--- a/src/capture/portal.c
+++ b/src/capture/portal.c
@@ -8,10 +8,9 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
+#include <limits.h>
#include <assert.h>
-#include <libavcodec/avcodec.h>
-
typedef struct {
gsr_capture_portal_params params;
diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c
index 2016a31..d8f4c27 100644
--- a/src/capture/xcomposite.c
+++ b/src/capture/xcomposite.c
@@ -12,9 +12,6 @@
#include <X11/Xlib.h>
-#include <libavutil/frame.h>
-#include <libavcodec/avcodec.h>
-
typedef struct {
gsr_capture_xcomposite_params params;
Display *display;
diff --git a/src/capture/ximage.c b/src/capture/ximage.c
new file mode 100644
index 0000000..259761d
--- /dev/null
+++ b/src/capture/ximage.c
@@ -0,0 +1,247 @@
+#include "../../include/capture/ximage.h"
+#include "../../include/utils.h"
+#include "../../include/cursor.h"
+#include "../../include/color_conversion.h"
+#include "../../include/window/window.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <X11/Xlib.h>
+
+/* TODO: update when monitors are reconfigured */
+
+typedef struct {
+ gsr_capture_ximage_params params;
+ Display *display;
+ gsr_cursor cursor;
+ gsr_monitor monitor;
+ vec2i capture_pos;
+ vec2i capture_size;
+ unsigned int texture_id;
+ Window root_window;
+} gsr_capture_ximage;
+
+static void gsr_capture_ximage_stop(gsr_capture_ximage *self) {
+ gsr_cursor_deinit(&self->cursor);
+ if(self->texture_id) {
+ self->params.egl->glDeleteTextures(1, &self->texture_id);
+ self->texture_id = 0;
+ }
+}
+
+static int max_int(int a, int b) {
+ return a > b ? a : b;
+}
+
+static int gsr_capture_ximage_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) {
+ gsr_capture_ximage *self = cap->priv;
+ self->root_window = DefaultRootWindow(self->display);
+
+ if(gsr_cursor_init(&self->cursor, self->params.egl, self->display) != 0) {
+ gsr_capture_ximage_stop(self);
+ return -1;
+ }
+
+ if(!get_monitor_by_name(self->params.egl, GSR_CONNECTION_X11, self->params.display_to_capture, &self->monitor)) {
+ fprintf(stderr, "gsr error: gsr_capture_ximage_start: failed to find monitor by name \"%s\"\n", self->params.display_to_capture);
+ gsr_capture_ximage_stop(self);
+ return -1;
+ }
+
+ self->capture_pos = self->monitor.pos;
+ self->capture_size = self->monitor.size;
+
+ if(self->params.region_size.x > 0 && self->params.region_size.y > 0)
+ self->capture_size = self->params.region_size;
+
+ if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) {
+ self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution);
+ capture_metadata->width = self->params.output_resolution.x;
+ capture_metadata->height = self->params.output_resolution.y;
+ } else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) {
+ capture_metadata->width = self->params.region_size.x;
+ capture_metadata->height = self->params.region_size.y;
+ } else {
+ capture_metadata->width = self->capture_size.x;
+ capture_metadata->height = self->capture_size.y;
+ }
+
+ self->texture_id = gl_create_texture(self->params.egl, self->capture_size.x, self->capture_size.y, GL_RGB8, GL_RGB, GL_LINEAR);
+ if(self->texture_id == 0) {
+ fprintf(stderr, "gsr error: gsr_capture_ximage_start: failed to create texture\n");
+ gsr_capture_ximage_stop(self);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void gsr_capture_ximage_on_event(gsr_capture *cap, gsr_egl *egl) {
+ gsr_capture_ximage *self = cap->priv;
+ XEvent *xev = gsr_window_get_event_data(egl->window);
+ gsr_cursor_on_event(&self->cursor, xev);
+}
+
+static bool gsr_capture_ximage_upload_to_texture(gsr_capture_ximage *self, int x, int y, int width, int height) {
+ const int max_width = XWidthOfScreen(DefaultScreenOfDisplay(self->display));
+ const int max_height = XHeightOfScreen(DefaultScreenOfDisplay(self->display));
+
+ if(x < 0)
+ x = 0;
+ else if(x >= max_width)
+ x = max_width - 1;
+
+ if(y < 0)
+ y = 0;
+ else if(y >= max_height)
+ y = max_height - 1;
+
+ if(width < 0)
+ width = 0;
+ else if(x + width >= max_width)
+ width = max_width - x;
+
+ if(height < 0)
+ height = 0;
+ else if(y + height >= max_height)
+ height = max_height - y;
+
+ XImage *image = XGetImage(self->display, self->root_window, x, y, width, height, AllPlanes, ZPixmap);
+ if(!image) {
+ fprintf(stderr, "gsr error: gsr_capture_ximage_upload_to_texture: XGetImage failed\n");
+ return false;
+ }
+
+ bool success = false;
+ uint8_t *image_data = malloc(image->width * image->height * 3);
+ if(!image_data) {
+ fprintf(stderr, "gsr error: gsr_capture_ximage_upload_to_texture: failed to allocate image data\n");
+ goto done;
+ }
+
+ for(int y = 0; y < image->height; ++y) {
+ for(int x = 0; x < image->width; ++x) {
+ unsigned long pixel = XGetPixel(image, x, y);
+ unsigned char red = (pixel & image->red_mask) >> 16;
+ unsigned char green = (pixel & image->green_mask) >> 8;
+ unsigned char blue = pixel & image->blue_mask;
+
+ const size_t texture_data_index = (x + y * image->width) * 3;
+ image_data[texture_data_index + 0] = red;
+ image_data[texture_data_index + 1] = green;
+ image_data[texture_data_index + 2] = blue;
+ }
+ }
+
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, self->texture_id);
+ self->params.egl->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, image->width, image->height, GL_RGB, GL_UNSIGNED_BYTE, image_data);
+ self->params.egl->glBindTexture(GL_TEXTURE_2D, 0);
+ success = true;
+
+ done:
+ free(image_data);
+ XDestroyImage(image);
+ return success;
+}
+
+static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *capture_metdata, gsr_color_conversion *color_conversion) {
+ gsr_capture_ximage *self = cap->priv;
+
+ const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
+ vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size;
+ output_size = scale_keep_aspect_ratio(self->capture_size, output_size);
+
+ const vec2i target_pos = { max_int(0, capture_metdata->width / 2 - output_size.x / 2), max_int(0, capture_metdata->height / 2 - output_size.y / 2) };
+ gsr_capture_ximage_upload_to_texture(self, self->capture_pos.x + self->params.region_position.x, self->capture_pos.y + self->params.region_position.y, self->capture_size.x, self->capture_size.y);
+
+ gsr_color_conversion_draw(color_conversion, self->texture_id,
+ target_pos, output_size,
+ (vec2i){0, 0}, self->capture_size,
+ 0.0f, false, GSR_SOURCE_COLOR_RGB);
+
+ if(self->params.record_cursor && self->cursor.visible) {
+ 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
+ };
+
+ gsr_cursor_tick(&self->cursor, self->root_window);
+
+ const vec2i cursor_pos = {
+ target_pos.x + (self->cursor.position.x - self->cursor.hotspot.x) * scale.x - self->capture_pos.x - self->params.region_position.x,
+ target_pos.y + (self->cursor.position.y - self->cursor.hotspot.y) * scale.y - self->capture_pos.y - self->params.region_position.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->cursor.texture_id,
+ cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y},
+ (vec2i){0, 0}, self->cursor.size,
+ 0.0f, false, GSR_SOURCE_COLOR_RGB);
+
+ self->params.egl->glDisable(GL_SCISSOR_TEST);
+ }
+
+ self->params.egl->glFlush();
+ self->params.egl->glFinish();
+
+ return 0;
+}
+
+static void gsr_capture_ximage_destroy(gsr_capture *cap) {
+ gsr_capture_ximage *self = cap->priv;
+ if(cap->priv) {
+ gsr_capture_ximage_stop(self);
+ free((void*)self->params.display_to_capture);
+ self->params.display_to_capture = NULL;
+ free(self);
+ cap->priv = NULL;
+ }
+ free(cap);
+}
+
+gsr_capture* gsr_capture_ximage_create(const gsr_capture_ximage_params *params) {
+ if(!params) {
+ fprintf(stderr, "gsr error: gsr_capture_ximage_create params is NULL\n");
+ return NULL;
+ }
+
+ gsr_capture *cap = calloc(1, sizeof(gsr_capture));
+ if(!cap)
+ return NULL;
+
+ gsr_capture_ximage *cap_ximage = calloc(1, sizeof(gsr_capture_ximage));
+ if(!cap_ximage) {
+ free(cap);
+ return NULL;
+ }
+
+ const char *display_to_capture = strdup(params->display_to_capture);
+ if(!display_to_capture) {
+ free(cap);
+ free(cap_ximage);
+ return NULL;
+ }
+
+ cap_ximage->params = *params;
+ cap_ximage->display = gsr_window_get_display(params->egl->window);
+ cap_ximage->params.display_to_capture = display_to_capture;
+
+ *cap = (gsr_capture) {
+ .start = gsr_capture_ximage_start,
+ .on_event = gsr_capture_ximage_on_event,
+ .tick = NULL,
+ .should_stop = NULL,
+ .capture = gsr_capture_ximage_capture,
+ .uses_external_image = NULL,
+ .get_window_id = NULL,
+ .destroy = gsr_capture_ximage_destroy,
+ .priv = cap_ximage
+ };
+
+ return cap;
+}
diff --git a/src/egl.c b/src/egl.c
index 3307f0f..7c9a877 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -288,6 +288,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
{ (void**)&self->glTexParameteriv, "glTexParameteriv" },
{ (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" },
{ (void**)&self->glTexImage2D, "glTexImage2D" },
+ { (void**)&self->glTexSubImage2D, "glTexSubImage2D" },
{ (void**)&self->glGetTexImage, "glGetTexImage" },
{ (void**)&self->glGenFramebuffers, "glGenFramebuffers" },
{ (void**)&self->glBindFramebuffer, "glBindFramebuffer" },
diff --git a/src/encoder/video/nvenc.c b/src/encoder/video/nvenc.c
index c78e94a..7d8cf5d 100644
--- a/src/encoder/video/nvenc.c
+++ b/src/encoder/video/nvenc.c
@@ -65,21 +65,6 @@ static bool gsr_video_encoder_nvenc_setup_context(gsr_video_encoder_nvenc *self,
return true;
}
-static unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format) {
- unsigned int texture_id = 0;
- egl->glGenTextures(1, &texture_id);
- egl->glBindTexture(GL_TEXTURE_2D, texture_id);
- egl->glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
-
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
- egl->glBindTexture(GL_TEXTURE_2D, 0);
- return texture_id;
-}
-
static bool cuda_register_opengl_texture(gsr_cuda *cuda, CUgraphicsResource *cuda_graphics_resource, CUarray *mapped_array, unsigned int texture_id) {
CUresult res;
res = cuda->cuGraphicsGLRegisterImage(cuda_graphics_resource, texture_id, GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_NONE);
@@ -110,7 +95,7 @@ static bool gsr_video_encoder_nvenc_setup_textures(gsr_video_encoder_nvenc *self
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
for(int i = 0; i < 2; ++i) {
- self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i]);
+ self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i], GL_LINEAR);
if(self->target_textures[i] == 0) {
fprintf(stderr, "gsr error: gsr_video_encoder_nvenc_setup_textures: failed to create opengl texture\n");
return false;
diff --git a/src/encoder/video/software.c b/src/encoder/video/software.c
index be227f2..33b8d9c 100644
--- a/src/encoder/video/software.c
+++ b/src/encoder/video/software.c
@@ -1,5 +1,6 @@
#include "../../../include/encoder/video/software.h"
#include "../../../include/egl.h"
+#include "../../../include/utils.h"
#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>
@@ -14,21 +15,6 @@ typedef struct {
unsigned int target_textures[2];
} gsr_video_encoder_software;
-static unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format) {
- unsigned int texture_id = 0;
- egl->glGenTextures(1, &texture_id);
- egl->glBindTexture(GL_TEXTURE_2D, texture_id);
- egl->glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
-
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
- egl->glBindTexture(GL_TEXTURE_2D, 0);
- return texture_id;
-}
-
static bool gsr_video_encoder_software_setup_textures(gsr_video_encoder_software *self, AVCodecContext *video_codec_context, AVFrame *frame) {
int res = av_frame_get_buffer(frame, LINESIZE_ALIGNMENT);
if(res < 0) {
@@ -48,7 +34,7 @@ static bool gsr_video_encoder_software_setup_textures(gsr_video_encoder_software
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
for(int i = 0; i < 2; ++i) {
- self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i]);
+ self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i], GL_LINEAR);
if(self->target_textures[i] == 0) {
fprintf(stderr, "gsr error: gsr_capture_kms_setup_cuda_textures: failed to create opengl texture\n");
return false;
diff --git a/src/encoder/video/vulkan.c b/src/encoder/video/vulkan.c
index d202ddd..24e24d6 100644
--- a/src/encoder/video/vulkan.c
+++ b/src/encoder/video/vulkan.c
@@ -71,22 +71,6 @@ static bool gsr_video_encoder_vulkan_setup_context(gsr_video_encoder_vulkan *sel
return true;
}
-static unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format) {
- unsigned int texture_id = 0;
- egl->glGenTextures(1, &texture_id);
- egl->glBindTexture(GL_TEXTURE_2D, texture_id);
- //egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT, GL_OPTIMAL_TILING_EXT);
- egl->glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
-
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
- egl->glBindTexture(GL_TEXTURE_2D, 0);
- return texture_id;
-}
-
static AVVulkanDeviceContext* video_codec_context_get_vulkan_data(AVCodecContext *video_codec_context) {
AVBufferRef *hw_frames_ctx = video_codec_context->hw_frames_ctx;
if(!hw_frames_ctx)
@@ -116,7 +100,7 @@ static bool gsr_video_encoder_vulkan_setup_textures(gsr_video_encoder_vulkan *se
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
for(int i = 0; i < 2; ++i) {
- self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i]);
+ self->target_textures[i] = gl_create_texture(self->params.egl, video_codec_context->width / div[i], video_codec_context->height / div[i], self->params.color_depth == GSR_COLOR_DEPTH_8_BITS ? internal_formats_nv12[i] : internal_formats_p010[i], formats[i], GL_LINEAR);
if(self->target_textures[i] == 0) {
fprintf(stderr, "gsr error: gsr_video_encoder_cuda_setup_textures: failed to create opengl texture\n");
return false;
diff --git a/src/image_writer.c b/src/image_writer.c
index d01a66c..3b4b24a 100644
--- a/src/image_writer.c
+++ b/src/image_writer.c
@@ -1,5 +1,6 @@
#include "../include/image_writer.h"
#include "../include/egl.h"
+#include "../include/utils.h"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "../external/stb_image_write.h"
@@ -9,29 +10,14 @@
#include <stdio.h>
#include <assert.h>
-static unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format) {
- unsigned int texture_id = 0;
- egl->glGenTextures(1, &texture_id);
- egl->glBindTexture(GL_TEXTURE_2D, texture_id);
- egl->glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
-
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
- egl->glBindTexture(GL_TEXTURE_2D, 0);
- return texture_id;
-}
-
/* TODO: Support hdr/10-bit */
-bool gsr_image_writer_init(gsr_image_writer *self, gsr_image_writer_source source, gsr_egl *egl, int width, int height) {
- assert(source == GSR_IMAGE_WRITER_SOURCE_OPENGL);
- self->source = source;
+bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height) {
+ memset(self, 0, sizeof(*self));
+ self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
self->egl = egl;
self->width = width;
self->height = height;
- self->texture = gl_create_texture(self->egl, self->width, self->height, GL_RGB8, GL_RGB); /* TODO: use GL_RGB16 instead of GL_RGB8 for hdr/10-bit */
+ self->texture = gl_create_texture(self->egl, self->width, self->height, GL_RGB8, GL_RGB, GL_LINEAR); /* TODO: use GL_RGB16 instead of GL_RGB8 for hdr/10-bit */
if(self->texture == 0) {
fprintf(stderr, "gsr error: gsr_image_writer_init: failed to create texture\n");
return false;
@@ -39,6 +25,15 @@ bool gsr_image_writer_init(gsr_image_writer *self, gsr_image_writer_source sourc
return true;
}
+bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height) {
+ memset(self, 0, sizeof(*self));
+ self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
+ self->width = width;
+ self->height = height;
+ self->memory = memory;
+ return true;
+}
+
void gsr_image_writer_deinit(gsr_image_writer *self) {
if(self->texture) {
self->egl->glDeleteTextures(1, &self->texture);
@@ -46,12 +41,30 @@ void gsr_image_writer_deinit(gsr_image_writer *self) {
}
}
-bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
+static bool gsr_image_writer_write_memory_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality, const void *data) {
if(quality < 1)
quality = 1;
else if(quality > 100)
quality = 100;
+ bool success = false;
+ switch(image_format) {
+ case GSR_IMAGE_FORMAT_JPEG:
+ success = stbi_write_jpg(filepath, self->width, self->height, 3, data, quality);
+ break;
+ case GSR_IMAGE_FORMAT_PNG:
+ success = stbi_write_png(filepath, self->width, self->height, 3, data, 0);
+ break;
+ }
+
+ if(!success)
+ fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to write image data to output file %s\n", filepath);
+
+ return success;
+}
+
+static bool gsr_image_writer_write_opengl_texture_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
+ assert(self->source == GSR_IMAGE_WRITER_SOURCE_OPENGL);
uint8_t *frame_data = malloc(self->width * self->height * 3);
if(!frame_data) {
fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to allocate memory for image frame\n");
@@ -67,19 +80,17 @@ bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath
self->egl->glFlush();
self->egl->glFinish();
- bool success = false;
- switch(image_format) {
- case GSR_IMAGE_FORMAT_JPEG:
- success = stbi_write_jpg(filepath, self->width, self->height, 3, frame_data, quality);
- break;
- case GSR_IMAGE_FORMAT_PNG:
- success = stbi_write_png(filepath, self->width, self->height, 3, frame_data, 0);
- break;
- }
-
- if(!success)
- fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to write image data to output file %s\n", filepath);
-
+ const bool success = gsr_image_writer_write_memory_to_file(self, filepath, image_format, quality, frame_data);
free(frame_data);
return success;
}
+
+bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
+ switch(self->source) {
+ case GSR_IMAGE_WRITER_SOURCE_OPENGL:
+ return gsr_image_writer_write_opengl_texture_to_file(self, filepath, image_format, quality);
+ case GSR_IMAGE_WRITER_SOURCE_MEMORY:
+ return gsr_image_writer_write_memory_to_file(self, filepath, image_format, quality, self->memory);
+ }
+ return false;
+}
diff --git a/src/main.cpp b/src/main.cpp
index 2e08030..34a5808 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,7 @@
extern "C" {
#include "../include/capture/nvfbc.h"
#include "../include/capture/xcomposite.h"
+#include "../include/capture/ximage.h"
#include "../include/capture/kms.h"
#ifdef GSR_PORTAL
#include "../include/capture/portal.h"
@@ -2427,7 +2428,7 @@ static std::string get_monitor_by_region_center(const gsr_egl *egl, vec2i region
return result;
}
-static gsr_capture* create_monitor_capture(const std::string &window_str, vec2i output_resolution, vec2i region_size, vec2i region_position, gsr_egl *egl, int fps, bool hdr, gsr_color_range color_range, bool record_cursor, gsr_color_depth color_depth) {
+static gsr_capture* create_monitor_capture(const std::string &window_str, vec2i output_resolution, vec2i region_size, vec2i region_position, gsr_egl *egl, int fps, bool hdr, bool record_cursor) {
if(!monitor_capture_use_drm(egl->window, egl->gpu_info.vendor)) {
const char *capture_target = window_str.c_str();
const bool direct_capture = strcmp(window_str.c_str(), "screen-direct") == 0 || strcmp(window_str.c_str(), "screen-direct-force") == 0;
@@ -2443,8 +2444,6 @@ static gsr_capture* create_monitor_capture(const std::string &window_str, vec2i
nvfbc_params.pos = { 0, 0 };
nvfbc_params.size = { 0, 0 };
nvfbc_params.direct_capture = direct_capture;
- nvfbc_params.color_depth = color_depth;
- nvfbc_params.color_range = color_range;
nvfbc_params.record_cursor = record_cursor;
nvfbc_params.output_resolution = output_resolution;
nvfbc_params.region_size = region_size;
@@ -2454,8 +2453,6 @@ static gsr_capture* create_monitor_capture(const std::string &window_str, vec2i
gsr_capture_kms_params kms_params;
kms_params.egl = egl;
kms_params.display_to_capture = window_str.c_str();
- kms_params.color_depth = color_depth;
- kms_params.color_range = color_range;
kms_params.record_cursor = record_cursor;
kms_params.hdr = hdr;
kms_params.fps = fps;
@@ -2466,9 +2463,33 @@ static gsr_capture* create_monitor_capture(const std::string &window_str, vec2i
}
}
-static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_resolution, vec2i region_size, vec2i region_position, bool wayland, gsr_egl *egl, int fps, bool hdr, gsr_color_range color_range,
- bool record_cursor, bool restore_portal_session, const char *portal_session_token_filepath,
- gsr_color_depth color_depth)
+static void region_get_data(std::string &window_str, gsr_egl *egl, vec2i *region_size, vec2i *region_position) {
+ vec2i monitor_pos = {0, 0};
+ vec2i monitor_size = {0, 0};
+ window_str = get_monitor_by_region_center(egl, *region_position, *region_size, &monitor_pos, &monitor_size);
+ if(window_str.empty()) {
+ const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11;
+ const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
+ fprintf(stderr, "Error: the region %dx%d+%d+%d doesn't match any monitor. Available monitors and their regions:\n", region_size->x, region_size->y, region_position->x, region_position->y);
+
+ MonitorOutputCallbackUserdata userdata;
+ userdata.window = egl->window;
+ for_each_active_monitor_output(egl->window, egl->card_path, connection_type, monitor_output_callback_print, &userdata);
+ _exit(51);
+ }
+
+ // Capture whole monitor when region size is set to 0x0
+ if(region_size->x == 0 && region_size->y == 0) {
+ region_position->x = 0;
+ region_position->y = 0;
+ } else {
+ region_position->x -= monitor_pos.x;
+ region_position->y -= monitor_pos.y;
+ }
+}
+
+static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_resolution, vec2i region_size, vec2i region_position, bool wayland, gsr_egl *egl, int fps, bool hdr,
+ bool record_cursor, bool restore_portal_session, const char *portal_session_token_filepath)
{
Window src_window_id = None;
bool follow_focused = false;
@@ -2496,8 +2517,6 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
gsr_capture_portal_params portal_params;
portal_params.egl = egl;
- portal_params.color_depth = color_depth;
- portal_params.color_range = color_range;
portal_params.record_cursor = record_cursor;
portal_params.restore_portal_session = restore_portal_session;
portal_params.portal_session_token_filepath = portal_session_token_filepath;
@@ -2510,35 +2529,13 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
_exit(2);
#endif
} else if(strcmp(window_str.c_str(), "region") == 0) {
- vec2i monitor_pos = {0, 0};
- vec2i monitor_size = {0, 0};
- window_str = get_monitor_by_region_center(egl, region_position, region_size, &monitor_pos, &monitor_size);
- if(window_str.empty()) {
- const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11;
- const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
- fprintf(stderr, "Error: the region %dx%d+%d+%d doesn't match any monitor. Available monitors and their regions:\n", region_size.x, region_size.y, region_position.x, region_position.y);
-
- MonitorOutputCallbackUserdata userdata;
- userdata.window = egl->window;
- for_each_active_monitor_output(egl->window, egl->card_path, connection_type, monitor_output_callback_print, &userdata);
- _exit(51);
- }
-
- // Capture whole monitor when region size is set to 0x0
- if(region_size.x == 0 && region_size.y == 0) {
- region_position.x = 0;
- region_position.y = 0;
- } else {
- region_position.x -= monitor_pos.x;
- region_position.y -= monitor_pos.y;
- }
-
- capture = create_monitor_capture(window_str, output_resolution, region_size, region_position, egl, fps, hdr, color_range, record_cursor, color_depth);
+ region_get_data(window_str, egl, &region_size, &region_position);
+ capture = create_monitor_capture(window_str, output_resolution, region_size, region_position, egl, fps, hdr, record_cursor);
if(!capture)
_exit(1);
} else if(contains_non_hex_number(window_str.c_str())) {
validate_monitor_get_valid(egl, window_str);
- capture = create_monitor_capture(window_str, output_resolution, region_size, region_position, egl, fps, hdr, color_range, record_cursor, color_depth);
+ capture = create_monitor_capture(window_str, output_resolution, region_size, region_position, egl, fps, hdr, record_cursor);
if(!capture)
_exit(1);
} else {
@@ -2560,9 +2557,7 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
xcomposite_params.egl = egl;
xcomposite_params.window = src_window_id;
xcomposite_params.follow_focused = follow_focused;
- xcomposite_params.color_range = color_range;
xcomposite_params.record_cursor = record_cursor;
- xcomposite_params.color_depth = color_depth;
xcomposite_params.output_resolution = output_resolution;
capture = gsr_capture_xcomposite_create(&xcomposite_params);
if(!capture)
@@ -2601,7 +2596,29 @@ static void capture_image_to_file(const char *filepath, std::string &window_str,
bool record_cursor, bool restore_portal_session, const char *portal_session_token_filepath, VideoQuality video_quality) {
const gsr_color_range color_range = image_format_to_color_range(image_format);
const int fps = 60;
- gsr_capture *capture = create_capture_impl(window_str, output_resolution, region_size, region_position, wayland, egl, fps, false, color_range, record_cursor, restore_portal_session, portal_session_token_filepath, GSR_COLOR_DEPTH_8_BITS);
+ gsr_capture *capture = nullptr;
+ switch(gsr_window_get_display_server(egl->window)) {
+ case GSR_DISPLAY_SERVER_X11: {
+ if(window_str == "region")
+ region_get_data(window_str, egl, &region_size, &region_position);
+ else
+ validate_monitor_get_valid(egl, window_str);
+
+ gsr_capture_ximage_params ximage_params;
+ ximage_params.egl = egl;
+ ximage_params.display_to_capture = window_str.c_str();
+ ximage_params.record_cursor = record_cursor;
+ ximage_params.output_resolution = output_resolution;
+ ximage_params.region_size = region_size;
+ ximage_params.region_position = region_position;
+ capture = gsr_capture_ximage_create(&ximage_params);
+ break;
+ }
+ case GSR_DISPLAY_SERVER_WAYLAND: {
+ capture = create_capture_impl(window_str, output_resolution, region_size, region_position, wayland, egl, fps, false, record_cursor, restore_portal_session, portal_session_token_filepath);
+ break;
+ }
+ }
gsr_capture_metadata capture_metadata;
capture_metadata.width = 0;
@@ -2612,13 +2629,13 @@ static void capture_image_to_file(const char *filepath, std::string &window_str,
int capture_result = gsr_capture_start(capture, &capture_metadata);
if(capture_result != 0) {
- fprintf(stderr, "gsr error: gsr_capture_start failed\n");
+ fprintf(stderr, "gsr error: capture_image_to_file_wayland: gsr_capture_start failed\n");
_exit(capture_result);
}
gsr_image_writer image_writer;
- if(!gsr_image_writer_init(&image_writer, GSR_IMAGE_WRITER_SOURCE_OPENGL, egl, capture_metadata.width, capture_metadata.height)) {
- fprintf(stderr, "gsr error: gsr_image_write_gl_init failed\n");
+ if(!gsr_image_writer_init_opengl(&image_writer, egl, capture_metadata.width, capture_metadata.height)) {
+ fprintf(stderr, "gsr error: capture_image_to_file_wayland: gsr_image_write_gl_init failed\n");
_exit(1);
}
@@ -2634,7 +2651,7 @@ static void capture_image_to_file(const char *filepath, std::string &window_str,
gsr_color_conversion color_conversion;
if(gsr_color_conversion_init(&color_conversion, &color_conversion_params) != 0) {
- fprintf(stderr, "gsr error: gsr_capture_kms_setup_vaapi_textures: failed to create color conversion\n");
+ fprintf(stderr, "gsr error: capture_image_to_file_wayland: failed to create color conversion\n");
_exit(1);
}
@@ -2664,7 +2681,7 @@ static void capture_image_to_file(const char *filepath, std::string &window_str,
const int image_quality = video_quality_to_image_quality_value(video_quality);
if(!gsr_image_writer_write_to_file(&image_writer, filepath, image_format, image_quality)) {
- fprintf(stderr, "gsr error: failed to write opengl texture to image output file %s\n", filepath);
+ fprintf(stderr, "gsr error: capture_image_to_file_wayland: failed to write opengl texture to image output file %s\n", filepath);
_exit(1);
}
@@ -3941,7 +3958,7 @@ int main(int argc, char **argv) {
const AVCodec *video_codec_f = select_video_codec_with_fallback(&video_codec, video_codec_to_use, file_extension.c_str(), use_software_video_encoder, &egl, &low_power);
const gsr_color_depth color_depth = video_codec_to_bit_depth(video_codec);
- gsr_capture *capture = create_capture_impl(window_str, output_resolution, region_size, region_position, wayland, &egl, fps, video_codec_is_hdr(video_codec), color_range, record_cursor, restore_portal_session, portal_session_token_filepath, color_depth);
+ gsr_capture *capture = create_capture_impl(window_str, output_resolution, region_size, region_position, wayland, &egl, fps, video_codec_is_hdr(video_codec), record_cursor, restore_portal_session, portal_session_token_filepath);
// (Some?) livestreaming services require at least one audio track to work.
// If not audio is provided then create one silent audio track.
@@ -4022,7 +4039,7 @@ int main(int argc, char **argv) {
gsr_color_conversion color_conversion;
if(gsr_color_conversion_init(&color_conversion, &color_conversion_params) != 0) {
- fprintf(stderr, "gsr error: gsr_capture_kms_setup_vaapi_textures: failed to create color conversion\n");
+ fprintf(stderr, "gsr error: main: failed to create color conversion\n");
_exit(1);
}
diff --git a/src/utils.c b/src/utils.c
index 473ac96..f053eed 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -912,3 +912,18 @@ vec2i scale_keep_aspect_ratio(vec2i from, vec2i to) {
return from;
}
+
+unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter) {
+ unsigned int texture_id = 0;
+ egl->glGenTextures(1, &texture_id);
+ egl->glBindTexture(GL_TEXTURE_2D, texture_id);
+ egl->glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
+
+ egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter);
+ egl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter);
+
+ egl->glBindTexture(GL_TEXTURE_2D, 0);
+ return texture_id;
+}