mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
Make '-w screen' capture the first monitor on nvidia x11 as well to make it work like amd, intel and nvidia wayland. Keep screen-direct for all monitors, that is gsync compatible
This commit is contained in:
@@ -23,9 +23,8 @@ Supported audio codecs:
|
|||||||
## Note
|
## Note
|
||||||
This software works on X11 and Wayland on AMD, Intel and NVIDIA.
|
This software works on X11 and Wayland on AMD, Intel and NVIDIA.
|
||||||
### TEMPORARY ISSUES
|
### TEMPORARY ISSUES
|
||||||
1) screen-direct capture has been temporary disabled as it causes issues with stuttering. This might be a nvfbc bug.
|
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) 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.
|
||||||
3) FLAC audio codec is disabled at the moment because of temporary issues.
|
|
||||||
### AMD/Intel/Wayland root permission
|
### AMD/Intel/Wayland root permission
|
||||||
When recording a window or when using the `-w portal` option under AMD/Intel no special user permission is required,
|
When recording a window or when using the `-w portal` option under AMD/Intel no special user permission is required,
|
||||||
however when recording a monitor (or when using wayland) the program needs root permission (to access KMS).\
|
however when recording a monitor (or when using wayland) the program needs root permission (to access KMS).\
|
||||||
@@ -44,7 +43,7 @@ To enable overclocking for optimal performance use the `-oc` option when running
|
|||||||
Note that this only works when Xorg server is running as root, and using this option will only give you a performance boost if the game you are recording is bottlenecked by your GPU.\
|
Note that this only works when Xorg server is running as root, and using this option will only give you a performance boost if the game you are recording is bottlenecked by your GPU.\
|
||||||
Note! use at your own risk!
|
Note! use at your own risk!
|
||||||
# VRR/G-SYNC
|
# VRR/G-SYNC
|
||||||
This should work fine on AMD/Intel X11 or Wayland. On Nvidia X11 G-SYNC only works with the -w screen-direct-force option, but because of bugs in the Nvidia driver this option is not always recommended.
|
This should work fine on AMD/Intel X11 or Wayland. On Nvidia X11 G-SYNC only works with the -w screen-direct option, but because of bugs in the Nvidia driver this option is not always recommended.
|
||||||
For example it can cause your computer to freeze when recording certain games.
|
For example it can cause your computer to freeze when recording certain games.
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|||||||
11
TODO
11
TODO
@@ -197,3 +197,14 @@ Improve software encoding performance.
|
|||||||
Add option to record audio from the recorded window only.
|
Add option to record audio from the recorded window only.
|
||||||
|
|
||||||
Add option to automatically select best video codec available. Add -k best, -k best_10bit and -k best_hdr.
|
Add option to automatically select best video codec available. Add -k best, -k best_10bit and -k best_hdr.
|
||||||
|
|
||||||
|
HDR is broken on kde plasma > 6.2 because of change to how HDR metadata works. See https://github.com/dec05eba/gpu-screen-recorder-issues/issues/60.
|
||||||
|
Use wayland color management protocol when it's available: https://gitlab.freedesktop.org/wayland/wayland-protocols/-/merge_requests/14.
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -137,7 +137,7 @@ bool gsr_damage_set_target_monitor(gsr_damage *self, const char *monitor_name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
memset(&self->monitor, 0, sizeof(self->monitor));
|
memset(&self->monitor, 0, sizeof(self->monitor));
|
||||||
if(strcmp(monitor_name, "screen") != 0 && strcmp(monitor_name, "screen-direct") != 0 && strcmp(monitor_name, "screen-direct-force") != 0) {
|
if(strcmp(monitor_name, "screen-direct") != 0 && strcmp(monitor_name, "screen-direct-force") != 0) {
|
||||||
if(!get_monitor_by_name(self->egl, GSR_CONNECTION_X11, monitor_name, &self->monitor))
|
if(!get_monitor_by_name(self->egl, GSR_CONNECTION_X11, monitor_name, &self->monitor))
|
||||||
fprintf(stderr, "gsr warning: gsr_damage_set_target_monitor: failed to find monitor: %s\n", monitor_name);
|
fprintf(stderr, "gsr warning: gsr_damage_set_target_monitor: failed to find monitor: %s\n", monitor_name);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -377,7 +377,7 @@ bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture) {
|
|||||||
self->glx_library = dlopen("libGLX.so.0", RTLD_LAZY);
|
self->glx_library = dlopen("libGLX.so.0", RTLD_LAZY);
|
||||||
|
|
||||||
self->gl_library = dlopen("libGL.so.1", RTLD_LAZY);
|
self->gl_library = dlopen("libGL.so.1", RTLD_LAZY);
|
||||||
if(!self->egl_library) {
|
if(!self->gl_library) {
|
||||||
fprintf(stderr, "gsr error: gsr_egl_load: failed to load libGL.so.1, error: %s\n", dlerror());
|
fprintf(stderr, "gsr error: gsr_egl_load: failed to load libGL.so.1, error: %s\n", dlerror());
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|||||||
115
src/main.cpp
115
src/main.cpp
@@ -1080,15 +1080,14 @@ static void usage_full() {
|
|||||||
usage_header();
|
usage_header();
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("OPTIONS:\n");
|
printf("OPTIONS:\n");
|
||||||
printf(" -w Window id to record, a display (monitor name), \"screen\", \"screen-direct-force\", \"focused\" or \"portal\".\n");
|
printf(" -w Window id to record, a display (monitor name), \"screen\", \"screen-direct\", \"focused\" or \"portal\".\n");
|
||||||
printf(" If this is \"portal\" then xdg desktop screencast portal with PipeWire will be used. Portal option is only available on Wayland.\n");
|
printf(" If this is \"portal\" then xdg desktop screencast portal with PipeWire will be used. Portal option is only available on Wayland.\n");
|
||||||
printf(" If you select to save the session (token) in the desktop portal capture popup then the session will be saved for the next time you use \"portal\",\n");
|
printf(" If you select to save the session (token) in the desktop portal capture popup then the session will be saved for the next time you use \"portal\",\n");
|
||||||
printf(" but the session will be ignored unless you run GPU Screen Recorder with the '-restore-portal-session yes' option.\n");
|
printf(" but the session will be ignored unless you run GPU Screen Recorder with the '-restore-portal-session yes' option.\n");
|
||||||
printf(" If this is \"screen\" or \"screen-direct-force\" then all monitors are recorded on Nvidia X11.\n");
|
printf(" If this is \"screen\" then the first monitor found is recorded.\n");
|
||||||
printf(" On AMD/Intel or wayland \"screen\" will record the first monitor found.\n");
|
printf(" \"screen-direct\" can only be used on Nvidia X11, to allow recording without breaking VRR (G-SYNC). This also records all of your monitors.\n");
|
||||||
printf(" \"screen-direct-force\" is not recommended unless you use a VRR (G-SYNC) monitor on Nvidia X11 and you are aware that using this option can cause\n");
|
printf(" Using this \"screen-direct\" option is not recommended unless you use VRR (G-SYNC) as there are Nvidia driver issues that can cause your system or games to freeze/crash.\n");
|
||||||
printf(" games to freeze/crash or other issues because of Nvidia driver issues.\n");
|
printf(" The \"screen-direct\" option is not needed on AMD, Intel nor Nvidia on Wayland as VRR works properly in those cases.\n");
|
||||||
printf(" \"screen-direct-force\" option is only available on Nvidia X11. VRR works without this option on other systems.\n");
|
|
||||||
printf(" Run GPU Screen Recorder with the --list-capture-options option to list valid values for this option.\n");
|
printf(" Run GPU Screen Recorder with the --list-capture-options option to list valid values for this option.\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf(" -c Container format for output file, for example mp4, or flv. Only required if no output file is specified or if recording in replay buffer mode.\n");
|
printf(" -c Container format for output file, for example mp4, or flv. Only required if no output file is specified or if recording in replay buffer mode.\n");
|
||||||
@@ -2060,7 +2059,7 @@ static void output_monitor_info(const gsr_monitor *monitor, void *userdata) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void list_supported_capture_options(const gsr_window *window, const char *card_path, gsr_gpu_vendor vendor, bool list_monitors) {
|
static void list_supported_capture_options(const gsr_window *window, const char *card_path, bool list_monitors) {
|
||||||
const bool wayland = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_WAYLAND;
|
const bool wayland = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_WAYLAND;
|
||||||
if(!wayland) {
|
if(!wayland) {
|
||||||
puts("window");
|
puts("window");
|
||||||
@@ -2070,14 +2069,9 @@ static void list_supported_capture_options(const gsr_window *window, const char
|
|||||||
if(list_monitors) {
|
if(list_monitors) {
|
||||||
capture_options_callback options;
|
capture_options_callback options;
|
||||||
options.window = window;
|
options.window = window;
|
||||||
if(monitor_capture_use_drm(window, vendor)) {
|
const bool is_x11 = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11;
|
||||||
const bool is_x11 = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11;
|
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
|
||||||
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
|
for_each_active_monitor_output(window, card_path, connection_type, output_monitor_info, &options);
|
||||||
for_each_active_monitor_output(window, card_path, connection_type, output_monitor_info, &options);
|
|
||||||
} else {
|
|
||||||
puts("screen"); // All monitors in one, only available on Nvidia X11
|
|
||||||
for_each_active_monitor_output(window, card_path, GSR_CONNECTION_X11, output_monitor_info, &options);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GSR_PORTAL
|
#ifdef GSR_PORTAL
|
||||||
@@ -2155,7 +2149,7 @@ static void info_command() {
|
|||||||
puts("section=video_codecs");
|
puts("section=video_codecs");
|
||||||
list_supported_video_codecs(&egl, wayland);
|
list_supported_video_codecs(&egl, wayland);
|
||||||
puts("section=capture_options");
|
puts("section=capture_options");
|
||||||
list_supported_capture_options(window, egl.card_path, egl.gpu_info.vendor, list_monitors);
|
list_supported_capture_options(window, egl.card_path, list_monitors);
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
@@ -2207,6 +2201,7 @@ static void list_application_audio_command() {
|
|||||||
|
|
||||||
// |card_path| can be NULL. If not NULL then |vendor| has to be valid
|
// |card_path| can be NULL. If not NULL then |vendor| has to be valid
|
||||||
static void list_capture_options_command(const char *card_path, gsr_gpu_vendor vendor) {
|
static void list_capture_options_command(const char *card_path, gsr_gpu_vendor vendor) {
|
||||||
|
(void)vendor;
|
||||||
bool wayland = false;
|
bool wayland = false;
|
||||||
Display *dpy = XOpenDisplay(nullptr);
|
Display *dpy = XOpenDisplay(nullptr);
|
||||||
if (!dpy) {
|
if (!dpy) {
|
||||||
@@ -2235,7 +2230,7 @@ static void list_capture_options_command(const char *card_path, gsr_gpu_vendor v
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(card_path) {
|
if(card_path) {
|
||||||
list_supported_capture_options(window, card_path, vendor, true);
|
list_supported_capture_options(window, card_path, true);
|
||||||
} else {
|
} else {
|
||||||
gsr_egl egl;
|
gsr_egl egl;
|
||||||
if(!gsr_egl_load(&egl, window, false)) {
|
if(!gsr_egl_load(&egl, window, false)) {
|
||||||
@@ -2252,7 +2247,7 @@ static void list_capture_options_command(const char *card_path, gsr_gpu_vendor v
|
|||||||
list_monitors = false;
|
list_monitors = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list_supported_capture_options(window, egl.card_path, egl.gpu_info.vendor, list_monitors);
|
list_supported_capture_options(window, egl.card_path, list_monitors);
|
||||||
}
|
}
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@@ -2281,6 +2276,35 @@ static bool gpu_vendor_from_string(const char *vendor_str, gsr_gpu_vendor *vendo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void validate_monitor_get_valid(const gsr_egl *egl, std::string &window_str) {
|
||||||
|
const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11;
|
||||||
|
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
|
||||||
|
const bool capture_use_drm = monitor_capture_use_drm(egl->window, egl->gpu_info.vendor);
|
||||||
|
|
||||||
|
if(strcmp(window_str.c_str(), "screen") == 0) {
|
||||||
|
FirstOutputCallback first_output;
|
||||||
|
first_output.output_name = NULL;
|
||||||
|
for_each_active_monitor_output(egl->window, egl->card_path, connection_type, get_first_output, &first_output);
|
||||||
|
|
||||||
|
if(first_output.output_name) {
|
||||||
|
window_str = first_output.output_name;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Error: no usable output found\n");
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
} else if(capture_use_drm || (strcmp(window_str.c_str(), "screen-direct") != 0 && strcmp(window_str.c_str(), "screen-direct-force") != 0)) {
|
||||||
|
gsr_monitor gmon;
|
||||||
|
if(!get_monitor_by_name(egl, connection_type, window_str.c_str(), &gmon)) {
|
||||||
|
fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str.c_str());
|
||||||
|
fprintf(stderr, " \"screen\"\n");
|
||||||
|
if(!capture_use_drm)
|
||||||
|
fprintf(stderr, " \"screen-direct\"\n");
|
||||||
|
for_each_active_monitor_output(egl->window, egl->card_path, connection_type, monitor_output_callback_print, NULL);
|
||||||
|
_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_resolution, bool wayland, gsr_egl *egl, int fps, VideoCodec video_codec, gsr_color_range color_range,
|
static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_resolution, bool wayland, gsr_egl *egl, int fps, VideoCodec video_codec, gsr_color_range color_range,
|
||||||
bool record_cursor, bool use_software_video_encoder, bool restore_portal_session, const char *portal_session_token_filepath,
|
bool record_cursor, bool use_software_video_encoder, bool restore_portal_session, const char *portal_session_token_filepath,
|
||||||
gsr_color_depth color_depth)
|
gsr_color_depth color_depth)
|
||||||
@@ -2325,60 +2349,13 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
|
|||||||
_exit(2);
|
_exit(2);
|
||||||
#endif
|
#endif
|
||||||
} else if(contains_non_hex_number(window_str.c_str())) {
|
} else if(contains_non_hex_number(window_str.c_str())) {
|
||||||
if(monitor_capture_use_drm(egl->window, egl->gpu_info.vendor)) {
|
validate_monitor_get_valid(egl, window_str);
|
||||||
const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11;
|
if(!monitor_capture_use_drm(egl->window, egl->gpu_info.vendor)) {
|
||||||
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
|
|
||||||
|
|
||||||
if(strcmp(window_str.c_str(), "screen") == 0) {
|
|
||||||
FirstOutputCallback first_output;
|
|
||||||
first_output.output_name = NULL;
|
|
||||||
for_each_active_monitor_output(egl->window, egl->card_path, connection_type, get_first_output, &first_output);
|
|
||||||
|
|
||||||
if(first_output.output_name) {
|
|
||||||
window_str = first_output.output_name;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error: no usable output found\n");
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
gsr_monitor gmon;
|
|
||||||
if(!get_monitor_by_name(egl, connection_type, window_str.c_str(), &gmon)) {
|
|
||||||
fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str.c_str());
|
|
||||||
fprintf(stderr, " \"screen\"\n");
|
|
||||||
for_each_active_monitor_output(egl->window, egl->card_path, connection_type, monitor_output_callback_print, NULL);
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(strcmp(window_str.c_str(), "screen") != 0 && strcmp(window_str.c_str(), "screen-direct") != 0 && strcmp(window_str.c_str(), "screen-direct-force") != 0) {
|
|
||||||
gsr_monitor gmon;
|
|
||||||
if(!get_monitor_by_name(egl, GSR_CONNECTION_X11, window_str.c_str(), &gmon)) {
|
|
||||||
Display *display = (Display*)gsr_window_get_display(egl->window);
|
|
||||||
const int screens_width = XWidthOfScreen(DefaultScreenOfDisplay(display));
|
|
||||||
const int screens_height = XWidthOfScreen(DefaultScreenOfDisplay(display));
|
|
||||||
fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str.c_str());
|
|
||||||
fprintf(stderr, " \"screen\" (%dx%d+%d+%d)\n", screens_width, screens_height, 0, 0);
|
|
||||||
fprintf(stderr, " \"screen-direct\" (%dx%d+%d+%d)\n", screens_width, screens_height, 0, 0);
|
|
||||||
fprintf(stderr, " \"screen-direct-force\" (%dx%d+%d+%d)\n", screens_width, screens_height, 0, 0);
|
|
||||||
for_each_active_monitor_output(egl->window, egl->card_path, GSR_CONNECTION_X11, monitor_output_callback_print, NULL);
|
|
||||||
_exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(egl->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA && !wayland) {
|
|
||||||
const char *capture_target = window_str.c_str();
|
const char *capture_target = window_str.c_str();
|
||||||
bool direct_capture = strcmp(window_str.c_str(), "screen-direct") == 0;
|
const bool direct_capture = strcmp(window_str.c_str(), "screen-direct") == 0 || strcmp(window_str.c_str(), "screen-direct-force") == 0;
|
||||||
if(direct_capture) {
|
if(direct_capture) {
|
||||||
capture_target = "screen";
|
capture_target = "screen";
|
||||||
// TODO: Temporary disable direct capture because push model causes stuttering when it's direct capturing. This might be a nvfbc bug. This does not happen when using a compositor.
|
fprintf(stderr, "Warning: %s capture option is not recommended unless you use G-SYNC as Nvidia has driver issues that can cause your system or games to freeze/crash.\n", window_str.c_str());
|
||||||
direct_capture = false;
|
|
||||||
fprintf(stderr, "Warning: screen-direct has temporary been disabled as it causes stuttering. This is likely a NvFBC bug. Falling back to \"screen\".\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strcmp(window_str.c_str(), "screen-direct-force") == 0) {
|
|
||||||
direct_capture = true;
|
|
||||||
capture_target = "screen";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gsr_capture_nvfbc_params nvfbc_params;
|
gsr_capture_nvfbc_params nvfbc_params;
|
||||||
|
|||||||
Reference in New Issue
Block a user