Compare commits

...

4 Commits
5.0.2 ... 5.1.0

Author SHA1 Message Date
dec05eba
cafcda1022 5.1.0 2025-01-25 19:56:43 +01:00
dec05eba
450bc0ac4a Dont normalize audio for mixed audio. This matches app audio|device audio mix, fixes mic audio being decreased 2025-01-25 19:54:33 +01:00
dec05eba
8e267bb3b0 m 2025-01-24 10:06:52 +01:00
dec05eba
6e545c7ca0 -overlay-replay > -restart-replay-on-save 2025-01-24 10:06:09 +01:00
4 changed files with 45 additions and 39 deletions

3
TODO
View File

@@ -2,7 +2,6 @@ Check for reparent.
Quickly changing workspace and back while recording under i3 breaks the screen recorder. i3 probably unmaps windows in other workspaces.
See https://trac.ffmpeg.org/wiki/EncodingForStreamingSites for optimizing streaming.
Look at VK_EXT_external_memory_dma_buf.
Allow setting a different output resolution than the input resolution.
Use mov+faststart.
Allow recording all monitors/selected monitor without nvfbc by recording the compositor proxy window and only recording the part that matches the monitor(s).
Allow recording a region by recording the compositor proxy window / nvfbc window and copying part of it.
@@ -10,7 +9,6 @@ Support amf and qsv.
Disable flipping on nvidia? this might fix some stuttering issues on some setups. See NvCtrlGetAttribute/NvCtrlSetAttributeAndGetStatus NV_CTRL_SYNC_TO_VBLANK https://github.com/NVIDIA/nvidia-settings/blob/d5f022976368cbceb2f20b838ddb0bf992f0cfb9/src/gtk%2B-2.x/ctkopengl.c.
Replays seem to have some issues with audio/video. Why?
Cleanup unused gl/egl functions, macro, etc.
Add option to disable overlapping of replays (the old behavior kinda. Remove the whole replay buffer data after saving when doing this).
Set audio track name to audio device name (if not merge of multiple audio devices).
Add support for webcam, but only really for amd/intel because amd/intel can get drm fd access to webcam, nvidia cant. This allows us to create an opengl texture directly from the webcam fd for optimal performance.
Reverse engineer nvapi so we can disable "force p2 state" on linux too (nvapi profile api with the settings id 0x50166c5e).
@@ -206,6 +204,7 @@ Ffmpeg fixed black bars in videos on amd when using hevc and when recording at s
https://github.com/FFmpeg/FFmpeg/commit/bcfbf2bac8f9eeeedc407b40596f5c7aaa0d5b47
https://github.com/FFmpeg/FFmpeg/commit/d0facac679faf45d3356dff2e2cb382580d7a521
Disable gpu screen recorder black bar handling when using hevc on amd when the libavcodec version is the one that comes after those commits.
Also consider the mesa version, to see if the gpu supports this.
Use opengl compute shader instead of graphics shader. This might allow for better performance when games are using 100% of graphics unit which might fix issue with 100% gpu usage causing gpu screen recorder to run slow when not using vaapi to convert rgb to nv12(?).

View File

@@ -1,4 +1,4 @@
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.0.2', default_options : ['warning_level=2'])
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.1.0', default_options : ['warning_level=2'])
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
if get_option('buildtype') == 'debug'

View File

@@ -1,7 +1,7 @@
[package]
name = "gpu-screen-recorder"
type = "executable"
version = "5.0.2"
version = "5.1.0"
platforms = ["posix"]
[config]

View File

