This commit is contained in:
2025-12-28 13:55:10 -08:00
commit 9b4219aa67
131 changed files with 32853 additions and 0 deletions

118
include/args_parser.h Normal file
View File

@ -0,0 +1,118 @@
#ifndef GSR_ARGS_PARSER_H
#define GSR_ARGS_PARSER_H
#include <stdbool.h>
#include <stdint.h>
#include "defs.h"
#include "vec2.h"
typedef struct gsr_egl gsr_egl;
#define NUM_ARGS 32
typedef enum {
GSR_CAPTURE_SOURCE_TYPE_WINDOW,
GSR_CAPTURE_SOURCE_TYPE_FOCUSED_WINDOW,
GSR_CAPTURE_SOURCE_TYPE_MONITOR,
GSR_CAPTURE_SOURCE_TYPE_REGION,
GSR_CAPTURE_SOURCE_TYPE_PORTAL,
GSR_CAPTURE_SOURCE_TYPE_V4L2
} CaptureSourceType;
typedef enum {
ARG_TYPE_STRING,
ARG_TYPE_BOOLEAN,
ARG_TYPE_ENUM,
ARG_TYPE_I64,
ARG_TYPE_DOUBLE,
} ArgType;
typedef struct {
const char *name;
int value;
} ArgEnum;
typedef struct {
ArgType type;
const char **values;
int capacity_num_values;
int num_values;
const char *key;
bool optional;
bool list;
const ArgEnum *enum_values;
int num_enum_values;
int64_t integer_value_min;
int64_t integer_value_max;
union {
bool boolean;
int enum_value;
int64_t i64_value;
double d_value;
} typed_value;
} Arg;
typedef struct {
void (*version)(void *userdata);
void (*info)(void *userdata);
void (*list_audio_devices)(void *userdata);
void (*list_application_audio)(void *userdata);
void (*list_v4l2_devices)(void *userdata);
void (*list_capture_options)(const char *card_path, void *userdata);
} args_handlers;
typedef struct {
Arg args[NUM_ARGS];
gsr_video_encoder_hardware video_encoder;
gsr_pixel_format pixel_format;
gsr_framerate_mode framerate_mode;
gsr_color_range color_range;
gsr_tune tune;
gsr_video_codec video_codec;
gsr_audio_codec audio_codec;
gsr_bitrate_mode bitrate_mode;
gsr_video_quality video_quality;
gsr_replay_storage replay_storage;
const char *capture_source;
const char *container_format;
const char *filename;
const char *replay_recording_directory;
const char *portal_session_token_filepath;
const char *recording_saved_script;
bool verbose;
bool gl_debug;
bool fallback_cpu_encoding;
bool record_cursor;
bool date_folders;
bool restore_portal_session;
bool restart_replay_on_save;
bool overclock;
bool is_livestream;
bool is_output_piped;
bool low_latency_recording;
bool very_old_gpu;
int64_t video_bitrate;
int64_t audio_bitrate;
int64_t fps;
int64_t replay_buffer_size_secs;
double keyint;
vec2i output_resolution;
vec2i region_size;
vec2i region_position;
} args_parser;
/* |argv| is stored as a reference */
bool args_parser_parse(args_parser *self, int argc, char **argv, const args_handlers *args_handlers, void *userdata);
void args_parser_deinit(args_parser *self);
bool args_parser_validate_with_gl_info(args_parser *self, gsr_egl *egl);
void args_parser_print_usage(void);
Arg* args_parser_get_arg(args_parser *self, const char *arg_name);
#endif /* GSR_ARGS_PARSER_H */

64
include/capture/capture.h Normal file
View File

@ -0,0 +1,64 @@
#ifndef GSR_CAPTURE_CAPTURE_H
#define GSR_CAPTURE_CAPTURE_H
#include "../color_conversion.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
typedef struct AVCodecContext AVCodecContext;
typedef struct AVStream AVStream;
typedef struct AVFrame AVFrame;
typedef struct AVMasteringDisplayMetadata AVMasteringDisplayMetadata;
typedef struct AVContentLightMetadata AVContentLightMetadata;
typedef struct gsr_capture gsr_capture;
typedef struct gsr_capture_metadata gsr_capture_metadata;
typedef enum {
GSR_CAPTURE_ALIGN_START,
GSR_CAPTURE_ALIGN_CENTER,
GSR_CAPTURE_ALIGN_END
} gsr_capture_alignment;
struct gsr_capture_metadata {
// Size of the video
vec2i video_size;
// The captured output gets scaled to this size. By default this will be the same size as the captured target
vec2i recording_size;
vec2i position;
int fps;
gsr_capture_alignment halign;
gsr_capture_alignment valign;
gsr_flip flip;
};
struct gsr_capture {
/* These methods should not be called manually. Call gsr_capture_* instead. |capture_metadata->video_size| should be set by this function */
int (*start)(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
void (*on_event)(gsr_capture *cap, gsr_egl *egl); /* can be NULL */
void (*tick)(gsr_capture *cap); /* can be NULL. If there is an event then |on_event| is called before this */
bool (*should_stop)(gsr_capture *cap, bool *err); /* can be NULL. If NULL, return false */
bool (*capture_has_synchronous_task)(gsr_capture *cap); /* can be NULL. If this returns true then the time spent in |capture| is ignored for video/audio (capture is paused while the synchronous task happens) */
void (*pre_capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); /* can be NULL */
int (*capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); /* Return 0 if the frame was captured */
bool (*uses_external_image)(gsr_capture *cap); /* can be NULL. If NULL, return false */
bool (*set_hdr_metadata)(gsr_capture *cap, AVMasteringDisplayMetadata *mastering_display_metadata, AVContentLightMetadata *light_metadata); /* can be NULL. If NULL, return false */
uint64_t (*get_window_id)(gsr_capture *cap); /* can be NULL. Returns 0 if unknown */
bool (*is_damaged)(gsr_capture *cap); /* can be NULL */
void (*clear_damage)(gsr_capture *cap); /* can be NULL */
void (*destroy)(gsr_capture *cap);
void *priv; /* can be NULL */
bool started;
};
int gsr_capture_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
void gsr_capture_on_event(gsr_capture *cap, gsr_egl *egl);
void gsr_capture_tick(gsr_capture *cap);
bool gsr_capture_should_stop(gsr_capture *cap, bool *err);
int gsr_capture_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion);
bool gsr_capture_uses_external_image(gsr_capture *cap);
bool gsr_capture_set_hdr_metadata(gsr_capture *cap, AVMasteringDisplayMetadata *mastering_display_metadata, AVContentLightMetadata *light_metadata);
void gsr_capture_destroy(gsr_capture *cap);
#endif /* GSR_CAPTURE_CAPTURE_H */

23
include/capture/kms.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef GSR_CAPTURE_KMS_H
#define GSR_CAPTURE_KMS_H
#include "capture.h"
#include "../cursor.h"
#include "../../kms/kms_shared.h"
typedef struct {
gsr_egl *egl;
gsr_cursor *x11_cursor;
gsr_kms_response *kms_response;
const char *display_to_capture; /* A copy is made of this */
bool hdr;
bool record_cursor;
int fps;
vec2i output_resolution;
vec2i region_size;
vec2i region_position;
} gsr_capture_kms_params;
gsr_capture* gsr_capture_kms_create(const gsr_capture_kms_params *params);
#endif /* GSR_CAPTURE_KMS_H */

20
include/capture/nvfbc.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef GSR_CAPTURE_NVFBC_H
#define GSR_CAPTURE_NVFBC_H
#include "capture.h"
#include "../vec2.h"
typedef struct {
gsr_egl *egl;
const char *display_to_capture; /* if this is "screen", then the entire x11 screen is captured (all displays). A copy is made of this */
int fps;
bool direct_capture;
bool record_cursor;
vec2i output_resolution;
vec2i region_size;
vec2i region_position;
} gsr_capture_nvfbc_params;
gsr_capture* gsr_capture_nvfbc_create(const gsr_capture_nvfbc_params *params);
#endif /* GSR_CAPTURE_NVFBC_H */

17
include/capture/portal.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef GSR_CAPTURE_PORTAL_H
#define GSR_CAPTURE_PORTAL_H
#include "capture.h"
typedef struct {
gsr_egl *egl;
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) */
const char *portal_session_token_filepath;
vec2i output_resolution;
} gsr_capture_portal_params;
gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params);
#endif /* GSR_CAPTURE_PORTAL_H */

30
include/capture/v4l2.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef GSR_CAPTURE_V4L2_H
#define GSR_CAPTURE_V4L2_H
#include "capture.h"
typedef enum {
GSR_CAPTURE_V4L2_PIXFMT_AUTO,
GSR_CAPTURE_V4L2_PIXFMT_YUYV,
GSR_CAPTURE_V4L2_PIXFMT_MJPEG
} gsr_capture_v4l2_pixfmt;
typedef struct {
bool yuyv;
bool mjpeg;
} gsr_capture_v4l2_supported_pixfmts;
typedef struct {
gsr_egl *egl;
vec2i output_resolution;
const char *device_path;
gsr_capture_v4l2_pixfmt pixfmt;
int fps;
} gsr_capture_v4l2_params;
gsr_capture* gsr_capture_v4l2_create(const gsr_capture_v4l2_params *params);
typedef void (*v4l2_devices_query_callback)(const char *path, gsr_capture_v4l2_supported_pixfmts supported_pixfmts, vec2i size, void *userdata);
void gsr_capture_v4l2_list_devices(v4l2_devices_query_callback callback, void *userdata);
#endif /* GSR_CAPTURE_V4L2_H */

View File

@ -0,0 +1,19 @@
#ifndef GSR_CAPTURE_XCOMPOSITE_H
#define GSR_CAPTURE_XCOMPOSITE_H
#include "capture.h"
#include "../vec2.h"
#include "../cursor.h"
typedef struct {
gsr_egl *egl;
gsr_cursor *cursor;
unsigned long window;
bool follow_focused; /* If this is set then |window| is ignored */
bool record_cursor;
vec2i output_resolution;
} gsr_capture_xcomposite_params;
gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params *params);
#endif /* GSR_CAPTURE_XCOMPOSITE_H */

