diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/args_parser.c | 8 | ||||
| -rw-r--r-- | src/main.cpp | 81 | ||||
| -rw-r--r-- | src/pipewire_video.c | 18 | ||||
| -rw-r--r-- | src/plugins.c | 18 | ||||
| -rw-r--r-- | src/wayland_host_bridge.c | 109 | ||||
| -rw-r--r-- | src/window/wayland.c | 3 |
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; |