@@ -1069,7 +1069,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";
printf("usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-overlap-replay yes|no] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [--list-capture-options [card_path] [vendor]] [--list-audio-devices] [--list-application-audio] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
printf("usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-restart-replay-on-save yes|no] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [--list-capture-options [card_path] [vendor]] [--list-audio-devices] [--list-application-audio] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
fflush(stdout);
}
@@ -1130,11 +1130,11 @@ static void usage_full() {
printf(" Note that the video data is stored in RAM, so don't use too long replay buffer time and use constant bitrate option (-bm cbr) to prevent RAM usage from going too high in busy scenes.\n");
printf(" Optional, disabled by default.\n");
printf("\n");
printf(" -overlap-replay\n");
printf(" Should replays overlap. For example if this is set to 'yes' and replay time (-r) is set to 60 seconds and a replay is saved once then the first replay video is 60 seconds long\n");
printf(" -restart-replay-on-save\n");
printf(" Restart replay on save. For example if this is set to 'no' and replay time (-r) is set to 60 seconds and a replay is saved once then the first replay video is 60 seconds long\n");
printf(" and if a replay is saved 10 seconds later then the second replay video will also be 60 seconds long and contain 50 seconds of the previous video as well.\n");
printf(" If this is set to 'no' then after a replay is saved the replay buffer data is cleared and the second replay will start from that point onward.\n");
printf(" Optional, set to 'yes' by default.\n");
printf(" If this is set to 'yes' then after a replay is saved the replay buffer data is cleared and the second replay will start from that point onward.\n");
printf(" Optional, set to 'no' by default.\n");
printf("\n");
printf(" -k Video codec to use. Should be either 'auto', 'h264', 'hevc', 'av1', 'vp8', 'vp9', 'hevc_hdr', 'av1_hdr', 'hevc_10bit' or 'av1_10bit'.\n");
printf(" Optional, set to 'auto' by default which defaults to 'h264'. Forcefully set to 'h264' if the file container type is 'flv'.\n");
@@ -1315,7 +1315,7 @@ static std::string get_date_str() {
time_t now = time(NULL);
struct tm *t = localtime(&now);
strftime(str, sizeof(str)-1, "%Y-%m-%d_%H-%M-%S", t);
return str;
return str;
}
static std::string get_date_only_str() {
@@ -1487,7 +1487,7 @@ static std::string save_replay_output_filepath;
static void save_replay_async(AVCodecContext *video_codec_context, int video_stream_index, std::vector<AudioTrack> &audio_tracks, std::deque<std::shared_ptr<PacketData>> &frame_data_queue, bool frames_erased, std::string output_dir, const char *container_format, const std::string &file_extension, std::mutex &write_output_mutex, bool date_folders, bool hdr, gsr_capture *capture) {
if(save_replay_thread.valid())
return;
size_t start_index = (size_t)-1;
int64_t video_pts_offset = 0;
int64_t audio_pts_offset = 0;
@@ -1508,7 +1508,7 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
if(frames_erased) {
video_pts_offset = frame_data_queue[start_index]->data.pts;
// Find the next audio packet to use as audio pts offset
for(size_t i = start_index; i < frame_data_queue.size(); ++i) {
const AVPacket &av_packet = frame_data_queue[i]->data;
@@ -1721,26 +1721,26 @@ static int init_filter_graph(AVCodecContext *audio_codec_context, AVFilterGraph
char ch_layout[64];
int err = 0;
ch_layout[0] = '\0';
AVFilterGraph *filter_graph = avfilter_graph_alloc();
if (!filter_graph) {
fprintf(stderr, "Unable to create filter graph.\n");
return AVERROR(ENOMEM);
}
for(size_t i = 0; i < num_sources; ++i) {
const AVFilter *abuffer = avfilter_get_by_name("abuffer");
if (!abuffer) {
fprintf(stderr, "Could not find the abuffer filter.\n");
return AVERROR_FILTER_NOT_FOUND;
}
AVFilterContext *abuffer_ctx = avfilter_graph_alloc_filter(filter_graph, abuffer, NULL);
if (!abuffer_ctx) {
fprintf(stderr, "Could not allocate the abuffer instance.\n");
return AVERROR(ENOMEM);
}
#if LIBAVCODEC_VERSION_MAJOR < 60
av_get_channel_layout_string(ch_layout, sizeof(ch_layout), 0, AV_CH_LAYOUT_STEREO);
#else
@@ -1751,7 +1751,7 @@ static int init_filter_graph(AVCodecContext *audio_codec_context, AVFilterGraph
av_opt_set_q (abuffer_ctx, "time_base", audio_codec_context->time_base, AV_OPT_SEARCH_CHILDREN);
av_opt_set_int(abuffer_ctx, "sample_rate", audio_codec_context->sample_rate, AV_OPT_SEARCH_CHILDREN);
av_opt_set_int(abuffer_ctx, "bit_rate", audio_codec_context->bit_rate, AV_OPT_SEARCH_CHILDREN);
err = avfilter_init_str(abuffer_ctx, NULL);
if (err < 0) {
fprintf(stderr, "Could not initialize the abuffer filter.\n");
@@ -1766,35 +1766,42 @@ static int init_filter_graph(AVCodecContext *audio_codec_context, AVFilterGraph
av_log(NULL, AV_LOG_ERROR, "Could not find the mix filter.\n");
return AVERROR_FILTER_NOT_FOUND;
}
#if LIBAVFILTER_VERSION_INT >= AV_VERSION_INT(7, 107, 100)
bool normalize = false;
char args[512];
snprintf(args, sizeof(args), "inputs=%d:normalize=%s", (int)num_sources, normalize ? "true" : "false");
#else
char args[512];
snprintf(args, sizeof(args), "inputs=%d", (int)num_sources);
fprintf(stderr, "Warning: your ffmpeg version doesn't support disabling normalizing of mixed audio. Volume might be lower than expected\n");
#endif
AVFilterContext *mix_ctx;
err = avfilter_graph_create_filter(&mix_ctx, mix_filter, "amix", args, NULL, filter_graph);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR, "Cannot create audio amix filter\n");
return err;
}
const AVFilter *abuffersink = avfilter_get_by_name("abuffersink");
if (!abuffersink) {
fprintf(stderr, "Could not find the abuffersink filter.\n");
return AVERROR_FILTER_NOT_FOUND;
}
AVFilterContext *abuffersink_ctx = avfilter_graph_alloc_filter(filter_graph, abuffersink, "sink");
if (!abuffersink_ctx) {
fprintf(stderr, "Could not allocate the abuffersink instance.\n");
return AVERROR(ENOMEM);
}
err = avfilter_init_str(abuffersink_ctx, NULL);
if (err < 0) {
fprintf(stderr, "Could not initialize the abuffersink instance.\n");
return err;
}
err = 0;
for(size_t i = 0; i < src_filter_ctx.size(); ++i) {
AVFilterContext *src_ctx = src_filter_ctx[i];
@@ -1807,16 +1814,16 @@ static int init_filter_graph(AVCodecContext *audio_codec_context, AVFilterGraph
av_log(NULL, AV_LOG_ERROR, "Error connecting filters\n");
return err;
}
err = avfilter_graph_config(filter_graph, NULL);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR, "Error configuring the filter graph\n");
return err;
}
*graph = filter_graph;
*sink = abuffersink_ctx;
return 0;
}
@@ -2633,7 +2640,7 @@ static AudioCodec select_audio_codec_with_fallback(AudioCodec audio_codec, const
}
static const char* video_codec_to_string(VideoCodec video_codec) {
switch(video_codec) {
switch(video_codec) {
case VideoCodec::H264: return "h264";
case VideoCodec::HEVC: return "hevc";
case VideoCodec::HEVC_HDR: return "hevc_hdr";
@@ -2650,7 +2657,7 @@ static const char* video_codec_to_string(VideoCodec video_codec) {
}
static bool video_codec_only_supports_low_power_mode(const gsr_supported_video_codecs &supported_video_codecs, VideoCodec video_codec) {
switch(video_codec) {
switch(video_codec) {
case VideoCodec::H264: return supported_video_codecs.h264.low_power;
case VideoCodec::HEVC: return supported_video_codecs.hevc.low_power;
case VideoCodec::HEVC_HDR: return supported_video_codecs.hevc_hdr.low_power;
@@ -3062,7 +3069,7 @@ int main(int argc, char **argv) {
{ "-q", Arg { {}, true, false } },
{ "-o", Arg { {}, true, false } },
{ "-r", Arg { {}, true, false } },
{ "-overlap-replay", Arg { {}, true, false } },
{ "-restart-replay-on-save", Arg { {}, true, false } },
{ "-k", Arg { {}, true, false } },
{ "-ac", Arg { {}, true, false } },
{ "-ab", Arg { {}, true, false } },
@@ -3389,17 +3396,17 @@ int main(int argc, char **argv) {
replay_buffer_size_secs += std::ceil(keyint); // Add a few seconds to account of lost packets because of non-keyframe packets skipped
}
bool overlap_replay = true;
const char *overlap_replay_str = args["-overlap-replay"].value();
if(!overlap_replay_str)
overlap_replay_str = "yes";
bool restart_replay_on_save = false;
const char *restart_replay_on_save_str = args["-restart-replay-on-save"].value();
if(!restart_replay_on_save_str)
restart_replay_on_save_str = "no";
if(strcmp(overlap_replay_str, "yes") == 0) {
overlap_replay = true;
} else if(strcmp(overlap_replay_str, "no") == 0) {
overlap_replay = false;
if(strcmp(restart_replay_on_save_str, "yes") == 0) {
restart_replay_on_save = true;
} else if(strcmp(restart_replay_on_save_str, "no") == 0) {
restart_replay_on_save = false;
} else {
fprintf(stderr, "Error: -overlap-replap should either be either 'yes' or 'no', got: '%s'\n", overlap_replay_str);
fprintf(stderr, "Error: -restart-replay-on-save should either be either 'yes' or 'no', got: '%s'\n", restart_replay_on_save_str);
usage();
}
@@ -4251,7 +4258,7 @@ int main(int argc, char **argv) {
std::lock_guard<std::mutex> lock(write_output_mutex);
save_replay_packets.clear();
if(!overlap_replay) {
if(restart_replay_on_save) {
frame_data_queue.clear();
frames_erased = true;
replay_start_time = clock_get_monotonic_seconds() - paused_time_offset;