20
include/capture/ximage.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef GSR_CAPTURE_XIMAGE_H
#define GSR_CAPTURE_XIMAGE_H
#include "capture.h"
#include "../vec2.h"
#include "../cursor.h"
typedef struct {
gsr_egl *egl;
gsr_cursor *cursor;
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 */

View File

@ -0,0 +1,25 @@
#ifndef GSR_CODEC_QUERY_H
#define GSR_CODEC_QUERY_H
#include <stdbool.h>
#include "../vec2.h"
typedef struct {
bool supported;
bool low_power;
vec2i max_resolution;
} gsr_supported_video_codec;
typedef struct {
gsr_supported_video_codec h264;
gsr_supported_video_codec hevc;
gsr_supported_video_codec hevc_hdr;
gsr_supported_video_codec hevc_10bit;
gsr_supported_video_codec av1;
gsr_supported_video_codec av1_hdr;
gsr_supported_video_codec av1_10bit;
gsr_supported_video_codec vp8;
gsr_supported_video_codec vp9;
} gsr_supported_video_codecs;
#endif /* GSR_CODEC_QUERY_H */

View File

@ -0,0 +1,8 @@
#ifndef GSR_CODEC_QUERY_NVENC_H
#define GSR_CODEC_QUERY_NVENC_H
#include "codec_query.h"
bool gsr_get_supported_video_codecs_nvenc(gsr_supported_video_codecs *video_codecs, bool cleanup);
#endif /* GSR_CODEC_QUERY_NVENC_H */

View File

@ -0,0 +1,8 @@
#ifndef GSR_CODEC_QUERY_VAAPI_H
#define GSR_CODEC_QUERY_VAAPI_H
#include "codec_query.h"
bool gsr_get_supported_video_codecs_vaapi(gsr_supported_video_codecs *video_codecs, const char *card_path, bool cleanup);
#endif /* GSR_CODEC_QUERY_VAAPI_H */

View File

@ -0,0 +1,8 @@
#ifndef GSR_CODEC_QUERY_VULKAN_H
#define GSR_CODEC_QUERY_VULKAN_H
#include "codec_query.h"
bool gsr_get_supported_video_codecs_vulkan(gsr_supported_video_codecs *video_codecs, const char *card_path, bool cleanup);
#endif /* GSR_CODEC_QUERY_VULKAN_H */

View File

@ -0,0 +1,78 @@
#ifndef GSR_COLOR_CONVERSION_H
#define GSR_COLOR_CONVERSION_H
#include "shader.h"
#include "defs.h"
#include "vec2.h"
#include <stdbool.h>
#define GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS 12
#define GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS 2
typedef enum {
GSR_SOURCE_COLOR_RGB,
GSR_SOURCE_COLOR_BGR,
GSR_SOURCE_COLOR_YUYV
} gsr_source_color;
typedef enum {
GSR_DESTINATION_COLOR_NV12, /* YUV420, BT709, 8-bit */
GSR_DESTINATION_COLOR_P010, /* YUV420, BT2020, 10-bit */
GSR_DESTINATION_COLOR_RGB8
} gsr_destination_color;
typedef enum {
GSR_ROT_0,
GSR_ROT_90,
GSR_ROT_180,
GSR_ROT_270
} gsr_rotation;
typedef enum {
GSR_FLIP_NONE = 0,
GSR_FLIP_HORIZONTAL = (1 << 0),
GSR_FLIP_VERTICAL = (1 << 1)
} gsr_flip;
typedef struct {
int rotation_matrix;
int offset;
} gsr_color_graphics_uniforms;
typedef struct {
gsr_egl *egl;
gsr_destination_color destination_color;
unsigned int destination_textures[2];
vec2i destination_textures_size[2];
int num_destination_textures;
gsr_color_range color_range;
bool load_external_image_shader;
} gsr_color_conversion_params;
typedef struct {
gsr_color_conversion_params params;
gsr_color_graphics_uniforms graphics_uniforms[GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS];
gsr_shader graphics_shaders[GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS];
unsigned int framebuffers[GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS];
unsigned int vertex_array_object_id;
unsigned int vertex_buffer_object_id;
bool schedule_clear;
} gsr_color_conversion;
int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conversion_params *params);
void gsr_color_conversion_deinit(gsr_color_conversion *self);
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_flip flip, gsr_source_color source_color, bool external_texture);
void gsr_color_conversion_clear(gsr_color_conversion *self);
void gsr_color_conversion_read_destination_texture(gsr_color_conversion *self, int destination_texture_index, int x, int y, int width, int height, unsigned int color_format, unsigned int data_format, void *pixels);
gsr_rotation gsr_monitor_rotation_to_rotation(gsr_monitor_rotation monitor_rotation);
#endif /* GSR_COLOR_CONVERSION_H */

108
include/cuda.h Normal file
View File

@ -0,0 +1,108 @@
#ifndef GSR_CUDA_H
#define GSR_CUDA_H
#include "overclock.h"
#include <stddef.h>
#include <stdbool.h>
// To prevent hwcontext_cuda.h from including cuda.h
#define CUDA_VERSION 11070
#define CU_CTX_SCHED_AUTO 0
#if defined(_WIN64) || defined(__LP64__)
typedef unsigned long long CUdeviceptr_v2;
#else
typedef unsigned int CUdeviceptr_v2;
#endif
typedef CUdeviceptr_v2 CUdeviceptr;
typedef int CUresult;
typedef int CUdevice_v1;
typedef CUdevice_v1 CUdevice;
typedef struct CUctx_st *CUcontext;
typedef struct CUstream_st *CUstream;
typedef struct CUarray_st *CUarray;
#define CUDA_SUCCESS 0
typedef enum CUgraphicsMapResourceFlags_enum {
CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE = 0x00,
CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY = 0x01,
CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD = 0x02
} CUgraphicsMapResourceFlags;
typedef enum CUgraphicsRegisterFlags_enum {
CU_GRAPHICS_REGISTER_FLAGS_NONE = 0x00,
CU_GRAPHICS_REGISTER_FLAGS_READ_ONLY = 0x01,
CU_GRAPHICS_REGISTER_FLAGS_WRITE_DISCARD = 0x02,
CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LDST = 0x04,
CU_GRAPHICS_REGISTER_FLAGS_TEXTURE_GATHER = 0x08
} CUgraphicsRegisterFlags;
typedef enum CUmemorytype_enum {
CU_MEMORYTYPE_HOST = 0x01, /**< Host memory */
CU_MEMORYTYPE_DEVICE = 0x02, /**< Device memory */
CU_MEMORYTYPE_ARRAY = 0x03, /**< Array memory */
CU_MEMORYTYPE_UNIFIED = 0x04 /**< Unified device or host memory */
} CUmemorytype;
typedef struct CUDA_MEMCPY2D_st {
size_t srcXInBytes; /**< Source X in bytes */
size_t srcY; /**< Source Y */
CUmemorytype srcMemoryType; /**< Source memory type (host, device, array) */
const void *srcHost; /**< Source host pointer */
CUdeviceptr srcDevice; /**< Source device pointer */
CUarray srcArray; /**< Source array reference */
size_t srcPitch; /**< Source pitch (ignored when src is array) */
size_t dstXInBytes; /**< Destination X in bytes */
size_t dstY; /**< Destination Y */
CUmemorytype dstMemoryType; /**< Destination memory type (host, device, array) */
void *dstHost; /**< Destination host pointer */
CUdeviceptr dstDevice; /**< Destination device pointer */
CUarray dstArray; /**< Destination array reference */
size_t dstPitch; /**< Destination pitch (ignored when dst is array) */
size_t WidthInBytes; /**< Width of 2D memory copy in bytes */
size_t Height; /**< Height of 2D memory copy */
} CUDA_MEMCPY2D_v2;
typedef CUDA_MEMCPY2D_v2 CUDA_MEMCPY2D;
typedef struct CUgraphicsResource_st *CUgraphicsResource;
typedef struct gsr_cuda gsr_cuda;
struct gsr_cuda {
gsr_overclock overclock;
bool do_overclock;
void *library;
CUcontext cu_ctx;
CUresult (*cuInit)(unsigned int Flags);
CUresult (*cuDeviceGetCount)(int *count);
CUresult (*cuDeviceGet)(CUdevice *device, int ordinal);
CUresult (*cuCtxCreate_v2)(CUcontext *pctx, unsigned int flags, CUdevice dev);
CUresult (*cuCtxDestroy_v2)(CUcontext ctx);
CUresult (*cuCtxPushCurrent_v2)(CUcontext ctx);
CUresult (*cuCtxPopCurrent_v2)(CUcontext *pctx);
CUresult (*cuGetErrorString)(CUresult error, const char **pStr);
CUresult (*cuMemcpy2D_v2)(const CUDA_MEMCPY2D *pCopy);
CUresult (*cuMemcpy2DAsync_v2)(const CUDA_MEMCPY2D *pcopy, CUstream hStream);
CUresult (*cuStreamSynchronize)(CUstream hStream);
CUresult (*cuGraphicsGLRegisterImage)(CUgraphicsResource *pCudaResource, unsigned int image, unsigned int target, unsigned int flags);
CUresult (*cuGraphicsEGLRegisterImage)(CUgraphicsResource *pCudaResource, void *image, unsigned int flags);
CUresult (*cuGraphicsResourceSetMapFlags)(CUgraphicsResource resource, unsigned int flags);
CUresult (*cuGraphicsMapResources)(unsigned int count, CUgraphicsResource *resources, CUstream hStream);
CUresult (*cuGraphicsUnmapResources)(unsigned int count, CUgraphicsResource *resources, CUstream hStream);
CUresult (*cuGraphicsUnregisterResource)(CUgraphicsResource resource);
CUresult (*cuGraphicsSubResourceGetMappedArray)(CUarray *pArray, CUgraphicsResource resource, unsigned int arrayIndex, unsigned int mipLevel);
};
bool gsr_cuda_load(gsr_cuda *self, Display *display, bool overclock);
void gsr_cuda_unload(gsr_cuda *self);
#endif /* GSR_CUDA_H */

