aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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
6 files changed, 181 insertions, 56 deletions
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;