mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-03 02:01:39 +09:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec90166c6e | ||
|
|
d34a1036ef | ||
|
|
bf6011ffcf | ||
|
|
20e101bfe9 | ||
|
|
e99605fec2 | ||
|
|
9e59f5f5cd | ||
|
|
96c62f2db2 | ||
|
|
dc15c69848 | ||
|
|
a545fe45c3 | ||
|
|
e623145de8 | ||
|
|
7d4a4068ff | ||
|
|
57ac400eab | ||
|
|
cf65b24d3d | ||
|
|
8c42c47627 | ||
|
|
dee37433af | ||
|
|
f9f0074f29 |
30
README.md
30
README.md
@@ -14,7 +14,7 @@ This is a cli-only tool, if you want an UI for this check out [GPU Screen Record
|
||||
Supported video codecs:
|
||||
* H264 (default)
|
||||
* HEVC (Optionally with HDR)
|
||||
* AV1 (Optionally with HDR. Not currently supported on NVIDIA if you use GPU Screen Recorder flatpak)
|
||||
* AV1 (Optionally with HDR. Not currently supported on NVIDIA in the flatpak version of GPU Screen Recorder)
|
||||
* VP8
|
||||
* VP9
|
||||
|
||||
@@ -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)
|
||||
@@ -154,6 +154,11 @@ Note that this option is currently only available on X11, or with desktop portal
|
||||
## NVIDIA
|
||||
Nvidia drivers have an issue where CUDA breaks if CUDA is running when suspend/hibernation happens, and it remains broken until you reload the nvidia driver. `extra/gsr-nvidia.conf` will be installed by default when you install GPU Screen Recorder and that should fix this issue. If this doesn't fix the issue for you then your distro may use a different path for modprobe files. In that case you have to install that `extra/gsr-nvidia.conf` yourself into that location.
|
||||
You have to reboot your computer after installing GPU Screen Recorder for the first time for the fix to have any effect.
|
||||
|
||||
# TEMPORARY ISSUES
|
||||
1) Videos are in variable framerate format. Use MPV to play such videos, otherwise you might experience stuttering in the video if you are using a buggy video player. You can try saving the video into a .mkv file instead as some software may have better support for .mkv files (such as kdenlive). You can use the "-fm cfr" option to to use constant framerate mode.
|
||||
2) FLAC audio codec is disabled at the moment because of temporary issues.
|
||||
|
||||
# Examples
|
||||
Look at the [scripts](https://git.dec05eba.com/gpu-screen-recorder/tree/scripts) directory for script examples. For example if you want to automatically save a recording/replay into a folder with the same name as the game you are recording.
|
||||
|
||||
@@ -185,10 +190,6 @@ See [https://git.dec05eba.com/?p=about](https://git.dec05eba.com/?p=about).
|
||||
# Demo
|
||||
[](https://www.youtube.com/watch?v=n5tm0g01n6A)
|
||||
|
||||
# TEMPORARY ISSUES
|
||||
1) Videos are in variable framerate format. Use MPV to play such videos, otherwise you might experience stuttering in the video if you are using a buggy video player. You can try saving the video into a .mkv file instead as some software may have better support for .mkv files (such as kdenlive). You can use the "-fm cfr" option to to use constant framerate mode.
|
||||
2) FLAC audio codec is disabled at the moment because of temporary issues.
|
||||
|
||||
# FAQ
|
||||
## It tells me that my AMD/Intel GPU is not supported or that my GPU doesn't support h264/hevc, but that's not true!
|
||||
Some linux distros (such as manjaro and fedora) disable hardware accelerated h264/hevc on AMD/Intel because of "patent license issues". If you are using an arch-based distro then you can install mesa-git instead of mesa and if you are using another distro then you may have to switch to a better distro. On fedora based distros you can follow this: [Hardware Accelerated Codec](https://rpmfusion.org/Howto/Multimedia).\
|
||||
@@ -202,7 +203,9 @@ Browsers and discord don't support hevc video codec at the moment. Choose h264 v
|
||||
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 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).
|
||||
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 ffmpeg that was fixed in ffmpeg version 8.\
|
||||
If your ffmpeg version is older than 8 then you can use the flatpak version of GPU Screen Recorder which comes with ffmpeg version >= 8.\
|
||||
Alternatively you can record with h264 codec (-k h264, which is also the default codec) to workaround this issue.
|
||||
## 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.
|
||||
## I get stutter in the video
|
||||
@@ -224,7 +227,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
|
||||
|
||||
38
TODO
38
TODO
@@ -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?
|
||||
@@ -189,13 +186,6 @@ Use wayland color management protocol when it's available: https://gitlab.freede
|
||||
Use different exit codes for different errors. Use one for invalid -w option, another one for invalid -a option for audio devices, etc. This is to make UI error reporting better.
|
||||
Document these exit codes in an exit code .md file, or finally create a manpage where this can be documented.
|
||||
|
||||
Ffmpeg fixed black bars in videos on amd when using hevc and when recording at some resolutions, such as 1080p:
|
||||
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.
|
||||
The version is libavcodec >= 61.28.100
|
||||
|
||||
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(?).
|
||||
|
||||
Always disable prime run/dri prime and list all monitors to record from from all cards.
|
||||
@@ -273,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.
|
||||
|
||||
@@ -314,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.
|
||||
@@ -336,3 +327,26 @@ 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.
|
||||
|
||||
Maybe use VK_VALVE_video_encode_rgb_conversion with vulkan encoding for shader-less rgb to yuv conversion. That would allow screen capture with no gpu processing.
|
||||
|
||||
Cursor sometimes doesn't have color when capturing region scaled (on kde plasma wayland at least).
|
||||
@@ -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;
|
||||
|
||||
@@ -60,7 +60,7 @@ typedef struct {
|
||||
int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conversion_params *params);
|
||||
void gsr_color_conversion_deinit(gsr_color_conversion *self);
|
||||
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture, bool alpha_blending);
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture);
|
||||
void gsr_color_conversion_clear(gsr_color_conversion *self);
|
||||
void gsr_color_conversion_read_destination_texture(gsr_color_conversion *self, int destination_texture_index, int x, int y, int width, int height, unsigned int color_format, unsigned int data_format, void *pixels);
|
||||
|
||||
|
||||
@@ -58,4 +58,6 @@ vec2i scale_keep_aspect_ratio(vec2i from, vec2i to);
|
||||
|
||||
unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter);
|
||||
|
||||
bool get_nvidia_driver_version(int *major, int *minor);
|
||||
|
||||
#endif /* GSR_UTILS_H */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.7.4', default_options : ['warning_level=2'])
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.8.2', default_options : ['warning_level=2'])
|
||||
|
||||
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
|
||||
if get_option('buildtype') == 'debug'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gpu-screen-recorder"
|
||||
type = "executable"
|
||||
version = "5.7.4"
|
||||
version = "5.8.2"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -293,8 +293,9 @@ static void usage_full() {
|
||||
printf(" Works only if your have \"Coolbits\" set to \"12\" in NVIDIA X settings, see README for more information. Note! use at your own risk! Optional, disabled by default.\n");
|
||||
printf("\n");
|
||||
printf(" -fm Framerate mode. Should be either 'cfr' (constant frame rate), 'vfr' (variable frame rate) or 'content'. Optional, set to 'vfr' by default.\n");
|
||||
printf(" 'vfr' is recommended for recording for less issue with very high system load but some applications such as video editors may not support it properly.\n");
|
||||
printf(" 'vfr' is recommended for general recording for less issue with very high system load but some applications such as video editors may not support it properly.\n");
|
||||
printf(" 'content' is currently only supported on X11 or when using portal capture option. The 'content' option matches the recording frame rate to the captured content.\n");
|
||||
printf(" 'content' is the best option to use when possible as it avoids capturing duplicate frames, which results in a smoother video.\n");
|
||||
printf("\n");
|
||||
printf(" -bm Bitrate mode. Should be either 'auto', 'qp' (constant quality), 'vbr' (variable bitrate) or 'cbr' (constant bitrate). Optional, set to 'auto' by default which defaults to 'qp' on all devices\n");
|
||||
printf(" except steam deck that has broken drivers and doesn't support qp.\n");
|
||||
@@ -341,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");
|
||||
@@ -469,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;
|
||||
@@ -586,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;
|
||||
}
|
||||
@@ -755,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);
|
||||
|
||||
@@ -508,7 +508,7 @@ static void render_drm_cursor(gsr_capture_kms *self, gsr_color_conversion *color
|
||||
gsr_color_conversion_draw(color_conversion, self->cursor_texture_id,
|
||||
cursor_pos, (vec2i){cursor_size.x * scale.x, cursor_size.y * scale.y},
|
||||
(vec2i){0, 0}, cursor_size, cursor_size,
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, cursor_texture_id_is_external, true);
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, cursor_texture_id_is_external);
|
||||
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
@@ -536,7 +536,7 @@ static void render_x11_cursor(gsr_capture_kms *self, gsr_color_conversion *color
|
||||
gsr_color_conversion_draw(color_conversion, self->x11_cursor.texture_id,
|
||||
cursor_pos, (vec2i){self->x11_cursor.size.x * scale.x, self->x11_cursor.size.y * scale.y},
|
||||
(vec2i){0, 0}, self->x11_cursor.size, self->x11_cursor.size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
@@ -654,7 +654,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
gsr_color_conversion_draw(color_conversion, self->external_texture_fallback ? self->external_input_texture_id : self->input_texture_id,
|
||||
target_pos, output_size,
|
||||
capture_pos, self->capture_size, (vec2i){ drm_fd->width, drm_fd->height },
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, self->external_texture_fallback, false);
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, self->external_texture_fallback);
|
||||
|
||||
if(self->params.record_cursor) {
|
||||
gsr_kms_response_item *cursor_drm_fd = find_cursor_drm_if_on_monitor(self, drm_fd->connector_id, capture_is_combined_plane);
|
||||
|
||||
@@ -55,40 +55,6 @@ static uint32_t get_output_id_from_display_name(NVFBC_RANDR_OUTPUT_INFO *outputs
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Test with optimus and open kernel modules */
|
||||
static bool get_driver_version(int *major, int *minor) {
|
||||
*major = 0;
|
||||
*minor = 0;
|
||||
|
||||
FILE *f = fopen("/proc/driver/nvidia/version", "rb");
|
||||
if(!f) {
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version (failed to read /proc/driver/nvidia/version)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, f);
|
||||
buffer[bytes_read] = '\0';
|
||||
|
||||
bool success = false;
|
||||
const char *p = strstr(buffer, "Kernel Module");
|
||||
if(p) {
|
||||
p += 13;
|
||||
int driver_major_version = 0, driver_minor_version = 0;
|
||||
if(sscanf(p, "%d.%d", &driver_major_version, &driver_minor_version) == 2) {
|
||||
*major = driver_major_version;
|
||||
*minor = driver_minor_version;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!success)
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version\n");
|
||||
|
||||
fclose(f);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool version_at_least(int major, int minor, int expected_major, int expected_minor) {
|
||||
return major > expected_major || (major == expected_major && minor >= expected_minor);
|
||||
}
|
||||
@@ -288,7 +254,7 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
self->supports_direct_cursor = false;
|
||||
int driver_major_version = 0;
|
||||
int driver_minor_version = 0;
|
||||
if(self->params.direct_capture && get_driver_version(&driver_major_version, &driver_minor_version)) {
|
||||
if(self->params.direct_capture && get_nvidia_driver_version(&driver_major_version, &driver_minor_version)) {
|
||||
fprintf(stderr, "gsr info: detected nvidia version: %d.%d\n", driver_major_version, driver_minor_version);
|
||||
|
||||
// TODO:
|
||||
@@ -397,7 +363,7 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, gsr_capture_metadata *cap
|
||||
gsr_color_conversion_draw(color_conversion, self->setup_params.dwTextures[grab_params.dwTextureIndex],
|
||||
target_pos, (vec2i){output_size.x, output_size.y},
|
||||
self->params.region_position, frame_size, original_frame_size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_BGR, false, false);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_BGR, false);
|
||||
|
||||
//self->params.egl->glFlush();
|
||||
//self->params.egl->glFinish();
|
||||
|
||||
@@ -373,7 +373,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
gsr_color_conversion_draw(color_conversion, self->pipewire_data.using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id,
|
||||
target_pos, output_size,
|
||||
(vec2i){self->pipewire_data.region.x, self->pipewire_data.region.y}, (vec2i){self->pipewire_data.region.width, self->pipewire_data.region.height}, actual_texture_size,
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, self->pipewire_data.using_external_image, fourcc_alpha);
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, self->pipewire_data.using_external_image);
|
||||
|
||||
if(self->params.record_cursor && self->texture_map.cursor_texture_id > 0 && self->pipewire_data.cursor_region.width > 0) {
|
||||
const vec2d scale = {
|
||||
@@ -394,7 +394,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
(vec2i){0, 0},
|
||||
(vec2i){self->pipewire_data.cursor_region.width, self->pipewire_data.cursor_region.height},
|
||||
(vec2i){self->pipewire_data.cursor_region.width, self->pipewire_data.cursor_region.height},
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, false, true);
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, false);
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata
|
||||
gsr_color_conversion_draw(color_conversion, window_texture_get_opengl_texture_id(&self->window_texture),
|
||||
target_pos, output_size,
|
||||
(vec2i){0, 0}, self->texture_size, self->texture_size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, false);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
if(self->params.record_cursor && self->cursor.visible) {
|
||||
const vec2d scale = {
|
||||
@@ -265,7 +265,7 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata
|
||||
gsr_color_conversion_draw(color_conversion, self->cursor.texture_id,
|
||||
cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y},
|
||||
(vec2i){0, 0}, self->cursor.size, self->cursor.size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
}
|
||||
|
||||
//self->params.egl->glFlush();
|
||||
|
||||
@@ -160,7 +160,7 @@ static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
gsr_color_conversion_draw(color_conversion, self->texture_id,
|
||||
target_pos, output_size,
|
||||
(vec2i){0, 0}, self->capture_size, self->capture_size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, false);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
if(self->params.record_cursor && self->cursor.visible) {
|
||||
const vec2d scale = {
|
||||
@@ -181,7 +181,7 @@ static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
gsr_color_conversion_draw(color_conversion, self->cursor.texture_id,
|
||||
cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y},
|
||||
(vec2i){0, 0}, self->cursor.size, self->cursor.size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,6 @@
|
||||
" 0.060118, 0.429412, -0.038049, 0.000000,\n" \
|
||||
" 0.062745, 0.500000, 0.500000, 1.000000);\n"
|
||||
|
||||
static int max_int(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
static const char* color_format_range_get_transform_matrix(gsr_destination_color color_format, gsr_color_range color_range) {
|
||||
switch(color_format) {
|
||||
case GSR_DESTINATION_COLOR_NV12: {
|
||||
@@ -483,6 +479,12 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
|
||||
const vec2i dest_texture_size = self->params.destination_textures_size[0];
|
||||
const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
||||
|
||||
if(rotation == GSR_ROT_90 || rotation == GSR_ROT_270) {
|
||||
const float tmp = texture_size.x;
|
||||
texture_size.x = texture_size.y;
|
||||
texture_size.y = tmp;
|
||||
}
|
||||
|
||||
self->params.egl->glBindTexture(texture_target, texture_id);
|
||||
gsr_color_conversion_swizzle_texture_source(self, source_color);
|
||||
|
||||
@@ -501,18 +503,11 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
|
||||
(float)source_position.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y),
|
||||
};
|
||||
|
||||
vec2f texture_size_norm = {
|
||||
const vec2f texture_size_norm = {
|
||||
(float)source_size.x / (texture_size.x == 0 ? 1.0f : (float)texture_size.x),
|
||||
(float)source_size.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y),
|
||||
};
|
||||
|
||||
if(rotation == GSR_ROT_90 || rotation == GSR_ROT_270) {
|
||||
const float ratio_x = (double)source_size.x / (double)source_size.y;
|
||||
const float ratio_y = (double)source_size.y / (double)source_size.x;
|
||||
texture_size_norm.x *= ratio_y;
|
||||
texture_size_norm.y *= ratio_x;
|
||||
}
|
||||
|
||||
const float vertices[] = {
|
||||
-1.0f + 0.0f, -1.0f + 0.0f + size_norm.y, texture_pos_norm.x, texture_pos_norm.y + texture_size_norm.y,
|
||||
-1.0f + 0.0f, -1.0f + 0.0f, texture_pos_norm.x, texture_pos_norm.y,
|
||||
@@ -574,7 +569,7 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
|
||||
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture, bool alpha_blending) {
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture) {
|
||||
assert(!external_texture || self->params.load_external_image_shader);
|
||||
if(external_texture && !self->params.load_external_image_shader) {
|
||||
fprintf(stderr, "gsr error: gsr_color_conversion_draw: external texture not loaded\n");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <libavutil/hwcontext_vaapi.h>
|
||||
#include <libavutil/intreadwrite.h>
|
||||
|
||||
#include <va/va.h>
|
||||
#include <va/va_drm.h>
|
||||
#include <va/va_drmcommon.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -150,13 +152,102 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self
|
||||
|
||||
static void gsr_video_encoder_vaapi_stop(gsr_video_encoder_vaapi *self, AVCodecContext *video_codec_context);
|
||||
|
||||
static bool supports_hevc_without_padding(const char *card_path) {
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 28, 100) && VA_CHECK_VERSION(1, 21, 0)
|
||||
VAStatus va_status;
|
||||
VAConfigID va_config = 0;
|
||||
unsigned int num_surface_attr = 0;
|
||||
VASurfaceAttrib *surface_attr_list = NULL;
|
||||
bool supports_surface_attrib_alignment_size = false;
|
||||
int va_major = 0;
|
||||
int va_minor = 0;
|
||||
bool initialized = false;
|
||||
|
||||
char render_path[128];
|
||||
if(!gsr_card_path_get_render_path(card_path, render_path)) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to get /dev/dri/renderDXXX file from %s\n", card_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int drm_fd = open(render_path, O_RDWR);
|
||||
if(drm_fd == -1) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to open device %s\n", render_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const VADisplay va_dpy = vaGetDisplayDRM(drm_fd);
|
||||
if(!va_dpy) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to get vaapi display for device %s\n", render_path);
|
||||
goto done;
|
||||
}
|
||||
|
||||
vaSetInfoCallback(va_dpy, NULL, NULL);
|
||||
|
||||
if(vaInitialize(va_dpy, &va_major, &va_minor) != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: vaInitialize failed\n");
|
||||
goto done;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
va_status = vaCreateConfig(va_dpy, VAProfileHEVCMain, VAEntrypointEncSlice, NULL, 0, &va_config);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
va_status = vaCreateConfig(va_dpy, VAProfileHEVCMain, VAEntrypointEncSliceLP, NULL, 0, &va_config);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to create hevc vaapi config, error: %s (%d)\n", vaErrorStr(va_status), va_status);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
va_status = vaQuerySurfaceAttributes(va_dpy, va_config, 0, &num_surface_attr);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to query vaapi surface attributes size, error: %s (%d)\n", vaErrorStr(va_status), va_status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
surface_attr_list = malloc(num_surface_attr * sizeof(VASurfaceAttrib));
|
||||
if(!surface_attr_list) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to allocate memory for %u vaapi surface attributes, error: %s (%d)\n", num_surface_attr, vaErrorStr(va_status), va_status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
va_status = vaQuerySurfaceAttributes(va_dpy, va_config, surface_attr_list, &num_surface_attr);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to query vaapi surface attributes data, error: %s (%d)\n", vaErrorStr(va_status), va_status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < num_surface_attr; ++i) {
|
||||
if(surface_attr_list[i].type == VASurfaceAttribAlignmentSize) {
|
||||
supports_surface_attrib_alignment_size = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
free(surface_attr_list);
|
||||
if(va_config > 0)
|
||||
vaDestroyConfig(va_dpy, va_config);
|
||||
if(initialized)
|
||||
vaTerminate(va_dpy);
|
||||
if(drm_fd > 0)
|
||||
close(drm_fd);
|
||||
return supports_surface_attrib_alignment_size;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool gsr_video_encoder_vaapi_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) {
|
||||
gsr_video_encoder_vaapi *self = encoder->priv;
|
||||
|
||||
if(self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && video_codec_context->codec_id == AV_CODEC_ID_HEVC) {
|
||||
// TODO: dont do this if using ffmpeg reports that this is not needed (AMD driver bug that was fixed recently)
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 64);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 16);
|
||||
if(supports_hevc_without_padding(self->params.egl->card_path)) {
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 2);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 2);
|
||||
} else {
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 64);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 16);
|
||||
}
|
||||
} else if(self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && video_codec_context->codec_id == AV_CODEC_ID_AV1) {
|
||||
// TODO: Dont do this for VCN 5 and forward which should fix this hardware bug
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 64);
|
||||
|
||||
161
src/main.cpp
161
src/main.cpp
@@ -380,8 +380,9 @@ static AVCodecContext *create_video_codec_context(AVPixelFormat pix_fmt, const A
|
||||
codec_context->colorspace = AVCOL_SPC_BT709;
|
||||
}
|
||||
//codec_context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||
if(codec->id == AV_CODEC_ID_HEVC)
|
||||
codec_context->codec_tag = MKTAG('h', 'v', 'c', '1'); // QuickTime on MacOS requires this or the video wont be playable
|
||||
// Can't use this because it's fucking broken in ffmpeg 8 or new mesa. It produces garbage output
|
||||
//if(codec->id == AV_CODEC_ID_HEVC)
|
||||
// codec_context->codec_tag = MKTAG('h', 'v', 'c', '1'); // QuickTime on MacOS requires this or the video wont be playable
|
||||
|
||||
if(arg_parser.bitrate_mode == GSR_BITRATE_MODE_CBR) {
|
||||
codec_context->bit_rate = arg_parser.video_bitrate;
|
||||
@@ -1636,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)
|
||||
@@ -2705,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:
|
||||
@@ -2743,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;
|
||||
}
|
||||
@@ -2809,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);
|
||||
}
|
||||
@@ -2837,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");
|
||||
}
|
||||
|
||||
@@ -2851,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");
|
||||
}
|
||||
|
||||
@@ -2863,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);
|
||||
@@ -3072,6 +3102,10 @@ int main(int argc, char **argv) {
|
||||
|
||||
set_display_server_environment_variables();
|
||||
|
||||
// Linux nvidia driver 580.105.08 added the environment variable CUDA_DISABLE_PERF_BOOST to disable the p2 power level issue,
|
||||
// where running cuda (which includes nvenc) causes the gpu to be forcefully set to p2 power level which on many nvidia gpus
|
||||
// decreases gpu performance in games. On my GTX 1080 it decreased game performance by 10% for absolutely no reason.
|
||||
setenv("CUDA_DISABLE_PERF_BOOST", "1", true);
|
||||
// Stop nvidia driver from buffering frames
|
||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
||||
// If this is set to 1 then cuGraphicsGLRegisterImage will fail for egl context with error: invalid OpenGL or DirectX context,
|
||||
@@ -3109,6 +3143,15 @@ int main(int argc, char **argv) {
|
||||
if(!args_parser_parse(&arg_parser, argc, argv, &arg_handlers, NULL))
|
||||
_exit(1);
|
||||
|
||||
if(arg_parser.overclock) {
|
||||
int driver_major_version = 0;
|
||||
int driver_minor_version = 0;
|
||||
if(get_nvidia_driver_version(&driver_major_version, &driver_minor_version) && (driver_major_version > 580 || (driver_major_version == 580 && driver_minor_version >= 105))) {
|
||||
fprintf(stderr, "gsr info: overclocking was set by has been forcefully disabled since your gpu supports CUDA_DISABLE_PERF_BOOST to workaround driver issue (overclocking is not needed)\n");
|
||||
arg_parser.overclock = false;
|
||||
}
|
||||
}
|
||||
|
||||
//av_log_set_level(AV_LOG_TRACE);
|
||||
|
||||
const Arg *audio_input_arg = args_parser_get_arg(&arg_parser, "-a");
|
||||
@@ -3281,7 +3324,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);
|
||||
@@ -3778,7 +3821,7 @@ int main(int argc, char **argv) {
|
||||
gsr_color_conversion_draw(&color_conversion, plugins.texture,
|
||||
{0, 0}, {capture_metadata.width, capture_metadata.height},
|
||||
{0, 0}, {capture_metadata.width, capture_metadata.height},
|
||||
{capture_metadata.width, capture_metadata.height}, GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
{capture_metadata.width, capture_metadata.height}, GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
}
|
||||
|
||||
if(capture_has_synchronous_task) {
|
||||
|
||||
36
src/utils.c
36
src/utils.c
@@ -148,6 +148,8 @@ int get_connector_type_by_name(const char *name) {
|
||||
return 3;
|
||||
else if(len >= 4 && strncmp(name, "eDP-", 4) == 0)
|
||||
return 4;
|
||||
else if(len >= 4 && strncmp(name, "DVI-", 4) == 0)
|
||||
return 5;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
@@ -606,3 +608,37 @@ unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal
|
||||
egl->glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return texture_id;
|
||||
}
|
||||
|
||||
/* TODO: Test with optimus and open kernel modules */
|
||||
bool get_nvidia_driver_version(int *major, int *minor) {
|
||||
*major = 0;
|
||||
*minor = 0;
|
||||
|
||||
FILE *f = fopen("/proc/driver/nvidia/version", "rb");
|
||||
if(!f) {
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version (failed to read /proc/driver/nvidia/version)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, f);
|
||||
buffer[bytes_read] = '\0';
|
||||
|
||||
bool success = false;
|
||||
const char *p = strstr(buffer, "Kernel Module");
|
||||
if(p) {
|
||||
p += 13;
|
||||
int driver_major_version = 0, driver_minor_version = 0;
|
||||
if(sscanf(p, "%d.%d", &driver_major_version, &driver_minor_version) == 2) {
|
||||
*major = driver_major_version;
|
||||
*minor = driver_minor_version;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!success)
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version\n");
|
||||
|
||||
fclose(f);
|
||||
return success;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user