28
include/cursor.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef GSR_CURSOR_H
#define GSR_CURSOR_H
#include "egl.h"
#include "vec2.h"
typedef struct {
gsr_egl *egl;
Display *display;
int x_fixes_event_base;
unsigned int texture_id;
vec2i size;
vec2i hotspot;
vec2i position;
bool cursor_image_set;
bool visible;
} gsr_cursor;
int gsr_cursor_init(gsr_cursor *self, gsr_egl *egl, Display *display);
void gsr_cursor_deinit(gsr_cursor *self);
/* Returns true if the cursor image has updated or if the cursor has moved */
bool gsr_cursor_on_event(gsr_cursor *self, XEvent *xev);
void gsr_cursor_tick(gsr_cursor *self, Window relative_to);
#endif /* GSR_CURSOR_H */

74
include/damage.h Normal file
View File

@ -0,0 +1,74 @@
#ifndef GSR_DAMAGE_H
#define GSR_DAMAGE_H
#include "cursor.h"
#include "utils.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct _XDisplay Display;
typedef union _XEvent XEvent;
#define GSR_DAMAGE_MAX_MONITORS 32
#define GSR_DAMAGE_MAX_TRACKED_TARGETS 12
typedef struct {
int64_t window_id;
vec2i window_pos;
vec2i window_size;
uint64_t damage;
int refcount;
} gsr_damage_window;
typedef struct {
char *monitor_name;
gsr_monitor *monitor;
int refcount;
} gsr_damage_monitor;
typedef struct {
gsr_egl *egl;
Display *display;
bool track_cursor;
int damage_event;
int damage_error;
bool damaged;
uint64_t monitor_damage;
int randr_event;
int randr_error;
gsr_cursor *cursor;
gsr_monitor monitors[GSR_DAMAGE_MAX_MONITORS];
int num_monitors;
gsr_damage_window windows_tracked[GSR_DAMAGE_MAX_TRACKED_TARGETS];
int num_windows_tracked;
gsr_damage_monitor monitors_tracked[GSR_DAMAGE_MAX_TRACKED_TARGETS];
int num_monitors_tracked;
int all_monitors_tracked_refcount;
vec2i cursor_pos;
} gsr_damage;
bool gsr_damage_init(gsr_damage *self, gsr_egl *egl, gsr_cursor *cursor, bool track_cursor);
void gsr_damage_deinit(gsr_damage *self);
/* This is reference counted */
bool gsr_damage_start_tracking_window(gsr_damage *self, int64_t window);
void gsr_damage_stop_tracking_window(gsr_damage *self, int64_t window);
/* This is reference counted. If |monitor_name| is NULL then all monitors are tracked */
bool gsr_damage_start_tracking_monitor(gsr_damage *self, const char *monitor_name);
void gsr_damage_stop_tracking_monitor(gsr_damage *self, const char *monitor_name);
void gsr_damage_on_event(gsr_damage *self, XEvent *xev);
void gsr_damage_tick(gsr_damage *self);
/* Also returns true if damage tracking is not available */
bool gsr_damage_is_damaged(gsr_damage *self);
void gsr_damage_clear(gsr_damage *self);
#endif /* GSR_DAMAGE_H */

49
include/dbus.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef GSR_DBUS_H
#define GSR_DBUS_H
#include <stdbool.h>
#include <stdint.h>
#include <dbus/dbus.h>
#define DBUS_RANDOM_STR_SIZE 16
typedef enum {
GSR_PORTAL_CAPTURE_TYPE_MONITOR = 1 << 0,
GSR_PORTAL_CAPTURE_TYPE_WINDOW = 1 << 1,
GSR_PORTAL_CAPTURE_TYPE_VIRTUAL = 1 << 2,
GSR_PORTAL_CAPTURE_TYPE_ALL = GSR_PORTAL_CAPTURE_TYPE_MONITOR | GSR_PORTAL_CAPTURE_TYPE_WINDOW | GSR_PORTAL_CAPTURE_TYPE_VIRTUAL
} gsr_portal_capture_type;
typedef enum {
GSR_PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
GSR_PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
GSR_PORTAL_CURSOR_MODE_METADATA = 1 << 2
} gsr_portal_cursor_mode;
typedef struct {
DBusConnection *con;
DBusError err;
char random_str[DBUS_RANDOM_STR_SIZE + 1];
unsigned int handle_counter;
bool desktop_portal_rule_added;
uint32_t screencast_version;
char *screencast_restore_token;
} gsr_dbus;
/* Blocking. TODO: Make non-blocking */
bool gsr_dbus_init(gsr_dbus *self, const char *screencast_restore_token);
void gsr_dbus_deinit(gsr_dbus *self);
/* The follow functions should be called in order to setup ScreenCast properly */
/* These functions that return an int return the response status code */
int gsr_dbus_screencast_create_session(gsr_dbus *self, char **session_handle);
/*
|capture_type| is a bitmask of gsr_portal_capture_type values. gsr_portal_capture_type values that are not supported by the desktop portal will be ignored.
|gsr_portal_cursor_mode| is a bitmask of gsr_portal_cursor_mode values. gsr_portal_cursor_mode values that are not supported will be ignored.
*/
int gsr_dbus_screencast_select_sources(gsr_dbus *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode);
int gsr_dbus_screencast_start(gsr_dbus *self, const char *session_handle, uint32_t *pipewire_node);
bool gsr_dbus_screencast_open_pipewire_remote(gsr_dbus *self, const char *session_handle, int *pipewire_fd);
const char* gsr_dbus_screencast_get_restore_token(gsr_dbus *self);
#endif /* GSR_DBUS_H */

112
include/defs.h Normal file
View File

@ -0,0 +1,112 @@
#ifndef GSR_DEFS_H
#define GSR_DEFS_H
#include <stdbool.h>
#define GSR_VIDEO_CODEC_AUTO -1
#define GSR_BITRATE_MODE_AUTO -1
typedef enum {
GSR_GPU_VENDOR_AMD,
GSR_GPU_VENDOR_INTEL,
GSR_GPU_VENDOR_NVIDIA,
GSR_GPU_VENDOR_BROADCOM,
} gsr_gpu_vendor;
typedef struct {
gsr_gpu_vendor vendor;
int gpu_version; /* 0 if unknown */
bool is_steam_deck;
} gsr_gpu_info;
typedef enum {
GSR_MONITOR_ROT_0,
GSR_MONITOR_ROT_90,
GSR_MONITOR_ROT_180,
GSR_MONITOR_ROT_270,
} gsr_monitor_rotation;
typedef enum {
GSR_CONNECTION_X11,
GSR_CONNECTION_WAYLAND,
GSR_CONNECTION_DRM,
} gsr_connection_type;
typedef enum {
GSR_VIDEO_QUALITY_MEDIUM,
GSR_VIDEO_QUALITY_HIGH,
GSR_VIDEO_QUALITY_VERY_HIGH,
GSR_VIDEO_QUALITY_ULTRA,
} gsr_video_quality;
typedef enum {
GSR_VIDEO_CODEC_H264,
GSR_VIDEO_CODEC_HEVC,
GSR_VIDEO_CODEC_HEVC_HDR,
GSR_VIDEO_CODEC_HEVC_10BIT,
GSR_VIDEO_CODEC_AV1,
GSR_VIDEO_CODEC_AV1_HDR,
GSR_VIDEO_CODEC_AV1_10BIT,
GSR_VIDEO_CODEC_VP8,
GSR_VIDEO_CODEC_VP9,
GSR_VIDEO_CODEC_H264_VULKAN,
GSR_VIDEO_CODEC_HEVC_VULKAN,
} gsr_video_codec;
typedef enum {
GSR_AUDIO_CODEC_AAC,
GSR_AUDIO_CODEC_OPUS,
GSR_AUDIO_CODEC_FLAC,
} gsr_audio_codec;
typedef enum {
GSR_PIXEL_FORMAT_YUV420,
GSR_PIXEL_FORMAT_YUV444,
} gsr_pixel_format;
typedef enum {
GSR_FRAMERATE_MODE_CONSTANT,
GSR_FRAMERATE_MODE_VARIABLE,
GSR_FRAMERATE_MODE_CONTENT,
} gsr_framerate_mode;
typedef enum {
GSR_BITRATE_MODE_QP,
GSR_BITRATE_MODE_VBR,
GSR_BITRATE_MODE_CBR,
} gsr_bitrate_mode;
typedef enum {
GSR_TUNE_PERFORMANCE,
GSR_TUNE_QUALITY,
} gsr_tune;
typedef enum {
GSR_VIDEO_ENCODER_HW_GPU,
GSR_VIDEO_ENCODER_HW_CPU,
} gsr_video_encoder_hardware;
typedef enum {
GSR_COLOR_RANGE_LIMITED,
GSR_COLOR_RANGE_FULL,
} gsr_color_range;
typedef enum {
GSR_COLOR_DEPTH_8_BITS,
GSR_COLOR_DEPTH_10_BITS,
} gsr_color_depth;
typedef enum {
GSR_REPLAY_STORAGE_RAM,
GSR_REPLAY_STORAGE_DISK,
} gsr_replay_storage;
bool video_codec_is_hdr(gsr_video_codec video_codec);
gsr_video_codec hdr_video_codec_to_sdr_video_codec(gsr_video_codec video_codec);
gsr_color_depth video_codec_to_bit_depth(gsr_video_codec video_codec);
const char* video_codec_to_string(gsr_video_codec video_codec);
bool video_codec_is_av1(gsr_video_codec video_codec);
bool video_codec_is_vulkan(gsr_video_codec video_codec);
const char* audio_codec_get_name(gsr_audio_codec audio_codec);
#endif /* GSR_DEFS_H */

321
include/egl.h Normal file
View File

