mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
Take screenshot with XGetImage on x11 to workaround nvidia driver (nvfbc) limitation that only allows one nvfbc session at a time
This commit is contained in:
@@ -5,9 +5,7 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gsr_egl *egl;
|
gsr_egl *egl;
|
||||||
const char *display_to_capture; /* if this is "screen", then the first monitor is captured. A copy is made of this */
|
const char *display_to_capture; /* A copy is made of this */
|
||||||
gsr_color_depth color_depth;
|
|
||||||
gsr_color_range color_range;
|
|
||||||
bool hdr;
|
bool hdr;
|
||||||
bool record_cursor;
|
bool record_cursor;
|
||||||
int fps;
|
int fps;
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ typedef struct {
|
|||||||
vec2i pos;
|
vec2i pos;
|
||||||
vec2i size;
|
vec2i size;
|
||||||
bool direct_capture;
|
bool direct_capture;
|
||||||
gsr_color_depth color_depth;
|
|
||||||
gsr_color_range color_range;
|
|
||||||
bool record_cursor;
|
bool record_cursor;
|
||||||
vec2i output_resolution;
|
vec2i output_resolution;
|
||||||
vec2i region_size;
|
vec2i region_size;
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gsr_egl *egl;
|
gsr_egl *egl;
|
||||||
gsr_color_depth color_depth;
|
|
||||||
gsr_color_range color_range;
|
|
||||||
bool record_cursor;
|
bool record_cursor;
|
||||||
bool restore_portal_session;
|
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) */
|
/* 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) */
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ typedef struct {
|
|||||||
gsr_egl *egl;
|
gsr_egl *egl;
|
||||||
unsigned long window;
|
unsigned long window;
|
||||||
bool follow_focused; /* If this is set then |window| is ignored */
|
bool follow_focused; /* If this is set then |window| is ignored */
|
||||||
gsr_color_range color_range;
|
|
||||||
bool record_cursor;
|
bool record_cursor;
|
||||||
gsr_color_depth color_depth;
|
|
||||||
vec2i output_resolution;
|
vec2i output_resolution;
|
||||||
} gsr_capture_xcomposite_params;
|
} gsr_capture_xcomposite_params;
|
||||||
|
|
||||||
|
|||||||
18
include/capture/ximage.h
Normal file
18
include/capture/ximage.h
Normal file
@@ -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 */
|
||||||
@@ -235,6 +235,7 @@ struct gsr_egl {
|
|||||||
void (*glTexParameteriv)(unsigned int target, unsigned int pname, const int *params);
|
void (*glTexParameteriv)(unsigned int target, unsigned int pname, const int *params);
|
||||||
void (*glGetTexLevelParameteriv)(unsigned int target, int level, unsigned int pname, 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 (*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 (*glGetTexImage)(unsigned int target, int level, unsigned int format, unsigned int type, void *pixels);
|
||||||
void (*glGenFramebuffers)(int n, unsigned int *framebuffers);
|
void (*glGenFramebuffers)(int n, unsigned int *framebuffers);
|
||||||
void (*glBindFramebuffer)(unsigned int target, unsigned int framebuffer);
|
void (*glBindFramebuffer)(unsigned int target, unsigned int framebuffer);
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ typedef enum {
|
|||||||
} gsr_image_format;
|
} gsr_image_format;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GSR_IMAGE_WRITER_SOURCE_OPENGL
|
GSR_IMAGE_WRITER_SOURCE_OPENGL,
|
||||||
|
GSR_IMAGE_WRITER_SOURCE_MEMORY
|
||||||
} gsr_image_writer_source;
|
} gsr_image_writer_source;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -20,9 +21,12 @@ typedef struct {
|
|||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
unsigned int texture;
|
unsigned int texture;
|
||||||
|
const void *memory; /* Reference */
|
||||||
} gsr_image_writer;
|
} 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);
|
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 */
|
/* Quality is between 1 and 100 where 100 is the max quality. Quality doesn't apply to lossless formats */
|
||||||
|
|||||||
@@ -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);
|
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 */
|
#endif /* GSR_UTILS_H */
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ src = [
|
|||||||
'src/capture/capture.c',
|
'src/capture/capture.c',
|
||||||
'src/capture/nvfbc.c',
|
'src/capture/nvfbc.c',
|
||||||
'src/capture/xcomposite.c',
|
'src/capture/xcomposite.c',
|
||||||
|
'src/capture/ximage.c',
|
||||||
'src/capture/kms.c',
|
'src/capture/kms.c',
|
||||||
'src/encoder/video/video.c',
|
'src/encoder/video/video.c',
|
||||||
'src/encoder/video/nvenc.c',
|
'src/encoder/video/nvenc.c',
|
||||||
|
|||||||
@@ -14,9 +14,7 @@
|
|||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <libdrm/drm_fourcc.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
|
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
#include <libavutil/mastering_display_metadata.h>
|
#include <libavutil/mastering_display_metadata.h>
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
|
|
||||||
#define FIND_CRTC_BY_NAME_TIMEOUT_SECONDS 2.0
|
#define FIND_CRTC_BY_NAME_TIMEOUT_SECONDS 2.0
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gsr_capture_nvfbc_params params;
|
gsr_capture_nvfbc_params params;
|
||||||
|
|||||||
@@ -8,10 +8,9 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gsr_capture_portal_params params;
|
gsr_capture_portal_params params;
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,6 @@
|
|||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
#include <libavutil/frame.h>
|
|
||||||
#include <libavcodec/avcodec.h>
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
gsr_capture_xcomposite_params params;
|
gsr_capture_xcomposite_params params;
|
||||||
Display *display;
|
Display *display;
|
||||||
|
|||||||
247
src/capture/ximage.c
Normal file
247
src/capture/ximage.c
Normal file
@@ -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;
|
||||||
|
}
|
||||||
@@ -288,6 +288,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
|
|||||||
{ (void**)&self->glTexParameteriv, "glTexParameteriv" },
|
{ (void**)&self->glTexParameteriv, "glTexParameteriv" },
|
||||||
{ (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" },
|
{ (void**)&self->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" },
|
||||||
{ (void**)&self->glTexImage2D, "glTexImage2D" },
|
{ (void**)&self->glTexImage2D, "glTexImage2D" },
|
||||||
|
{ (void**)&self->glTexSubImage2D, "glTexSubImage2D" },
|
||||||
{ (void**)&self->glGetTexImage, "glGetTexImage" },
|
{ (void**)&self->glGetTexImage, "glGetTexImage" },
|
||||||
{ (void**)&self->glGenFramebuffers, "glGenFramebuffers" },
|
{ (void**)&self->glGenFramebuffers, "glGenFramebuffers" },
|
||||||
{ (void**)&self->glBindFramebuffer, "glBindFramebuffer" },
|
{ (void**)&self->glBindFramebuffer, "glBindFramebuffer" },
|
||||||
|
|||||||
@@ -65,21 +65,6 @@ static bool gsr_video_encoder_nvenc_setup_context(gsr_video_encoder_nvenc *self,
|
|||||||
return true;
|
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) {
|
static bool cuda_register_opengl_texture(gsr_cuda *cuda, CUgraphicsResource *cuda_graphics_resource, CUarray *mapped_array, unsigned int texture_id) {
|
||||||
CUresult res;
|
CUresult res;
|
||||||
res = cuda->cuGraphicsGLRegisterImage(cuda_graphics_resource, texture_id, GL_TEXTURE_2D, CU_GRAPHICS_REGISTER_FLAGS_NONE);
|
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
|
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
|
||||||
|
|
||||||
for(int i = 0; i < 2; ++i) {
|
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) {
|
if(self->target_textures[i] == 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_video_encoder_nvenc_setup_textures: failed to create opengl texture\n");
|
fprintf(stderr, "gsr error: gsr_video_encoder_nvenc_setup_textures: failed to create opengl texture\n");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "../../../include/encoder/video/software.h"
|
#include "../../../include/encoder/video/software.h"
|
||||||
#include "../../../include/egl.h"
|
#include "../../../include/egl.h"
|
||||||
|
#include "../../../include/utils.h"
|
||||||
|
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavutil/frame.h>
|
#include <libavutil/frame.h>
|
||||||
@@ -14,21 +15,6 @@ typedef struct {
|
|||||||
unsigned int target_textures[2];
|
unsigned int target_textures[2];
|
||||||
} gsr_video_encoder_software;
|
} 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) {
|
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);
|
int res = av_frame_get_buffer(frame, LINESIZE_ALIGNMENT);
|
||||||
if(res < 0) {
|
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
|
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
|
||||||
|
|
||||||
for(int i = 0; i < 2; ++i) {
|
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) {
|
if(self->target_textures[i] == 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_capture_kms_setup_cuda_textures: failed to create opengl texture\n");
|
fprintf(stderr, "gsr error: gsr_capture_kms_setup_cuda_textures: failed to create opengl texture\n");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -71,22 +71,6 @@ static bool gsr_video_encoder_vulkan_setup_context(gsr_video_encoder_vulkan *sel
|
|||||||
return true;
|
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) {
|
static AVVulkanDeviceContext* video_codec_context_get_vulkan_data(AVCodecContext *video_codec_context) {
|
||||||
AVBufferRef *hw_frames_ctx = video_codec_context->hw_frames_ctx;
|
AVBufferRef *hw_frames_ctx = video_codec_context->hw_frames_ctx;
|
||||||
if(!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
|
const int div[2] = {1, 2}; // divide UV texture size by 2 because chroma is half size
|
||||||
|
|
||||||
for(int i = 0; i < 2; ++i) {
|
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) {
|
if(self->target_textures[i] == 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_video_encoder_cuda_setup_textures: failed to create opengl texture\n");
|
fprintf(stderr, "gsr error: gsr_video_encoder_cuda_setup_textures: failed to create opengl texture\n");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "../include/image_writer.h"
|
#include "../include/image_writer.h"
|
||||||
#include "../include/egl.h"
|
#include "../include/egl.h"
|
||||||
|
#include "../include/utils.h"
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "../external/stb_image_write.h"
|
#include "../external/stb_image_write.h"
|
||||||
@@ -9,29 +10,14 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <assert.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 */
|
/* 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) {
|
bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height) {
|
||||||
assert(source == GSR_IMAGE_WRITER_SOURCE_OPENGL);
|
memset(self, 0, sizeof(*self));
|
||||||
self->source = source;
|
self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
|
||||||
self->egl = egl;
|
self->egl = egl;
|
||||||
self->width = width;
|
self->width = width;
|
||||||
self->height = height;
|
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) {
|
if(self->texture == 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_image_writer_init: failed to create texture\n");
|
fprintf(stderr, "gsr error: gsr_image_writer_init: failed to create texture\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -39,6 +25,15 @@ bool gsr_image_writer_init(gsr_image_writer *self, gsr_image_writer_source sourc
|
|||||||
return true;
|
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) {
|
void gsr_image_writer_deinit(gsr_image_writer *self) {
|
||||||
if(self->texture) {
|
if(self->texture) {
|
||||||
self->egl->glDeleteTextures(1, &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)
|
if(quality < 1)
|
||||||
quality = 1;
|
quality = 1;
|
||||||
else if(quality > 100)
|
else if(quality > 100)
|
||||||
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);
|
uint8_t *frame_data = malloc(self->width * self->height * 3);
|
||||||
if(!frame_data) {
|
if(!frame_data) {
|
||||||
fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to allocate memory for image frame\n");
|
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->glFlush();
|
||||||
self->egl->glFinish();
|
self->egl->glFinish();
|
||||||
|
|
||||||
bool success = false;
|
const bool success = gsr_image_writer_write_memory_to_file(self, filepath, image_format, quality, frame_data);
|
||||||
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);
|
|
||||||
|
|
||||||
free(frame_data);
|
free(frame_data);
|
||||||
return success;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
107
src/main.cpp
107
src/main.cpp
@@ -1,6 +1,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#include "../include/capture/nvfbc.h"
|
#include "../include/capture/nvfbc.h"
|
||||||
#include "../include/capture/xcomposite.h"
|
#include "../include/capture/xcomposite.h"
|
||||||
|
#include "../include/capture/ximage.h"
|
||||||
#include "../include/capture/kms.h"
|
#include "../include/capture/kms.h"
|
||||||
#ifdef GSR_PORTAL
|
#ifdef GSR_PORTAL
|
||||||
#include "../include/capture/portal.h"
|
#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;
|
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)) {
|
if(!monitor_capture_use_drm(egl->window, egl->gpu_info.vendor)) {
|
||||||
const char *capture_target = window_str.c_str();
|
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;
|
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.pos = { 0, 0 };
|
||||||
nvfbc_params.size = { 0, 0 };
|
nvfbc_params.size = { 0, 0 };
|
||||||
nvfbc_params.direct_capture = direct_capture;
|
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.record_cursor = record_cursor;
|
||||||
nvfbc_params.output_resolution = output_resolution;
|
nvfbc_params.output_resolution = output_resolution;
|
||||||
nvfbc_params.region_size = region_size;
|
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;
|
gsr_capture_kms_params kms_params;
|
||||||
kms_params.egl = egl;
|
kms_params.egl = egl;
|
||||||
kms_params.display_to_capture = window_str.c_str();
|
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.record_cursor = record_cursor;
|
||||||
kms_params.hdr = hdr;
|
kms_params.hdr = hdr;
|
||||||
kms_params.fps = fps;
|
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,
|
static void region_get_data(std::string &window_str, gsr_egl *egl, vec2i *region_size, vec2i *region_position) {
|
||||||
bool record_cursor, bool restore_portal_session, const char *portal_session_token_filepath,
|
vec2i monitor_pos = {0, 0};
|
||||||
gsr_color_depth color_depth)
|
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;
|
Window src_window_id = None;
|
||||||
bool follow_focused = false;
|
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;
|
gsr_capture_portal_params portal_params;
|
||||||
portal_params.egl = egl;
|
portal_params.egl = egl;
|
||||||
portal_params.color_depth = color_depth;
|
|
||||||
portal_params.color_range = color_range;
|
|
||||||
portal_params.record_cursor = record_cursor;
|
portal_params.record_cursor = record_cursor;
|
||||||
portal_params.restore_portal_session = restore_portal_session;
|
portal_params.restore_portal_session = restore_portal_session;
|
||||||
portal_params.portal_session_token_filepath = portal_session_token_filepath;
|
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);
|
_exit(2);
|
||||||
#endif
|
#endif
|
||||||
} else if(strcmp(window_str.c_str(), "region") == 0) {
|
} else if(strcmp(window_str.c_str(), "region") == 0) {
|
||||||
vec2i monitor_pos = {0, 0};
|
region_get_data(window_str, egl, ®ion_size, ®ion_position);
|
||||||
vec2i monitor_size = {0, 0};
|
capture = create_monitor_capture(window_str, output_resolution, region_size, region_position, egl, fps, hdr, record_cursor);
|
||||||
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);
|
|
||||||
if(!capture)
|
if(!capture)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
} else if(contains_non_hex_number(window_str.c_str())) {
|
} else if(contains_non_hex_number(window_str.c_str())) {
|
||||||
validate_monitor_get_valid(egl, window_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)
|
if(!capture)
|
||||||
_exit(1);
|
_exit(1);
|
||||||
} else {
|
} else {
|
||||||
@@ -2560,9 +2557,7 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
|
|||||||
xcomposite_params.egl = egl;
|
xcomposite_params.egl = egl;
|
||||||
xcomposite_params.window = src_window_id;
|
xcomposite_params.window = src_window_id;
|
||||||
xcomposite_params.follow_focused = follow_focused;
|
xcomposite_params.follow_focused = follow_focused;
|
||||||
xcomposite_params.color_range = color_range;
|
|
||||||
xcomposite_params.record_cursor = record_cursor;
|
xcomposite_params.record_cursor = record_cursor;
|
||||||
xcomposite_params.color_depth = color_depth;
|
|
||||||
xcomposite_params.output_resolution = output_resolution;
|
xcomposite_params.output_resolution = output_resolution;
|
||||||
capture = gsr_capture_xcomposite_create(&xcomposite_params);
|
capture = gsr_capture_xcomposite_create(&xcomposite_params);
|
||||||
if(!capture)
|
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) {
|
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 gsr_color_range color_range = image_format_to_color_range(image_format);
|
||||||
const int fps = 60;
|
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, ®ion_size, ®ion_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;
|
gsr_capture_metadata capture_metadata;
|
||||||
capture_metadata.width = 0;
|
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);
|
int capture_result = gsr_capture_start(capture, &capture_metadata);
|
||||||
if(capture_result != 0) {
|
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);
|
_exit(capture_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
gsr_image_writer image_writer;
|
gsr_image_writer image_writer;
|
||||||
if(!gsr_image_writer_init(&image_writer, GSR_IMAGE_WRITER_SOURCE_OPENGL, egl, capture_metadata.width, capture_metadata.height)) {
|
if(!gsr_image_writer_init_opengl(&image_writer, egl, capture_metadata.width, capture_metadata.height)) {
|
||||||
fprintf(stderr, "gsr error: gsr_image_write_gl_init failed\n");
|
fprintf(stderr, "gsr error: capture_image_to_file_wayland: gsr_image_write_gl_init failed\n");
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2634,7 +2651,7 @@ static void capture_image_to_file(const char *filepath, std::string &window_str,
|
|||||||
|
|
||||||
gsr_color_conversion color_conversion;
|
gsr_color_conversion color_conversion;
|
||||||
if(gsr_color_conversion_init(&color_conversion, &color_conversion_params) != 0) {
|
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);
|
_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);
|
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)) {
|
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);
|
_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 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);
|
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.
|
// (Some?) livestreaming services require at least one audio track to work.
|
||||||
// If not audio is provided then create one silent audio track.
|
// 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;
|
gsr_color_conversion color_conversion;
|
||||||
if(gsr_color_conversion_init(&color_conversion, &color_conversion_params) != 0) {
|
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);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
src/utils.c
15
src/utils.c
@@ -912,3 +912,18 @@ vec2i scale_keep_aspect_ratio(vec2i from, vec2i to) {
|
|||||||
|
|
||||||
return from;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user