diff options
| author | pants <niklasjo2004@gmail.com> | 2026-09-21 14:16:59 -0700 |
|---|---|---|
| committer | pants <niklasjo2004@gmail.com> | 2026-09-21 14:16:59 -0700 |
| commit | a091c00c00c1d5ae5e3da7ef31b98293c76d479f (patch) | |
| tree | 62c4b3bc114faac9922873ae48672c46cc2cf7d4 /include | |
Diffstat (limited to 'include')
43 files changed, 2093 insertions, 0 deletions
diff --git a/include/args_parser.h b/include/args_parser.h new file mode 100644 index 0000000..316b5e1 --- /dev/null +++ b/include/args_parser.h @@ -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 */ diff --git a/include/capture/capture.h b/include/capture/capture.h new file mode 100644 index 0000000..56cf909 --- /dev/null +++ b/include/capture/capture.h @@ -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 */ diff --git a/include/capture/kms.h b/include/capture/kms.h new file mode 100644 index 0000000..deb12a0 --- /dev/null +++ b/include/capture/kms.h @@ -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 */ diff --git a/include/capture/nvfbc.h b/include/capture/nvfbc.h new file mode 100644 index 0000000..7e30d01 --- /dev/null +++ b/include/capture/nvfbc.h @@ -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 */ diff --git a/include/capture/portal.h b/include/capture/portal.h new file mode 100644 index 0000000..74cdba9 --- /dev/null +++ b/include/capture/portal.h @@ -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 */ diff --git a/include/capture/v4l2.h b/include/capture/v4l2.h new file mode 100644 index 0000000..b7665aa --- /dev/null +++ b/include/capture/v4l2.h @@ -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 */ diff --git a/include/capture/xcomposite.h b/include/capture/xcomposite.h new file mode 100644 index 0000000..15ab2a8 --- /dev/null +++ b/include/capture/xcomposite.h @@ -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 */ diff --git a/include/capture/ximage.h b/include/capture/ximage.h new file mode 100644 index 0000000..8b86618 --- /dev/null +++ b/include/capture/ximage.h @@ -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 */ diff --git a/include/codec_query/codec_query.h b/include/codec_query/codec_query.h new file mode 100644 index 0000000..4993ced --- /dev/null +++ b/include/codec_query/codec_query.h @@ -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 */ diff --git a/include/codec_query/nvenc.h b/include/codec_query/nvenc.h new file mode 100644 index 0000000..c01acf6 --- /dev/null +++ b/include/codec_query/nvenc.h @@ -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 */ diff --git a/include/codec_query/vaapi.h b/include/codec_query/vaapi.h new file mode 100644 index 0000000..60bdeca --- /dev/null +++ b/include/codec_query/vaapi.h @@ -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 */ diff --git a/include/codec_query/vulkan.h b/include/codec_query/vulkan.h new file mode 100644 index 0000000..bb06c6b --- /dev/null +++ b/include/codec_query/vulkan.h @@ -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 */ diff --git a/include/color_conversion.h b/include/color_conversion.h new file mode 100644 index 0000000..9157712 --- /dev/null +++ b/include/color_conversion.h @@ -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 */ diff --git a/include/cuda.h b/include/cuda.h new file mode 100644 index 0000000..fd1f9f9 --- /dev/null +++ b/include/cuda.h @@ -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 */ diff --git a/include/cursor.h b/include/cursor.h new file mode 100644 index 0000000..1564714 --- /dev/null +++ b/include/cursor.h @@ -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 */ diff --git a/include/damage.h b/include/damage.h new file mode 100644 index 0000000..9f8bc64 --- /dev/null +++ b/include/damage.h @@ -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 */ diff --git a/include/dbus.h b/include/dbus.h new file mode 100644 index 0000000..229f7ea --- /dev/null +++ b/include/dbus.h @@ -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 */ diff --git a/include/defs.h b/include/defs.h new file mode 100644 index 0000000..d780005 --- /dev/null +++ b/include/defs.h @@ -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 */ diff --git a/include/egl.h b/include/egl.h new file mode 100644 index 0000000..b10fa06 --- /dev/null +++ b/include/egl.h @@ -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 */ diff --git a/include/encoder/encoder.h b/include/encoder/encoder.h new file mode 100644 index 0000000..7e550f6 --- /dev/null +++ b/include/encoder/encoder.h @@ -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 */ diff --git a/include/encoder/video/nvenc.h b/include/encoder/video/nvenc.h new file mode 100644 index 0000000..d4a906b --- /dev/null +++ b/include/encoder/video/nvenc.h @@ -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 */ diff --git a/include/encoder/video/software.h b/include/encoder/video/software.h new file mode 100644 index 0000000..fd2dc6b --- /dev/null +++ b/include/encoder/video/software.h @@ -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 */ diff --git a/include/encoder/video/vaapi.h b/include/encoder/video/vaapi.h new file mode 100644 index 0000000..b509f17 --- /dev/null +++ b/include/encoder/video/vaapi.h @@ -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 */ diff --git a/include/encoder/video/video.h b/include/encoder/video/video.h new file mode 100644 index 0000000..a77a1aa --- /dev/null +++ b/include/encoder/video/video.h @@ -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 */ diff --git a/include/encoder/video/vulkan.h b/include/encoder/video/vulkan.h new file mode 100644 index 0000000..383fc4f --- /dev/null +++ b/include/encoder/video/vulkan.h @@ -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 */ diff --git a/include/image_writer.h b/include/image_writer.h new file mode 100644 index 0000000..01101b2 --- /dev/null +++ b/include/image_writer.h @@ -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 */ diff --git a/include/library_loader.h b/include/library_loader.h new file mode 100644 index 0000000..47bc9f0 --- /dev/null +++ b/include/library_loader.h @@ -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 */ diff --git a/include/overclock.h b/include/overclock.h new file mode 100644 index 0000000..d6ff901 --- /dev/null +++ b/include/overclock.h @@ -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 */ diff --git a/include/pipewire_audio.h b/include/pipewire_audio.h new file mode 100644 index 0000000..c5a3b92 --- /dev/null +++ b/include/pipewire_audio.h @@ -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 */ diff --git a/include/pipewire_video.h b/include/pipewire_video.h new file mode 100644 index 0000000..8722d21 --- /dev/null +++ b/include/pipewire_video.h @@ -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 */ diff --git a/include/plugins.h b/include/plugins.h new file mode 100644 index 0000000..7588b61 --- /dev/null +++ b/include/plugins.h @@ -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 */ diff --git a/include/replay_buffer/replay_buffer.h b/include/replay_buffer/replay_buffer.h new file mode 100644 index 0000000..a04a3be --- /dev/null +++ b/include/replay_buffer/replay_buffer.h @@ -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 */
\ No newline at end of file diff --git a/include/replay_buffer/replay_buffer_disk.h b/include/replay_buffer/replay_buffer_disk.h new file mode 100644 index 0000000..6873bb0 --- /dev/null +++ b/include/replay_buffer/replay_buffer_disk.h @@ -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 */
\ No newline at end of file diff --git a/include/replay_buffer/replay_buffer_ram.h b/include/replay_buffer/replay_buffer_ram.h new file mode 100644 index 0000000..a43d1b9 --- /dev/null +++ b/include/replay_buffer/replay_buffer_ram.h @@ -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 */
\ No newline at end of file diff --git a/include/shader.h b/include/shader.h new file mode 100644 index 0000000..2fd2603 --- /dev/null +++ b/include/shader.h @@ -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 */ diff --git a/include/sound.hpp b/include/sound.hpp new file mode 100644 index 0000000..c7164f8 --- /dev/null +++ b/include/sound.hpp @@ -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 */ diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..ff8cb90 --- /dev/null +++ b/include/utils.h @@ -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 */ diff --git a/include/vec2.h b/include/vec2.h new file mode 100644 index 0000000..8fd3858 --- /dev/null +++ b/include/vec2.h @@ -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 */ diff --git a/include/window/wayland.h b/include/window/wayland.h new file mode 100644 index 0000000..3535b0f --- /dev/null +++ b/include/window/wayland.h @@ -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 */ diff --git a/include/window/window.h b/include/window/window.h new file mode 100644 index 0000000..7839f6a --- /dev/null +++ b/include/window/window.h @@ -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 */ diff --git a/include/window/x11.h b/include/window/x11.h new file mode 100644 index 0000000..e0c2948 --- /dev/null +++ b/include/window/x11.h @@ -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 */ diff --git a/include/window_texture.h b/include/window_texture.h new file mode 100644 index 0000000..bdada33 --- /dev/null +++ b/include/window_texture.h @@ -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 */ diff --git a/include/xnvctrl.h b/include/xnvctrl.h new file mode 100644 index 0000000..33fc442 --- /dev/null +++ b/include/xnvctrl.h @@ -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 */ |