@ -0,0 +1,321 @@
#ifndef GSR_EGL_H
#define GSR_EGL_H
/* OpenGL EGL library with a hidden window context (to allow using the opengl functions) */
#include <X11/X.h>
#include <X11/Xutil.h>
#include <stdbool.h>
#include <stdint.h>
#include "vec2.h"
#include "defs.h"
typedef struct gsr_window gsr_window;
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
typedef void* EGLDisplay;
typedef void* EGLNativeDisplayType;
typedef uintptr_t EGLNativeWindowType;
typedef uintptr_t EGLNativePixmapType;
typedef void* EGLConfig;
typedef void* EGLSurface;
typedef void* EGLContext;
typedef void* EGLClientBuffer;
typedef void* EGLImage;
typedef void* EGLImageKHR;
typedef void *GLeglImageOES;
typedef void (*__eglMustCastToProperFunctionPointerType)(void);
typedef struct __GLXFBConfigRec *GLXFBConfig;
typedef struct __GLXcontextRec *GLXContext;
typedef XID GLXDrawable;
typedef void(*__GLXextFuncPtr)(void);
#define EGL_SUCCESS 0x3000
#define EGL_BUFFER_SIZE 0x3020
#define EGL_RENDERABLE_TYPE 0x3040
#define EGL_OPENGL_API 0x30A2
#define EGL_OPENGL_ES_API 0x30A0
#define EGL_OPENGL_BIT 0x0008
#define EGL_OPENGL_ES_BIT 0x0001
#define EGL_OPENGL_ES2_BIT 0x0004
#define EGL_OPENGL_ES3_BIT 0x00000040
#define EGL_NONE 0x3038
#define EGL_CONTEXT_CLIENT_VERSION 0x3098
#define EGL_CONTEXT_OPENGL_DEBUG 0x31B0
#define EGL_BACK_BUFFER 0x3084
#define EGL_GL_TEXTURE_2D 0x30B1
#define EGL_TRUE 1
#define EGL_IMAGE_PRESERVED_KHR 0x30D2
#define EGL_NATIVE_PIXMAP_KHR 0x30B0
#define EGL_LINUX_DRM_FOURCC_EXT 0x3271
#define EGL_WIDTH 0x3057
#define EGL_HEIGHT 0x3056
#define EGL_DMA_BUF_PLANE0_FD_EXT 0x3272
#define EGL_DMA_BUF_PLANE0_OFFSET_EXT 0x3273
#define EGL_DMA_BUF_PLANE0_PITCH_EXT 0x3274
#define EGL_DMA_BUF_PLANE1_FD_EXT 0x3275
#define EGL_DMA_BUF_PLANE1_OFFSET_EXT 0x3276
#define EGL_DMA_BUF_PLANE1_PITCH_EXT 0x3277
#define EGL_DMA_BUF_PLANE2_FD_EXT 0x3278
#define EGL_DMA_BUF_PLANE2_OFFSET_EXT 0x3279
#define EGL_DMA_BUF_PLANE2_PITCH_EXT 0x327A
#define EGL_DMA_BUF_PLANE3_FD_EXT 0x3440
#define EGL_DMA_BUF_PLANE3_OFFSET_EXT 0x3441
#define EGL_DMA_BUF_PLANE3_PITCH_EXT 0x3442
#define EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT 0x3443
#define EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT 0x3444
#define EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT 0x3445
#define EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT 0x3446
#define EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT 0x3447
#define EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT 0x3448
#define EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT 0x3449
#define EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT 0x344A
#define EGL_LINUX_DMA_BUF_EXT 0x3270
#define EGL_RED_SIZE 0x3024
#define EGL_ALPHA_SIZE 0x3021
#define EGL_BLUE_SIZE 0x3022
#define EGL_GREEN_SIZE 0x3023
#define EGL_DEVICE_EXT 0x322C
#define EGL_DRM_DEVICE_FILE_EXT 0x3233
#define GL_FLOAT 0x1406
#define GL_FALSE 0
#define GL_TRUE 1
#define GL_TRIANGLES 0x0004
#define GL_TEXTURE_2D 0x0DE1
#define GL_TEXTURE_EXTERNAL_OES 0x8D65
#define GL_RED 0x1903
#define GL_GREEN 0x1904
#define GL_BLUE 0x1905
#define GL_ALPHA 0x1906
#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
#define GL_RG 0x8227
#define GL_RGB 0x1907
#define GL_RGBA 0x1908
#define GL_RGB8 0x8051
#define GL_RGBA8 0x8058
#define GL_RGBA16 0x805B
#define GL_R8 0x8229
#define GL_RG8 0x822B
#define GL_R16 0x822A
#define GL_RG16 0x822C
#define GL_RGB16 0x8054
#define GL_RGBA32F 0x8814
#define GL_UNSIGNED_BYTE 0x1401
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_TEXTURE_WRAP_S 0x2802
#define GL_TEXTURE_WRAP_T 0x2803
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_NEAREST 0x2600
#define GL_CLAMP_TO_EDGE 0x812F
#define GL_LINEAR 0x2601
#define GL_FRAMEBUFFER 0x8D40
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_FRAMEBUFFER_COMPLETE 0x8CD5
#define GL_DYNAMIC_DRAW 0x88E8
#define GL_ARRAY_BUFFER 0x8892
#define GL_BLEND 0x0BE2
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
#define GL_SCISSOR_TEST 0x0C11
#define GL_PACK_ALIGNMENT 0x0D05
#define GL_UNPACK_ALIGNMENT 0x0CF5
#define GL_TEXTURE0 0x84C0
#define GL_TEXTURE1 0x84C1
#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020
#define GL_ALL_BARRIER_BITS 0xFFFFFFFF
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
#define GL_MAP_WRITE_BIT 0x0002
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
#define GL_COMPILE_STATUS 0x8B81
#define GL_INFO_LOG_LENGTH 0x8B84
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
#define GL_COMPILE_STATUS 0x8B81
#define GL_LINK_STATUS 0x8B82
typedef unsigned int (*FUNC_eglExportDMABUFImageQueryMESA)(EGLDisplay dpy, EGLImageKHR image, int *fourcc, int *num_planes, uint64_t *modifiers);
typedef unsigned int (*FUNC_eglExportDMABUFImageMESA)(EGLDisplay dpy, EGLImageKHR image, int *fds, int32_t *strides, int32_t *offsets);
typedef void (*FUNC_glEGLImageTargetTexture2DOES)(unsigned int target, GLeglImageOES image);
typedef GLXContext (*FUNC_glXCreateContextAttribsARB)(Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list);
typedef void (*FUNC_glXSwapIntervalEXT)(Display * dpy, GLXDrawable drawable, int interval);
typedef int (*FUNC_glXSwapIntervalMESA)(unsigned int interval);
typedef int (*FUNC_glXSwapIntervalSGI)(int interval);
typedef void (*GLDEBUGPROC)(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, int length, const char *message, const void *userParam);
typedef int (*FUNC_eglQueryDisplayAttribEXT)(EGLDisplay dpy, int32_t attribute, intptr_t *value);
typedef const char* (*FUNC_eglQueryDeviceStringEXT)(void *device, int32_t name);
typedef int (*FUNC_eglQueryDmaBufModifiersEXT)(EGLDisplay dpy, int32_t format, int32_t max_modifiers, uint64_t *modifiers, int *external_only, int32_t *num_modifiers);
typedef void (*FUNC_glCreateMemoryObjectsEXT)(int n, unsigned int *memoryObjects);
typedef void (*FUNC_glImportMemoryFdEXT)(unsigned int memory, uint64_t size, unsigned int handleType, int fd);
typedef unsigned char (*FUNC_glIsMemoryObjectEXT)(unsigned int memoryObject);
typedef void (*FUNC_glTexStorageMem2DEXT)(unsigned int target, int levels, unsigned int internalFormat, int width, int height, unsigned int memory, uint64_t offset);
typedef void (*FUNC_glBufferStorageMemEXT)(unsigned int target, ssize_t size, unsigned int memory, uint64_t offset);
typedef void (*FUNC_glNamedBufferStorageMemEXT)(unsigned int buffer, ssize_t size, unsigned int memory, uint64_t offset);
typedef void (*FUNC_glMemoryObjectParameterivEXT)(unsigned int memoryObject, unsigned int pname, const int *params);
typedef enum {
GSR_GL_CONTEXT_TYPE_EGL,
GSR_GL_CONTEXT_TYPE_GLX
} gsr_gl_context_type;
typedef struct gsr_egl gsr_egl;
struct gsr_egl {
void *egl_library;
void *glx_library;
void *gl_library;
gsr_gl_context_type context_type;
gsr_window *window;
EGLDisplay egl_display;
EGLSurface egl_surface;
EGLContext egl_context;
const char *dri_card_path;
void *glx_context;
void *glx_fb_config;
gsr_gpu_info gpu_info;
char card_path[128];
int32_t (*eglGetError)(void);
EGLDisplay (*eglGetDisplay)(EGLNativeDisplayType display_id);
unsigned int (*eglInitialize)(EGLDisplay dpy, int32_t *major, int32_t *minor);
unsigned int (*eglTerminate)(EGLDisplay dpy);
unsigned int (*eglChooseConfig)(EGLDisplay dpy, const int32_t *attrib_list, EGLConfig *configs, int32_t config_size, int32_t *num_config);
EGLSurface (*eglCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const int32_t *attrib_list);
EGLContext (*eglCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const int32_t *attrib_list);
unsigned int (*eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
EGLImage (*eglCreateImage)(EGLDisplay dpy, EGLContext ctx, unsigned int target, EGLClientBuffer buffer, const intptr_t *attrib_list);
unsigned int (*eglDestroyContext)(EGLDisplay dpy, EGLContext ctx);
unsigned int (*eglDestroySurface)(EGLDisplay dpy, EGLSurface surface);
unsigned int (*eglDestroyImage)(EGLDisplay dpy, EGLImage image);
unsigned int (*eglSwapInterval)(EGLDisplay dpy, int32_t interval);
unsigned int (*eglSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
unsigned int (*eglBindAPI)(unsigned int api);
__eglMustCastToProperFunctionPointerType (*eglGetProcAddress)(const char *procname);
FUNC_eglExportDMABUFImageQueryMESA eglExportDMABUFImageQueryMESA;
FUNC_eglExportDMABUFImageMESA eglExportDMABUFImageMESA;
FUNC_glEGLImageTargetTexture2DOES glEGLImageTargetTexture2DOES;
FUNC_eglQueryDisplayAttribEXT eglQueryDisplayAttribEXT;
FUNC_eglQueryDeviceStringEXT eglQueryDeviceStringEXT;
FUNC_eglQueryDmaBufModifiersEXT eglQueryDmaBufModifiersEXT;
FUNC_glCreateMemoryObjectsEXT glCreateMemoryObjectsEXT;
FUNC_glImportMemoryFdEXT glImportMemoryFdEXT;
FUNC_glIsMemoryObjectEXT glIsMemoryObjectEXT;
FUNC_glTexStorageMem2DEXT glTexStorageMem2DEXT;
FUNC_glBufferStorageMemEXT glBufferStorageMemEXT;
FUNC_glNamedBufferStorageMemEXT glNamedBufferStorageMemEXT;
FUNC_glMemoryObjectParameterivEXT glMemoryObjectParameterivEXT;
__GLXextFuncPtr (*glXGetProcAddress)(const unsigned char *procName);
GLXFBConfig* (*glXChooseFBConfig)(Display *dpy, int screen, const int *attribList, int *nitems);
Bool (*glXMakeContextCurrent)(Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx);
// TODO: Remove
GLXContext (*glXCreateNewContext)(Display *dpy, GLXFBConfig config, int renderType, GLXContext shareList, Bool direct);
void (*glXDestroyContext)(Display *dpy, GLXContext ctx);
void (*glXSwapBuffers)(Display *dpy, GLXDrawable drawable);
FUNC_glXCreateContextAttribsARB glXCreateContextAttribsARB;
/* Optional */
FUNC_glXSwapIntervalEXT glXSwapIntervalEXT;
FUNC_glXSwapIntervalMESA glXSwapIntervalMESA;
FUNC_glXSwapIntervalSGI glXSwapIntervalSGI;
unsigned int (*glGetError)(void);
const unsigned char* (*glGetString)(unsigned int name);
void (*glFlush)(void);
void (*glFinish)(void);
void (*glClear)(unsigned int mask);
void (*glClearColor)(float red, float green, float blue, float alpha);
void (*glGenTextures)(int n, unsigned int *textures);
void (*glDeleteTextures)(int n, const unsigned int *texture);
void (*glActiveTexture)(unsigned int texture);
void (*glBindTexture)(unsigned int target, unsigned int texture);
void (*glBindImageTexture)(unsigned int unit, unsigned int texture, int level, unsigned char layered, int layer, unsigned int access, unsigned int format);
void (*glTexParameteri)(unsigned int target, unsigned int pname, int param);
void (*glTexParameteriv)(unsigned int target, unsigned int pname, const int *params);
void (*glTexParameterfv)(unsigned int target, unsigned int pname, const float *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 (*glTexStorage2D)(unsigned int target, int levels, unsigned int internalformat, int width, int height);
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);
void (*glDeleteFramebuffers)(int n, const unsigned int *framebuffers);
/* TODO: Note: this is only available since OpenGL ES 3.1 */
void (*glMemoryBarrier)(unsigned int barriers);
void (*glViewport)(int x, int y, int width, int height);
void (*glFramebufferTexture2D)(unsigned int target, unsigned int attachment, unsigned int textarget, unsigned int texture, int level);
void (*glDrawBuffers)(int n, const unsigned int *bufs);
unsigned int (*glCheckFramebufferStatus)(unsigned int target);
void (*glBindBuffer)(unsigned int target, unsigned int buffer);
void (*glGenBuffers)(int n, unsigned int *buffers);
void (*glBufferData)(unsigned int target, khronos_ssize_t size, const void *data, unsigned int usage);
void (*glBufferSubData)(unsigned int target, khronos_intptr_t offset, khronos_ssize_t size, const void *data);
void (*glDeleteBuffers)(int n, const unsigned int *buffers);
void (*glGenVertexArrays)(int n, unsigned int *arrays);
void (*glBindVertexArray)(unsigned int array);
void (*glDeleteVertexArrays)(int n, const unsigned int *arrays);
unsigned int (*glCreateProgram)(void);
unsigned int (*glCreateShader)(unsigned int type);
void (*glAttachShader)(unsigned int program, unsigned int shader);
void (*glBindAttribLocation)(unsigned int program, unsigned int index, const char *name);
void (*glCompileShader)(unsigned int shader);
void (*glLinkProgram)(unsigned int program);
void (*glShaderSource)(unsigned int shader, int count, const char *const*string, const int *length);
void (*glUseProgram)(unsigned int program);
void (*glGetProgramInfoLog)(unsigned int program, int bufSize, int *length, char *infoLog);
void (*glGetShaderiv)(unsigned int shader, unsigned int pname, int *params);
void (*glGetShaderInfoLog)(unsigned int shader, int bufSize, int *length, char *infoLog);
void (*glDeleteProgram)(unsigned int program);
void (*glDeleteShader)(unsigned int shader);
void (*glGetProgramiv)(unsigned int program, unsigned int pname, int *params);
void (*glVertexAttribPointer)(unsigned int index, int size, unsigned int type, unsigned char normalized, int stride, const void *pointer);
void (*glEnableVertexAttribArray)(unsigned int index);
void (*glDrawArrays)(unsigned int mode, int first, int count);
void (*glEnable)(unsigned int cap);
void (*glDisable)(unsigned int cap);
void (*glBlendFunc)(unsigned int sfactor, unsigned int dfactor);
void (*glPixelStorei)(unsigned int pname, int param);
int (*glGetUniformLocation)(unsigned int program, const char *name);
void (*glUniform1f)(int location, float v0);
void (*glUniform2f)(int location, float v0, float v1);
void (*glUniform1i)(int location, int v0);
void (*glUniform2i)(int location, int v0, int v1);
void (*glUniformMatrix2fv)(int location, int count, unsigned char transpose, const float *value);
void (*glDebugMessageCallback)(GLDEBUGPROC callback, const void *userParam);
void (*glScissor)(int x, int y, int width, int height);
void (*glCreateBuffers)(int n, unsigned int *buffers);
void (*glReadPixels)(int x, int y, int width, int height, unsigned int format, unsigned int type, void *pixels);
void* (*glMapBufferRange)(unsigned int target, intptr_t offset, ssize_t length, unsigned int access);
unsigned char (*glUnmapBuffer)(unsigned int target);
void (*glGetIntegerv)(unsigned int pname, int *params);
};
bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture, bool enable_debug);
void gsr_egl_unload(gsr_egl *self);
/* Does opengl swap with egl or glx, depending on which one is active */
void gsr_egl_swap_buffers(gsr_egl *self);
#endif /* GSR_EGL_H */

43
include/encoder/encoder.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef GSR_ENCODER_H
#define GSR_ENCODER_H
#include "../replay_buffer/replay_buffer.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <pthread.h>
#define GSR_MAX_RECORDING_DESTINATIONS 128
typedef struct AVCodecContext AVCodecContext;
typedef struct AVFormatContext AVFormatContext;
typedef struct AVStream AVStream;
typedef struct {
size_t id;
AVCodecContext *codec_context;
AVFormatContext *format_context;
AVStream *stream;
int64_t start_pts;
bool has_received_keyframe;
} gsr_encoder_recording_destination;
typedef struct {
gsr_replay_buffer *replay_buffer;
pthread_mutex_t file_write_mutex;
bool mutex_created;
gsr_encoder_recording_destination recording_destinations[GSR_MAX_RECORDING_DESTINATIONS];
size_t num_recording_destinations;
size_t recording_destination_id_counter;
} gsr_encoder;
bool gsr_encoder_init(gsr_encoder *self, gsr_replay_storage replay_storage, size_t replay_buffer_num_packets, double replay_buffer_time, const char *replay_directory);
void gsr_encoder_deinit(gsr_encoder *self);
void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_context, int64_t pts, int stream_index);
/* Returns the id to the recording destination, or -1 on error */
size_t gsr_encoder_add_recording_destination(gsr_encoder *self, AVCodecContext *codec_context, AVFormatContext *format_context, AVStream *stream, int64_t start_pts);
bool gsr_encoder_remove_recording_destination(gsr_encoder *self, size_t id);
#endif /* GSR_ENCODER_H */

