diff options
Diffstat (limited to 'include')
42 files changed, 1380 insertions, 197 deletions
diff --git a/include/args_parser.h b/include/args_parser.h new file mode 100644 index 0000000..e2fa46e --- /dev/null +++ b/include/args_parser.h @@ -0,0 +1,106 @@ +#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 30 + +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_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; + char window[64]; + 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 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 index 82a9555..634eee0 100644 --- a/include/capture/capture.h +++ b/include/capture/capture.h @@ -1,32 +1,51 @@ #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 { + int width; + int height; + int fps; + AVCodecContext *video_codec_context; /* can be NULL */ + AVFrame *frame; /* can be NULL, but will never be NULL if |video_codec_context| is set */ +} gsr_capture_metadata; + struct gsr_capture { /* These methods should not be called manually. Call gsr_capture_* instead */ - int (*start)(gsr_capture *cap, AVCodecContext *video_codec_context); - void (*tick)(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame); /* can be NULL */ - bool (*should_stop)(gsr_capture *cap, bool *err); /* can be NULL */ - int (*capture)(gsr_capture *cap, AVFrame *frame); - void (*capture_end)(gsr_capture *cap, AVFrame *frame); /* can be NULL */ - void (*destroy)(gsr_capture *cap, AVCodecContext *video_codec_context); + 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 */ + int (*capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); + 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, AVCodecContext *video_codec_context); -void gsr_capture_tick(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame); +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, AVFrame *frame); -void gsr_capture_end(gsr_capture *cap, AVFrame *frame); -/* Calls |gsr_capture_stop| as well */ -void gsr_capture_destroy(gsr_capture *cap, AVCodecContext *video_codec_context); +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..ce09817 --- /dev/null +++ b/include/capture/kms.h @@ -0,0 +1,19 @@ +#ifndef GSR_CAPTURE_KMS_H +#define GSR_CAPTURE_KMS_H + +#include "capture.h" + +typedef struct { + gsr_egl *egl; + 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/kms_cuda.h b/include/capture/kms_cuda.h deleted file mode 100644 index c2b2ad8..0000000 --- a/include/capture/kms_cuda.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef GSR_CAPTURE_KMS_CUDA_H -#define GSR_CAPTURE_KMS_CUDA_H - -#include "../vec2.h" -#include "../utils.h" -#include "capture.h" - -typedef struct _XDisplay Display; - -typedef struct { - gsr_egl *egl; - const char *display_to_capture; /* if this is "screen", then the first monitor is captured. A copy is made of this */ - gsr_gpu_info gpu_inf; - const char *card_path; /* reference */ -} gsr_capture_kms_cuda_params; - -gsr_capture* gsr_capture_kms_cuda_create(const gsr_capture_kms_cuda_params *params); - -#endif /* GSR_CAPTURE_KMS_CUDA_H */ diff --git a/include/capture/kms_vaapi.h b/include/capture/kms_vaapi.h deleted file mode 100644 index 26cda2c..0000000 --- a/include/capture/kms_vaapi.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef GSR_CAPTURE_KMS_VAAPI_H -#define GSR_CAPTURE_KMS_VAAPI_H - -#include "../vec2.h" -#include "../utils.h" -#include "capture.h" - -typedef struct _XDisplay Display; - -typedef struct { - gsr_egl *egl; - const char *display_to_capture; /* if this is "screen", then the first monitor is captured. A copy is made of this */ - gsr_gpu_info gpu_inf; - const char *card_path; /* reference */ - bool wayland; -} gsr_capture_kms_vaapi_params; - -gsr_capture* gsr_capture_kms_vaapi_create(const gsr_capture_kms_vaapi_params *params); - -#endif /* GSR_CAPTURE_KMS_VAAPI_H */ diff --git a/include/capture/nvfbc.h b/include/capture/nvfbc.h index 5678473..7e30d01 100644 --- a/include/capture/nvfbc.h +++ b/include/capture/nvfbc.h @@ -2,20 +2,17 @@ #define GSR_CAPTURE_NVFBC_H #include "capture.h" -#include "../egl.h" #include "../vec2.h" -typedef struct _XDisplay Display; - typedef struct { - Display *dpy; 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; - vec2i pos; - vec2i size; bool direct_capture; - bool overclock; + 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); 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/xcomposite.h b/include/capture/xcomposite.h new file mode 100644 index 0000000..bf6532e --- /dev/null +++ b/include/capture/xcomposite.h @@ -0,0 +1,17 @@ +#ifndef GSR_CAPTURE_XCOMPOSITE_H +#define GSR_CAPTURE_XCOMPOSITE_H + +#include "capture.h" +#include "../vec2.h" + +typedef struct { + gsr_egl *egl; + 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/xcomposite_cuda.h b/include/capture/xcomposite_cuda.h deleted file mode 100644 index 2106714..0000000 --- a/include/capture/xcomposite_cuda.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef GSR_CAPTURE_XCOMPOSITE_CUDA_H -#define GSR_CAPTURE_XCOMPOSITE_CUDA_H - -#include "capture.h" -#include "../egl.h" -#include "../vec2.h" -#include <X11/X.h> - -typedef struct _XDisplay Display; - -typedef struct { - gsr_egl *egl; - Display *dpy; - Window window; - bool follow_focused; /* If this is set then |window| is ignored */ - vec2i region_size; /* This is currently only used with |follow_focused| */ - bool overclock; -} gsr_capture_xcomposite_cuda_params; - -gsr_capture* gsr_capture_xcomposite_cuda_create(const gsr_capture_xcomposite_cuda_params *params); - -#endif /* GSR_CAPTURE_XCOMPOSITE_CUDA_H */ diff --git a/include/capture/xcomposite_vaapi.h b/include/capture/xcomposite_vaapi.h deleted file mode 100644 index e80c60a..0000000 --- a/include/capture/xcomposite_vaapi.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef GSR_CAPTURE_XCOMPOSITE_VAAPI_H -#define GSR_CAPTURE_XCOMPOSITE_VAAPI_H - -#include "capture.h" -#include "../egl.h" -#include "../vec2.h" -#include <X11/X.h> - -typedef struct _XDisplay Display; - -typedef struct { - gsr_egl *egl; - Display *dpy; - Window window; - bool follow_focused; /* If this is set then |window| is ignored */ - vec2i region_size; /* This is currently only used with |follow_focused| */ - const char *card_path; /* reference */ -} gsr_capture_xcomposite_vaapi_params; - -gsr_capture* gsr_capture_xcomposite_vaapi_create(const gsr_capture_xcomposite_vaapi_params *params); - -#endif /* GSR_CAPTURE_XCOMPOSITE_VAAPI_H */ diff --git a/include/capture/ximage.h b/include/capture/ximage.h new file mode 100644 index 0000000..e6c3607 --- /dev/null +++ b/include/capture/ximage.h @@ -0,0 +1,18 @@ +#ifndef GSR_CAPTURE_XIMAGE_H +#define GSR_CAPTURE_XIMAGE_H + +#include "capture.h" +#include "../vec2.h" + +typedef struct { + gsr_egl *egl; + const char *display_to_capture; /* A copy is made of this */ + bool record_cursor; + vec2i output_resolution; + vec2i region_size; + vec2i region_position; +} gsr_capture_ximage_params; + +gsr_capture* gsr_capture_ximage_create(const gsr_capture_ximage_params *params); + +#endif /* GSR_CAPTURE_XIMAGE_H */ diff --git a/include/codec_query/codec_query.h b/include/codec_query/codec_query.h new file mode 100644 index 0000000..316217d --- /dev/null +++ b/include/codec_query/codec_query.h @@ -0,0 +1,23 @@ +#ifndef GSR_CODEC_QUERY_H +#define GSR_CODEC_QUERY_H + +#include <stdbool.h> + +typedef struct { + bool supported; + bool low_power; +} 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 index 738cba5..76d8be5 100644 --- a/include/color_conversion.h +++ b/include/color_conversion.h @@ -2,41 +2,82 @@ #define GSR_COLOR_CONVERSION_H #include "shader.h" +#include "defs.h" #include "vec2.h" +#include <stdbool.h> + +#define GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS 12 +#define GSR_COLOR_CONVERSION_MAX_GRAPHICS_SHADERS 6 +#define GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS 2 typedef enum { - GSR_SOURCE_COLOR_RGB + GSR_SOURCE_COLOR_RGB, + GSR_SOURCE_COLOR_BGR } gsr_source_color; typedef enum { - GSR_DESTINATION_COLOR_BGR, - GSR_DESTINATION_COLOR_NV12 /* YUV420, BT709, limited */ + 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 struct { + int rotation_matrix; + int offset; +} gsr_color_graphics_uniforms; + +typedef struct { + int rotation_matrix; + int source_position; + int target_position; + int scale; +} gsr_color_compute_uniforms; + typedef struct { gsr_egl *egl; - gsr_source_color source_color; gsr_destination_color destination_color; unsigned int destination_textures[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; - int rotation_uniforms[2]; - gsr_shader shaders[2]; + gsr_color_compute_uniforms compute_uniforms[GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS]; + gsr_shader compute_shaders[GSR_COLOR_CONVERSION_MAX_COMPUTE_SHADERS]; + + /* These are only loader if compute shaders (of the same type) fail to load */ + 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[2]; + bool compute_shaders_failed_to_load; + bool external_compute_shaders_failed_to_load; + + unsigned int framebuffers[GSR_COLOR_CONVERSION_MAX_FRAMEBUFFERS]; unsigned int vertex_array_object_id; unsigned int vertex_buffer_object_id; + + int max_local_size_dim; } 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); -int gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i source_pos, vec2i source_size, vec2i texture_pos, vec2i texture_size, float rotation); +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_source_color source_color, bool external_texture, bool alpha_blending); +void gsr_color_conversion_clear(gsr_color_conversion *self); + +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 index 41fe15b..fd1f9f9 100644 --- a/include/cuda.h +++ b/include/cuda.h @@ -73,7 +73,8 @@ typedef CUDA_MEMCPY2D_v2 CUDA_MEMCPY2D; typedef struct CUgraphicsResource_st *CUgraphicsResource; -typedef struct { +typedef struct gsr_cuda gsr_cuda; +struct gsr_cuda { gsr_overclock overclock; bool do_overclock; @@ -88,8 +89,9 @@ typedef struct { CUresult (*cuCtxPushCurrent_v2)(CUcontext ctx); CUresult (*cuCtxPopCurrent_v2)(CUcontext *pctx); CUresult (*cuGetErrorString)(CUresult error, const char **pStr); - CUresult (*cuMemsetD8_v2)(CUdeviceptr dstDevice, unsigned char uc, size_t N); 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); @@ -98,7 +100,7 @@ typedef struct { 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); -} gsr_cuda; +}; bool gsr_cuda_load(gsr_cuda *self, Display *display, bool overclock); void gsr_cuda_unload(gsr_cuda *self); 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..4b10e58 --- /dev/null +++ b/include/damage.h @@ -0,0 +1,52 @@ +#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; + +typedef enum { + GSR_DAMAGE_TRACK_NONE, + GSR_DAMAGE_TRACK_WINDOW, + GSR_DAMAGE_TRACK_MONITOR +} gsr_damage_track_type; + +typedef struct { + gsr_egl *egl; + Display *display; + bool track_cursor; + gsr_damage_track_type track_type; + + int damage_event; + int damage_error; + uint64_t damage; + bool damaged; + + int randr_event; + int randr_error; + + uint64_t window; + //vec2i window_pos; + vec2i window_size; + + gsr_cursor cursor; /* Relative to |window| */ + gsr_monitor monitor; + char monitor_name[32]; +} gsr_damage; + +bool gsr_damage_init(gsr_damage *self, gsr_egl *egl, bool track_cursor); +void gsr_damage_deinit(gsr_damage *self); + +bool gsr_damage_set_target_window(gsr_damage *self, uint64_t window); +bool gsr_damage_set_target_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..58edf3c --- /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 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; + +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; + +/* 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 index ea71b5a..8e7496c 100644 --- a/include/egl.h +++ b/include/egl.h @@ -8,6 +8,9 @@ #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; @@ -33,12 +36,18 @@ 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_NONE 0x3038 #define EGL_CONTEXT_CLIENT_VERSION 0x3098 #define EGL_BACK_BUFFER 0x3084 @@ -52,8 +61,23 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #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 @@ -63,16 +87,31 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define EGL_CONTEXT_PRIORITY_HIGH_IMG 0x3101 #define EGL_CONTEXT_PRIORITY_MEDIUM_IMG 0x3102 #define EGL_CONTEXT_PRIORITY_LOW_IMG 0x3103 +#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 // TODO: Use this where applicable +#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_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 @@ -87,75 +126,82 @@ typedef void (*__eglMustCastToProperFunctionPointerType)(void); #define GL_FRAMEBUFFER 0x8D40 #define GL_COLOR_ATTACHMENT0 0x8CE0 #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 -#define GL_STREAM_DRAW 0x88E0 +#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 0x92E0 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_MAX_COMPUTE_FIXED_GROUP_INVOCATIONS 0x90EB +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_CLAMP_TO_BORDER 0x812D +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF #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_COMPUTE_SHADER 0x91B9 #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; -#define GSR_MAX_OUTPUTS 32 - -typedef struct { - Display *dpy; - Window window; -} gsr_x11; - -typedef struct { - uint32_t wl_name; - void *output; - vec2i pos; - vec2i size; - char *name; -} gsr_wayland_output; - -typedef struct { - void *dpy; - void *window; - void *registry; - void *surface; - void *compositor; - void *export_manager; - void *current_frame; - void *frame_callback; - gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; - int num_outputs; - gsr_wayland_output *output_to_capture; -} gsr_wayland; - -typedef struct { +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; - gsr_x11 x11; - gsr_wayland wayland; + void *glx_context; + void *glx_fb_config; - int fd; - uint32_t x; - uint32_t y; - uint32_t width; - uint32_t height; - uint32_t pitch; - uint32_t offset; - uint32_t pixel_format; - uint64_t modifier; + gsr_gpu_info gpu_info; + + char card_path[128]; int32_t (*eglGetError)(void); EGLDisplay (*eglGetDisplay)(EGLNativeDisplayType display_id); @@ -177,22 +223,54 @@ typedef struct { 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 (*glGetTexLevelParameteriv)(unsigned int target, int level, unsigned int pname, int *params); void (*glTexImage2D)(unsigned int target, int level, int internalFormat, int width, int height, int border, unsigned int format, unsigned int type, const void *pixels); - void (*glCopyImageSubData)(unsigned int srcName, unsigned int srcTarget, int srcLevel, int srcX, int srcY, int srcZ, unsigned int dstName, unsigned int dstTarget, int dstLevel, int dstX, int dstY, int dstZ, int srcWidth, int srcHeight, int srcDepth); - void (*glClearTexImage)(unsigned int texture, unsigned int level, unsigned int format, unsigned int type, const void *data); + void (*glTexSubImage2D)(unsigned int target, int level, int xoffset, int yoffset, int width, int height, unsigned format, unsigned type, const void *pixels); + void (*glGetTexImage)(unsigned int target, int level, unsigned int format, unsigned int type, void *pixels); void (*glGenFramebuffers)(int n, unsigned int *framebuffers); void (*glBindFramebuffer)(unsigned int target, unsigned int framebuffer); void (*glDeleteFramebuffers)(int n, const unsigned int *framebuffers); + void (*glDispatchCompute)(unsigned int num_groups_x, unsigned int num_groups_y, unsigned int num_groups_z); + 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); @@ -223,18 +301,28 @@ typedef struct { 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); -} gsr_egl; + 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* (*glMapBuffer)(unsigned int target, unsigned int access); + unsigned char (*glUnmapBuffer)(unsigned int target); + void (*glGetIntegerv)(unsigned int pname, int *params); +}; -bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland); +bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture, bool enable_debug); void gsr_egl_unload(gsr_egl *self); -/* wayland protocol capture, does not include kms capture */ -bool gsr_egl_supports_wayland_capture(gsr_egl *self); -bool gsr_egl_start_capture(gsr_egl *self, const char *monitor_to_capture); -void gsr_egl_update(gsr_egl *self); -void gsr_egl_cleanup_frame(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..7a706b5 --- /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, 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, 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..65e7497 --- /dev/null +++ b/include/image_writer.h @@ -0,0 +1,35 @@ +#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 enum { + GSR_IMAGE_WRITER_SOURCE_OPENGL, + GSR_IMAGE_WRITER_SOURCE_MEMORY +} gsr_image_writer_source; + +typedef struct { + gsr_image_writer_source source; + gsr_egl *egl; + int width; + int height; + unsigned int texture; + const void *memory; /* Reference */ +} gsr_image_writer; + +bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height); +/* |memory| is taken as a reference. The data is expected to be in rgba8 format (8 bit rgba) */ +bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height); +void gsr_image_writer_deinit(gsr_image_writer *self); + +/* Quality is between 1 and 100 where 100 is the max quality. Quality doesn't apply to lossless formats */ +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/pipewire_audio.h b/include/pipewire_audio.h new file mode 100644 index 0000000..10d8c9b --- /dev/null +++ b/include/pipewire_audio.h @@ -0,0 +1,157 @@ +#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> + +#define GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES 128 +#define GSR_PIPEWIRE_AUDIO_MAX_PORTS 256 +#define GSR_PIPEWIRE_AUDIO_MAX_LINKS 256 +#define GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS 32 +#define GSR_PIPEWIRE_AUDIO_MAX_VIRTUAL_SINKS 32 + +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[GSR_PIPEWIRE_AUDIO_MAX_STREAM_NODES]; + int num_stream_nodes; + + gsr_pipewire_audio_port ports[GSR_PIPEWIRE_AUDIO_MAX_PORTS]; + int num_ports; + + gsr_pipewire_audio_link links[GSR_PIPEWIRE_AUDIO_MAX_LINKS]; + int num_links; + + gsr_pipewire_audio_requested_link requested_links[GSR_PIPEWIRE_AUDIO_MAX_REQUESTED_LINKS]; + int num_requested_links; + + struct pw_proxy *virtual_sink_proxies[GSR_PIPEWIRE_AUDIO_MAX_VIRTUAL_SINKS]; + int num_virtual_sink_proxies; +} gsr_pipewire_audio; + +bool gsr_pipewire_audio_init(gsr_pipewire_audio *self); +void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self); + +bool gsr_pipewire_audio_create_virtual_sink(gsr_pipewire_audio *self, const char *name); + +/* + 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 |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..92622b8 --- /dev/null +++ b/include/pipewire_video.h @@ -0,0 +1,112 @@ +#ifndef GSR_PIPEWIRE_VIDEO_H +#define GSR_PIPEWIRE_VIDEO_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 12 +#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_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; + + 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; +} 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); + +/* |dmabuf_data| should be at least GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES in size */ +bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map texture_map, gsr_pipewire_video_region *region, gsr_pipewire_video_region *cursor_region, gsr_pipewire_video_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image); +bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self); +void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self); + +#endif /* GSR_PIPEWIRE_VIDEO_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 index 37f4c09..285758d 100644 --- a/include/shader.h +++ b/include/shader.h @@ -1,7 +1,9 @@ #ifndef GSR_SHADER_H #define GSR_SHADER_H -#include "egl.h" +#include <stdbool.h> + +typedef struct gsr_egl gsr_egl; typedef struct { gsr_egl *egl; @@ -9,11 +11,13 @@ typedef struct { } 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); +int gsr_shader_init(gsr_shader *self, gsr_egl *egl, const char *vertex_shader, const char *fragment_shader, const char *compute_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 index 6873e90..87e2e2d 100644 --- a/include/sound.hpp +++ b/include/sound.hpp @@ -26,12 +26,30 @@ typedef struct { unsigned int frames; } SoundDevice; -struct AudioInput { +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; }; @@ -42,9 +60,10 @@ typedef enum { } AudioFormat; /* - Get a sound device by name, returning the device into the @device parameter. - The device should be closed with @sound_device_close after it has been used - to clean up internal resources. + Get a sound device by name, returning the device into the |device| parameter. + |device_name| can be a device name or "default_output" or "default_input". + 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 *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format); @@ -55,8 +74,9 @@ 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); +int sound_device_read_next_chunk(SoundDevice *device, void **buffer, double timeout_sec, double *latency_seconds); -std::vector<AudioInput> get_pulseaudio_inputs(); +AudioDevices get_pulseaudio_inputs(); +bool pulseaudio_server_is_pipewire(); #endif /* GPU_SCREEN_RECORDER_H */ diff --git a/include/utils.h b/include/utils.h index 392591a..b6f51c1 100644 --- a/include/utils.h +++ b/include/utils.h @@ -3,36 +3,25 @@ #include "vec2.h" #include "../include/egl.h" +#include "../include/defs.h" #include <stdbool.h> #include <stdint.h> -#include <X11/extensions/Xrandr.h> -typedef enum { - GSR_GPU_VENDOR_AMD, - GSR_GPU_VENDOR_INTEL, - GSR_GPU_VENDOR_NVIDIA -} gsr_gpu_vendor; +#define CONNECTOR_TYPE_COUNTS 32 -typedef struct { - gsr_gpu_vendor vendor; - int gpu_version; /* 0 if unknown */ -} gsr_gpu_info; +typedef struct AVCodecContext AVCodecContext; +typedef struct AVFrame AVFrame; typedef struct { const char *name; int name_len; - vec2i pos; + vec2i pos; /* This is 0, 0 on wayland. Use |drm_monitor_get_display_server_data| to get the position */ vec2i size; - XRRCrtcInfo *crt_info; /* Only on x11 */ - uint32_t connector_id; /* Only on drm */ + 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 enum { - GSR_CONNECTION_X11, - GSR_CONNECTION_WAYLAND, - GSR_CONNECTION_DRM -} gsr_connection_type; - typedef struct { const char *name; int name_len; @@ -40,19 +29,42 @@ typedef struct { bool found_monitor; } get_monitor_by_name_userdata; +typedef struct { + int type; + int count; + int count_active; +} drm_connector_type_count; + 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(void *connection, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata); -bool get_monitor_by_name(void *connection, gsr_connection_type connection_type, const char *name, gsr_monitor *monitor); +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); +drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type); +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(char *output); +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 even_number_ceil(int value); +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); +bool video_codec_context_is_vaapi(AVCodecContext *video_codec_context); + +vec2i scale_keep_aspect_ratio(vec2i from, vec2i to); + +unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter); #endif /* GSR_UTILS_H */ diff --git a/include/vec2.h b/include/vec2.h index 3e33cfb..8fd3858 100644 --- a/include/vec2.h +++ b/include/vec2.h @@ -9,4 +9,8 @@ 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 index 75bb2a7..6ee5df4 100644 --- a/include/window_texture.h +++ b/include/window_texture.h @@ -7,6 +7,7 @@ typedef struct { Display *display; Window window; Pixmap pixmap; + EGLImage image; unsigned int texture_id; int redirected; gsr_egl *egl; |