From 846241189e2a73dcd11605d3f77d61dce0469e02 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 8 Aug 2024 01:45:35 +0200 Subject: Add -portal-session-token-filepath option to specify where desktop portal session token is saved/restore --- README.md | 2 +- TODO | 2 ++ include/capture/portal.h | 2 ++ src/capture/portal.c | 54 ++++++++++++++++++++++++++---------------------- src/egl.c | 3 ++- src/main.cpp | 25 ++++++++++++++++++---- 6 files changed, 57 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index d1851a9..21f9a40 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ It seems like ffmpeg earlier than version 6.1 has some type of bug. Install ffmp Browsers and discord don't support hevc video codec at the moment. Choose h264 video codec instead with the -k h264 option. Note that websites such as youtube support hevc so there is no need to choose h264 video codec if you intend to upload the video to youtube or if you want to play the video locally or if you intend to edit the video with a video editor. Hevc allows for better video quality (especially at lower file sizes) so hevc (or av1) is recommended for source videos. -## I get a black bar/distorted colors on the right/bottom in the video +## I get a black bar/distorted colors on the sides in the video This is mostly an issue on AMD. For av1 it's a hardware issue, see: https://gitlab.freedesktop.org/mesa/mesa/-/issues/9185. For hevc it's a software issue in the AMD driver that hasn't been fixed yet. This issue happens at certain video resolutions. If you get this issue then a workaround is to record with h264 video codec instead (using the -k h264 option). ## The video doesn't display or has a green/yellow overlay This can happen if your video player is missing the H264/HEVC video codecs. Either install the codecs or use mpv. diff --git a/TODO b/TODO index 90e4c89..22747cd 100644 --- a/TODO +++ b/TODO @@ -153,3 +153,5 @@ Desktop portal capture on kde plasma makes notifications not show up unless the Add the option to specify a path to desktop portal session token, to allow different recording modes (streaming, recording, replay) to restore with different setup. Explicit sync is done with the drm property IN_FENCE_FD (see https://drmdb.emersion.fr/properties/4008636142/IN_FENCE_FD). Check if this needs to be used on wayland (especially on nvidia) when capturing a monitor directly without desktop portal. + +The update fps appear to be lower when recording a monitor instead of using portal on intel. Does this reflect in game framerate? \ No newline at end of file diff --git a/include/capture/portal.h b/include/capture/portal.h index 989b042..f80325d 100644 --- a/include/capture/portal.h +++ b/include/capture/portal.h @@ -9,6 +9,8 @@ typedef struct { bool hdr; bool record_cursor; bool restore_portal_session; + /* If this is set to NULL then this defaults to $XDG_CONFIG_HOME/gpu-screen-recorder/restore_token ($XDG_CONFIG_HOME defaults to $HOME/.config) */ + const char *portal_session_token_filepath; } gsr_capture_portal_params; gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params); diff --git a/src/capture/portal.c b/src/capture/portal.c index a53f0a1..00972e3 100644 --- a/src/capture/portal.c +++ b/src/capture/portal.c @@ -78,19 +78,7 @@ static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) { self->params.egl->glBindTexture(GL_TEXTURE_2D, 0); } -static void get_gpu_screen_recorder_config_directory_path(char *buffer, size_t buffer_size) { - const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); - if(xdg_config_home) { - snprintf(buffer, buffer_size, "%s/gpu-screen-recorder", xdg_config_home); - } else { - const char *home = getenv("HOME"); - if(!home) - home = "/tmp"; - snprintf(buffer, buffer_size, "%s/.config/gpu-screen-recorder", home); - } -} - -static void get_gpu_screen_recorder_restore_token_path(char *buffer, size_t buffer_size) { +static void get_default_gpu_screen_recorder_restore_token_path(char *buffer, size_t buffer_size) { const char *xdg_config_home = getenv("XDG_CONFIG_HOME"); if(xdg_config_home) { snprintf(buffer, buffer_size, "%s/gpu-screen-recorder/restore_token", xdg_config_home); @@ -102,19 +90,32 @@ static void get_gpu_screen_recorder_restore_token_path(char *buffer, size_t buff } } -static void gsr_capture_portal_save_restore_token(const char *restore_token) { - char config_path[PATH_MAX]; - config_path[0] = '\0'; - get_gpu_screen_recorder_config_directory_path(config_path, sizeof(config_path)); +static bool create_directory_to_file(const char *filepath) { + char dir[PATH_MAX]; + dir[0] = '\0'; - if(create_directory_recursive(config_path) != 0) { - fprintf(stderr, "gsr warning: gsr_capture_portal_save_restore_token: failed to create directory (%s) for restore token\n", config_path); - return; + const char *split = strrchr(filepath, '/'); + if(!split) /* Assuming it's the current directory (for example if filepath is "restore_token"), which doesn't need to be created */ + return true; + + snprintf(dir, sizeof(dir), "%.*s", (int)(split - filepath), filepath); + if(create_directory_recursive(dir) != 0) { + fprintf(stderr, "gsr warning: gsr_capture_portal_save_restore_token: failed to create directory (%s) for restore token\n", dir); + return false; } + return true; +} +static void gsr_capture_portal_save_restore_token(const char *restore_token, const char *portal_session_token_filepath) { char restore_token_path[PATH_MAX]; restore_token_path[0] = '\0'; - get_gpu_screen_recorder_restore_token_path(restore_token_path, sizeof(restore_token_path)); + if(portal_session_token_filepath) + snprintf(restore_token_path, sizeof(restore_token_path), "%s", portal_session_token_filepath); + else + get_default_gpu_screen_recorder_restore_token_path(restore_token_path, sizeof(restore_token_path)); + + if(!create_directory_to_file(restore_token_path)) + return; FILE *f = fopen(restore_token_path, "wb"); if(!f) { @@ -133,13 +134,16 @@ static void gsr_capture_portal_save_restore_token(const char *restore_token) { fclose(f); } -static void gsr_capture_portal_get_restore_token_from_cache(char *buffer, size_t buffer_size) { +static void gsr_capture_portal_get_restore_token_from_cache(char *buffer, size_t buffer_size, const char *portal_session_token_filepath) { assert(buffer_size > 0); buffer[0] = '\0'; char restore_token_path[PATH_MAX]; restore_token_path[0] = '\0'; - get_gpu_screen_recorder_restore_token_path(restore_token_path, sizeof(restore_token_path)); + if(portal_session_token_filepath) + snprintf(restore_token_path, sizeof(restore_token_path), "%s", portal_session_token_filepath); + else + get_default_gpu_screen_recorder_restore_token_path(restore_token_path, sizeof(restore_token_path)); FILE *f = fopen(restore_token_path, "rb"); if(!f) { @@ -173,7 +177,7 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire char restore_token[1024]; restore_token[0] = '\0'; if(self->params.restore_portal_session) - gsr_capture_portal_get_restore_token_from_cache(restore_token, sizeof(restore_token)); + gsr_capture_portal_get_restore_token_from_cache(restore_token, sizeof(restore_token), self->params.portal_session_token_filepath); if(!gsr_dbus_init(&self->dbus, restore_token)) return -1; @@ -201,7 +205,7 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire const char *screencast_restore_token = gsr_dbus_screencast_get_restore_token(&self->dbus); if(screencast_restore_token) - gsr_capture_portal_save_restore_token(screencast_restore_token); + gsr_capture_portal_save_restore_token(screencast_restore_token, self->params.portal_session_token_filepath); fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: OpenPipeWireRemote\n"); if(!gsr_dbus_screencast_open_pipewire_remote(&self->dbus, self->session_handle, pipewire_fd)) { diff --git a/src/egl.c b/src/egl.c index da8dc9f..87bb8fb 100644 --- a/src/egl.c +++ b/src/egl.c @@ -12,7 +12,7 @@ #include #include -// TODO: rename gsr_egl to something else since this includes both egl and eglx and in the future maybe vulkan too +// TODO: rename gsr_egl to something else since this includes both egl and glx and in the future maybe vulkan too // TODO: Move this shit to a separate wayland file, and have a separate file for x11. @@ -251,6 +251,7 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { } if(wayland) { + // TODO: Error check? self->wayland.surface = wl_compositor_create_surface(self->wayland.compositor); self->wayland.window = wl_egl_window_create(self->wayland.surface, 16, 16); self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->wayland.window, NULL); diff --git a/src/main.cpp b/src/main.cpp index 1067ccd..b4bb46b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -379,7 +379,7 @@ static AVCodecContext *create_video_codec_context(AVPixelFormat pix_fmt, } //codec_context->chroma_sample_location = AVCHROMA_LOC_CENTER; if(codec->id == AV_CODEC_ID_HEVC) - codec_context->codec_tag = MKTAG('h', 'v', 'c', '1'); + codec_context->codec_tag = MKTAG('h', 'v', 'c', '1'); // QuickTime on MacOS requires this or the video wont be playable switch(video_quality) { case VideoQuality::MEDIUM: //codec_context->qmin = 35; @@ -1004,7 +1004,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 [-c ] [-s WxH] -f [-a ] [-q ] [-r ] [-k h264|hevc|hevc_hdr|av1|av1_hdr|vp8|vp9] [-ac aac|opus|flac] [-ab ] [-oc yes|no] [-fm cfr|vfr|content] [-cr limited|full] [-df yes|no] [-sc ] [-cursor yes|no] [-keyint ] [-restore-portal-session yes|no] [-encoder gpu|cpu] [-o ] [-v yes|no] [-h|--help]\n", program_name); + fprintf(stderr, "usage: %s -w [-c ] [-s WxH] -f [-a ] [-q ] [-r ] [-k h264|hevc|hevc_hdr|av1|av1_hdr|vp8|vp9] [-ac aac|opus|flac] [-ab ] [-oc yes|no] [-fm cfr|vfr|content] [-cr limited|full] [-df yes|no] [-sc ] [-cursor yes|no] [-keyint ] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o ] [-v yes|no] [-h|--help]\n", program_name); } // TODO: Update with portal info @@ -1088,6 +1088,12 @@ static void usage_full() { fprintf(stderr, " If GPU Screen Recorder should use the same capture option as the last time. Using this option removes the popup asking what you want to record the next time you record with '-w portal' if you selected the option to save session (token) in the desktop portal screencast popup.\n"); fprintf(stderr, " This option may not have any effect on your Wayland compositor and your systems desktop portal needs to support ScreenCast version 5 or later. Optional, set to 'no' by default.\n"); fprintf(stderr, "\n"); + fprintf(stderr, " -portal-session-token-filepath\n"); + fprintf(stderr, " This option is used together with -restore-portal-session option to specify the file path to save/restore the portal session token to/from.\n"); + fprintf(stderr, " This can be used to remember different portal capture options depending on different recording option (such as recording/replay).\n"); + fprintf(stderr, " Optional, set to \"$XDG_CONFIG_HOME/gpu-screen-recorder/restore_token\" by default ($XDG_CONFIG_HOME defaults to \"$HOME/.config\").\n"); + fprintf(stderr, " Note: the directory to the portal session token file is created automatically if it doesn't exist.\n"); + fprintf(stderr, "\n"); fprintf(stderr, " -encoder\n"); fprintf(stderr, " Which device should be used for video encoding. Should either be 'gpu' or 'cpu'. Does currently only work with h264 codec option (-k).\n"); fprintf(stderr, " Optional, set to 'gpu' by default.\n"); @@ -1819,7 +1825,7 @@ static void list_audio_devices_command() { _exit(0); } -static gsr_capture* create_capture_impl(const char *window_str, const char *screen_region, bool wayland, gsr_egl *egl, int fps, bool overclock, VideoCodec video_codec, gsr_color_range color_range, bool record_cursor, bool track_damage, bool use_software_video_encoder, bool restore_portal_session) { +static gsr_capture* create_capture_impl(const char *window_str, const char *screen_region, bool wayland, gsr_egl *egl, int fps, bool overclock, VideoCodec video_codec, gsr_color_range color_range, bool record_cursor, bool track_damage, bool use_software_video_encoder, bool restore_portal_session, const char *portal_session_token_filepath) { vec2i region_size = { 0, 0 }; Window src_window_id = None; bool follow_focused = false; @@ -1865,6 +1871,7 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre portal_params.color_range = color_range; portal_params.record_cursor = record_cursor; portal_params.restore_portal_session = restore_portal_session; + portal_params.portal_session_token_filepath = portal_session_token_filepath; capture = gsr_capture_portal_create(&portal_params); if(!capture) _exit(1); @@ -2154,6 +2161,7 @@ int main(int argc, char **argv) { { "-cursor", Arg { {}, true, false } }, { "-keyint", Arg { {}, true, false } }, { "-restore-portal-session", Arg { {}, true, false } }, + { "-portal-session-token-filepath", Arg { {}, true, false } }, { "-encoder", Arg { {}, true, false } }, }; @@ -2341,6 +2349,15 @@ int main(int argc, char **argv) { usage(); } + const char *portal_session_token_filepath = args["-portal-session-token-filepath"].value(); + if(portal_session_token_filepath) { + int len = strlen(portal_session_token_filepath); + if(len > 0 && portal_session_token_filepath[len - 1] == '/') { + fprintf(stderr, "Error: -portal-session-token-filepath should be a path to a file but it ends with a /: %s\n", portal_session_token_filepath); + _exit(1); + } + } + const char *recording_saved_script = args["-sc"].value(); if(recording_saved_script) { struct stat buf; @@ -2795,7 +2812,7 @@ int main(int argc, char **argv) { _exit(2); } - gsr_capture *capture = create_capture_impl(window_str, screen_region, wayland, &egl, fps, overclock, video_codec, color_range, record_cursor, framerate_mode == FramerateMode::CONTENT, use_software_video_encoder, restore_portal_session); + gsr_capture *capture = create_capture_impl(window_str, screen_region, wayland, &egl, fps, overclock, video_codec, color_range, record_cursor, framerate_mode == FramerateMode::CONTENT, use_software_video_encoder, restore_portal_session, portal_session_token_filepath); // (Some?) livestreaming services require at least one audio track to work. // If not audio is provided then create one silent audio track. -- cgit v1.2.3