View File

@ -0,0 +1,16 @@
#ifndef GSR_ENCODER_VIDEO_NVENC_H
#define GSR_ENCODER_VIDEO_NVENC_H
#include "video.h"
typedef struct gsr_egl gsr_egl;
typedef struct {
gsr_egl *egl;
bool overclock;
gsr_color_depth color_depth;
} gsr_video_encoder_nvenc_params;
gsr_video_encoder* gsr_video_encoder_nvenc_create(const gsr_video_encoder_nvenc_params *params);
#endif /* GSR_ENCODER_VIDEO_NVENC_H */

View File

@ -0,0 +1,15 @@
#ifndef GSR_ENCODER_VIDEO_SOFTWARE_H
#define GSR_ENCODER_VIDEO_SOFTWARE_H
#include "video.h"
typedef struct gsr_egl gsr_egl;
typedef struct {
gsr_egl *egl;
gsr_color_depth color_depth;
} gsr_video_encoder_software_params;
gsr_video_encoder* gsr_video_encoder_software_create(const gsr_video_encoder_software_params *params);
#endif /* GSR_ENCODER_VIDEO_SOFTWARE_H */

View File

@ -0,0 +1,15 @@
#ifndef GSR_ENCODER_VIDEO_VAAPI_H
#define GSR_ENCODER_VIDEO_VAAPI_H
#include "video.h"
typedef struct gsr_egl gsr_egl;
typedef struct {
gsr_egl *egl;
gsr_color_depth color_depth;
} gsr_video_encoder_vaapi_params;
gsr_video_encoder* gsr_video_encoder_vaapi_create(const gsr_video_encoder_vaapi_params *params);
#endif /* GSR_ENCODER_VIDEO_VAAPI_H */

