From cb9cb6c567181de2150c445a238f66ea2741f6fd Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 30 Dec 2025 02:33:12 +0100 Subject: [PATCH] Add -ffmpeg-opts argument to pass additional options to ffmpeg --- gpu-screen-recorder.1 | 5 +++++ include/args_parser.h | 3 ++- src/args_parser.c | 7 +++++-- src/main.cpp | 23 +++++++++++++---------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/gpu-screen-recorder.1 b/gpu-screen-recorder.1 index bd15d75..bab12b2 100644 --- a/gpu-screen-recorder.1 +++ b/gpu-screen-recorder.1 @@ -301,6 +301,11 @@ Script to run after saving video. Receives filepath and type ("regular", "replay .BI \-portal\-session\-token\-filepath " path" Portal session token file (default: ~/.config/gpu-screen-recorder/restore_token). .TP +.BI \-ffmpeg-opts " options" +Additional arguments to pass to FFmpeg in a list of key-values pairs in the format "key=value;key=value", +.br +for example: -ffmpeg-opts "hls_list_size=3;hls_time=1;hls_flags=delete_segments". +.TP .BI \-gl\-debug " yes|no" OpenGL debug output (default: no). .TP diff --git a/include/args_parser.h b/include/args_parser.h index 316b5e1..0a03024 100644 --- a/include/args_parser.h +++ b/include/args_parser.h @@ -8,7 +8,7 @@ typedef struct gsr_egl gsr_egl; -#define NUM_ARGS 32 +#define NUM_ARGS 33 typedef enum { GSR_CAPTURE_SOURCE_TYPE_WINDOW, @@ -85,6 +85,7 @@ typedef struct { const char *replay_recording_directory; const char *portal_session_token_filepath; const char *recording_saved_script; + const char *ffmpeg_opts; bool verbose; bool gl_debug; bool fallback_cpu_encoding; diff --git a/src/args_parser.c b/src/args_parser.c index ed89205..c95c43a 100644 --- a/src/args_parser.c +++ b/src/args_parser.c @@ -196,8 +196,8 @@ static void usage_header(void) { "[-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab ] [-oc yes|no] [-fm cfr|vfr|content] " "[-bm auto|qp|vbr|cbr] [-cr limited|full] [-tune performance|quality] [-df yes|no] [-sc ] [-p ] " "[-cursor yes|no] [-keyint ] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] " - "[-fallback-cpu-encoding yes|no] [-o ] [-ro ] [--list-capture-options [card_path]] [--list-audio-devices] " - "[--list-application-audio] [--list-v4l2-devices] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name); + "[-fallback-cpu-encoding yes|no] [-o ] [-ro ] [-ffmpeg-opts ] [--list-capture-options [card_path]] " + "[--list-audio-devices] [--list-application-audio] [--list-v4l2-devices] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name); fflush(stdout); } @@ -440,6 +440,8 @@ static bool args_parser_set_values(args_parser *self) { self->recording_saved_script = NULL; } + self->ffmpeg_opts = args_get_value_by_key(self->args, NUM_ARGS, "-ffmpeg-opts"); + return true; } @@ -529,6 +531,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand self->args[arg_index++] = (Arg){ .key = "-fallback-cpu-encoding", .optional = true, .list = false, .type = ARG_TYPE_BOOLEAN }; self->args[arg_index++] = (Arg){ .key = "-replay-storage", .optional = true, .list = false, .type = ARG_TYPE_ENUM, .enum_values = replay_storage_enums, .num_enum_values = sizeof(replay_storage_enums)/sizeof(ArgEnum) }; self->args[arg_index++] = (Arg){ .key = "-p", .optional = true, .list = true, .type = ARG_TYPE_STRING }; + self->args[arg_index++] = (Arg){ .key = "-ffmpeg-opts", .optional = true, .list = false, .type = ARG_TYPE_STRING }; assert(arg_index == NUM_ARGS); for(int i = 1; i < argc; i += 2) { diff --git a/src/main.cpp b/src/main.cpp index 396426f..bcfce99 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1193,9 +1193,9 @@ struct VideoSource { CaptureSource *capture_source; }; -static RecordingStartResult start_recording_create_streams(const char *filename, const char *container_format, AVCodecContext *video_codec_context, const std::vector &audio_tracks, bool hdr, std::vector &video_sources) { +static RecordingStartResult start_recording_create_streams(const char *filename, const args_parser &args_parser, AVCodecContext *video_codec_context, const std::vector &audio_tracks, bool hdr, std::vector &video_sources) { AVFormatContext *av_format_context; - avformat_alloc_output_context2(&av_format_context, nullptr, container_format, filename); + avformat_alloc_output_context2(&av_format_context, nullptr, args_parser.container_format, filename); AVStream *video_stream = create_stream(av_format_context, video_codec_context); avcodec_parameters_from_context(video_stream->codecpar, video_codec_context); @@ -1219,6 +1219,8 @@ static RecordingStartResult start_recording_create_streams(const char *filename, AVDictionary *options = nullptr; av_dict_set(&options, "strict", "experimental", 0); + if(args_parser.ffmpeg_opts) + av_dict_parse_string(&options, args_parser.ffmpeg_opts, "=", ";", 0); const int header_write_ret = avformat_write_header(av_format_context, &options); av_dict_free(&options); @@ -1282,7 +1284,7 @@ struct AudioPtsOffset { int stream_index = 0; }; -static void save_replay_async(AVCodecContext *video_codec_context, int video_stream_index, const std::vector &audio_tracks, gsr_replay_buffer *replay_buffer, std::string output_dir, const char *container_format, const std::string &file_extension, bool date_folders, bool hdr, std::vector &video_sources, int current_save_replay_seconds) { +static void save_replay_async(AVCodecContext *video_codec_context, int video_stream_index, const std::vector &audio_tracks, gsr_replay_buffer *replay_buffer, const args_parser &arg_parser, const std::string &file_extension, bool date_folders, bool hdr, std::vector &video_sources, int current_save_replay_seconds) { if(save_replay_thread.valid()) return; @@ -1310,8 +1312,8 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str return; } - std::string output_filepath = create_new_recording_filepath_from_timestamp(output_dir, "Replay", file_extension, date_folders); - RecordingStartResult recording_start_result = start_recording_create_streams(output_filepath.c_str(), container_format, video_codec_context, audio_tracks, hdr, video_sources); + std::string output_filepath = create_new_recording_filepath_from_timestamp(arg_parser.filename, "Replay", file_extension, date_folders); + RecordingStartResult recording_start_result = start_recording_create_streams(output_filepath.c_str(), arg_parser, video_codec_context, audio_tracks, hdr, video_sources); if(!recording_start_result.av_format_context) return; @@ -3430,7 +3432,7 @@ 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) { +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)); @@ -3439,7 +3441,8 @@ static bool av_open_file_write_header(AVFormatContext *av_format_context, const AVDictionary *options = nullptr; av_dict_set(&options, "strict", "experimental", 0); - //av_dict_set_int(&av_format_context->metadata, "video_full_range_flag", 1, 0); + if(ffmpeg_opts) + av_dict_parse_string(&options, ffmpeg_opts, "=", ";", 0); ret = avformat_write_header(av_format_context, &options); if(ret < 0) @@ -3989,7 +3992,7 @@ 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)) + if(!av_open_file_write_header(av_format_context, arg_parser.filename, arg_parser.ffmpeg_opts)) _exit(1); } @@ -4448,7 +4451,7 @@ int main(int argc, char **argv) { std::lock_guard lock(audio_filter_mutex); replay_recording_items.clear(); replay_recording_filepath = create_new_recording_filepath_from_timestamp(arg_parser.replay_recording_directory, "Video", file_extension, arg_parser.date_folders); - replay_recording_start_result = start_recording_create_streams(replay_recording_filepath.c_str(), arg_parser.container_format, video_codec_context, audio_tracks, hdr, video_sources); + replay_recording_start_result = start_recording_create_streams(replay_recording_filepath.c_str(), arg_parser, video_codec_context, audio_tracks, hdr, video_sources); if(replay_recording_start_result.av_format_context) { const size_t video_recording_destination_id = gsr_encoder_add_recording_destination(&encoder, video_codec_context, replay_recording_start_result.av_format_context, replay_recording_start_result.video_stream, video_frame->pts); if(video_recording_destination_id != (size_t)-1) @@ -4510,7 +4513,7 @@ int main(int argc, char **argv) { save_replay_seconds = 0; save_replay_output_filepath.clear(); - save_replay_async(video_codec_context, VIDEO_STREAM_INDEX, audio_tracks, encoder.replay_buffer, arg_parser.filename, arg_parser.container_format, file_extension, arg_parser.date_folders, hdr, video_sources, current_save_replay_seconds); + save_replay_async(video_codec_context, VIDEO_STREAM_INDEX, audio_tracks, encoder.replay_buffer, arg_parser, file_extension, arg_parser.date_folders, hdr, video_sources, current_save_replay_seconds); if(arg_parser.restart_replay_on_save && current_save_replay_seconds == save_replay_seconds_full) gsr_replay_buffer_clear(encoder.replay_buffer);