Compare commits

..

27 Commits
4.3.4 ... 5.0.2

Author SHA1 Message Date
dec05eba
802067d1df -overlapping-replay > -overlap-replay 2025-01-24 00:29:57 +01:00
dec05eba
b55096544b Add version to --info output 2025-01-23 23:56:19 +01:00
dec05eba
e87ade6ee3 Add -overlapping-replay option to clear replay buffer after a save 2025-01-23 23:52:31 +01:00
dec05eba
832052a012 m 2025-01-19 00:32:20 +01:00
dec05eba
bae0fdd949 Mesa check version 24.3.6 2025-01-18 21:59:29 +01:00
dec05eba
bcaf312bca 5.0.1 2025-01-18 21:38:39 +01:00
dec05eba
b559c56d80 Mention amd recording performance issue 2025-01-18 21:38:17 +01:00
dec05eba
59df69bf6a amd: disable vaapi surface copy unless mesa 2.3.4 is used, which fixes a performance issue. Otherwise we get stutter in some games 2025-01-18 21:12:28 +01:00
dec05eba
b68400ca20 Add -gl-debug option to make it easier to debug user issues that cant easily be reproduced 2025-01-16 22:55:20 +01:00
dec05eba
4211dfa2f8 Mention OpenMandriva package 2025-01-13 21:29:22 +01:00
dec05eba
5baa4b82e3 Fix possibility of monitor captured changing on wayland when monitors are reconfigured 2025-01-13 00:42:00 +01:00
dec05eba
3a200a4c9f Workaround teamspeak crashing when recording app audio 2025-01-10 23:59:15 +01:00
dec05eba
f6f8f20686 Update TODO 2025-01-08 17:28:17 +01:00
dec05eba
43d353b7b4 Unset DRI_PRIME as well when gpu offloading cant be used 2025-01-08 17:17:33 +01:00
dec05eba
621f253f00 Minor change 2025-01-03 17:14:30 +01:00
dec05eba
68a7dc1b7f README: mention icc profile 2025-01-02 10:42:27 +01:00
dec05eba
fbaa73bfc7 Prefix program arguments error with error: 2024-12-31 10:40:07 +01:00
dec05eba
01e3a7a8cd Use poll instead of select 2024-12-30 05:24:40 +01:00
dec05eba
027b29cb6e 5.0.0 2024-12-29 21:13:10 +01:00
David Rosca
954f9abe2a kms_server: Use MOD_INVALID when modifiers are not supported
Fixes VAAPI import on older AMD cards that doesn't support modifiers.
2024-12-28 10:39:25 +01:00
dec05eba
2c51e8630d Exit with exit code 50 if invalid audio device. Exit with exit code 51 if invalid monitor 2024-12-26 15:21:47 +01:00
dec05eba
c1048a3d20 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 2024-12-26 13:49:20 +01:00
dec05eba
a006261ade Fix --list-capture-options with card path not working on x11 nvidia because x11 nvidia doesn't use dri 2024-12-09 01:57:10 +01:00
dec05eba
3af2ea52c2 4.3.5 - fix build on some distros with older spa version 2024-12-08 23:33:48 +01:00
dec05eba
da08e318a6 Remove shit 2024-12-08 23:26:14 +01:00
dec05eba
10dd7f1c64 Yeet the unecessary properties error location code, it fails to build on some versions of spa 2024-12-08 23:24:36 +01:00
dec05eba
fc45439b31 Attempt to fix build on some distros 2024-12-08 23:01:29 +01:00
16 changed files with 264 additions and 147 deletions

View File