View File

@ -0,0 +1,30 @@
#ifndef GSR_ENCODER_VIDEO_H
#define GSR_ENCODER_VIDEO_H
#include "../../color_conversion.h"
#include <stdbool.h>
#define GSR_MAX_RECORDING_DESTINATIONS 128
typedef struct gsr_video_encoder gsr_video_encoder;
typedef struct AVCodecContext AVCodecContext;
typedef struct AVFrame AVFrame;
struct gsr_video_encoder {
bool (*start)(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame);
void (*destroy)(gsr_video_encoder *encoder, AVCodecContext *video_codec_context);
void (*copy_textures_to_frame)(gsr_video_encoder *encoder, AVFrame *frame, gsr_color_conversion *color_conversion); /* Can be NULL */
/* |textures| should be able to fit 2 elements */
void (*get_textures)(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color);
void *priv;
bool started;
};
/* Set |replay_buffer_time_seconds| and |fps| to 0 to disable replay buffer */
bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame);
void gsr_video_encoder_destroy(gsr_video_encoder *encoder, AVCodecContext *video_codec_context);
void gsr_video_encoder_copy_textures_to_frame(gsr_video_encoder *encoder, AVFrame *frame, gsr_color_conversion *color_conversion);
void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, vec2i *texture_sizes, int *num_textures, gsr_destination_color *destination_color);
#endif /* GSR_ENCODER_VIDEO_H */

View File

@ -0,0 +1,15 @@
#ifndef GSR_ENCODER_VIDEO_VULKAN_H
#define GSR_ENCODER_VIDEO_VULKAN_H
#include "video.h"
typedef struct gsr_egl gsr_egl;
typedef struct {
gsr_egl *egl;
gsr_color_depth color_depth;
} gsr_video_encoder_vulkan_params;
gsr_video_encoder* gsr_video_encoder_vulkan_create(const gsr_video_encoder_vulkan_params *params);
#endif /* GSR_ENCODER_VIDEO_VULKAN_H */

26
include/image_writer.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef GSR_IMAGE_WRITER_H
#define GSR_IMAGE_WRITER_H
#include <stdbool.h>
typedef struct gsr_egl gsr_egl;
typedef enum {
GSR_IMAGE_FORMAT_JPEG,
GSR_IMAGE_FORMAT_PNG
} gsr_image_format;
typedef struct {
gsr_egl *egl;
int width;
int height;
unsigned int texture;
} gsr_image_writer;
bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, 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 */
bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality);
#endif /* GSR_IMAGE_WRITER_H */

17
include/library_loader.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef GSR_LIBRARY_LOADER_H
#define GSR_LIBRARY_LOADER_H
#include <stdbool.h>
typedef struct {
void **func;
const char *name;
} dlsym_assign;
void* dlsym_print_fail(void *handle, const char *name, bool required);
/* |dlsyms| should be null terminated */
bool dlsym_load_list(void *handle, const dlsym_assign *dlsyms);
/* |dlsyms| should be null terminated */
void dlsym_load_list_optional(void *handle, const dlsym_assign *dlsyms);
#endif /* GSR_LIBRARY_LOADER_H */

17
include/overclock.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef GSR_OVERCLOCK_H
#define GSR_OVERCLOCK_H
#include "xnvctrl.h"
typedef struct {
gsr_xnvctrl xnvctrl;
int num_performance_levels;
} gsr_overclock;
bool gsr_overclock_load(gsr_overclock *self, Display *display);
void gsr_overclock_unload(gsr_overclock *self);
bool gsr_overclock_start(gsr_overclock *self);
void gsr_overclock_stop(gsr_overclock *self);
#endif /* GSR_OVERCLOCK_H */

163
include/pipewire_audio.h Normal file
View File

@ -0,0 +1,163 @@
#ifndef GSR_PIPEWIRE_AUDIO_H
#define GSR_PIPEWIRE_AUDIO_H
#include <pipewire/thread-loop.h>
#include <pipewire/context.h>
#include <pipewire/core.h>
#include <spa/utils/hook.h>
#include <stdbool.h>
typedef enum {
GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT, /* Application audio */
GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_INPUT, /* Audio recording input */
GSR_PIPEWIRE_AUDIO_NODE_TYPE_SINK_OR_SOURCE /* Audio output or input device or combined (virtual) sink */
} gsr_pipewire_audio_node_type;
typedef struct {
uint32_t id;
char *name;
gsr_pipewire_audio_node_type type;
} gsr_pipewire_audio_node;
typedef enum {
GSR_PIPEWIRE_AUDIO_PORT_DIRECTION_INPUT,
GSR_PIPEWIRE_AUDIO_PORT_DIRECTION_OUTPUT
} gsr_pipewire_audio_port_direction;
typedef struct {
uint32_t id;
uint32_t node_id;
gsr_pipewire_audio_port_direction direction;
char *name;
} gsr_pipewire_audio_port;
typedef struct {
uint32_t id;
uint32_t output_node_id;
uint32_t input_node_id;
} gsr_pipewire_audio_link;
typedef enum {
GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_STREAM, /* Application */
GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_SINK /* Combined (virtual) sink */
} gsr_pipewire_audio_link_input_type;
typedef enum {
GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_STANDARD,
GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_OUTPUT,
GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_INPUT
} gsr_pipewire_audio_requested_type;
typedef struct {
char *name;
gsr_pipewire_audio_requested_type type;
} gsr_pipewire_audio_requested_output;
typedef struct {
gsr_pipewire_audio_requested_output *outputs;
int num_outputs;
char *input_name;
bool inverted;
gsr_pipewire_audio_node_type output_type;
gsr_pipewire_audio_link_input_type input_type;
} gsr_pipewire_audio_requested_link;
typedef struct {
struct pw_thread_loop *thread_loop;
struct pw_context *context;
struct pw_core *core;
struct spa_hook core_listener;
struct pw_registry *registry;
struct spa_hook registry_listener;
int server_version_sync;
struct pw_proxy *metadata_proxy;
struct spa_hook metadata_listener;
struct spa_hook metadata_proxy_listener;
char default_output_device_name[128];
char default_input_device_name[128];
gsr_pipewire_audio_node *stream_nodes;
size_t num_stream_nodes;
size_t stream_nodes_capacity_items;
gsr_pipewire_audio_port *ports;
size_t num_ports;
size_t ports_capacity_items;
gsr_pipewire_audio_link *links;
size_t num_links;
size_t links_capacity_items;
gsr_pipewire_audio_requested_link *requested_links;
size_t num_requested_links;
size_t requested_links_capacity_items;
bool running;
} gsr_pipewire_audio;
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self);
void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self);
/*
This function links audio source outputs from applications that match the name |app_names| to the input
that matches the name |stream_name_input|.
If an application or a new application starts outputting audio after this function is called and the app name matches
then it will automatically link the audio sources.
|app_names| and |stream_name_input| are case-insensitive matches.
*/
bool gsr_pipewire_audio_add_link_from_apps_to_stream(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *stream_name_input);
/*
This function links audio source outputs from all applications except the ones that match the name |app_names| to the input
that matches the name |stream_name_input|.
If an application or a new application starts outputting audio after this function is called and the app name doesn't match
then it will automatically link the audio sources.
|app_names| and |stream_name_input| are case-insensitive matches.
*/
bool gsr_pipewire_audio_add_link_from_apps_to_stream_inverted(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *stream_name_input);
/*
This function links audio source outputs from applications that match the name |app_names| to the input
that matches the name |sink_name_input|.
If an application or a new application starts outputting audio after this function is called and the app name matches
then it will automatically link the audio sources.
|app_names| and |sink_name_input| are case-insensitive matches.
*/
bool gsr_pipewire_audio_add_link_from_apps_to_sink(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *sink_name_input);
/*
This function links audio source outputs from all applications except the ones that match the name |app_names| to the input
that matches the name |sink_name_input|.
If an application or a new application starts outputting audio after this function is called and the app name doesn't match
then it will automatically link the audio sources.
|app_names| and |sink_name_input| are case-insensitive matches.
*/
bool gsr_pipewire_audio_add_link_from_apps_to_sink_inverted(gsr_pipewire_audio *self, const char **app_names, int num_app_names, const char *sink_name_input);
/*
This function links audio source outputs from devices that match the name |source_names| to the input
that matches the name |stream_name_input|.
If a device or a new device starts outputting audio after this function is called and the device name matches
then it will automatically link the audio sources.
|source_names| and |stream_name_input| are case-insensitive matches.
|source_names| can include "default_output" or "default_input" to use the default output/input
and it will automatically switch when the default output/input is changed in system audio settings.
*/
bool gsr_pipewire_audio_add_link_from_sources_to_stream(gsr_pipewire_audio *self, const char **source_names, int num_source_names, const char *stream_name_input);
/*
This function links audio source outputs from devices that match the name |source_names| to the input
that matches the name |sink_name_input|.
If a device or a new device starts outputting audio after this function is called and the device name matches
then it will automatically link the audio sources.
|source_names| and |sink_name_input| are case-insensitive matches.
|source_names| can include "default_output" or "default_input" to use the default output/input
and it will automatically switch when the default output/input is changed in system audio settings.
*/
bool gsr_pipewire_audio_add_link_from_sources_to_sink(gsr_pipewire_audio *self, const char **source_names, int num_source_names, const char *sink_name_input);
/* Return true to continue */
typedef bool (*gsr_pipewire_audio_app_query_callback)(const char *app_name, void *userdata);
void gsr_pipewire_audio_for_each_app(gsr_pipewire_audio *self, gsr_pipewire_audio_app_query_callback callback, void *userdata);
#endif /* GSR_PIPEWIRE_AUDIO_H */

