Compare commits

...

6 Commits
5.8.0 ... 5.8.1

Author SHA1 Message Date
dec05eba
dc15c69848 5.8.1 2025-11-02 22:57:38 +01:00
dec05eba
a545fe45c3 Allow negative position for region 2025-11-02 18:20:18 +01:00
dec05eba
e623145de8 Force QP mode when capturing with software encoder and attempting to use vbr 2025-10-27 19:02:00 +01:00
dec05eba
7d4a4068ff Add -fallback-cpu-encoding option to use cpu encoding (x264) if gpu encoding is not available on the system 2025-10-24 11:51:10 +02:00
dec05eba
57ac400eab Show better error on vaapi init failure 2025-10-22 10:21:11 +02:00
dec05eba
cf65b24d3d Update README.md 2025-10-15 17:48:38 +02:00
7 changed files with 130 additions and 74 deletions

View File

@@ -31,7 +31,7 @@ This software works on X11 and Wayland on AMD, Intel and NVIDIA.
# Installation
If you are running an Arch Linux based distro then you can find gpu screen recorder on aur under the name gpu-screen-recorder (`yay -S gpu-screen-recorder`).\
If you are running another distro then you can run `sudo ./install.sh`, but you need to manually install the dependencies, as described below.\
You can also install gpu screen recorder ([the gtk gui version](https://git.dec05eba.com/gpu-screen-recorder-gtk/)) from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder), which is the easiest method
You can also install gpu screen recorder ([the ui version](https://git.dec05eba.com/gpu-screen-recorder-gtk/)) from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder), which is the easiest method
to install GPU Screen Recorder on non-arch based distros.\
If you install GPU Screen Recorder flatpak, which is the gtk gui version then you can still run GPU Screen Recorder command line by using the flatpak command option, for example `flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder -w screen -f 60 -o video.mp4`. Note that if you want to record your monitor on AMD/Intel then you need to install the flatpak system-wide (like so: `flatpak install --system com.dec05eba.gpu_screen_recorder`).
@@ -55,14 +55,14 @@ GPU Screen Recorder uses meson build system so you need to install `meson` to bu
## Build dependencies
These are the dependencies needed to build GPU Screen Recorder:
* vulkan-headers
* ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)
* x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage)
* libpulse
* wayland (wayland-client, wayland-egl, wayland-scanner)
* ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)
* libva (and libva-drm)
* libpulse
* libdrm
* libcap
* wayland (wayland-client, wayland-egl, wayland-scanner)
* vulkan-headers
## Optional dependencies
When building GPU Screen Recorder with portal support (`-Dportal=true` meson option, which is enabled by default) these dependencies are also needed:
@@ -80,7 +80,7 @@ There are also additional dependencies needed at runtime depending on your GPU v
### Intel
* mesa
* vaapi (intel-media-driver/libva-intel-driver/linux-firmware-intel, depending on which intel iGPU you have)
* vaapi (intel-media-driver/libva-intel-driver/linux-firmware-intel, depending on which intel GPU you have)
### NVIDIA
* cuda runtime (libcuda.so.1) (libnvidia-compute)
@@ -226,7 +226,8 @@ You have to use external software for that, such as Easy Effects or NoiseTorch.
It's not really possible except in some cases. You can only record with the GPU that is displaying the graphics on your monitor.\
Some laptops have display adapters that connect external monitors directly to the external GPU (if you have one)
and on Wayland the external GPU will display the graphics for that monitor.
In that case you can record the monitor with the external GPU by launching GPU Screen Recorder with [prime-run or by setting the DRI_PRIME environment variable](https://wiki.archlinux.org/title/PRIME) depending on your GPU brand.\
In that case you can record the monitor with the external GPU by launching GPU Screen Recorder with [prime-run or by setting the DRI_PRIME environment variable](https://wiki.archlinux.org/title/PRIME) depending on your GPU brand.
Alternatively you can capture with the desktop portal option (`-w portal`), which should allow you to capture any monitor.\
However if you really want to change which GPU you want to record and encode with with then you can instead configure your display server (Xorg or Wayland compositor) to run with that GPU,
then GPU Screen Recorder will automatically use that same GPU for recording and encoding.
## The rotation of the video is incorrect when the monitor is rotated when using desktop portal capture

27
TODO
View File

@@ -30,9 +30,6 @@ Window capture doesn't work properly in _control_ game after going from pause me
Monitor capture on steam deck is slightly below the game fps, but only when capturing on the steam deck screen. If capturing on another monitor, there is no issue.
Is this related to the dma buf rotation issue? different modifier being slow? does this always happen?
Fallback to vaapi copy in kms if opengl version fails. This can happen on steam deck for some reason (driver bug?). Also vaapi copy uses less gpu since it uses video codec unit to copy.
Test if vaapi copy version uses less memory than opengl version.
Intel is a bit weird with monitor capture and multiple monitors. If one of the monitors is rotated then all the kms will be rotated as well.
Is that only the case when the primary monitor is rotated? Also the primary monitor becomes position 0, 0 so crtc (x11 randr) position doesn't match the drm pos. Maybe get monitor position and size from drm instead.
How about if multiple monitors are rotated?
@@ -266,7 +263,8 @@ Add the option to set audio track name, for example with -a "track-name:blabla|d
Maybe disable qp/vbr for replay. In that case we can preallocate all replay data (for both ram and disk) and write to that directly when receiving packet (dont do that when also recording at the same time).
That could improve performance/disk write optimization and maybe even reduce ram usage because of less blocks/fragmentation.
When rpc is added add the option to add/remove audio devices/app audio and also overlays (from new capture sources).
When rpc is added add the option to add/remove audio devices/app audio and also overlays (from new capture sources). It should also be possible to save a replay of any duration (below duration set with -r).
Have an rpc info/status command that just returns the status. This is to check if gpu screen recorder is running.
Support hdr screenshot.
@@ -307,7 +305,7 @@ It's possible for microphone audio to get desynced when recording together with
We can use dri2connect/dri3open to get the /dev/dri/card device. Note that this doesn't work on nvidia x11.
Add support for QVBR (QP with target bitrate).
Add support for QVBR (QP with target bitrate). Maybe use VBR instead, since nvidia doesn't support QVBR and neither does vulkan.
KDE Plasma Wayland seems to use overlay planes now in non-fullscreen mode(limited to 1 overlay plane per gpu). Check if this is the case in the latest kde on arch linux.
If it is, then support it in kms capture.
@@ -329,3 +327,22 @@ Application audio capture isn't good enough. It creates a sink that for some aut
Add -k best/best_hdr/best_10bit option, to automatically choose the best codec (prefer av1, then hevc and then h264. For webm files choose vp9 and then vp8).
Check if region capture works properly with fractional scaling on wayland.
Add option to specify medium/high/very high/ultra for -bm cbr as well, which should automatically pick bitrate based on resolution and framerate.
This should also be reflected in gsr ui.
Create a manpage and move --help text there and mention the manpage command to view it (and make it work in flatpak, maybe with man <link-to-manpage-file>).
Implement webcam support by using mjpeg with v4l2 and use ffmpeg mjpeg decoder.
After adding rpc, making recording while in replay/streaming work differently. Start recording should take audio as an argument, to optionally specify different audio for recording than replay/stream.
After adding rpc, make it possible to add/remove audio and video. The same number of audio tracks should remain, but the audio devices/app should be possible to configure. You should be able to configure the capture sources however you want.
It takes a while to shutdown gpu screen recorder when using replay with a long replay time (when the replay buffer is for example 6gb). Maybe this has to do with cleaning up so much memory. Try if _exit(0) is faster, ignoring cleanup of anything (assuming the gpu driver is happy with that, nvidia doesn't like such things. GPU drivers are often buggy when it comes to automatic cleanup).
Support desktop portal hdr capture when this gets merged: https://invent.kde.org/plasma/kwin/-/merge_requests/8293 note that it only works with pipewire >= 1.5.81. If codec is set to non-hdr codec when use desktop portal without hdr (convert to sdr, remove the code that forces non-hdr equivalent of the codec). Update gsr-gtk and gsr-ui accordingly.
Add option to save screenshot as .qoi. Use that then in gsr-ui for the background for the window (capture the monitor that the ui is going to show on). Do that on cosmic and hyprland, which are unable to display things behind the fullscreen window.
Support pausing recording when recording while replay/streaming.

View File

@@ -8,7 +8,7 @@
typedef struct gsr_egl gsr_egl;
#define NUM_ARGS 31
#define NUM_ARGS 32
typedef enum {
ARG_TYPE_STRING,
@@ -76,6 +76,7 @@ typedef struct {
const char *recording_saved_script;
bool verbose;
bool gl_debug;
bool fallback_cpu_encoding;
bool record_cursor;
bool date_folders;
bool restore_portal_session;

View File

@@ -1,4 +1,4 @@
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.8.0', default_options : ['warning_level=2'])
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.8.1', 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.8.0"
version = "5.8.1"
platforms = ["posix"]
[config]

View File

@@ -195,7 +195,7 @@ static void usage_header() {
"[-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] [-tune performance|quality] [-df yes|no] [-sc <script_path>] [-p <plugin_path>] "
"[-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] "
"[-o <output_file>] [-ro <output_directory>] [--list-capture-options [card_path]] [--list-audio-devices] [--list-application-audio] "
"[-fallback-cpu-encoding yes|no] [-o <output_file>] [-ro <output_directory>] [--list-capture-options [card_path]] [--list-audio-devices] [--list-application-audio] "
"[-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
fflush(stdout);
}
@@ -342,6 +342,12 @@ static void usage_full() {
printf(" Which device should be used for video encoding. Should either be 'gpu' or 'cpu'. 'cpu' option currently only work with h264 codec option (-k).\n");
printf(" Optional, set to 'gpu' by default.\n");
printf("\n");
printf(" -fallback-cpu-encoding\n");
printf(" If this is set to 'yes' and GPU encoding is not available on the system then CPU encoding will be used instead. Optional, set to 'no' by default.\n");
printf(" If the fallback to CPU encoding happens then h264 codec will forcefully be used.\n");
printf(" This should ideally not be used. Instead properly install vaapi on your system to encode the video with your GPU.\n");
printf(" Some very old GPUs dont support video encoding in which case this option may be used.\n");
printf("\n");
printf(" --info\n");
printf(" List info about the system. Lists the following information (prints them to stdout and exits):\n");
printf(" Supported video codecs (h264, h264_software, hevc, hevc_hdr, hevc_10bit, av1, av1_hdr, av1_10bit, vp8, vp9) and image codecs (jpeg, png) (if supported).\n");
@@ -470,6 +476,7 @@ static bool args_parser_set_values(args_parser *self) {
self->restore_portal_session = args_get_boolean_by_key(self->args, NUM_ARGS, "-restore-portal-session", false);
self->restart_replay_on_save = args_get_boolean_by_key(self->args, NUM_ARGS, "-restart-replay-on-save", false);
self->overclock = args_get_boolean_by_key(self->args, NUM_ARGS, "-oc", false);
self->fallback_cpu_encoding = args_get_boolean_by_key(self->args, NUM_ARGS, "-fallback-cpu-encoding", false);
self->audio_bitrate = args_get_i64_by_key(self->args, NUM_ARGS, "-ab", 0);
self->audio_bitrate *= 1000LL;
@@ -587,8 +594,8 @@ static bool args_parser_set_values(args_parser *self) {
return false;
}
if(self->region_size.x < 0 || self->region_size.y < 0 || self->region_position.x < 0 || self->region_position.y < 0) {
fprintf(stderr, "gsr error: invalid value for option -region '%s', expected width, height, x and y to be greater or equal to 0\n", region_str);
if(self->region_size.x < 0 || self->region_size.y < 0) {
fprintf(stderr, "gsr error: invalid value for option -region '%s', expected width and height to be greater or equal to 0\n", region_str);
usage();
return false;
}
@@ -756,6 +763,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
self->args[arg_index++] = (Arg){ .key = "-restore-portal-session", .optional = true, .list = false, .type = ARG_TYPE_BOOLEAN };
self->args[arg_index++] = (Arg){ .key = "-portal-session-token-filepath", .optional = true, .list = false, .type = ARG_TYPE_STRING };
self->args[arg_index++] = (Arg){ .key = "-encoder", .optional = true, .list = false, .type = ARG_TYPE_ENUM, .enum_values = video_encoder_enums, .num_enum_values = sizeof(video_encoder_enums)/sizeof(ArgEnum) };
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 };
assert(arg_index == NUM_ARGS);

View File

@@ -1637,6 +1637,32 @@ static bool get_supported_video_codecs(gsr_egl *egl, gsr_video_codec video_codec
return false;
}
static bool get_supported_video_codecs_with_cpu_fallback(gsr_egl *egl, args_parser *args_parser, bool cleanup, gsr_supported_video_codecs *video_codecs) {
if(get_supported_video_codecs(egl, args_parser->video_codec, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, cleanup, video_codecs))
return true;
if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU || !args_parser->fallback_cpu_encoding)
return false;
fprintf(stderr, "gsr warning: gpu encoding is not available on your system, trying cpu encoding instead because -fallback-cpu-encoding is enabled. Install the proper vaapi drivers on your system (if supported) if you experience performance issues\n");
if(get_supported_video_codecs(egl, GSR_VIDEO_CODEC_H264, true, cleanup, video_codecs)) {
if(args_parser->video_codec != (gsr_video_codec)GSR_VIDEO_CODEC_AUTO && args_parser->video_codec != GSR_VIDEO_CODEC_H264) {
fprintf(stderr, "gsr warning: cpu encoding is used but video codec isn't set to h264. Forcing video codec to h264\n");
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
}
args_parser->video_encoder = GSR_VIDEO_ENCODER_HW_CPU;
if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU && args_parser->bitrate_mode == GSR_BITRATE_MODE_VBR) {
fprintf(stderr, "gsr warning: bitrate mode has been forcefully set to qp because software encoding option doesn't support vbr option\n");
args_parser->bitrate_mode = GSR_BITRATE_MODE_QP;
}
return true;
}
return false;
}
static void xwayland_check_callback(const gsr_monitor *monitor, void *userdata) {
bool *xwayland_found = (bool*)userdata;
if(monitor->name_len >= 8 && strncmp(monitor->name, "XWAYLAND", 8) == 0)
@@ -2706,36 +2732,47 @@ static bool codec_supports_resolution(vec2i codec_max_resolution, vec2i capture_
return codec_max_resolution.x >= capture_resolution.x && codec_max_resolution.y >= capture_resolution.y;
}
static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *egl, bool use_software_video_encoder, bool video_codec_auto, bool is_flv, bool *low_power, gsr_supported_video_codecs *supported_video_codecs) {
static void print_codec_error(gsr_video_codec video_codec) {
if(video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO)
video_codec = GSR_VIDEO_CODEC_H264;
const char *video_codec_name = video_codec_to_string(video_codec);
fprintf(stderr, "gsr error: your gpu does not support '%s' video codec. If you are sure that your gpu does support '%s' video encoding and you are using an AMD/Intel GPU,\n"
" then make sure you have installed the GPU specific vaapi packages (intel-media-driver, libva-intel-driver, libva-mesa-driver and linux-firmware).\n"
" It's also possible that your distro has disabled hardware accelerated video encoding for '%s' video codec.\n"
" This may be the case on corporate distros such as Manjaro, Fedora or OpenSUSE.\n"
" You can test this by running 'vainfo | grep VAEntrypointEncSlice' to see if it matches any H264/HEVC/AV1/VP8/VP9 profile.\n"
" On such distros, you need to manually install mesa from source to enable H264/HEVC hardware acceleration, or use a more user friendly distro. Alternatively record with AV1 if supported by your GPU.\n"
" You can alternatively use the flatpak version of GPU Screen Recorder (https://flathub.org/apps/com.dec05eba.gpu_screen_recorder) which bypasses system issues with patented H264/HEVC codecs.\n"
" Make sure you have mesa-extra freedesktop runtime installed when using the flatpak (this should be the default), which can be installed with this command:\n"
" flatpak install --system org.freedesktop.Platform.GL.default//23.08-extra\n"
" If your GPU doesn't support hardware accelerated video encoding then you can use '-encoder cpu' option to encode with your cpu instead.\n", video_codec_name, video_codec_name, video_codec_name);
}
static const AVCodec* pick_video_codec(gsr_egl *egl, args_parser *args_parser, bool is_flv, bool *low_power, gsr_supported_video_codecs *supported_video_codecs) {
// TODO: software encoder for hevc, av1, vp8 and vp9
*low_power = false;
const AVCodec *video_codec_f = get_av_codec_if_supported(*video_codec, egl, use_software_video_encoder, supported_video_codecs);
const AVCodec *video_codec_f = get_av_codec_if_supported(args_parser->video_codec, egl, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, supported_video_codecs);
if(!video_codec_auto && !video_codec_f && !is_flv) {
switch(*video_codec) {
if(!video_codec_f && !is_flv && args_parser->video_encoder != GSR_VIDEO_ENCODER_HW_CPU) {
switch(args_parser->video_codec) {
case GSR_VIDEO_CODEC_H264: {
fprintf(stderr, "gsr warning: selected video codec h264 is not supported, trying hevc instead\n");
*video_codec = GSR_VIDEO_CODEC_HEVC;
if(supported_video_codecs->hevc.supported)
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
args_parser->video_codec = GSR_VIDEO_CODEC_HEVC;
break;
}
case GSR_VIDEO_CODEC_HEVC:
case GSR_VIDEO_CODEC_HEVC_HDR:
case GSR_VIDEO_CODEC_HEVC_10BIT: {
fprintf(stderr, "gsr warning: selected video codec hevc is not supported, trying h264 instead\n");
*video_codec = GSR_VIDEO_CODEC_H264;
if(supported_video_codecs->h264.supported)
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
break;
}
case GSR_VIDEO_CODEC_AV1:
case GSR_VIDEO_CODEC_AV1_HDR:
case GSR_VIDEO_CODEC_AV1_10BIT: {
fprintf(stderr, "gsr warning: selected video codec av1 is not supported, trying h264 instead\n");
*video_codec = GSR_VIDEO_CODEC_H264;
if(supported_video_codecs->h264.supported)
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
break;
}
case GSR_VIDEO_CODEC_VP8:
@@ -2744,47 +2781,37 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
break;
case GSR_VIDEO_CODEC_H264_VULKAN: {
fprintf(stderr, "gsr warning: selected video codec h264_vulkan is not supported, trying h264 instead\n");
*video_codec = GSR_VIDEO_CODEC_H264;
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
// Need to do a query again because this time it's without vulkan
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, supported_video_codecs)) {
if(!get_supported_video_codecs_with_cpu_fallback(egl, args_parser, true, supported_video_codecs)) {
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
print_codec_error(args_parser->video_codec);
_exit(11);
}
if(supported_video_codecs->h264.supported)
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
break;
}
case GSR_VIDEO_CODEC_HEVC_VULKAN: {
fprintf(stderr, "gsr warning: selected video codec hevc_vulkan is not supported, trying hevc instead\n");
*video_codec = GSR_VIDEO_CODEC_HEVC;
args_parser->video_codec = GSR_VIDEO_CODEC_HEVC;
// Need to do a query again because this time it's without vulkan
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, supported_video_codecs)) {
if(!get_supported_video_codecs_with_cpu_fallback(egl, args_parser, true, supported_video_codecs)) {
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
print_codec_error(args_parser->video_codec);
_exit(11);
}
if(supported_video_codecs->hevc.supported)
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
break;
}
}
video_codec_f = get_av_codec_if_supported(args_parser->video_codec, egl, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, supported_video_codecs);
}
if(!video_codec_f) {
const char *video_codec_name = video_codec_to_string(*video_codec);
fprintf(stderr, "gsr error: your gpu does not support '%s' video codec. If you are sure that your gpu does support '%s' video encoding and you are using an AMD/Intel GPU,\n"
" then make sure you have installed the GPU specific vaapi packages (intel-media-driver, libva-intel-driver, libva-mesa-driver and linux-firmware).\n"
" It's also possible that your distro has disabled hardware accelerated video encoding for '%s' video codec.\n"
" This may be the case on corporate distros such as Manjaro, Fedora or OpenSUSE.\n"
" You can test this by running 'vainfo | grep VAEntrypointEncSlice' to see if it matches any H264/HEVC/AV1/VP8/VP9 profile.\n"
" On such distros, you need to manually install mesa from source to enable H264/HEVC hardware acceleration, or use a more user friendly distro. Alternatively record with AV1 if supported by your GPU.\n"
" You can alternatively use the flatpak version of GPU Screen Recorder (https://flathub.org/apps/com.dec05eba.gpu_screen_recorder) which bypasses system issues with patented H264/HEVC codecs.\n"
" Make sure you have mesa-extra freedesktop runtime installed when using the flatpak (this should be the default), which can be installed with this command:\n"
" flatpak install --system org.freedesktop.Platform.GL.default//23.08-extra\n"
" If your GPU doesn't support hardware accelerated video encoding then you can use '-encoder cpu' option to encode with your cpu instead.\n", video_codec_name, video_codec_name, video_codec_name);
print_codec_error(args_parser->video_codec);
_exit(54);
}
*low_power = video_codec_only_supports_low_power_mode(*supported_video_codecs, *video_codec);
*low_power = video_codec_only_supports_low_power_mode(*supported_video_codecs, args_parser->video_codec);
return video_codec_f;
}
@@ -2810,25 +2837,33 @@ static gsr_video_codec select_appropriate_video_codec_automatically(gsr_capture_
}
}
static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capture_metadata, gsr_video_codec *video_codec, const char *file_extension, bool use_software_video_encoder, gsr_egl *egl, bool *low_power) {
static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capture_metadata, args_parser *args_parser, const char *file_extension, gsr_egl *egl, bool *low_power) {
gsr_supported_video_codecs supported_video_codecs;
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, &supported_video_codecs)) {
if(!get_supported_video_codecs_with_cpu_fallback(egl, args_parser, true, &supported_video_codecs)) {
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
print_codec_error(args_parser->video_codec);
_exit(11);
}
set_supported_video_codecs_ffmpeg(&supported_video_codecs, nullptr, egl->gpu_info.vendor);
const bool video_codec_auto = *video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO;
if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU && args_parser->video_codec != (gsr_video_codec)GSR_VIDEO_CODEC_AUTO && args_parser->video_codec != GSR_VIDEO_CODEC_H264) {
fprintf(stderr, "gsr warning: cpu encoding is used but video codec isn't set to h264. Forcing video codec to h264\n");
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
}
if(args_parser->video_encoder != GSR_VIDEO_ENCODER_HW_CPU)
set_supported_video_codecs_ffmpeg(&supported_video_codecs, nullptr, egl->gpu_info.vendor);
const bool video_codec_auto = args_parser->video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO;
if(video_codec_auto) {
if(strcmp(file_extension, "webm") == 0) {
fprintf(stderr, "gsr info: using vp8 encoder because a codec was not specified and the file extension is .webm\n");
*video_codec = GSR_VIDEO_CODEC_VP8;
} else if(use_software_video_encoder) {
args_parser->video_codec = GSR_VIDEO_CODEC_VP8;
} else if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU) {
fprintf(stderr, "gsr info: using h264 encoder because a codec was not specified\n");
*video_codec = GSR_VIDEO_CODEC_H264;
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
} else {
*video_codec = select_appropriate_video_codec_automatically(capture_metadata, &supported_video_codecs);
if(*video_codec == (gsr_video_codec)-1) {
args_parser->video_codec = select_appropriate_video_codec_automatically(capture_metadata, &supported_video_codecs);
if(args_parser->video_codec == (gsr_video_codec)-1) {
fprintf(stderr, "gsr error: no video encoder was specified and neither h264, hevc nor av1 are supported on your system or you are trying to capture at a resolution higher than your system supports for each codec\n");
_exit(52);
}
@@ -2838,8 +2873,8 @@ static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capt
// TODO: Allow hevc, vp9 and av1 in (enhanced) flv (supported since ffmpeg 6.1)
const bool is_flv = strcmp(file_extension, "flv") == 0;
if(is_flv) {
if(*video_codec != GSR_VIDEO_CODEC_H264) {
*video_codec = GSR_VIDEO_CODEC_H264;
if(args_parser->video_codec != GSR_VIDEO_CODEC_H264) {
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
fprintf(stderr, "gsr warning: hevc/av1 is not compatible with flv, falling back to h264 instead.\n");
}
@@ -2852,8 +2887,8 @@ static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capt
const bool is_hls = strcmp(file_extension, "m3u8") == 0;
if(is_hls) {
if(video_codec_is_av1(*video_codec)) {
*video_codec = GSR_VIDEO_CODEC_HEVC;
if(video_codec_is_av1(args_parser->video_codec)) {
args_parser->video_codec = GSR_VIDEO_CODEC_HEVC;
fprintf(stderr, "gsr warning: av1 is not compatible with hls (m3u8), falling back to hevc instead.\n");
}
@@ -2864,18 +2899,12 @@ static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capt
// }
}
if(use_software_video_encoder && *video_codec != GSR_VIDEO_CODEC_H264) {
fprintf(stderr, "gsr error: \"-encoder cpu\" option is currently only available when using h264 codec option (-k)\n");
args_parser_print_usage();
_exit(1);
}
const AVCodec *codec = pick_video_codec(egl, args_parser, is_flv, low_power, &supported_video_codecs);
const AVCodec *codec = pick_video_codec(video_codec, egl, use_software_video_encoder, video_codec_auto, is_flv, low_power, &supported_video_codecs);
const vec2i codec_max_resolution = codec_get_max_resolution(*video_codec, use_software_video_encoder, &supported_video_codecs);
const vec2i codec_max_resolution = codec_get_max_resolution(args_parser->video_codec, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, &supported_video_codecs);
const vec2i capture_size = {capture_metadata.width, capture_metadata.height};
if(!codec_supports_resolution(codec_max_resolution, capture_size)) {
const char *video_codec_name = video_codec_to_string(*video_codec);
const char *video_codec_name = video_codec_to_string(args_parser->video_codec);
fprintf(stderr, "gsr error: The max resolution for video codec %s is %dx%d while you are trying to capture at resolution %dx%d. Change capture resolution or video codec and try again\n",
video_codec_name, codec_max_resolution.x, codec_max_resolution.y, capture_size.x, capture_size.y);
_exit(53);
@@ -3282,7 +3311,7 @@ int main(int argc, char **argv) {
std::vector<AudioTrack> audio_tracks;
bool low_power = false;
const AVCodec *video_codec_f = select_video_codec_with_fallback(capture_metadata, &arg_parser.video_codec, file_extension.c_str(), arg_parser.video_encoder == GSR_VIDEO_ENCODER_HW_CPU, &egl, &low_power);
const AVCodec *video_codec_f = select_video_codec_with_fallback(capture_metadata, &arg_parser, file_extension.c_str(), &egl, &low_power);
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, capture_metadata.width, capture_metadata.height);