@@ -23,9 +23,8 @@ Supported audio codecs:
## Note
This software works on X11 and Wayland on AMD, Intel and NVIDIA.
### TEMPORARY ISSUES
1) screen-direct capture has been temporary disabled as it causes issues with stuttering. This might be a nvfbc bug.
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.
3) FLAC audio codec is disabled at the moment because of 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.
### 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,
however when recording a monitor (or when using wayland) the program needs root permission (to access KMS).\
@@ -37,14 +36,15 @@ When recording Legend of Zelda Breath of the Wild at 4k, fps drops from 30 to 7
When recording GTA V at 4k on highest settings, fps drops from 60 to 23 when using obs-nvfbc + nvenc, however when using this screen recorder the fps only drops to 58.\
GPU Screen Recorder also produces much smoother videos than OBS when GPU utilization is close to 100%, see comparison here: [https://www.youtube.com/watch?v=zfj4sNVLLLg](https://www.youtube.com/watch?v=zfj4sNVLLLg).\
GPU Screen Recorder has much better performance than OBS Studio even with version 30.2 that does "zero-copy" recording and encoding, see: [https://www.youtube.com/watch?v=jdroRjibsDw](https://www.youtube.com/watch?v=jdroRjibsDw).\
It is recommended to save the video to a SSD because of the large file size, which a slow HDD might not be fast enough to handle. Using variable framerate mode (-fm vfr) which is the default is also recommended as this reduces encoding load. Ultra quality is also overkill most of the time, very high (the default) or lower quality is usually enough.
It is recommended to save the video to a SSD because of the large file size, which a slow HDD might not be fast enough to handle. Using variable framerate mode (-fm vfr) which is the default is also recommended as this reduces encoding load. Ultra quality is also overkill most of the time, very high (the default) or lower quality is usually enough.\
Note that recording on AMD can have some performance issues on Wayland in the recording itself when recording without desktop portal unless your mesa version is 25.0.0 or greater.
## Note about optimal performance on NVIDIA
NVIDIA driver has a "feature" (read: bug) where it will downclock memory transfer rate when a program uses cuda (or nvenc, which uses cuda), such as GPU Screen Recorder. To work around this bug, GPU Screen Recorder can overclock your GPU memory transfer rate to it's normal optimal level.\
To enable overclocking for optimal performance use the `-oc` option when running GPU Screen Recorder. You also need to have "Coolbits" NVIDIA X setting set to "12" to enable overclocking. You can automatically add this option if you run `sudo nvidia-xconfig --cool-bits=12` and then reboot your computer.\
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!
# 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.
# Installation
@@ -63,6 +63,7 @@ Here are some known unofficial packages:
* Nix: [NixOS wiki](https://wiki.nixos.org/wiki/Gpu-screen-recorder)
* openSUSE: [openSUSE software repository](https://software.opensuse.org/package/gpu-screen-recorder)
* Fedora: [Copr](https://copr.fedorainfracloud.org/coprs/brycensranch/gpu-screen-recorder-git/)
* OpenMandriva: [gpu-screen-recorder](https://github.com/OpenMandrivaAssociation/gpu-screen-recorder/tree/master)
# Dependencies
GPU Screen Recorder uses meson build system so you need to install `meson` to build GPU Screen Recorder.
@@ -179,5 +180,5 @@ You have to either record in hdr mode (-k `hevc_hdr` or -k `av1_hdr` option) to
You can record with desktop portal option (`-w portal`) instead which ignores night light, if you are ok with recording without HDR.
## Kdenlive says that the video is not usable for editing because it has variable frame rate
To fix this you can either record the video in .mkv format or constant frame rate (-fm cfr).
## Colors look incorrect when recording HDR with hevc_hdr/av1_hdr
The latest version of KDE Plasma breaks HDR for recording applications. Wayland in general doesn't properly support recording HDR yet. Use desktop portal option (`-w portal`) for now to turn HDR recording into SDR.
## Colors look incorrect when recording HDR (with hevc_hdr/av1_hdr) or using an ICC profile
The latest version of KDE Plasma breaks HDR and ICC profiles for recording applications. Wayland in general doesn't properly support recording HDR yet. Use desktop portal option (`-w portal`) for now to turn HDR recording into SDR and to be able to record with correct colors when using an ICC profile.

30
TODO
View File

@@ -69,8 +69,6 @@ Exit if X11/Wayland killed (if drm plane dead or something?)
Use SRC_W and SRC_H for screen plane instead of crtc_w and crtc_h.
Make it possible to select which /dev/dri/card* to use, but that requires opengl to also use the same card. Not sure if that is possible for amd, intel and nvidia without using vulkan instead.
Test if p2 state can be worked around by using pure nvenc api and overwriting cuInit/cuCtxCreate* to not do anything. Cuda might be loaded when using nvenc but it might not be used, with certain record options? (such as h264 p5).
nvenc uses cuda when using b frames and rgb->yuv conversion, so convert the image ourselves instead.-
@@ -195,3 +193,31 @@ Hide application audio module-null-sink by using sink_properties=media.class="Au
Improve software encoding performance.
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.
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.
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.
Do this instead of adding an option to choose which gpu to use.
On X11 the primary gpu will always have the framebuffer for all monitors combined.
Use randr to list all monitors and always record and encode with the primary gpu.
On Wayland each gpu will have its own list of monitors with framebuffers.
Iterate through all cards with drm and list all monitors with associated framebuffers and when choosing a monitor to record
automatically use the associated gpu card.
Allow flv av1 if recent ffmpeg version and streaming to youtube (and twitch?) and for custom services.
Use explicit sync in pipewire video code: https://docs.pipewire.org/page_dma_buf.html.
Support vaapi rotation. Support for it is added in mesa here: https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/32919.

View File

@@ -277,7 +277,7 @@ struct gsr_egl {
unsigned char (*glUnmapBuffer)(unsigned int target);
};
bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture);
bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture, bool enable_debug);
void gsr_egl_unload(gsr_egl *self);
/* Does opengl swap with egl or glx, depending on which one is active */

View File

@@ -50,7 +50,7 @@ drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count
uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count);
bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info);
bool gl_driver_version_greater_than(const gsr_egl *egl, int major, int minor, int patch);
bool gl_driver_version_greater_than(const gsr_gpu_info *gpu_info, int major, int minor, int patch);
bool try_card_has_valid_plane(const char *card_path);
/* |output| should be at least 128 bytes in size */

View File

@@ -11,6 +11,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/capability.h>
@@ -318,17 +319,14 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
}
fprintf(stderr, "gsr info: gsr_kms_client_init: waiting for server to connect\n");
struct pollfd poll_fd = {
.fd = self->initial_socket_fd,
.events = POLLIN,
.revents = 0
};
for(;;) {
struct timeval tv;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(self->initial_socket_fd, &rfds);
tv.tv_sec = 0;
tv.tv_usec = 100 * 1000; // 100 ms
int select_res = select(1 + self->initial_socket_fd, &rfds, NULL, NULL, &tv);
if(select_res > 0) {
int poll_res = poll(&poll_fd, 1, 100);
if(poll_res > 0 && (poll_fd.revents & POLLIN)) {
socklen_t sock_len = 0;
self->initial_client_fd = accept(self->initial_socket_fd, (struct sockaddr*)&remote_addr, &sock_len);
if(self->initial_client_fd == -1) {

View File

@@ -19,6 +19,7 @@
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <drm_mode.h>
#include <drm_fourcc.h>
#define MAX_CONNECTORS 32
@@ -362,7 +363,7 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response, connector_to_crt
response->items[item_index].width = drmfb->width;
response->items[item_index].height = drmfb->height;
response->items[item_index].pixel_format = drmfb->pixel_format;
response->items[item_index].modifier = drmfb->modifier;
response->items[item_index].modifier = drmfb->flags & DRM_MODE_FB_MODIFIERS ? drmfb->modifier : DRM_FORMAT_MOD_INVALID;
response->items[item_index].connector_id = crtc_pair ? crtc_pair->connector_id : 0;
response->items[item_index].is_cursor = property_mask & PLANE_PROPERTY_IS_CURSOR;
if(property_mask & PLANE_PROPERTY_IS_CURSOR) {

View File

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

View File

@@ -18,6 +18,8 @@
#include <libavutil/mastering_display_metadata.h>
#include <libavformat/avformat.h>
#define FIND_CRTC_BY_NAME_TIMEOUT_SECONDS 2.0
#define HDMI_STATIC_METADATA_TYPE1 0
#define HDMI_EOTF_SMPTE_ST2084 2
@@ -56,6 +58,7 @@ typedef struct {
AVCodecContext *video_codec_context;
bool performance_error_shown;
bool fast_path_failed;
bool mesa_supports_compute_only_vaapi_copy;
//int drm_fd;
//uint64_t prev_sequence;
@@ -63,6 +66,8 @@ typedef struct {
vec2i prev_target_pos;
vec2i prev_plane_size;
double last_time_monitor_check;
} gsr_capture_kms;
static void gsr_capture_kms_cleanup_kms_fds(gsr_capture_kms *self) {
@@ -227,14 +232,17 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c
video_codec_context->height = FFALIGN(self->params.output_resolution.y, 2);
}
self->fast_path_failed = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !gl_driver_version_greater_than(self->params.egl, 24, 0, 9);
self->fast_path_failed = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 0, 9);
if(self->fast_path_failed)
fprintf(stderr, "gsr warning: gsr_capture_kms_start: your amd driver (mesa) version is known to be buggy (<= version 24.0.9), falling back to opengl copy\n");
self->mesa_supports_compute_only_vaapi_copy = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 3, 6);
frame->width = video_codec_context->width;
frame->height = video_codec_context->height;
self->video_codec_context = video_codec_context;
self->last_time_monitor_check = clock_get_monotonic_seconds();
return 0;
}
@@ -428,7 +436,7 @@ static gsr_kms_response_item* find_monitor_drm(gsr_capture_kms *self, bool *capt
}
// Will never happen on wayland unless the target monitor has been disconnected
if(!drm_fd) {
if(!drm_fd && self->is_x11) {
drm_fd = find_largest_drm(&self->kms_response);
*capture_is_combined_plane = true;
}
@@ -555,6 +563,55 @@ static void gsr_capture_kms_update_capture_size_change(gsr_capture_kms *self, gs
}
}
static void gsr_capture_kms_update_connector_ids(gsr_capture_kms *self) {
const double now = clock_get_monotonic_seconds();
if(now - self->last_time_monitor_check < FIND_CRTC_BY_NAME_TIMEOUT_SECONDS)
return;
self->last_time_monitor_check = now;
/* TODO: Assume for now that there is only 1 framebuffer for all monitors and it doesn't change */
if(self->is_x11)
return;
self->monitor_id.num_connector_ids = 0;
const gsr_connection_type connection_type = self->is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
// MonitorCallbackUserdata monitor_callback_userdata = {
// &self->monitor_id,
// self->params.display_to_capture, strlen(self->params.display_to_capture),
// 0,
// };
// for_each_active_monitor_output(self->params.egl->window, self->params.egl->card_path, connection_type, monitor_callback, &monitor_callback_userdata);
gsr_monitor monitor;
if(!get_monitor_by_name(self->params.egl, connection_type, self->params.display_to_capture, &monitor)) {
fprintf(stderr, "gsr error: gsr_capture_kms_update_connector_ids: failed to find monitor by name \"%s\"\n", self->params.display_to_capture);
return;
}
self->monitor_id.num_connector_ids = 1;
self->monitor_id.connector_ids[0] = monitor.connector_id;
monitor.name = self->params.display_to_capture;
self->monitor_rotation = drm_monitor_get_display_server_rotation(self->params.egl->window, &monitor);
self->capture_pos = monitor.pos;
/* Monitor size is already rotated on x11 when the monitor is rotated, no need to apply it ourselves */
if(self->is_x11)
self->capture_size = monitor.size;
else
self->capture_size = rotate_capture_size_if_rotated(self, monitor.size);
}
static void gsr_capture_kms_fail_fast_path_if_not_fast(gsr_capture_kms *self, uint32_t pixel_format) {
const uint8_t pixel_format_color_depth_1 = (pixel_format >> 16) & 0xFF;
if(!self->fast_path_failed && self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !self->mesa_supports_compute_only_vaapi_copy && (pixel_format_color_depth_1 == '3' || pixel_format_color_depth_1 == '4')) {
self->fast_path_failed = true;
fprintf(stderr, "gsr warning: gsr_capture_kms_capture: the monitor you are recording is in 10/12-bit color format and your mesa version is <= 24.3.6, composition will be used."
" If you experience performance problems in the video then record on a single window on X11 or use portal capture option instead or disable 10/12-bit color option in your desktop environment settings,"
" or try to record the monitor on X11 instead (if you aren't already doing that) or update your mesa version.\n");
}
}
static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) {
gsr_capture_kms *self = cap->priv;
@@ -574,6 +631,8 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
return -1;
}
gsr_capture_kms_update_connector_ids(self);
bool capture_is_combined_plane = false;
const gsr_kms_response_item *drm_fd = find_monitor_drm(self, &capture_is_combined_plane);
if(!drm_fd) {
@@ -586,10 +645,13 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
if(!self->performance_error_shown && self->monitor_rotation != GSR_MONITOR_ROT_0 && video_codec_context_is_vaapi(self->video_codec_context) && self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD) {
self->performance_error_shown = true;
fprintf(stderr,"gsr warning: gsr_capture_kms_capture: the monitor you are recording is rotated, composition will have to be used."
" If you are experience performance problems in the video then record a single window on X11 or use portal capture option instead\n");
self->fast_path_failed = true;
fprintf(stderr, "gsr warning: gsr_capture_kms_capture: the monitor you are recording is rotated, composition will have to be used."
" If you experience performance problems in the video then record a single window on X11 or use portal capture option instead\n");
}
gsr_capture_kms_fail_fast_path_if_not_fast(self, drm_fd->pixel_format);
self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h });
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;

View File

@@ -27,6 +27,7 @@ typedef struct {
AVCodecContext *video_codec_context;
bool fast_path_failed;
bool mesa_supports_compute_only_vaapi_copy;
} gsr_capture_portal;
static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) {
@@ -310,10 +311,12 @@ static int gsr_capture_portal_start(gsr_capture *cap, AVCodecContext *video_code
video_codec_context->height = FFALIGN(self->params.output_resolution.y, 2);
}
self->fast_path_failed = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !gl_driver_version_greater_than(self->params.egl, 24, 0, 9);
self->fast_path_failed = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 0, 9);
if(self->fast_path_failed)
fprintf(stderr, "gsr warning: gsr_capture_kms_start: your amd driver (mesa) version is known to be buggy (<= version 24.0.9), falling back to opengl copy\n");
self->mesa_supports_compute_only_vaapi_copy = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 3, 6);
frame->width = video_codec_context->width;
frame->height = video_codec_context->height;
@@ -325,6 +328,16 @@ static int max_int(int a, int b) {
return a > b ? a : b;
}
static void gsr_capture_portal_fail_fast_path_if_not_fast(gsr_capture_portal *self, uint32_t pixel_format) {
const uint8_t pixel_format_color_depth_1 = (pixel_format >> 16) & 0xFF;
if(!self->fast_path_failed && self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !self->mesa_supports_compute_only_vaapi_copy && (pixel_format_color_depth_1 == '3' || pixel_format_color_depth_1 == '4')) {
self->fast_path_failed = true;
fprintf(stderr, "gsr warning: gsr_capture_kms_capture: the monitor you are recording is in 10/12-bit color format and your mesa version is <= 24.3.6, composition will be used."
" If you experience performance problems in the video then record on a single window on X11 instead or disable 10/12-bit color option in your desktop environment settings,"
" or try to record the monitor on X11 instead (if you aren't already doing that) or update your mesa version.\n");
}
}
static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_color_conversion *color_conversion) {
(void)frame;
(void)color_conversion;
@@ -346,6 +359,8 @@ static int gsr_capture_portal_capture(gsr_capture *cap, AVFrame *frame, gsr_colo
return 0;
}
gsr_capture_portal_fail_fast_path_if_not_fast(self, pipewire_fourcc);
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size;
output_size = scale_keep_aspect_ratio(self->capture_size, output_size);

View File

@@ -124,7 +124,7 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_
video_codec_context->height = FFALIGN(self->params.output_resolution.y, 2);
}
self->fast_path_failed = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !gl_driver_version_greater_than(self->params.egl, 24, 0, 9);
self->fast_path_failed = self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && !gl_driver_version_greater_than(&self->params.egl->gpu_info, 24, 0, 9);
if(self->fast_path_failed)
fprintf(stderr, "gsr warning: gsr_capture_kms_start: your amd driver (mesa) version is known to be buggy (<= version 24.0.9), falling back to opengl copy\n");