132
include/pipewire_video.h Normal file
View File

@ -0,0 +1,132 @@
#ifndef GSR_PIPEWIRE_VIDEO_H
#define GSR_PIPEWIRE_VIDEO_H
#include "defs.h"
#include <stdbool.h>
#include <stdint.h>
#include <pthread.h>
#include <spa/utils/hook.h>
#include <spa/param/video/format.h>
#define GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS 1024
#define GSR_PIPEWIRE_VIDEO_MAX_VIDEO_FORMATS 10
#define GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES 4
typedef struct gsr_egl gsr_egl;
typedef struct {
int major;
int minor;
int micro;
} gsr_pipewire_video_data_version;
typedef struct {
uint32_t fps_num;
uint32_t fps_den;
} gsr_pipewire_video_video_info;
typedef struct {
int fd;
uint32_t offset;
int32_t stride;
} gsr_pipewire_video_dmabuf_data;
typedef struct {
int x, y;
int width, height;
} gsr_pipewire_video_region;
typedef struct {
enum spa_video_format format;
size_t modifiers_index;
size_t modifiers_size;
} gsr_video_format;
typedef struct {
unsigned int texture_id;
unsigned int external_texture_id;
unsigned int cursor_texture_id;
} gsr_texture_map;
typedef struct {
gsr_pipewire_video_region region;
gsr_pipewire_video_region cursor_region;
gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
int num_dmabuf_data;
uint32_t fourcc;
uint64_t modifiers;
bool using_external_image;
gsr_monitor_rotation rotation;
int texture_width;
int texture_height;
} gsr_map_texture_output;
typedef struct {
gsr_egl *egl;
int fd;
uint32_t node;
pthread_mutex_t mutex;
bool mutex_initialized;
struct pw_thread_loop *thread_loop;
struct pw_context *context;
struct pw_core *core;
struct spa_hook core_listener;
struct pw_stream *stream;
struct spa_hook stream_listener;
struct spa_source *reneg;
struct spa_video_info format;
int server_version_sync;
bool negotiated;
bool damaged;
bool has_modifier;
struct {
bool visible;
bool valid;
uint8_t *data;
int x, y;
int hotspot_x, hotspot_y;
int width, height;
} cursor;
struct {
bool valid;
int x, y;
uint32_t width, height;
} crop;
gsr_video_format supported_video_formats[GSR_PIPEWIRE_VIDEO_MAX_VIDEO_FORMATS];
gsr_pipewire_video_data_version server_version;
gsr_pipewire_video_video_info video_info;
gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES];
size_t dmabuf_num_planes;
bool no_modifiers_fallback;
bool external_texture_fallback;
uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS];
size_t num_modifiers;
bool paused;
double paused_start_secs;
gsr_monitor_rotation rotation;
} gsr_pipewire_video;
/*
|capture_cursor| only applies to when capturing a window or region.
In other cases |pipewire_node|'s setup will determine if the cursor is included.
Note that the cursor is not guaranteed to be shown even if set to true, it depends on the wayland compositor.
*/
bool gsr_pipewire_video_init(gsr_pipewire_video *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl);
void gsr_pipewire_video_deinit(gsr_pipewire_video *self);
bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map texture_map, gsr_map_texture_output *output);
bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self);
void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self);
bool gsr_pipewire_video_should_restart(gsr_pipewire_video *self);
#endif /* GSR_PIPEWIRE_VIDEO_H */

38
include/plugins.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef GSR_PLUGINS_H
#define GSR_PLUGINS_H
#include "../plugin/plugin.h"
#include <stdbool.h>
#include "color_conversion.h"
#define GSR_MAX_PLUGINS 128
typedef bool (*gsr_plugin_init_func)(const gsr_plugin_init_params *params, gsr_plugin_init_return *ret);
typedef void (*gsr_plugin_deinit_func)(void *userdata);
typedef struct {
gsr_plugin_init_return data;
void *lib;
gsr_plugin_init_func gsr_plugin_init;
gsr_plugin_deinit_func gsr_plugin_deinit;
} gsr_plugin;
typedef struct {
gsr_plugin plugins[GSR_MAX_PLUGINS];
int num_plugins;
gsr_plugin_init_params init_params;
gsr_egl *egl;
unsigned int texture;
gsr_color_conversion color_conversion;
} gsr_plugins;
bool gsr_plugins_init(gsr_plugins *self, gsr_plugin_init_params init_params, gsr_egl *egl);
/* Plugins are unloaded in reverse order */
void gsr_plugins_deinit(gsr_plugins *self);
bool gsr_plugins_load_plugin(gsr_plugins *self, const char *plugin_filepath);
void gsr_plugins_draw(gsr_plugins *self);
#endif /* GSR_PLUGINS_H */

View File

@ -0,0 +1,54 @@
#ifndef GSR_REPLAY_BUFFER_H
#define GSR_REPLAY_BUFFER_H
#include "../defs.h"
#include <pthread.h>
#include <stdbool.h>
#include <libavcodec/packet.h>
typedef struct gsr_replay_buffer gsr_replay_buffer;
typedef struct {
size_t packet_index;
size_t file_index;
} gsr_replay_buffer_iterator;
struct gsr_replay_buffer {
void (*destroy)(gsr_replay_buffer *self);
bool (*append)(gsr_replay_buffer *self, const AVPacket *av_packet, double timestamp);
void (*clear)(gsr_replay_buffer *self);
AVPacket* (*iterator_get_packet)(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
/* The returned data should be free'd with free */
uint8_t* (*iterator_get_packet_data)(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
/* The clone has to be destroyed before the replay buffer it clones is destroyed */
gsr_replay_buffer* (*clone)(gsr_replay_buffer *self);
/* Returns {0, 0} if replay buffer is empty */
gsr_replay_buffer_iterator (*find_packet_index_by_time_passed)(gsr_replay_buffer *self, int seconds);
/* Returns {-1, 0} if not found */
gsr_replay_buffer_iterator (*find_keyframe)(gsr_replay_buffer *self, gsr_replay_buffer_iterator start_iterator, int stream_index, bool invert_stream_index);
bool (*iterator_next)(gsr_replay_buffer *self, gsr_replay_buffer_iterator *iterator);
pthread_mutex_t mutex;
bool mutex_initialized;
gsr_replay_buffer *original_replay_buffer;
};
gsr_replay_buffer* gsr_replay_buffer_create(gsr_replay_storage replay_storage, const char *replay_directory, double replay_buffer_time, size_t replay_buffer_num_packets);
void gsr_replay_buffer_destroy(gsr_replay_buffer *self);
void gsr_replay_buffer_lock(gsr_replay_buffer *self);
void gsr_replay_buffer_unlock(gsr_replay_buffer *self);
bool gsr_replay_buffer_append(gsr_replay_buffer *self, const AVPacket *av_packet, double timestamp);
void gsr_replay_buffer_clear(gsr_replay_buffer *self);
AVPacket* gsr_replay_buffer_iterator_get_packet(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
/* The returned data should be free'd with free */
uint8_t* gsr_replay_buffer_iterator_get_packet_data(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);
/* The clone has to be destroyed before the replay buffer it clones is destroyed */
gsr_replay_buffer* gsr_replay_buffer_clone(gsr_replay_buffer *self);
/* Returns {0, 0} if replay buffer is empty */
gsr_replay_buffer_iterator gsr_replay_buffer_find_packet_index_by_time_passed(gsr_replay_buffer *self, int seconds);
/* Returns {-1, 0} if not found */
gsr_replay_buffer_iterator gsr_replay_buffer_find_keyframe(gsr_replay_buffer *self, gsr_replay_buffer_iterator start_iterator, int stream_index, bool invert_stream_index);
bool gsr_replay_buffer_iterator_next(gsr_replay_buffer *self, gsr_replay_buffer_iterator *iterator);
#endif /* GSR_REPLAY_BUFFER_H */

View File

@ -0,0 +1,44 @@
#ifndef GSR_REPLAY_BUFFER_DISK_H
#define GSR_REPLAY_BUFFER_DISK_H
#include "replay_buffer.h"
#include <limits.h>
#define GSR_REPLAY_BUFFER_CAPACITY_NUM_FILES 1024
typedef struct {
AVPacket packet;
size_t data_index;
double timestamp;
} gsr_av_packet_disk;
typedef struct {
size_t id;
double start_timestamp;
double end_timestamp;
int ref_counter;
int fd;
gsr_av_packet_disk *packets;
size_t capacity_num_packets;
size_t num_packets;
} gsr_replay_buffer_file;
typedef struct {
gsr_replay_buffer replay_buffer;
double replay_buffer_time;
size_t storage_counter;
size_t storage_num_bytes_written;
int storage_fd;
gsr_replay_buffer_file *files[GSR_REPLAY_BUFFER_CAPACITY_NUM_FILES]; // GSR_REPLAY_BUFFER_CAPACITY_NUM_FILES * REPLAY_BUFFER_FILE_SIZE_BYTES = 256gb, should be enough for everybody
size_t num_files;
char replay_directory[PATH_MAX];
bool owns_directory;
} gsr_replay_buffer_disk;
gsr_replay_buffer* gsr_replay_buffer_disk_create(const char *replay_directory, double replay_buffer_time);
#endif /* GSR_REPLAY_BUFFER_DISK_H */

View File

@ -0,0 +1,22 @@
#ifndef GSR_REPLAY_BUFFER_RAM_H
#define GSR_REPLAY_BUFFER_RAM_H
#include "replay_buffer.h"
typedef struct {
AVPacket packet;
int ref_counter;
double timestamp;
} gsr_av_packet_ram;
typedef struct {
gsr_replay_buffer replay_buffer;
gsr_av_packet_ram **packets;
size_t capacity_num_packets;
size_t num_packets;
size_t index;
} gsr_replay_buffer_ram;
gsr_replay_buffer* gsr_replay_buffer_ram_create(size_t replay_buffer_num_packets);
#endif /* GSR_REPLAY_BUFFER_RAM_H */

23
include/shader.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef GSR_SHADER_H
#define GSR_SHADER_H
#include <stdbool.h>
typedef struct gsr_egl gsr_egl;
typedef struct {
gsr_egl *egl;
unsigned int program_id;
} gsr_shader;
/* |vertex_shader| or |fragment_shader| may be NULL */
int gsr_shader_init(gsr_shader *self, gsr_egl *egl, const char *vertex_shader, const char *fragment_shader);
void gsr_shader_deinit(gsr_shader *self);
int gsr_shader_bind_attribute_location(gsr_shader *self, const char *attribute, int location);
void gsr_shader_use(gsr_shader *self);
void gsr_shader_use_none(gsr_shader *self);
void gsr_shader_enable_debug_output(bool enable);
#endif /* GSR_SHADER_H */

82
include/sound.hpp Normal file
View File

@ -0,0 +1,82 @@
/*
Copyright (C) 2020 dec05eba
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef GPU_SCREEN_RECORDER_H
#define GPU_SCREEN_RECORDER_H
#include <vector>
#include <string>
typedef struct {
void *handle;
unsigned int frames;
} SoundDevice;
struct AudioDevice {
std::string name;
std::string description;
};
struct AudioDevices {
std::string default_output;
std::string default_input;
std::vector<AudioDevice> audio_inputs;
};
enum class AudioInputType {
DEVICE,
APPLICATION
};
struct AudioInput {
std::string name;
AudioInputType type = AudioInputType::DEVICE;
bool inverted = false;
};
struct MergedAudioInputs {
std::string track_name;
std::vector<AudioInput> audio_inputs;
};
typedef enum {
S16,
S32,
F32
} AudioFormat;
/*
Get a sound device by name, returning the device into the |device| parameter.
|device_name| can be a device name or "default_output", "default_input" or "" to not connect to any device (used for app audio for example).
If the device name is "default_output" or "default_input" then it will automatically switch which
device is records from when the default output/input is changed in the system audio settings.
Returns 0 on success, or a negative value on failure.
*/
int sound_device_get_by_name(SoundDevice *device, const char *node_name, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format);
void sound_device_close(SoundDevice *device);
/*
Returns the next chunk of audio into @buffer.
Returns the number of frames read, or a negative value on failure.
*/
int sound_device_read_next_chunk(SoundDevice *device, void **buffer, double timeout_sec, double *latency_seconds);
AudioDevices get_pulseaudio_inputs();
bool pulseaudio_server_is_pipewire();
#endif /* GPU_SCREEN_RECORDER_H */

65
include/utils.h Normal file
View File

@ -0,0 +1,65 @@
#ifndef GSR_UTILS_H
#define GSR_UTILS_H
#include "vec2.h"
#include "../include/egl.h"
#include "../include/defs.h"
#include <stdbool.h>
#include <stdint.h>
typedef struct AVCodecContext AVCodecContext;
typedef struct AVFrame AVFrame;
typedef struct gsr_capture_metadata gsr_capture_metadata;
typedef struct {
const char *name;
int name_len;
vec2i pos; /* This is 0, 0 on wayland. Use |drm_monitor_get_display_server_data| to get the position */
vec2i size;
uint32_t connector_id; /* Only on x11 and drm */
gsr_monitor_rotation rotation; /* Only on x11 and wayland */
uint32_t monitor_identifier; /* On x11 this is the crtc id */
} gsr_monitor;
typedef struct {
const char *name;
int name_len;
gsr_monitor *monitor;
bool found_monitor;
} get_monitor_by_name_userdata;
double clock_get_monotonic_seconds(void);
bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size);
bool generate_random_characters_standard_alphabet(char *buffer, int buffer_size);
typedef void (*active_monitor_callback)(const gsr_monitor *monitor, void *userdata);
void for_each_active_monitor_output_x11_not_cached(Display *display, active_monitor_callback callback, void *userdata);
void for_each_active_monitor_output(const gsr_window *window, const char *card_path, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata);
bool get_monitor_by_name(const gsr_egl *egl, gsr_connection_type connection_type, const char *name, gsr_monitor *monitor);
bool drm_monitor_get_display_server_data(const gsr_window *window, const gsr_monitor *monitor, gsr_monitor_rotation *monitor_rotation, vec2i *monitor_position);
int get_connector_type_by_name(const char *name);
int get_connector_type_id_by_name(const char *name);
uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count);
bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info);
bool try_card_has_valid_plane(const char *card_path);
/* |output| should be at least 128 bytes in size */
bool gsr_get_valid_card_path(gsr_egl *egl, char *output, bool is_monitor_capture);
/* |render_path| should be at least 128 bytes in size */
bool gsr_card_path_get_render_path(const char *card_path, char *render_path);
int create_directory_recursive(char *path);
/* |img_attr| needs to be at least 44 in size */
void setup_dma_buf_attrs(intptr_t *img_attr, uint32_t format, uint32_t width, uint32_t height, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, int num_planes, bool use_modifier);
vec2i scale_keep_aspect_ratio(vec2i from, vec2i to);
vec2i gsr_capture_get_target_position(vec2i output_size, gsr_capture_metadata *capture_metadata);
unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter);
bool get_nvidia_driver_version(int *major, int *minor);
#endif /* GSR_UTILS_H */

