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 {
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
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 (*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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
|
||||
typedef struct {
|
||||
gsr_capture_nvfbc_params params;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
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->glGetTexLevelParameteriv, "glGetTexLevelParameteriv" },
|
||||
{ (void**)&self->glTexImage2D, "glTexImage2D" },
|
||||
{ (void**)&self->glTexSubImage2D, "glTexSubImage2D" },
|
||||
{ (void**)&self->glGetTexImage, "glGetTexImage" },
|
||||
{ (void**)&self->glGenFramebuffers, "glGenFramebuffers" },
|
||||
{ (void**)&self->glBindFramebuffer, "glBindFramebuffer" },
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
107
src/main.cpp
107
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, ®ion_size, ®ion_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, ®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;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
15
src/utils.c
15
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user