View File

@@ -137,7 +137,7 @@ bool gsr_damage_set_target_monitor(gsr_damage *self, const char *monitor_name) {
}
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))
fprintf(stderr, "gsr warning: gsr_damage_set_target_monitor: failed to find monitor: %s\n", monitor_name);
}

View File

@@ -344,25 +344,18 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
return true;
}
// #define GL_DEBUG_TYPE_ERROR 0x824C
// static void debug_callback( unsigned int source,
// unsigned int type,
// unsigned int id,
// unsigned int severity,
// int length,
// const char* message,
// const void* userParam )
// {
// (void)source;
// (void)id;
// (void)length;
// (void)userParam;
// fprintf( stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
// ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
// type, severity, message );
// }
#define GL_DEBUG_TYPE_ERROR 0x824C
#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
static void debug_callback(unsigned int source, unsigned int type, unsigned int id, unsigned int severity, int length, const char* message, const void* userParam) {
(void)source;
(void)id;
(void)length;
(void)userParam;
if(severity != GL_DEBUG_SEVERITY_NOTIFICATION)
fprintf(stderr, "gsr info: gl callback: %s type = 0x%x, severity = 0x%x, message = %s\n", type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "", type, severity, message);
}
bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture) {
bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture, bool enable_debug) {
memset(self, 0, sizeof(gsr_egl));
self->context_type = GSR_GL_CONTEXT_TYPE_EGL;
self->window = window;
@@ -377,7 +370,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->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());
goto fail;
}
@@ -418,8 +411,10 @@ bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture) {
self->glEnable(GL_BLEND);
self->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//self->glEnable(GL_DEBUG_OUTPUT);
//self->glDebugMessageCallback(debug_callback, NULL);
if(enable_debug) {
self->glEnable(GL_DEBUG_OUTPUT);
self->glDebugMessageCallback(debug_callback, NULL);
}
return true;

View File

@@ -1069,7 +1069,7 @@ static void open_video_hardware(AVCodecContext *codec_context, VideoQuality vide
static void usage_header() {
const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
const char *program_name = inside_flatpak ? "flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder" : "gpu-screen-recorder";
printf("usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [--list-capture-options [card_path] [vendor]] [--list-audio-devices] [--list-application-audio] [-v yes|no] [--version] [-h|--help]\n", program_name);
printf("usage: %s -w <window_id|monitor|focused|portal> [-c <container_format>] [-s WxH] -f <fps> [-a <audio_input>] [-q <quality>] [-r <replay_buffer_size_sec>] [-overlap-replay yes|no] [-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] [-bm auto|qp|vbr|cbr] [-cr limited|full] [-df yes|no] [-sc <script_path>] [-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] [-o <output_file>] [--list-capture-options [card_path] [vendor]] [--list-audio-devices] [--list-application-audio] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
fflush(stdout);
}
@@ -1080,15 +1080,14 @@ static void usage_full() {
usage_header();
printf("\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 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(" If this is \"screen\" or \"screen-direct-force\" then all monitors are recorded on Nvidia X11.\n");
printf(" On AMD/Intel or wayland \"screen\" will record the first monitor found.\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(" games to freeze/crash or other issues because of Nvidia driver issues.\n");
printf(" \"screen-direct-force\" option is only available on Nvidia X11. VRR works without this option on other systems.\n");
printf(" If this is \"screen\" then the first monitor found is recorded.\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(" 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(" The \"screen-direct\" option is not needed on AMD, Intel nor Nvidia on Wayland as VRR works properly in those cases.\n");
printf(" Run GPU Screen Recorder with the --list-capture-options option to list valid values for this option.\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");
@@ -1131,6 +1130,12 @@ static void usage_full() {
printf(" Note that the video data is stored in RAM, so don't use too long replay buffer time and use constant bitrate option (-bm cbr) to prevent RAM usage from going too high in busy scenes.\n");
printf(" Optional, disabled by default.\n");
printf("\n");
printf(" -overlap-replay\n");
printf(" Should replays overlap. For example if this is set to 'yes' and replay time (-r) is set to 60 seconds and a replay is saved once then the first replay video is 60 seconds long\n");
printf(" and if a replay is saved 10 seconds later then the second replay video will also be 60 seconds long and contain 50 seconds of the previous video as well.\n");
printf(" If this is set to 'no' then after a replay is saved the replay buffer data is cleared and the second replay will start from that point onward.\n");
printf(" Optional, set to 'yes' by default.\n");
printf("\n");
printf(" -k Video codec to use. Should be either 'auto', 'h264', 'hevc', 'av1', 'vp8', 'vp9', 'hevc_hdr', 'av1_hdr', 'hevc_10bit' or 'av1_10bit'.\n");
printf(" Optional, set to 'auto' by default which defaults to 'h264'. Forcefully set to 'h264' if the file container type is 'flv'.\n");
printf(" 'hevc_hdr' and 'av1_hdr' option is not available on X11 nor when using the portal capture option.\n");
@@ -1230,6 +1235,9 @@ static void usage_full() {
printf("\n");
printf(" -v Prints fps and damage info once per second. Optional, set to 'yes' by default.\n");
printf("\n");
printf(" -gl-debug\n");
printf(" Print opengl debug output. Optional, set to 'no' by default.\n");
printf("\n");
printf(" -h, --help\n");
printf(" Show this help.\n");
printf("\n");
@@ -1895,7 +1903,7 @@ static bool is_xwayland(Display *display) {
static bool is_using_prime_run() {
const char *prime_render_offload = getenv("__NV_PRIME_RENDER_OFFLOAD");
return prime_render_offload && strcmp(prime_render_offload, "1") == 0;
return (prime_render_offload && strcmp(prime_render_offload, "1") == 0) || getenv("DRI_PRIME");
}
static void disable_prime_run() {
@@ -1903,6 +1911,7 @@ static void disable_prime_run() {
unsetenv("__NV_PRIME_RENDER_OFFLOAD_PROVIDER");
unsetenv("__GLX_VENDOR_LIBRARY_NAME");
unsetenv("__VK_LAYER_NV_optimus");
unsetenv("DRI_PRIME");
}
static gsr_window* gsr_window_create(Display *display, bool wayland) {
@@ -2060,7 +2069,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;
if(!wayland) {
puts("window");
@@ -2070,14 +2079,9 @@ static void list_supported_capture_options(const gsr_window *window, const char
if(list_monitors) {
capture_options_callback options;
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 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);
} 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);
}
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;
for_each_active_monitor_output(window, card_path, connection_type, output_monitor_info, &options);
}
#ifdef GSR_PORTAL
@@ -2127,7 +2131,7 @@ static void info_command() {
}
gsr_egl egl;
if(!gsr_egl_load(&egl, window, false)) {
if(!gsr_egl_load(&egl, window, false, false)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(22);
}
@@ -2150,12 +2154,13 @@ static void info_command() {
puts("is_steam_deck|yes");
else
puts("is_steam_deck|no");
printf("gsr_version|%s\n", GSR_VERSION);
puts("section=gpu_info");
list_gpu_info(&egl);
puts("section=video_codecs");
list_supported_video_codecs(&egl, wayland);
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);
@@ -2207,6 +2212,7 @@ static void list_application_audio_command() {
// |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) {
(void)vendor;
bool wayland = false;
Display *dpy = XOpenDisplay(nullptr);
if (!dpy) {
@@ -2235,10 +2241,10 @@ static void list_capture_options_command(const char *card_path, gsr_gpu_vendor v
}
if(card_path) {
list_supported_capture_options(window, card_path, vendor, true);
list_supported_capture_options(window, card_path, true);
} else {
gsr_egl egl;
if(!gsr_egl_load(&egl, window, false)) {
if(!gsr_egl_load(&egl, window, false, false)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(1);
}
@@ -2252,7 +2258,7 @@ static void list_capture_options_command(const char *card_path, gsr_gpu_vendor v
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);
@@ -2281,6 +2287,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(51);
}
} 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(51);
}
}
}
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,
gsr_color_depth color_depth)
@@ -2325,60 +2360,13 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
_exit(2);
#endif
} else if(contains_non_hex_number(window_str.c_str())) {
if(monitor_capture_use_drm(egl->window, egl->gpu_info.vendor)) {
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;
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) {
validate_monitor_get_valid(egl, window_str);
if(!monitor_capture_use_drm(egl->window, egl->gpu_info.vendor)) {
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) {
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.
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";
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());
}
gsr_capture_nvfbc_params nvfbc_params;
@@ -2412,7 +2400,7 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re
}
} else {
if(wayland) {
fprintf(stderr, "Error: GPU Screen Recorder window capture only works in a pure X11 session. Xwayland is not supported. You can record a monitor instead on wayland\n");
fprintf(stderr, "Error: GPU Screen Recorder window capture only works in a pure X11 session. Xwayland is not supported. You can record a monitor instead on wayland or use -w portal option which supports window capture if your wayland compositor supports window capture\n");
_exit(2);
}
@@ -2533,7 +2521,7 @@ static std::vector<MergedAudioInputs> parse_audio_inputs(const AudioDevices &aud
for(const auto &audio_device_input : audio_devices.audio_inputs) {
fprintf(stderr, " %s (%s)\n", audio_device_input.name.c_str(), audio_device_input.description.c_str());
}
_exit(2);
_exit(50);
}
}
}
@@ -3043,11 +3031,6 @@ int main(int argc, char **argv) {
_exit(0);
} else if(argc == 4) {
const char *card_path = argv[2];
if(!try_card_has_valid_plane(card_path)) {
fprintf(stderr, "Error: \"%s\" is not a valid /dev/dri/cardN card. Make sure that you have at least one monitor connected\n", card_path);
_exit(1);
}
const char *vendor_str = argv[3];
gsr_gpu_vendor vendor;
if(!gpu_vendor_from_string(vendor_str, &vendor)) {
@@ -3079,6 +3062,7 @@ int main(int argc, char **argv) {
{ "-q", Arg { {}, true, false } },
{ "-o", Arg { {}, true, false } },
{ "-r", Arg { {}, true, false } },
{ "-overlap-replay", Arg { {}, true, false } },
{ "-k", Arg { {}, true, false } },
{ "-ac", Arg { {}, true, false } },
{ "-ab", Arg { {}, true, false } },
@@ -3087,6 +3071,7 @@ int main(int argc, char **argv) {
{ "-bm", Arg { {}, true, false } },
{ "-pixfmt", Arg { {}, true, false } },
{ "-v", Arg { {}, true, false } },
{ "-gl-debug", Arg { {}, true, false } },
{ "-df", Arg { {}, true, false } },
{ "-sc", Arg { {}, true, false } },
{ "-cr", Arg { {}, true, false } },
@@ -3100,17 +3085,17 @@ int main(int argc, char **argv) {
for(int i = 1; i < argc; i += 2) {
auto it = args.find(argv[i]);
if(it == args.end()) {
fprintf(stderr, "Invalid argument '%s'\n", argv[i]);
fprintf(stderr, "Error: invalid argument '%s'\n", argv[i]);
usage();
}
if(!it->second.values.empty() && !it->second.list) {
fprintf(stderr, "Expected argument '%s' to only be specified once\n", argv[i]);
fprintf(stderr, "Error: expected argument '%s' to only be specified once\n", argv[i]);
usage();
}
if(i + 1 >= argc) {
fprintf(stderr, "Missing value for argument '%s'\n", argv[i]);
fprintf(stderr, "Error: missing value for argument '%s'\n", argv[i]);
usage();
}
@@ -3119,7 +3104,7 @@ int main(int argc, char **argv) {
for(auto &it : args) {
if(!it.second.optional && !it.second.value()) {
fprintf(stderr, "Missing argument '%s'\n", it.first.c_str());
fprintf(stderr, "Error: missing argument '%s'\n", it.first.c_str());
usage();
}
}
@@ -3254,6 +3239,20 @@ int main(int argc, char **argv) {
usage();
}
bool gl_debug = false;
const char *gl_debug_str = args["-gl-debug"].value();
if(!gl_debug_str)
gl_debug_str = "no";
if(strcmp(gl_debug_str, "yes") == 0) {
gl_debug = true;
} else if(strcmp(gl_debug_str, "no") == 0) {
gl_debug = false;
} else {
fprintf(stderr, "Error: -gl-debug should either be either 'yes' or 'no', got: '%s'\n", gl_debug_str);
usage();
}
bool record_cursor = true;
const char *record_cursor_str = args["-cursor"].value();
if(!record_cursor_str)
@@ -3390,6 +3389,20 @@ int main(int argc, char **argv) {
replay_buffer_size_secs += std::ceil(keyint); // Add a few seconds to account of lost packets because of non-keyframe packets skipped
}
bool overlap_replay = true;
const char *overlap_replay_str = args["-overlap-replay"].value();
if(!overlap_replay_str)
overlap_replay_str = "yes";
if(strcmp(overlap_replay_str, "yes") == 0) {
overlap_replay = true;
} else if(strcmp(overlap_replay_str, "no") == 0) {
overlap_replay = false;
} else {
fprintf(stderr, "Error: -overlap-replap should either be either 'yes' or 'no', got: '%s'\n", overlap_replay_str);
usage();
}
std::string window_str = args["-w"].value();
const bool is_portal_capture = strcmp(window_str.c_str(), "portal") == 0;
@@ -3436,7 +3449,7 @@ int main(int argc, char **argv) {
const bool is_monitor_capture = strcmp(window_str.c_str(), "focused") != 0 && !is_portal_capture && contains_non_hex_number(window_str.c_str());
gsr_egl egl;
if(!gsr_egl_load(&egl, window, is_monitor_capture)) {
if(!gsr_egl_load(&egl, window, is_monitor_capture, gl_debug)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(1);
}
@@ -3875,6 +3888,7 @@ int main(int argc, char **argv) {
std::mutex audio_filter_mutex;
const double record_start_time = clock_get_monotonic_seconds();
std::atomic<double> replay_start_time(record_start_time);
std::deque<std::shared_ptr<PacketData>> frame_data_queue;
bool frames_erased = false;
@@ -3993,7 +4007,7 @@ int main(int argc, char **argv) {
ret = avcodec_send_frame(audio_track.codec_context, audio_device.frame);
if(ret >= 0) {
// TODO: Move to separate thread because this could write to network (for example when livestreaming)
receive_frames(audio_track.codec_context, audio_track.stream_index, audio_track.stream, audio_device.frame->pts, av_format_context, record_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
receive_frames(audio_track.codec_context, audio_track.stream_index, audio_track.stream, audio_device.frame->pts, av_format_context, replay_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
} else {
fprintf(stderr, "Failed to encode audio!\n");
}
@@ -4025,7 +4039,7 @@ int main(int argc, char **argv) {
ret = avcodec_send_frame(audio_track.codec_context, audio_device.frame);
if(ret >= 0) {
// TODO: Move to separate thread because this could write to network (for example when livestreaming)
receive_frames(audio_track.codec_context, audio_track.stream_index, audio_track.stream, audio_device.frame->pts, av_format_context, record_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
receive_frames(audio_track.codec_context, audio_track.stream_index, audio_track.stream, audio_device.frame->pts, av_format_context, replay_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
} else {
fprintf(stderr, "Failed to encode audio!\n");
}
@@ -4059,7 +4073,7 @@ int main(int argc, char **argv) {
err = avcodec_send_frame(audio_track.codec_context, aframe);
if(err >= 0){
// TODO: Move to separate thread because this could write to network (for example when livestreaming)
receive_frames(audio_track.codec_context, audio_track.stream_index, audio_track.stream, aframe->pts, av_format_context, record_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
receive_frames(audio_track.codec_context, audio_track.stream_index, audio_track.stream, aframe->pts, av_format_context, replay_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
} else {
fprintf(stderr, "Failed to encode audio!\n");
}
@@ -4205,7 +4219,7 @@ int main(int argc, char **argv) {
if(ret == 0) {
// TODO: Move to separate thread because this could write to network (for example when livestreaming)
receive_frames(video_codec_context, VIDEO_STREAM_INDEX, video_stream, video_frame->pts, av_format_context,
record_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
replay_start_time, frame_data_queue, replay_buffer_size_secs, frames_erased, write_output_mutex, paused_time_offset);
} else {
fprintf(stderr, "Error: avcodec_send_frame failed, error: %s\n", av_error_to_string(ret));
}
@@ -4234,8 +4248,14 @@ int main(int argc, char **argv) {
fflush(stdout);
if(recording_saved_script)
run_recording_saved_script_async(recording_saved_script, save_replay_output_filepath.c_str(), "replay");
std::lock_guard<std::mutex> lock(write_output_mutex);
save_replay_packets.clear();
if(!overlap_replay) {
frame_data_queue.clear();
frames_erased = true;
replay_start_time = clock_get_monotonic_seconds() - paused_time_offset;
}
}
if(save_replay == 1 && !save_replay_thread.valid() && replay_buffer_size_secs != -1) {

View File

@@ -370,12 +370,11 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
}
static struct pw_properties* gsr_pipewire_create_null_audio_sink(const char *name) {
struct spa_error_location err_loc;
char props_str[512];
snprintf(props_str, sizeof(props_str), "{ factory.name=support.null-audio-sink node.name=\"%s\" media.class=Audio/Sink object.linger=false audio.position=[FL FR] monitor.channel-volumes=true monitor.passthrough=true adjust_time=0 slaves=\"\" }", name);
struct pw_properties *props = pw_properties_new_string_checked(props_str, strlen(props_str), &err_loc);
snprintf(props_str, sizeof(props_str), "{ factory.name=support.null-audio-sink node.name=\"%s\" media.class=Audio/Sink object.linger=false audio.position=[FL FR] monitor.channel-volumes=true monitor.passthrough=true adjust_time=0 node.description=gsr-app-sink slaves=\"\" }", name);
struct pw_properties *props = pw_properties_new_string(props_str);
if(!props) {
fprintf(stderr, "gsr error: gsr_pipewire_create_null_audio_sink: failed to create virtual sink properties, error: %d:%d: %s\n", err_loc.line, err_loc.col, err_loc.reason);
fprintf(stderr, "gsr error: gsr_pipewire_create_null_audio_sink: failed to create virtual sink properties\n");
return NULL;
}
return props;

View File

@@ -417,8 +417,8 @@ static bool version_greater_than(int major, int minor, int patch, int other_majo
return (major > other_major) || (major == other_major && minor > other_minor) || (major == other_major && minor == other_minor && patch > other_patch);
}
bool gl_driver_version_greater_than(const gsr_egl *egl, int major, int minor, int patch) {
return version_greater_than(egl->gpu_info.driver_major, egl->gpu_info.driver_minor, egl->gpu_info.driver_patch, major, minor, patch);
bool gl_driver_version_greater_than(const gsr_gpu_info *gpu_info, int major, int minor, int patch) {
return version_greater_than(gpu_info->driver_major, gpu_info->driver_minor, gpu_info->driver_patch, major, minor, patch);
}
bool try_card_has_valid_plane(const char *card_path) {