16
include/vec2.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef VEC2_H
#define VEC2_H
typedef struct {
int x, y;
} vec2i;
typedef struct {
float x, y;
} vec2f;
typedef struct {
double x, y;
} vec2d;
#endif /* VEC2_H */

8
include/window/wayland.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef GSR_WINDOW_WAYLAND_H
#define GSR_WINDOW_WAYLAND_H
#include "window.h"
gsr_window* gsr_window_wayland_create(void);
#endif /* GSR_WINDOW_WAYLAND_H */

37
include/window/window.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef GSR_WINDOW_H
#define GSR_WINDOW_H
#include "../utils.h"
#include <stdbool.h>
typedef union _XEvent XEvent;
typedef struct gsr_window gsr_window;
typedef enum {
GSR_DISPLAY_SERVER_X11,
GSR_DISPLAY_SERVER_WAYLAND
} gsr_display_server;
struct gsr_window {
void (*destroy)(gsr_window *self);
/* Returns true if an event is available */
bool (*process_event)(gsr_window *self);
XEvent* (*get_event_data)(gsr_window *self); /* can be NULL */
gsr_display_server (*get_display_server)(void);
void* (*get_display)(gsr_window *self);
void* (*get_window)(gsr_window *self);
void (*for_each_active_monitor_output_cached)(const gsr_window *self, active_monitor_callback callback, void *userdata);
void *priv;
};
void gsr_window_destroy(gsr_window *self);
/* Returns true if an event is available */
bool gsr_window_process_event(gsr_window *self);
XEvent* gsr_window_get_event_data(gsr_window *self);
gsr_display_server gsr_window_get_display_server(const gsr_window *self);
void* gsr_window_get_display(gsr_window *self);
void* gsr_window_get_window(gsr_window *self);
void gsr_window_for_each_active_monitor_output_cached(const gsr_window *self, active_monitor_callback callback, void *userdata);
#endif /* GSR_WINDOW_H */

10
include/window/x11.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef GSR_WINDOW_X11_H
#define GSR_WINDOW_X11_H
#include "window.h"
typedef struct _XDisplay Display;
gsr_window* gsr_window_x11_create(Display *display);
#endif /* GSR_WINDOW_X11_H */

30
include/window_texture.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef WINDOW_TEXTURE_H
#define WINDOW_TEXTURE_H
#include "egl.h"
typedef struct {
Display *display;
Window window;
Pixmap pixmap;
EGLImage image;
unsigned int texture_id;
int redirected;
gsr_egl *egl;
unsigned int window_width;
unsigned int window_height;
} WindowTexture;
/* Returns 0 on success */
int window_texture_init(WindowTexture *window_texture, Display *display, Window window, gsr_egl *egl);
void window_texture_deinit(WindowTexture *self);
/*
This should ONLY be called when the target window is resized.
Returns 0 on success.
*/
int window_texture_on_resize(WindowTexture *self);
unsigned int window_texture_get_opengl_texture_id(WindowTexture *self);
#endif /* WINDOW_TEXTURE_H */

45
include/xnvctrl.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef GSR_XNVCTRL_H
#define GSR_XNVCTRL_H
#include <stdbool.h>
#include <stdint.h>
#define NV_CTRL_GPU_NVCLOCK_OFFSET 409
#define NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET 410
#define NV_CTRL_GPU_NVCLOCK_OFFSET_ALL_PERFORMANCE_LEVELS 424
#define NV_CTRL_GPU_MEM_TRANSFER_RATE_OFFSET_ALL_PERFORMANCE_LEVELS 425
#define NV_CTRL_TARGET_TYPE_GPU 1
#define NV_CTRL_STRING_PERFORMANCE_MODES 29
typedef struct _XDisplay Display;
typedef struct {
int type;
union {
struct {
int64_t min;
int64_t max;
} range;
struct {
unsigned int ints;
} bits;
} u;
unsigned int permissions;
} NVCTRLAttributeValidValuesRec;
typedef struct {
Display *display;
void *library;
int (*XNVCTRLQueryExtension)(Display *dpy, int *event_basep, int *error_basep);
int (*XNVCTRLSetTargetAttributeAndGetStatus)(Display *dpy, int target_type, int target_id, unsigned int display_mask, unsigned int attribute, int value);
int (*XNVCTRLQueryValidTargetAttributeValues)(Display *dpy, int target_type, int target_id, unsigned int display_mask, unsigned int attribute, NVCTRLAttributeValidValuesRec *values);
int (*XNVCTRLQueryTargetStringAttribute)(Display *dpy, int target_type, int target_id, unsigned int display_mask, unsigned int attribute, char **ptr);
} gsr_xnvctrl;
bool gsr_xnvctrl_load(gsr_xnvctrl *self, Display *display);
void gsr_xnvctrl_unload(gsr_xnvctrl *self);
#endif /* GSR_XNVCTRL_H */