aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMaria Lisina <sekoohaka.sarisan@gmail.com>2026-06-02 23:49:13 +0300
committerMaria Lisina <sekoohaka.sarisan@gmail.com>2026-06-02 23:49:13 +0300
commit319eb4f1a6746feb824ef46547ca1f99f4a10233 (patch)
tree3da2c4f1f4cbdc6b3148caf391e7d864d751b7e6
parentf550425e361bba38600867f9eab4f0849d22c9d8 (diff)
parent227e44a649d7c19a4b25f071da7fffb1f67e3385 (diff)
Merge tag '5.13.9' of https://repo.dec05eba.com/gpu-screen-recorder
-rw-r--r--README.md13
-rw-r--r--TODO5
-rw-r--r--gpu-screen-recorder.112
-rw-r--r--include/args_parser.h1
-rw-r--r--include/plugins.h2
-rw-r--r--include/wayland_host_bridge.h7
-rw-r--r--meson.build4
-rw-r--r--plugin/examples/hello_triangle/triangle.c10
-rw-r--r--plugin/plugin.h6
-rw-r--r--project.conf3
-rw-r--r--src/args_parser.c8
-rw-r--r--src/main.cpp81
-rw-r--r--src/pipewire_video.c18
-rw-r--r--src/plugins.c18
-rw-r--r--src/wayland_host_bridge.c109
-rw-r--r--src/window/wayland.c3
16 files changed, 237 insertions, 63 deletions
diff --git a/README.md b/README.md
index 898332b..4ce7221 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,8 @@ The only official ways to install GPU Screen Recorder is either from source, arc
If you install GPU Screen Recorder from somewhere else and have an issue then try installing it from one of the official sources before reporting it as an issue.\
If you still prefer to install GPU Screen Recorder with a package manager instead of from source or as a flatpak then you may be able to find a package for your distro.\
Here are some known unofficial packages:
-* Debian/Ubuntu: [Pacstall](https://pacstall.dev/packages/gpu-screen-recorder)
+* Ubuntu: [gpu-screen-recorder-cli](https://launchpad.net/ubuntu/+source/gpu-screen-recorder)
+* Debian: [gpu-screen-recorder-cli](https://tracker.debian.org/pkg/gpu-screen-recorder)
* Nix: [NixOS wiki](https://wiki.nixos.org/wiki/Gpu-screen-recorder)
* openSUSE: [openSUSE software repository](https://software.opensuse.org/package/gpu-screen-recorder)
* Fedora, CentOS: [Copr](https://copr.fedorainfracloud.org/coprs/brycensranch/gpu-screen-recorder-git/)
@@ -121,6 +122,11 @@ By default videos are recorded with constant quality, but with replay mode you m
Streaming works the same way as recording, but the `-o` argument should be path to the live streaming service you want to use (including your live streaming key). Take a look at `scripts/twitch-stream.sh` to see an example of how to stream to twitch.\
GPU Screen Recorder uses Ffmpeg so GPU Screen Recorder supports all protocols that Ffmpeg supports.\
If you want to reduce latency one thing you can do is to use the `-keyint` option, for example `-keyint 0.5`. Lower value means lower latency at the cost of increased bitrate/decreased quality.
+## Very low latency streaming (WHIP)
+FFmpeg since version 7.1 supports the WHIP protocol for very low latency streaming (milliseconds latency). Here is a simple way to set it up:
+1. Download the latest [mediamtx](https://github.com/bluenviron/mediamtx/releases/) binary and run it (just run `./mediamtx`)
+2. Run `gpu-screen-recorder` with WHIP, for example: `gpu-screen-recorder -w screen -f 60 -k h264 -bm cbr -q 20000 -a default_output -ac opus -c whip -o "http://localhost:8889/mystream/whip"`.
+ If you have a high refresh rate monitor you can set `-f 60` framerate option to your monitors framerate (for example `-f 144`) to have even lower latency.
## Recording while using replay/streaming
You can record a regular video while using replay/streaming by launching GPU Screen Recorder with the `-ro` option to specify a directory where to save the recording (for example: `gpu-screen-recorder -w screen -c mp4 -r 60 -o "$HOME/Videos/replays" -ro "$HOME/Videos/recordings"`).\
To start/stop (and save) recording use the SIGRTMIN signal, for example `pkill -SIGRTMIN -f "^gpu-screen-recorder"`. The path to the video will be displayed in stdout when saving the video.\
@@ -275,3 +281,8 @@ This is an issue in some broken video players such as vlc. Play the video with a
This may be caused by buggy GPU drivers. Try recording with HEVC video codec instead (`-k hevc`).
## The quality is low when recording my desktop
Color gradients encode badly, especially with h264 video codec. You can increase the bitrate, for example by using `-bm cbr -q 30000` or by using hevc (`-k hevc`) and increasing the quality (`-q ultra`).
+## My system stutters sometimes while recording on nvidia
+This is an nvidia power management driver bug which can happen when the system is idle. You can change the gpu power performance level to "performance" in nvidia settings to fix this.
+## Recording fails when using webcam on nvidia X11
+This is a known issue. The issue is that nvfbc which is used for screen capture only supports glx on older systems and glx is not compatible with the webcam capture method used in GPU Screen Recorder.
+This will be fixed in the future.
diff --git a/TODO b/TODO
index 935388e..933df3a 100644
--- a/TODO
+++ b/TODO
@@ -422,3 +422,8 @@ Make -fm content the default, but first make it record at no lower than 2 fps if
Fix camera capture on nvidia x11. It doesn't work with mjpeg either now because external shader compilation fails.
Revert the revert of improving pipewire capture, for better cursor capture. It caused glitches for some, especially in kde plasma when hdr is enabled. Try marking test and moving the cursor and see.
+
+Add the option to use application audio with exectable name, or automatically check against both application name and executable name.
+ In the ui it should also list the application name beside the app audio.
+
+Region capture works incorrectly on gnome with hidpi, especially with 150% scaling. Same in gsr ui.
diff --git a/gpu-screen-recorder.1 b/gpu-screen-recorder.1
index e511165..40a3b3e 100644
--- a/gpu-screen-recorder.1
+++ b/gpu-screen-recorder.1
@@ -484,7 +484,15 @@ Instant replay (last 60 seconds):
.PP
.nf
.RS
-gpu-screen-recorder -w screen -c mkv -r 60 -o ~/Videos
+gpu-screen-recorder -w screen -c mp4 -r 60 -o ~/Videos
+.RE
+.fi
+.PP
+Instant replay with constant framerate (recommended for predicable ram usage):
+.PP
+.nf
+.RS
+gpu-screen-recorder -w screen -c mp4 -r 60 -bm cbr -q 40000 -o ~/Videos
.RE
.fi
.PP
@@ -508,7 +516,7 @@ Instant replay and launch a script when saving replay:
.PP
.nf
.RS
-gpu-screen-recorder -w screen -c mkv -r 60 -sc ./script.sh -o ~/Videos
+gpu-screen-recorder -w screen -c mp4 -r 60 -sc ./script.sh -o ~/Videos
.RE
.fi
.PP
diff --git a/include/args_parser.h b/include/args_parser.h
index 75d8bfa..2208a9a 100644
--- a/include/args_parser.h
+++ b/include/args_parser.h
@@ -98,6 +98,7 @@ typedef struct {
bool restore_portal_session;
bool restart_replay_on_save;
bool write_first_frame_ts;
+ bool is_replaying;
bool is_livestream;
bool is_output_piped;
bool low_latency_recording;
diff --git a/include/plugins.h b/include/plugins.h
index 7588b61..db10336 100644
--- a/include/plugins.h
+++ b/include/plugins.h
@@ -34,5 +34,7 @@ void gsr_plugins_deinit(gsr_plugins *self);
bool gsr_plugins_load_plugin(gsr_plugins *self, const char *plugin_filepath);
void gsr_plugins_draw(gsr_plugins *self);
+bool gsr_plugins_is_damaged(gsr_plugins *self);
+void gsr_plugins_clear_damage(gsr_plugins *self);
#endif /* GSR_PLUGINS_H */
diff --git a/include/wayland_host_bridge.h b/include/wayland_host_bridge.h
new file mode 100644
index 0000000..9cf6d63
--- /dev/null
+++ b/include/wayland_host_bridge.h
@@ -0,0 +1,7 @@
+#ifndef GSR_WAYLAND_HOST_BRIDGE_H
+#define GSR_WAYLAND_HOST_BRIDGE_H
+
+struct wl_display;
+struct wl_display *wayland_connect_to_host();
+
+#endif /* GSR_WAYLAND_HOST_BRIDGE_H */
diff --git a/meson.build b/meson.build
index 4f62972..70b02e5 100644
--- a/meson.build
+++ b/meson.build
@@ -1,4 +1,4 @@
-project('gpu-screen-recorder', ['c', 'cpp'], version : '5.13.8', default_options : ['warning_level=2'])
+project('gpu-screen-recorder', ['c', 'cpp'], version : '5.13.9', default_options : ['warning_level=2'])
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
if get_option('buildtype') == 'debug'
@@ -43,6 +43,7 @@ src = [
'src/args_parser.c',
'src/defs.c',
'src/plugins.c',
+ 'src/wayland_host_bridge.c',
'src/sound.cpp',
'src/main.cpp',
]
@@ -72,7 +73,6 @@ dep = [
dependency('libdrm'),
dependency('wayland-egl'),
dependency('wayland-client'),
- dependency('vulkan'),
]
if build_machine.system() == 'linux'
diff --git a/plugin/examples/hello_triangle/triangle.c b/plugin/examples/hello_triangle/triangle.c
index 194cf92..fd27a3d 100644
--- a/plugin/examples/hello_triangle/triangle.c
+++ b/plugin/examples/hello_triangle/triangle.c
@@ -62,6 +62,14 @@ static void draw(const gsr_plugin_draw_params *params, void *userdata) {
++triangle->counter;
}
+static bool is_damaged(void *userdata) {
+ return true;
+}
+
+static void clear_damage(void *userdata) {
+
+}
+
bool gsr_plugin_init(const gsr_plugin_init_params *params, gsr_plugin_init_return *ret) {
Triangle *triangle = calloc(1, sizeof(Triangle));
if(!triangle)
@@ -98,6 +106,8 @@ bool gsr_plugin_init(const gsr_plugin_init_params *params, gsr_plugin_init_retur
ret->version = 1;
ret->userdata = triangle;
ret->draw = draw;
+ ret->is_damaged = is_damaged;
+ ret->clear_damage = clear_damage;
return true;
}
diff --git a/plugin/plugin.h b/plugin/plugin.h
index 902d1d7..769a629 100644
--- a/plugin/plugin.h
+++ b/plugin/plugin.h
@@ -43,6 +43,12 @@ typedef struct {
/* Optional, called when the plugin is expected to draw something to the current framebuffer */
void (*draw)(const gsr_plugin_draw_params *params, void *userdata);
+ /* Optional, returns false if this function is NULL. If this function is defined then this should return true
+ if there is an update visually that the plugin wants to display. This is useful when used with the "content" framerate mode
+ which only captures a frame when the frame has changed */
+ bool (*is_damaged)(void *userdata);
+ /* Optional */
+ void (*clear_damage)(void *userdata);
} gsr_plugin_init_return;
/* The plugin is expected to implement these functions and export them: */
diff --git a/project.conf b/project.conf
index b890eac..e6e43eb 100644
--- a/project.conf
+++ b/project.conf
@@ -1,7 +1,7 @@
[package]
name = "gpu-screen-recorder"
type = "executable"
-version = "5.13.8"
+version = "5.13.9"
platforms = ["posix"]
[config]
@@ -33,4 +33,3 @@ wayland-client = ">=1"
dbus-1 = ">=1"
libpipewire-0.3 = ">=1"
libspa-0.2 = ">=0"
-vulkan = ">=1"
diff --git a/src/args_parser.c b/src/args_parser.c
index 658e559..1957f32 100644
--- a/src/args_parser.c
+++ b/src/args_parser.c
@@ -398,18 +398,18 @@ static bool args_parser_set_values(args_parser *self) {
if(self->container_format && strcmp(self->container_format, "mkv") == 0)
self->container_format = "matroska";
- const bool is_replaying = self->replay_buffer_size_secs != -1;
+ self->is_replaying = self->replay_buffer_size_secs != -1;
self->is_livestream = false;
self->filename = args_get_value_by_key(self->args, NUM_ARGS, "-o");
if(self->filename) {
self->is_livestream = is_livestream_path(self->filename);
if(self->is_livestream) {
- if(is_replaying) {
+ if(self->is_replaying) {
fprintf(stderr, "gsr error: replay mode is not applicable to live streaming\n");
return false;
}
} else {
- if(!is_replaying) {
+ if(!self->is_replaying) {
char directory_buf[PATH_MAX];
snprintf(directory_buf, sizeof(directory_buf), "%s", self->filename);
char *directory = dirname(directory_buf);
@@ -435,7 +435,7 @@ static bool args_parser_set_values(args_parser *self) {
}
}
} else {
- if(!is_replaying) {
+ if(!self->is_replaying) {
self->filename = "/dev/stdout";
} else {
fprintf(stderr, "gsr error: Option -o is required when using option -r\n");
diff --git a/src/main.cpp b/src/main.cpp
index c2a5516..e4d1cbe 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1039,19 +1039,23 @@ static bool add_hdr_metadata_to_video_stream(gsr_capture *cap, AVStream *video_s
}
static void set_format_context_options(AVFormatContext *av_format_context) {
- bool hybrid_fragmented_available = false;
- av_opt_set(av_format_context->priv_data, "use_editlist", "1", 0);
- const AVOption *opt = av_opt_find(av_format_context->priv_data, "movflags", NULL, 0, 0);
- if (opt && opt->unit) {
- const AVOption *flag = av_opt_find(av_format_context->priv_data, "hybrid_fragmented", opt->unit, 0, 0);
- if (flag) {
- hybrid_fragmented_available = true;
- av_opt_set(av_format_context->priv_data, "movflags", "+hybrid_fragmented", 0);
+ if(LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(62, 6, 101)) {
+ bool hybrid_fragmented_available = false;
+ av_opt_set(av_format_context->priv_data, "use_editlist", "1", 0);
+ const AVOption *opt = av_opt_find(av_format_context->priv_data, "movflags", NULL, 0, 0);
+ if (opt && opt->unit) {
+ const AVOption *flag = av_opt_find(av_format_context->priv_data, "hybrid_fragmented", opt->unit, 0, 0);
+ if (flag) {
+ hybrid_fragmented_available = true;
+ av_opt_set(av_format_context->priv_data, "movflags", "+hybrid_fragmented", 0);
+ }
}
- }
- if(!hybrid_fragmented_available)
- fprintf(stderr, "gsr warning: hybrid_fragmented movflags is not available in your FFmpeg version. If you experience stutter in the mp4 file then update your FFmpeg or record to a mkv file\n");
+ if(!hybrid_fragmented_available)
+ fprintf(stderr, "gsr warning: hybrid_fragmented movflags is not available in your FFmpeg version. If you experience stutter in the mp4 file then update your FFmpeg to at least version 8 or record to a mkv file\n");
+ } else {
+ fprintf(stderr, "gsr warning: your FFmpeg version is known to be buggy (it doesn't have working hybrid_fragmented movflags). If you experience stutter in the mp4 file then update your FFmpeg to at least version 8 or record to a mkv file\n");
+ }
}
struct RecordingStartAudio {
@@ -3022,7 +3026,7 @@ static gsr_audio_codec select_audio_codec_with_fallback(gsr_audio_codec audio_co
break;
}
case GSR_AUDIO_CODEC_OPUS: {
- if(file_extension != "mp4" && file_extension != "mkv" && file_extension != "webm" && file_extension != "ts") {
+ if(file_extension != "mp4" && file_extension != "mkv" && file_extension != "webm" && file_extension != "ts" && file_extension != "whip") {
//audio_codec_to_use = "aac";
audio_codec = GSR_AUDIO_CODEC_AAC;
fprintf(stderr, "gsr warning: opus audio codec is only supported by .mp4, .mkv, .webm and .ts files, falling back to aac instead\n");
@@ -3489,30 +3493,18 @@ static bool get_image_format_from_filename(const char *filename, gsr_image_forma
}
}
-// TODO: replace this with start_recording_create_steams
-static bool av_open_file_write_header(AVFormatContext *av_format_context, const char *filename, const char *ffmpeg_opts) {
- int ret = avio_open(&av_format_context->pb, filename, AVIO_FLAG_WRITE);
- if(ret < 0) {
- fprintf(stderr, "gsr error: Could not open '%s': %s\n", filename, av_error_to_string(ret));
- return false;
- }
-
+static void av_write_header(AVFormatContext *av_format_context, const char *ffmpeg_opts) {
AVDictionary *options = nullptr;
av_dict_set(&options, "strict", "experimental", 0);
if(ffmpeg_opts)
av_dict_parse_string(&options, ffmpeg_opts, "=", ";", 0);
- ret = avformat_write_header(av_format_context, &options);
+ const int ret = avformat_write_header(av_format_context, &options);
if(ret < 0)
fprintf(stderr, "Error occurred when writing header to output file: %s\n", av_error_to_string(ret));
- const bool success = ret >= 0;
- if(!success)
- avio_close(av_format_context->pb);
-
av_dict_free(&options);
- return success;
}
static int audio_codec_get_frame_size(gsr_audio_codec audio_codec) {
@@ -3620,6 +3612,9 @@ static void validate_args_with_capture_sources(args_parser &arg_parser, const st
}
static void install_cuda_no_stable_perf_limit() {
+ if(access("/proc/driver/nvidia/version", F_OK) != 0)
+ return;
+
const char *home = getenv("HOME");
if(!home) {
fprintf(stderr, "gsr warning: install_cuda_no_stable_perf_limit: $HOME not set\n");
@@ -3776,8 +3771,6 @@ int main(int argc, char **argv) {
validate_merged_audio_inputs_app_audio(requested_audio_inputs, app_audio_names);
- const bool is_replaying = arg_parser.replay_buffer_size_secs != -1;
-
bool wayland = false;
Display *dpy = XOpenDisplay(nullptr);
if(dpy) {
@@ -3892,6 +3885,9 @@ int main(int argc, char **argv) {
file_extension = file_extension.substr(0, comma_index);
}
+ if(file_extension.empty())
+ file_extension = arg_parser.container_format ? arg_parser.container_format : "";
+
const bool force_no_audio_offset = arg_parser.is_livestream || arg_parser.is_output_piped || (file_extension != "mp4" && file_extension != "mkv" && file_extension != "webm");
const double target_fps = 1.0 / (double)arg_parser.fps;
@@ -3923,7 +3919,7 @@ int main(int argc, char **argv) {
const enum AVPixelFormat video_pix_fmt = get_pixel_format(arg_parser.video_codec, egl.gpu_info.vendor, arg_parser.video_encoder == GSR_VIDEO_ENCODER_HW_CPU);
AVCodecContext *video_codec_context = create_video_codec_context(video_pix_fmt, video_codec_f, egl, arg_parser, video_size.x, video_size.y);
- if(!is_replaying)
+ if(!arg_parser.is_replaying)
video_stream = create_stream(av_format_context, video_codec_context);
AVFrame *video_frame = av_frame_alloc();
@@ -4010,7 +4006,7 @@ int main(int argc, char **argv) {
AVCodecContext *audio_codec_context = create_audio_codec_context(arg_parser.fps, arg_parser.audio_codec, use_amix, arg_parser.audio_bitrate);
AVStream *audio_stream = nullptr;
- if(!is_replaying) {
+ if(!arg_parser.is_replaying) {
audio_stream = create_stream(av_format_context, audio_codec_context);
if(gsr_encoder_add_recording_destination(&encoder, audio_codec_context, av_format_context, audio_stream, 0) == (size_t)-1)
fprintf(stderr, "gsr error: added too many audio sources\n");
@@ -4076,11 +4072,17 @@ int main(int argc, char **argv) {
//av_dump_format(av_format_context, 0, filename, 1);
- if(!is_replaying) {
- if(!av_open_file_write_header(av_format_context, arg_parser.filename, arg_parser.ffmpeg_opts))
+ if(!arg_parser.is_replaying && !(output_format->flags & AVFMT_NOFILE)) {
+ const int ret = avio_open(&av_format_context->pb, arg_parser.filename, AVIO_FLAG_WRITE);
+ if(ret < 0) {
+ fprintf(stderr, "gsr error: Could not open '%s': %s\n", arg_parser.filename, av_error_to_string(ret));
_exit(1);
+ }
}
+ if(!arg_parser.is_replaying)
+ av_write_header(av_format_context, arg_parser.ffmpeg_opts);
+
double fps_start_time = clock_get_monotonic_seconds();
//double frame_timer_start = fps_start_time;
int fps_counter = 0;
@@ -4388,6 +4390,8 @@ int main(int argc, char **argv) {
damaged = true;
}
+ damaged |= gsr_plugins_is_damaged(&plugins);
+
// TODO: Readd wayland sync warning when removing this
if(arg_parser.framerate_mode != GSR_FRAMERATE_MODE_CONTENT)
damaged = true;
@@ -4417,6 +4421,7 @@ int main(int argc, char **argv) {
egl.glClear(0);
gsr_damage_clear(&damage);
+ gsr_plugins_clear_damage(&plugins);
gsr_capture_kms_cleanup_kms_fds();
if(kms_client_initialized) {
@@ -4471,7 +4476,7 @@ int main(int argc, char **argv) {
gsr_video_encoder_copy_textures_to_frame(video_encoder, video_frame, output_color_conversion);
for(VideoSource &video_source : video_sources) {
- if(hdr && !hdr_metadata_set && !is_replaying && add_hdr_metadata_to_video_stream(video_source.capture, video_stream))
+ if(hdr && !hdr_metadata_set && !arg_parser.is_replaying && add_hdr_metadata_to_video_stream(video_source.capture, video_stream))
hdr_metadata_set = true;
}
@@ -4509,7 +4514,7 @@ int main(int argc, char **argv) {
video_pts_counter += num_missed_frames;
}
- if(toggle_pause == 1 && !is_replaying) {
+ if(toggle_pause == 1 && !arg_parser.is_replaying) {
const bool new_paused_state = !paused;
if(new_paused_state) {
paused_time_start = clock_get_monotonic_seconds();
@@ -4596,7 +4601,7 @@ int main(int argc, char **argv) {
}
}
- if(save_replay_seconds != 0 && !save_replay_thread.valid() && is_replaying) {
+ if(save_replay_seconds != 0 && !save_replay_thread.valid() && arg_parser.is_replaying) {
int current_save_replay_seconds = save_replay_seconds;
if(current_save_replay_seconds > 0)
current_save_replay_seconds += arg_parser.keyint;
@@ -4676,11 +4681,11 @@ int main(int argc, char **argv) {
amix_thread.join();
// TODO: Replace this with start_recording_create_steams
- if(!is_replaying && av_write_trailer(av_format_context) != 0) {
+ if(!arg_parser.is_replaying && av_write_trailer(av_format_context) != 0) {
//fprintf(stderr, "Failed to write trailer\n");
}
- if(!is_replaying) {
+ if(!arg_parser.is_replaying && !(output_format->flags & AVFMT_NOFILE)) {
avio_close(av_format_context->pb);
avformat_free_context(av_format_context);
}
@@ -4701,7 +4706,7 @@ int main(int argc, char **argv) {
gsr_kms_client_deinit(&kms_client);
}
- if(!is_replaying && arg_parser.recording_saved_script)
+ if(!arg_parser.is_replaying && arg_parser.recording_saved_script)
run_recording_saved_script_async(arg_parser.recording_saved_script, arg_parser.filename, "regular");
if(dpy) {
diff --git a/src/pipewire_video.c b/src/pipewire_video.c
index 077e733..30e00a8 100644
--- a/src/pipewire_video.c
+++ b/src/pipewire_video.c
@@ -514,19 +514,15 @@ static bool spa_video_format_get_modifiers(gsr_pipewire_video *self, const enum
if(!self->egl->eglQueryDmaBufModifiersEXT(self->egl->egl_display, drm_format, max_modifiers, modifiers, NULL, num_modifiers)) {
fprintf(stderr, "gsr error: spa_video_format_get_modifiers: eglQueryDmaBufModifiersEXT failed with drm format %d, %" PRIi64 "\n", (int)format, drm_format);
- //modifiers[0] = DRM_FORMAT_MOD_LINEAR;
- //modifiers[1] = DRM_FORMAT_MOD_INVALID;
- //*num_modifiers = 2;
modifiers[0] = DRM_FORMAT_MOD_INVALID;
*num_modifiers = 1;
return false;
}
- // if(*num_modifiers + 2 <= max_modifiers) {
- // modifiers[*num_modifiers + 0] = DRM_FORMAT_MOD_LINEAR;
- // modifiers[*num_modifiers + 1] = DRM_FORMAT_MOD_INVALID;
- // *num_modifiers += 2;
- // }
+ if(*num_modifiers < max_modifiers) {
+ modifiers[*num_modifiers] = DRM_FORMAT_MOD_INVALID;
+ *num_modifiers += 1;
+ }
return true;
}
@@ -557,12 +553,8 @@ static size_t gsr_pipewire_video_format_remove_modifier(gsr_pipewire_video *self
}
static void gsr_pipewire_video_remove_modifier(gsr_pipewire_video *self, uint64_t modifier) {
- self->num_modifiers = 0;
for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) {
- gsr_video_format *video_format = &self->supported_video_formats[i];
- const size_t num_modifiers_video_format = gsr_pipewire_video_format_remove_modifier(self, video_format, modifier);
- video_format->modifiers_index = self->num_modifiers;
- self->num_modifiers += num_modifiers_video_format;
+ gsr_pipewire_video_format_remove_modifier(self, &self->supported_video_formats[i], modifier);
}
}
diff --git a/src/plugins.c b/src/plugins.c
index 764dbc7..053b2a3 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -135,3 +135,21 @@ void gsr_plugins_draw(gsr_plugins *self) {
self->egl->glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
+
+bool gsr_plugins_is_damaged(gsr_plugins *self) {
+ bool damaged = false;
+ for(int i = 0; i < self->num_plugins; ++i) {
+ const gsr_plugin *plugin = &self->plugins[i];
+ if(plugin->data.is_damaged)
+ damaged |= plugin->data.is_damaged(plugin->data.userdata);
+ }
+ return damaged;
+}
+
+void gsr_plugins_clear_damage(gsr_plugins *self) {
+ for(int i = 0; i < self->num_plugins; ++i) {
+ const gsr_plugin *plugin = &self->plugins[i];
+ if(plugin->data.clear_damage)
+ plugin->data.clear_damage(plugin->data.userdata);
+ }
+}
diff --git a/src/wayland_host_bridge.c b/src/wayland_host_bridge.c
new file mode 100644
index 0000000..4e8319b
--- /dev/null
+++ b/src/wayland_host_bridge.c
@@ -0,0 +1,109 @@
+#include "../include/wayland_host_bridge.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <wayland-client.h>
+
+#define GSR_UI_UNIX_SOCKET_DOMAIN_FD 3
+
+static const char *bridge_host_path =
+ "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/gsr-wayland-bridge";
+
+static int recv_fd(int sock) {
+ char dummy = 0;
+ struct iovec iov = { &dummy, 1 };
+ char cbuf[CMSG_SPACE(sizeof(int))];
+ memset(cbuf, 0, sizeof(cbuf));
+ struct msghdr msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+
+ const ssize_t n = recvmsg(sock, &msg, 0);
+ if(n <= 0)
+ return -1;
+
+ for(struct cmsghdr *c = CMSG_FIRSTHDR(&msg); c; c = CMSG_NXTHDR(&msg, c)) {
+ if(c->cmsg_level == SOL_SOCKET && c->cmsg_type == SCM_RIGHTS) {
+ int fd = -1;
+ memcpy(&fd, CMSG_DATA(c), sizeof(int));
+ return fd;
+ }
+ }
+ return -1;
+}
+
+static struct wl_display* connect_via_bridge() {
+ const char *wayland_display = getenv("WAYLAND_DISPLAY");
+
+ int sv[2];
+ if(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) != 0) {
+ perror("WaylandHostBridge: socketpair");
+ return NULL;
+ }
+
+ const pid_t pid = fork();
+ if(pid < 0) {
+ perror("WaylandHostBridge: fork");
+ close(sv[0]);
+ close(sv[1]);
+ return NULL;
+ }
+
+ if(pid == 0) {
+ close(sv[0]);
+ if(sv[1] != GSR_UI_UNIX_SOCKET_DOMAIN_FD) {
+ dup2(sv[1], GSR_UI_UNIX_SOCKET_DOMAIN_FD);
+ close(sv[1]);
+ }
+ const int flags = fcntl(GSR_UI_UNIX_SOCKET_DOMAIN_FD, F_GETFD);
+ if(flags >= 0)
+ fcntl(GSR_UI_UNIX_SOCKET_DOMAIN_FD, F_SETFD, flags & ~FD_CLOEXEC);
+
+ char forward_fd_arg[32];
+ snprintf(forward_fd_arg, sizeof(forward_fd_arg), "--forward-fd=%d", GSR_UI_UNIX_SOCKET_DOMAIN_FD);
+
+ execlp("flatpak-spawn", "flatpak-spawn", "--host", forward_fd_arg, bridge_host_path, wayland_display, (char*)NULL);
+ _exit(127);
+ }
+
+ close(sv[1]);
+ const int wayland_fd = recv_fd(sv[0]);
+ close(sv[0]);
+
+ int status = 0;
+ waitpid(pid, &status, 0);
+
+ if(wayland_fd < 0) {
+ fprintf(stderr, "WaylandHostBridge: gsr-wayland-bridge did not return a wayland fd\n");
+ return NULL;
+ }
+
+ struct wl_display *dpy = wl_display_connect_to_fd(wayland_fd);
+ if(!dpy) {
+ close(wayland_fd);
+ fprintf(stderr, "WaylandHostBridge: wl_display_connect_to_fd failed\n");
+ return NULL;
+ }
+ return dpy;
+}
+
+struct wl_display* wayland_connect_to_host() {
+ const int inside_flatpak = getenv("FLATPAK_ID") != NULL;
+ if(inside_flatpak) {
+ struct wl_display *dpy = connect_via_bridge();
+ if(dpy)
+ return dpy;
+ fprintf(stderr, "WaylandHostBridge: falling back to sandboxed wl_display_connect\n");
+ }
+ return wl_display_connect(NULL);
+}
diff --git a/src/window/wayland.c b/src/window/wayland.c
index f126aad..f7218b3 100644
--- a/src/window/wayland.c
+++ b/src/window/wayland.c
@@ -1,4 +1,5 @@
#include "../../include/window/wayland.h"
+#include "../../include/wayland_host_bridge.h"
#include "../../include/vec2.h"
#include "../../include/defs.h"
@@ -295,7 +296,7 @@ static void gsr_window_wayland_deinit(gsr_window_wayland *self) {
}
static bool gsr_window_wayland_init(gsr_window_wayland *self) {
- self->display = wl_display_connect(NULL);
+ self->display = wayland_connect_to_host();
if(!self->display) {
fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to connect to the Wayland server\n");
goto fail;