diff options
-rw-r--r-- | include/pipewire_audio.h | 4 | ||||
-rw-r--r-- | include/pipewire_video.h (renamed from include/pipewire.h) | 44 | ||||
-rw-r--r-- | meson.build | 20 | ||||
-rw-r--r-- | meson_options.txt | 3 | ||||
-rw-r--r-- | project.conf | 1 | ||||
-rw-r--r-- | src/capture/portal.c | 26 | ||||
-rw-r--r-- | src/main.cpp | 19 | ||||
-rw-r--r-- | src/pipewire_audio.c | 1 | ||||
-rw-r--r-- | src/pipewire_video.c (renamed from src/pipewire.c) | 132 |
9 files changed, 140 insertions, 110 deletions
diff --git a/include/pipewire_audio.h b/include/pipewire_audio.h new file mode 100644 index 0000000..ae14cb3 --- /dev/null +++ b/include/pipewire_audio.h @@ -0,0 +1,4 @@ +#ifndef GSR_PIPEWIRE_AUDIO_H +#define GSR_PIPEWIRE_AUDIO_H + +#endif /* GSR_PIPEWIRE_AUDIO_H */ diff --git a/include/pipewire.h b/include/pipewire_video.h index 1908e2d..00e2835 100644 --- a/include/pipewire.h +++ b/include/pipewire_video.h @@ -1,5 +1,5 @@ -#ifndef GSR_PIPEWIRE_H -#define GSR_PIPEWIRE_H +#ifndef GSR_PIPEWIRE_VIDEO_H +#define GSR_PIPEWIRE_VIDEO_H #include <stdbool.h> #include <stdint.h> @@ -8,9 +8,9 @@ #include <spa/utils/hook.h> #include <spa/param/video/format.h> -#define GSR_PIPEWIRE_MAX_MODIFIERS 1024 -#define GSR_PIPEWIRE_NUM_VIDEO_FORMATS 6 -#define GSR_PIPEWIRE_DMABUF_MAX_PLANES 4 +#define GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS 1024 +#define GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS 6 +#define GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES 4 typedef struct gsr_egl gsr_egl; @@ -18,23 +18,23 @@ typedef struct { int major; int minor; int micro; -} gsr_pipewire_data_version; +} gsr_pipewire_video_data_version; typedef struct { uint32_t fps_num; uint32_t fps_den; -} gsr_pipewire_video_info; +} gsr_pipewire_video_video_info; typedef struct { int fd; uint32_t offset; int32_t stride; -} gsr_pipewire_dmabuf_data; +} gsr_pipewire_video_dmabuf_data; typedef struct { int x, y; int width, height; -} gsr_pipewire_region; +} gsr_pipewire_video_region; typedef struct { enum spa_video_format format; @@ -82,31 +82,31 @@ typedef struct { uint32_t width, height; } crop; - gsr_video_format supported_video_formats[GSR_PIPEWIRE_NUM_VIDEO_FORMATS]; + gsr_video_format supported_video_formats[GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS]; - gsr_pipewire_data_version server_version; - gsr_pipewire_video_info video_info; - gsr_pipewire_dmabuf_data dmabuf_data[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + 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_MAX_MODIFIERS]; + uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS]; size_t num_modifiers; -} gsr_pipewire; +} 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_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl); -void gsr_pipewire_deinit(gsr_pipewire *self); +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_DMABUF_MAX_PLANES in size */ -bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, gsr_pipewire_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image); -bool gsr_pipewire_is_damaged(gsr_pipewire *self); -void gsr_pipewire_clear_damage(gsr_pipewire *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_H */ +#endif /* GSR_PIPEWIRE_VIDEO_H */ diff --git a/meson.build b/meson.build index cd35625..f5c9214 100644 --- a/meson.build +++ b/meson.build @@ -57,20 +57,32 @@ dep = [ dependency('wayland-client'), ] +uses_pipewire = false + if get_option('portal') == true src += [ 'src/capture/portal.c', 'src/dbus.c', - 'src/pipewire.c', + 'src/pipewire_video.c', ] + dep += dependency('dbus-1') + add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp']) + uses_pipewire = true +endif +if get_option('app_audio') == true + src += [ + 'src/pipewire_audio.c', + ] + add_project_arguments('-DGSR_APP_AUDIO', language : ['c', 'cpp']) + uses_pipewire = true +endif + +if uses_pipewire == true dep += [ - dependency('dbus-1'), dependency('libpipewire-0.3'), dependency('libspa-0.2'), ] - - add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp']) endif add_project_arguments('-DGSR_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp']) diff --git a/meson_options.txt b/meson_options.txt index 61972c6..5936927 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ option('systemd', type : 'boolean', value : true, description : 'Install systemd service file') option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability to remove password prompt when recording monitor (without desktop portal option) on amd/intel or nvidia wayland') option('nvidia_suspend_fix', type : 'boolean', value : true, description : 'Install nvidia modprobe config file to tell nvidia driver to preserve video memory on suspend. This is a workaround for an nvidia driver bug that breaks cuda (and gpu screen recorder) on suspend') -option('portal', type : 'boolean', value : true, description : 'If GPU Screen Recorder should be built with support for xdg desktop portal ScreenCast capture (wayland only)') +option('portal', type : 'boolean', value : true, description : 'Build with support for xdg desktop portal ScreenCast capture (wayland only) (-w portal option)') +option('app_audio', type : 'boolean', value : true, description : 'Build with support for recording a single audio source (-aa option). Requires pipewire')
\ No newline at end of file diff --git a/project.conf b/project.conf index 586b44a..776df96 100644 --- a/project.conf +++ b/project.conf @@ -10,6 +10,7 @@ ignore_dirs = ["kms/server", "build", "debug-build"] [define] GSR_PORTAL = "1" +GSR_APP_AUDIO = "1" [dependencies] libavcodec = ">=58" diff --git a/src/capture/portal.c b/src/capture/portal.c index b04d5e7..a42bac0 100644 --- a/src/capture/portal.c +++ b/src/capture/portal.c @@ -3,7 +3,7 @@ #include "../../include/egl.h" #include "../../include/utils.h" #include "../../include/dbus.h" -#include "../../include/pipewire.h" +#include "../../include/pipewire_video.h" #include <stdlib.h> #include <stdio.h> @@ -20,9 +20,9 @@ typedef struct { gsr_dbus dbus; char *session_handle; - gsr_pipewire pipewire; + gsr_pipewire_video pipewire; vec2i capture_size; - gsr_pipewire_dmabuf_data dmabuf_data[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; + gsr_pipewire_video_dmabuf_data dmabuf_data[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES]; int num_dmabuf_data; AVCodecContext *video_codec_context; @@ -57,7 +57,7 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) { gsr_capture_portal_cleanup_plane_fds(self); - gsr_pipewire_deinit(&self->pipewire); + gsr_pipewire_video_deinit(&self->pipewire); if(self->session_handle) { free(self->session_handle); @@ -233,8 +233,8 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire } static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) { - gsr_pipewire_region region = {0, 0, 0, 0}; - gsr_pipewire_region cursor_region = {0, 0, 0, 0}; + gsr_pipewire_video_region region = {0, 0, 0, 0}; + gsr_pipewire_video_region cursor_region = {0, 0, 0, 0}; fprintf(stderr, "gsr info: gsr_capture_portal_start: waiting for pipewire negotiation\n"); const double start_time = clock_get_monotonic_seconds(); @@ -242,7 +242,7 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) { bool uses_external_image = false; uint32_t fourcc = 0; uint64_t modifiers = 0; - if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &fourcc, &modifiers, &uses_external_image)) { + if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &fourcc, &modifiers, &uses_external_image)) { gsr_capture_portal_cleanup_plane_fds(self); self->capture_size.x = region.width; self->capture_size.y = region.height; @@ -285,7 +285,7 @@ static int gsr_capture_portal_start(gsr_capture *cap, AVCodecContext *video_code fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n"); /* TODO: support hdr when pipewire supports it */ /* gsr_pipewire closes the pipewire fd, even on failure */ - if(!gsr_pipewire_init(&self->pipewire, pipewire_fd, pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) { + if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, video_codec_context->framerate.num, self->params.record_cursor, self->params.egl)) { fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node); gsr_capture_portal_stop(self); return -1; @@ -327,12 +327,12 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo gsr_capture_portal *self = cap->priv; /* TODO: Handle formats other than RGB(a) */ - gsr_pipewire_region region = {0, 0, 0, 0}; - gsr_pipewire_region cursor_region = {0, 0, 0, 0}; + gsr_pipewire_video_region region = {0, 0, 0, 0}; + gsr_pipewire_video_region cursor_region = {0, 0, 0, 0}; uint32_t pipewire_fourcc = 0; uint64_t pipewire_modifiers = 0; bool using_external_image = false; - if(gsr_pipewire_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &pipewire_fourcc, &pipewire_modifiers, &using_external_image)) { + if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, ®ion, &cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &pipewire_fourcc, &pipewire_modifiers, &using_external_image)) { if(region.width != self->capture_size.x || region.height != self->capture_size.y) { self->capture_size.x = region.width; self->capture_size.y = region.height; @@ -420,12 +420,12 @@ static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) { static bool gsr_capture_portal_is_damaged(gsr_capture *cap) { gsr_capture_portal *self = cap->priv; - return gsr_pipewire_is_damaged(&self->pipewire); + return gsr_pipewire_video_is_damaged(&self->pipewire); } static void gsr_capture_portal_clear_damage(gsr_capture *cap) { gsr_capture_portal *self = cap->priv; - gsr_pipewire_clear_damage(&self->pipewire); + gsr_pipewire_video_clear_damage(&self->pipewire); } static void gsr_capture_portal_destroy(gsr_capture *cap, AVCodecContext *video_codec_context) { diff --git a/src/main.cpp b/src/main.cpp index 0366b3e..35b5119 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -990,6 +990,8 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide // TODO: Enable multipass + // TODO: Set "usage" option to "record"/"stream" and "content" option to "rendered" for vulkan encoding + if(vendor == GSR_GPU_VENDOR_NVIDIA) { // TODO: These dont seem to be necessary // av_dict_set_int(&options, "zerolatency", 1, 0); @@ -1062,7 +1064,7 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide static void usage_header() { const bool inside_flatpak = getenv("FLATPAK_ID") != NULL; const char *program_name = inside_flatpak ? "flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder" : "gpu-screen-recorder"; - fprintf(stderr, "usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [-v yes|no] [--version] [-h|--help]\n", program_name); + fprintf(stderr, "usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-aa <application_name>] [-q <quality>] [-r <replay_buffer_size_sec>] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [-v yes|no] [--version] [-h|--help]\n", program_name); } // TODO: Update with portal info @@ -1100,6 +1102,15 @@ static void usage_full() { fprintf(stderr, " If the audio device is an empty string then the audio device is ignored.\n"); fprintf(stderr, " Optional, no audio track is added by default.\n"); fprintf(stderr, "\n"); +#ifdef GSR_APP_AUDIO + fprintf(stderr, " -aa Audio device to record from (pulse audio device). Can be specified multiple times. Each time this is specified a new audio track is added for the specified audio device.\n"); + fprintf(stderr, " A name can be given to the audio input device by prefixing the audio input with <name>/, for example \"dummy/alsa_output.pci-0000_00_1b.0.analog-stereo.monitor\".\n"); + fprintf(stderr, " Multiple audio devices can be merged into one audio track by using \"|\" as a separator into one -a argument, for example: -a \"alsa_output1|alsa_output2\".\n"); + fprintf(stderr, " The audio device can also be \"default_output\" in which case the default output device is used, or \"default_input\" in which case the default input device is used.\n"); + fprintf(stderr, " If the audio device is an empty string then the audio device is ignored.\n"); + fprintf(stderr, " Optional, no audio track is added by default.\n"); + fprintf(stderr, "\n"); +#endif fprintf(stderr, " -q Video quality. Should be either 'medium', 'high', 'very_high' or 'ultra' when using '-bm qp' or '-bm vbr' options, and '-bm qp' is the default option used.\n"); fprintf(stderr, " 'high' is the recommended option when live streaming or when you have a slower harddrive.\n"); fprintf(stderr, " When using '-bm cbr' option then this is option is instead used to specify the video bitrate in kbps.\n"); @@ -2120,7 +2131,7 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re if(!capture) _exit(1); #else - fprintf(stderr, "Error: option '-w portal' used but GPU Screen Recorder was compiled without desktop portal support\n"); + fprintf(stderr, "Error: option '-w portal' used but GPU Screen Recorder was compiled without desktop portal support. Please recompile GPU Screen recorder with the -Dportal=true option\n"); _exit(2); #endif } else if(contains_non_hex_number(window_str.c_str())) { @@ -2307,8 +2318,8 @@ static std::vector<MergedAudioInputs> parse_audio_inputs(const AudioDevices &aud fprintf(stderr, " default_output (Default output)\n"); if(!audio_devices.default_input.empty()) fprintf(stderr, " default_input (Default input)\n"); - for(const auto &audio_input : audio_devices.audio_inputs) { - fprintf(stderr, " %s (%s)\n", audio_input.name.c_str(), audio_input.description.c_str()); + for(const auto &audio_device_input : audio_devices.audio_inputs) { + fprintf(stderr, " %s (%s)\n", audio_device_input.name.c_str(), audio_device_input.description.c_str()); } _exit(2); } diff --git a/src/pipewire_audio.c b/src/pipewire_audio.c new file mode 100644 index 0000000..2c18432 --- /dev/null +++ b/src/pipewire_audio.c @@ -0,0 +1 @@ +#include "../include/pipewire_audio.h"
\ No newline at end of file diff --git a/src/pipewire.c b/src/pipewire_video.c index 3bf54db..b5f1562 100644 --- a/src/pipewire.c +++ b/src/pipewire_video.c @@ -1,4 +1,4 @@ -#include "../include/pipewire.h" +#include "../include/pipewire_video.h" #include "../include/egl.h" #include "../include/utils.h" @@ -13,7 +13,7 @@ /* This code is partially based on xr-video-player pipewire implementation which is based on obs-studio's pipewire implementation */ -/* TODO: Make gsr_pipewire_init asynchronous */ +/* TODO: Make gsr_pipewire_video_init asynchronous */ /* TODO: Support 10-bit capture (hdr) when pipewire supports it */ /* TODO: Test all of the image formats */ @@ -25,12 +25,12 @@ (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) + \ width * height * 4) -static bool parse_pw_version(gsr_pipewire_data_version *dst, const char *version) { +static bool parse_pw_version(gsr_pipewire_video_data_version *dst, const char *version) { const int n_matches = sscanf(version, "%d.%d.%d", &dst->major, &dst->minor, &dst->micro); return n_matches == 3; } -static bool check_pw_version(const gsr_pipewire_data_version *pw_version, int major, int minor, int micro) { +static bool check_pw_version(const gsr_pipewire_video_data_version *pw_version, int major, int minor, int micro) { if (pw_version->major != major) return pw_version->major > major; if (pw_version->minor != minor) @@ -38,7 +38,7 @@ static bool check_pw_version(const gsr_pipewire_data_version *pw_version, int ma return pw_version->micro >= micro; } -static void update_pw_versions(gsr_pipewire *self, const char *version) { +static void update_pw_versions(gsr_pipewire_video *self, const char *version) { fprintf(stderr, "gsr info: pipewire: server version: %s\n", version); fprintf(stderr, "gsr info: pipewire: library version: %s\n", pw_get_library_version()); fprintf(stderr, "gsr info: pipewire: header version: %s\n", pw_get_headers_version()); @@ -47,18 +47,18 @@ static void update_pw_versions(gsr_pipewire *self, const char *version) { } static void on_core_info_cb(void *user_data, const struct pw_core_info *info) { - gsr_pipewire *self = user_data; + gsr_pipewire_video *self = user_data; update_pw_versions(self, info->version); } static void on_core_error_cb(void *user_data, uint32_t id, int seq, int res, const char *message) { - gsr_pipewire *self = user_data; + gsr_pipewire_video *self = user_data; fprintf(stderr, "gsr error: pipewire: error id:%u seq:%d res:%d: %s\n", id, seq, res, message); pw_thread_loop_signal(self->thread_loop, false); } static void on_core_done_cb(void *user_data, uint32_t id, int seq) { - gsr_pipewire *self = user_data; + gsr_pipewire_video *self = user_data; if (id == PW_ID_CORE && self->server_version_sync == seq) pw_thread_loop_signal(self->thread_loop, false); } @@ -86,7 +86,7 @@ static const struct pw_core_events core_events = { }; static void on_process_cb(void *user_data) { - gsr_pipewire *self = user_data; + gsr_pipewire_video *self = user_data; struct spa_meta_cursor *cursor = NULL; //struct spa_meta *video_damage = NULL; @@ -122,8 +122,8 @@ static void on_process_cb(void *user_data) { } self->dmabuf_num_planes = buffer->n_datas; - if(self->dmabuf_num_planes > GSR_PIPEWIRE_DMABUF_MAX_PLANES) - self->dmabuf_num_planes = GSR_PIPEWIRE_DMABUF_MAX_PLANES; + if(self->dmabuf_num_planes > GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES) + self->dmabuf_num_planes = GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES; for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { self->dmabuf_data[i].fd = dup(buffer->datas[i].fd); @@ -206,7 +206,7 @@ read_metadata: } static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_pod *param) { - gsr_pipewire *self = user_data; + gsr_pipewire_video *self = user_data; if (!param || id != SPA_PARAM_Format) return; @@ -276,7 +276,7 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p static void on_state_changed_cb(void *user_data, enum pw_stream_state old, enum pw_stream_state state, const char *error) { (void)old; - gsr_pipewire *self = user_data; + gsr_pipewire_video *self = user_data; fprintf(stderr, "gsr info: pipewire: stream %p state: \"%s\" (error: %s)\n", (void*)self->stream, pw_stream_state_as_string(state), @@ -291,7 +291,7 @@ static const struct pw_stream_events stream_events = { }; static inline struct spa_pod *build_format(struct spa_pod_builder *b, - const gsr_pipewire_video_info *ovi, + const gsr_pipewire_video_video_info *ovi, uint32_t format, const uint64_t *modifiers, size_t modifier_count) { @@ -358,13 +358,13 @@ static const enum spa_video_format video_formats[] = { SPA_VIDEO_FORMAT_RGB, }; -static bool gsr_pipewire_build_format_params(gsr_pipewire *self, struct spa_pod_builder *pod_builder, struct spa_pod **params, uint32_t *num_params) { +static bool gsr_pipewire_video_build_format_params(gsr_pipewire_video *self, struct spa_pod_builder *pod_builder, struct spa_pod **params, uint32_t *num_params) { *num_params = 0; if(!check_pw_version(&self->server_version, 0, 3, 33)) return false; - for(size_t i = 0; i < GSR_PIPEWIRE_NUM_VIDEO_FORMATS; i++) { + for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) { if(self->supported_video_formats[i].modifiers_size == 0) continue; params[i] = build_format(pod_builder, &self->video_info, self->supported_video_formats[i].format, self->modifiers + self->supported_video_formats[i].modifiers_index, self->supported_video_formats[i].modifiers_size); @@ -376,15 +376,15 @@ static bool gsr_pipewire_build_format_params(gsr_pipewire *self, struct spa_pod_ static void renegotiate_format(void *data, uint64_t expirations) { (void)expirations; - gsr_pipewire *self = (gsr_pipewire*)data; + gsr_pipewire_video *self = (gsr_pipewire_video*)data; pw_thread_loop_lock(self->thread_loop); - struct spa_pod *params[GSR_PIPEWIRE_NUM_VIDEO_FORMATS]; + struct spa_pod *params[GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS]; uint32_t num_video_formats = 0; uint8_t params_buffer[2048]; struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); - if (!gsr_pipewire_build_format_params(self, &pod_builder, params, &num_video_formats)) { + if (!gsr_pipewire_video_build_format_params(self, &pod_builder, params, &num_video_formats)) { pw_thread_loop_unlock(self->thread_loop); return; } @@ -393,7 +393,7 @@ static void renegotiate_format(void *data, uint64_t expirations) { pw_thread_loop_unlock(self->thread_loop); } -static bool spa_video_format_get_modifiers(gsr_pipewire *self, const enum spa_video_format format, uint64_t *modifiers, int32_t max_modifiers, int32_t *num_modifiers) { +static bool spa_video_format_get_modifiers(gsr_pipewire_video *self, const enum spa_video_format format, uint64_t *modifiers, int32_t max_modifiers, int32_t *num_modifiers) { *num_modifiers = 0; if(max_modifiers == 0) { @@ -430,36 +430,36 @@ static bool spa_video_format_get_modifiers(gsr_pipewire *self, const enum spa_vi return true; } -static void gsr_pipewire_init_modifiers(gsr_pipewire *self) { - for(size_t i = 0; i < GSR_PIPEWIRE_NUM_VIDEO_FORMATS; i++) { +static void gsr_pipewire_video_init_modifiers(gsr_pipewire_video *self) { + for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) { self->supported_video_formats[i].format = video_formats[i]; int32_t num_modifiers = 0; - spa_video_format_get_modifiers(self, self->supported_video_formats[i].format, self->modifiers + self->num_modifiers, GSR_PIPEWIRE_MAX_MODIFIERS - self->num_modifiers, &num_modifiers); + spa_video_format_get_modifiers(self, self->supported_video_formats[i].format, self->modifiers + self->num_modifiers, GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS - self->num_modifiers, &num_modifiers); self->supported_video_formats[i].modifiers_index = self->num_modifiers; self->supported_video_formats[i].modifiers_size = num_modifiers; } } -static bool gsr_pipewire_setup_stream(gsr_pipewire *self) { - struct spa_pod *params[GSR_PIPEWIRE_NUM_VIDEO_FORMATS]; +static bool gsr_pipewire_video_setup_stream(gsr_pipewire_video *self) { + struct spa_pod *params[GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS]; uint32_t num_video_formats = 0; uint8_t params_buffer[2048]; struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT(params_buffer, sizeof(params_buffer)); self->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL); if(!self->thread_loop) { - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to create pipewire thread\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create pipewire thread\n"); goto error; } self->context = pw_context_new(pw_thread_loop_get_loop(self->thread_loop), NULL, 0); if(!self->context) { - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to create pipewire context\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create pipewire context\n"); goto error; } if(pw_thread_loop_start(self->thread_loop) < 0) { - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to start thread\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to start thread\n"); goto error; } @@ -469,20 +469,20 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) { self->core = pw_context_connect_fd(self->context, fcntl(self->fd, F_DUPFD_CLOEXEC, 5), NULL, 0); if(!self->core) { pw_thread_loop_unlock(self->thread_loop); - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to connect to fd %d\n", self->fd); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to connect to fd %d\n", self->fd); goto error; } // TODO: Error check pw_core_add_listener(self->core, &self->core_listener, &core_events, self); - gsr_pipewire_init_modifiers(self); + gsr_pipewire_video_init_modifiers(self); // TODO: Cleanup? self->reneg = pw_loop_add_event(pw_thread_loop_get_loop(self->thread_loop), renegotiate_format, self); if(!self->reneg) { pw_thread_loop_unlock(self->thread_loop); - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: pw_loop_add_event failed\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: pw_loop_add_event failed\n"); goto error; } @@ -495,14 +495,14 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) { PW_KEY_MEDIA_ROLE, "Screen", NULL)); if(!self->stream) { pw_thread_loop_unlock(self->thread_loop); - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to create stream\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to create stream\n"); goto error; } pw_stream_add_listener(self->stream, &self->stream_listener, &stream_events, self); - if(!gsr_pipewire_build_format_params(self, &pod_builder, params, &num_video_formats)) { + if(!gsr_pipewire_video_build_format_params(self, &pod_builder, params, &num_video_formats)) { pw_thread_loop_unlock(self->thread_loop); - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to build format params\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to build format params\n"); goto error; } @@ -512,7 +512,7 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) { num_video_formats) < 0) { pw_thread_loop_unlock(self->thread_loop); - fprintf(stderr, "gsr error: gsr_pipewire_setup_stream: failed to connect stream\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_setup_stream: failed to connect stream\n"); goto error; } @@ -549,7 +549,7 @@ static bool gsr_pipewire_setup_stream(gsr_pipewire *self) { } static int pw_init_counter = 0; -bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl) { +bool gsr_pipewire_video_init(gsr_pipewire_video *self, int pipewire_fd, uint32_t pipewire_node, int fps, bool capture_cursor, gsr_egl *egl) { if(pw_init_counter == 0) pw_init(NULL, NULL); ++pw_init_counter; @@ -559,8 +559,8 @@ bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_no self->fd = pipewire_fd; self->node = pipewire_node; if(pthread_mutex_init(&self->mutex, NULL) != 0) { - fprintf(stderr, "gsr error: gsr_pipewire_init: failed to initialize mutex\n"); - gsr_pipewire_deinit(self); + fprintf(stderr, "gsr error: gsr_pipewire_video_init: failed to initialize mutex\n"); + gsr_pipewire_video_deinit(self); return false; } self->mutex_initialized = true; @@ -568,15 +568,15 @@ bool gsr_pipewire_init(gsr_pipewire *self, int pipewire_fd, uint32_t pipewire_no self->video_info.fps_den = 1; self->cursor.visible = capture_cursor; - if(!gsr_pipewire_setup_stream(self)) { - gsr_pipewire_deinit(self); + if(!gsr_pipewire_video_setup_stream(self)) { + gsr_pipewire_video_deinit(self); return false; } return true; } -void gsr_pipewire_deinit(gsr_pipewire *self) { +void gsr_pipewire_video_deinit(gsr_pipewire_video *self) { if(self->thread_loop) { //pw_thread_loop_wait(self->thread_loop); pw_thread_loop_stop(self->thread_loop); @@ -636,7 +636,7 @@ void gsr_pipewire_deinit(gsr_pipewire *self) { } } -static EGLImage gsr_pipewire_create_egl_image(gsr_pipewire *self, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, bool use_modifiers) { +static EGLImage gsr_pipewire_video_create_egl_image(gsr_pipewire_video *self, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, bool use_modifiers) { intptr_t img_attr[44]; setup_dma_buf_attrs(img_attr, spa_video_format_to_drm_format(self->format.info.raw.format), self->format.info.raw.size.width, self->format.info.raw.size.height, fds, offsets, pitches, modifiers, self->dmabuf_num_planes, use_modifiers); @@ -650,11 +650,11 @@ static EGLImage gsr_pipewire_create_egl_image(gsr_pipewire *self, const int *fds return image; } -static EGLImage gsr_pipewire_create_egl_image_with_fallback(gsr_pipewire *self) { - int fds[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; - uint32_t offsets[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; - uint32_t pitches[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; - uint64_t modifiers[GSR_PIPEWIRE_DMABUF_MAX_PLANES]; +static EGLImage gsr_pipewire_video_create_egl_image_with_fallback(gsr_pipewire_video *self) { + int fds[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES]; + uint32_t offsets[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES]; + uint32_t pitches[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES]; + uint64_t modifiers[GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES]; for(size_t i = 0; i < self->dmabuf_num_planes; ++i) { fds[i] = self->dmabuf_data[i].fd; offsets[i] = self->dmabuf_data[i].offset; @@ -664,19 +664,19 @@ static EGLImage gsr_pipewire_create_egl_image_with_fallback(gsr_pipewire *self) EGLImage image = NULL; if(self->no_modifiers_fallback) { - image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false); + image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, false); } else { - image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, true); + image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, true); if(!image) { - fprintf(stderr, "gsr error: gsr_pipewire_create_egl_image_with_fallback: failed to create egl image with modifiers, trying without modifiers\n"); + fprintf(stderr, "gsr error: gsr_pipewire_video_create_egl_image_with_fallback: failed to create egl image with modifiers, trying without modifiers\n"); self->no_modifiers_fallback = true; - image = gsr_pipewire_create_egl_image(self, fds, offsets, pitches, modifiers, false); + image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, false); } } return image; } -static bool gsr_pipewire_bind_image_to_texture(gsr_pipewire *self, EGLImage image, unsigned int texture_id, bool external_texture) { +static bool gsr_pipewire_video_bind_image_to_texture(gsr_pipewire_video *self, EGLImage image, unsigned int texture_id, bool external_texture) { const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D; while(self->egl->glGetError() != 0){} self->egl->glBindTexture(texture_target, texture_id); @@ -686,19 +686,19 @@ static bool gsr_pipewire_bind_image_to_texture(gsr_pipewire *self, EGLImage imag return success; } -static void gsr_pipewire_bind_image_to_texture_with_fallback(gsr_pipewire *self, gsr_texture_map texture_map, EGLImage image) { +static void gsr_pipewire_video_bind_image_to_texture_with_fallback(gsr_pipewire_video *self, gsr_texture_map texture_map, EGLImage image) { if(self->external_texture_fallback) { - gsr_pipewire_bind_image_to_texture(self, image, texture_map.external_texture_id, true); + gsr_pipewire_video_bind_image_to_texture(self, image, texture_map.external_texture_id, true); } else { - if(!gsr_pipewire_bind_image_to_texture(self, image, texture_map.texture_id, false)) { - fprintf(stderr, "gsr error: gsr_pipewire_map_texture: failed to bind image to texture, trying with external texture\n"); + if(!gsr_pipewire_video_bind_image_to_texture(self, image, texture_map.texture_id, false)) { + fprintf(stderr, "gsr error: gsr_pipewire_video_map_texture: failed to bind image to texture, trying with external texture\n"); self->external_texture_fallback = true; - gsr_pipewire_bind_image_to_texture(self, image, texture_map.external_texture_id, true); + gsr_pipewire_video_bind_image_to_texture(self, image, texture_map.external_texture_id, true); } } } -static void gsr_pipewire_update_cursor_texture(gsr_pipewire *self, gsr_texture_map texture_map) { +static void gsr_pipewire_video_update_cursor_texture(gsr_pipewire_video *self, gsr_texture_map texture_map) { if(!self->cursor.data) return; @@ -715,9 +715,9 @@ static void gsr_pipewire_update_cursor_texture(gsr_pipewire *self, gsr_texture_m self->cursor.data = NULL; } -bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, gsr_pipewire_region *region, gsr_pipewire_region *cursor_region, gsr_pipewire_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image) { - for(int i = 0; i < GSR_PIPEWIRE_DMABUF_MAX_PLANES; ++i) { - memset(&dmabuf_data[i], 0, sizeof(gsr_pipewire_dmabuf_data)); +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) { + for(int i = 0; i < GSR_PIPEWIRE_VIDEO_DMABUF_MAX_PLANES; ++i) { + memset(&dmabuf_data[i], 0, sizeof(gsr_pipewire_video_dmabuf_data)); } *num_dmabuf_data = 0; *using_external_image = self->external_texture_fallback; @@ -730,14 +730,14 @@ bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, g return false; } - EGLImage image = gsr_pipewire_create_egl_image_with_fallback(self); + EGLImage image = gsr_pipewire_video_create_egl_image_with_fallback(self); if(image) { - gsr_pipewire_bind_image_to_texture_with_fallback(self, texture_map, image); + gsr_pipewire_video_bind_image_to_texture_with_fallback(self, texture_map, image); *using_external_image = self->external_texture_fallback; self->egl->eglDestroyImage(self->egl->egl_display, image); } - gsr_pipewire_update_cursor_texture(self, texture_map); + gsr_pipewire_video_update_cursor_texture(self, texture_map); region->x = 0; region->y = 0; @@ -773,7 +773,7 @@ bool gsr_pipewire_map_texture(gsr_pipewire *self, gsr_texture_map texture_map, g return true; } -bool gsr_pipewire_is_damaged(gsr_pipewire *self) { +bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self) { bool damaged = false; pthread_mutex_lock(&self->mutex); damaged = self->damaged; @@ -781,7 +781,7 @@ bool gsr_pipewire_is_damaged(gsr_pipewire *self) { return damaged; } -void gsr_pipewire_clear_damage(gsr_pipewire *self) { +void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self) { pthread_mutex_lock(&self->mutex); self->damaged = false; pthread_mutex_unlock(&self->mutex); |