Compare commits

..

17 Commits

Author SHA1 Message Date
dec05eba
4ed04830c1 Add void linux to README, update pkill command 2026-03-18 15:39:41 +01:00
dec05eba
755340454d Poll reset revents 2026-03-11 11:32:45 +01:00
dec05eba
2f8212b122 5.12.5 2026-03-02 00:00:38 +01:00
dec05eba
23782889be Minor check 2026-03-01 23:52:59 +01:00
dec05eba
fd08cdb9b4 pipewire video: fail video map texture if state is not streaming
Fixes portal capture failing on some systems
2026-03-01 19:30:17 +01:00
dec05eba
6079a0162d Portal capture: test disable modifier recheck same 2026-03-01 18:12:00 +01:00
dec05eba
eff5d619eb Fix incorrect manpage 2026-02-25 01:08:05 +01:00
dec05eba
309c4e5f2e Update info about reporting bugs in the manpage, move gpu-screen-recorder.env to ~/.config/gpu-screen-recorder but keep old path as well 2026-02-23 23:15:09 +01:00
dec05eba
0555cfde58 FAQ browser av1 2026-02-19 22:29:55 +01:00
p0358
ff030ba63f don't show microphone indicator on KDE 2026-02-19 20:04:55 +01:00
dec05eba
0de75e5b7e 5.12.4 2026-02-12 01:24:26 +01:00
dec05eba
c79fb1e5c9 pipewire-video: do correct removal of modifier 2026-02-12 01:07:48 +01:00
eonphi
4a4af85b6d attempt EGLImage without modifiers when last failed 2026-02-12 00:41:21 +01:00
dec05eba
8f7608e7ee Add --list-monitors option to list only monitors, refactor 2026-02-09 15:03:10 +01:00
dec05eba
f3235ed1bf Improve manpage 2026-02-09 13:59:18 +01:00
dec05eba
3666bba518 aur -> official repo 2026-02-06 19:02:23 +01:00
dec05eba
5d8d14eeaf FAQ vlc 2026-02-04 01:43:27 +01:00
15 changed files with 176 additions and 124 deletions

View File

@@ -29,14 +29,14 @@ Supported image formats:
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 an Arch Linux based distro then you can find gpu screen recorder in the official repositories under the name gpu-screen-recorder (`sudo pacman -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 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`).
## Unofficial install methods
The only official ways to install GPU Screen Recorder is either from source, AUR or flathub. Other sources may be out of date and missing features or may not work correctly.\
The only official ways to install GPU Screen Recorder is either from source, arch linux extra repository or flathub. Other sources may be out of date and missing features or may not work correctly.\
If you install GPU Screen Recorder from somewhere else and have an issue then try installing it from one of the official sources before reporting it as an issue.\
If you still prefer to install GPU Screen Recorder with a package manager instead of from source or as a flatpak then you may be able to find a package for your distro.\
Here are some known unofficial packages:
@@ -48,6 +48,7 @@ Here are some known unofficial packages:
* Solus: [gpu-screen-recorder](https://github.com/getsolus/packages/tree/main/packages/g/gpu-screen-recorder)
* Nobara: [Nobara wiki](https://wiki.nobaraproject.org/en/general-usage/additional-software/GPU-Screen-Recorder)
* AppImage [AppImage GitHub releases](https://github.com/pkgforge-dev/gpu-screen-recorder-AppImage/releases)
* Void Linux: [gpu-screen-recorder](https://voidlinux.org/packages/?arch=x86_64&q=gpu-screen-recorder) (Make sure to read the README in the package)
# Dependencies
GPU Screen Recorder uses meson build system so you need to install `meson` to build GPU Screen Recorder.
@@ -96,7 +97,7 @@ There is also a gui for the gpu screen recorder called [GPU Screen Recorder GTK]
There is also a new alternative UI for GPU Screen Recorder in the style of ShadowPlay called [GPU Screen Recorder UI](https://git.dec05eba.com/gpu-screen-recorder-ui/).
## Recording
Here is an example of how to record your monitor and the default audio output: `gpu-screen-recorder -w screen -f 60 -a default_output -o ~/Videos/test_video.mp4`.
Yyou can stop and save the recording with `Ctrl+C` or by running `pkill -SIGINT -f gpu-screen-recorder`.
Yyou can stop and save the recording with `Ctrl+C` or by running `pkill -SIGINT -f "^gpu-screen-recorder"`.
You can see a list of capture options to record if you run `gpu-screen-recorder --list-capture-options`. This will list possible capture options and monitor names, for example:\
```
window
@@ -122,12 +123,12 @@ GPU Screen Recorder uses Ffmpeg so GPU Screen Recorder supports all protocols th
If you want to reduce latency one thing you can do is to use the `-keyint` option, for example `-keyint 0.5`. Lower value means lower latency at the cost of increased bitrate/decreased quality.
## Recording while using replay/streaming
You can record a regular video while using replay/streaming by launching GPU Screen Recorder with the `-ro` option to specify a directory where to save the recording (for example: `gpu-screen-recorder -w screen -c mp4 -r 60 -o "$HOME/Videos/replays" -ro "$HOME/Videos/recordings"`).\
To start/stop (and save) recording use the SIGRTMIN signal, for example `pkill -SIGRTMIN -f gpu-screen-recorder`. The path to the video will be displayed in stdout when saving the video.\
To start/stop (and save) recording use the SIGRTMIN signal, for example `pkill -SIGRTMIN -f "^gpu-screen-recorder"`. The path to the video will be displayed in stdout when saving the video.\
This way of recording while using replay/streaming is more efficient than running GPU Screen Recorder multiple times since this way it only records the screen and encodes the video once.
## Controlling GPU Screen Recorder remotely
To save a video in replay mode, you need to send signal SIGUSR1 to gpu screen recorder. You can do this by running `pkill -SIGUSR1 -f gpu-screen-recorder`.\
To stop recording send SIGINT to gpu screen recorder. You can do this by running `pkill -SIGINT -f gpu-screen-recorder` or pressing `Ctrl-C` in the terminal that runs gpu screen recorder. When recording a regular non-replay video this will also save the video.\
To pause/unpause recording send SIGUSR2 to gpu screen recorder. You can do this by running `pkill -SIGUSR2 -f gpu-screen-recorder`. This is only applicable and useful when recording (not streaming nor replay).\
To save a video in replay mode, you need to send signal SIGUSR1 to gpu screen recorder. You can do this by running `pkill -SIGUSR1 -f "^gpu-screen-recorder"`.\
To stop recording send SIGINT to gpu screen recorder. You can do this by running `pkill -SIGINT -f "^gpu-screen-recorder"` or pressing `Ctrl-C` in the terminal that runs gpu screen recorder. When recording a regular non-replay video this will also save the video.\
To pause/unpause recording send SIGUSR2 to gpu screen recorder. You can do this by running `pkill -SIGUSR2 -f "^gpu-screen-recorder"`. This is only applicable and useful when recording (not streaming nor replay).\
There are more signals to control GPU Screen Recorder. Run `gpu-screen-recorder --help` to list them all (under `NOTES` section).
## Simple way to run replay without gui
Run the script `scripts/start-replay.sh` to start replay and then `scripts/save-replay.sh` to save a replay and `scripts/stop-replay.sh` to stop the replay. The videos are saved to `$HOME/Videos`.
@@ -135,7 +136,7 @@ You can use these scripts to start replay at system startup if you add `scripts/
hotkey settings on your system and choose a hotkey to run the script `scripts/save-replay.sh`. Modify `scripts/start-replay.sh` if you want to use other replay options.
## Run replay on system startup
If you installed GPU Screen Recorder from AUR or from source and you are running a distro that uses systemd then you will have a systemd service installed that can be started with `systemctl enable --now --user gpu-screen-recorder`. This systemd service runs GPU Screen Recorder on system startup.\
It's configured with `$HOME/.config/gpu-screen-recorder.env` (create it if it doesn't exist). You can look at [extra/gpu-screen-recorder.env](https://git.dec05eba.com/gpu-screen-recorder/plain/extra/gpu-screen-recorder.env) to see an example.
It's configured with `$HOME/.config/gpu-screen-recorder/gpu-screen-recorder.env` (create it if it doesn't exist). You can look at [extra/gpu-screen-recorder.env](https://git.dec05eba.com/gpu-screen-recorder/plain/extra/gpu-screen-recorder.env) to see an example.
You can see which variables that you can use in the `gpu-screen-recorder.env` file by looking at the `extra/gpu-screen-recorder.service` file. Note that all of the variables are optional, you only have to set the ones that are you interested in.
You can use the `scripts/save-replay.sh` script to save a replay and by default the systemd service saves videos in `$HOME/Videos`.
## Run a script when a video is saved
@@ -209,7 +210,7 @@ Newer ffmpeg versions don't support older nvidia cards. Try installing GPU Scree
## I get a black screen/glitches while live streaming
It seems like ffmpeg earlier than version 6.1 has some type of bug. Install ffmpeg version 6.1 or later and then reinstall GPU Screen Recorder to fix this issue. The flatpak version of GPU Screen Recorder comes with a newer version of ffmpeg so no extra steps are needed.
## I can't play the video in my browser directly or in discord
Browsers and discord don't support hevc video codec at the moment. Choose h264 video codec instead with the -k h264 option.
Browsers and discord don't support hevc video codec at the moment. You can instead choose h264 video codec with the -k h264 option or av1 video codec with the -k av1 option.
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
@@ -261,3 +262,5 @@ If the root user is disabled on your system then you can instead record with `-w
## GPU usage is high on my laptop
GPU usage on battery powered devices is misleading. For example Intel iGPUs has multiple performance levels and the GPU usage reported on the system is the GPU usage at the current performance level.
The performance level changes depending on the GPU load, so it may say that GPU usage is 80%, but the actual GPU usage may be 5%.
## The video is too dark when capturing full-range video or 10-bit video
This is an issue in some broken video players such as vlc. Play the video with a video player such as mpv (or a mpv frontend such as celluloid) or a browser instead.

12
TODO
View File

@@ -312,8 +312,6 @@ 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.
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.
@@ -398,4 +396,12 @@ Should -low-power option also use vaapi/vulkan low power, if available?
Should capture option x=bla;y=bla be scaled by -s (output resolution scale)? width and height is.
Certain webcam resolutions yuyv resolutions dont work (on amd at least), such as 800x600. Maybe it's because of alignment issue, 600 isn't divisible by 16.
Add option to capture all monitors automatically.
Make -w optional, to only capture audio.
Use GL_RGBA16F or GL_RGBA32F for hdr, that allows color values to be outside the 0.0 to 1.0 range.
https://registry.khronos.org/EGL/extensions/EXT/EGL_EXT_surface_SMPTE2086_metadata.txt
Doesn't work: sibs run --args -w "/dev/video0;camera_width=800;camera_height=600;pixfmt=yuyv" -fm content -o video.mp4

View File

@@ -3,6 +3,7 @@ Description=GPU Screen Recorder Service
[Service]
EnvironmentFile=-%h/.config/gpu-screen-recorder.env
EnvironmentFile=-%h/.config/gpu-screen-recorder/gpu-screen-recorder.env
Environment=WINDOW=screen
Environment=CONTAINER=mp4
Environment=QUALITY=40000

View File

@@ -14,13 +14,17 @@ gpu-screen-recorder \- The fastest screen recording tool for Linux
|
.B \-\-version
|
.B \-\-info
|
.B \-\-list\-capture\-options
|
.B \-\-list\-monitors
|
.B \-\-list\-v4l2\-devices
|
.B \-\-list\-audio\-devices
|
.B \-\-list\-application\-audio
|
.B \-\-info
.SH DESCRIPTION
.B gpu-screen-recorder
is the fastest screen recording tool for Linux. It uses the GPU
@@ -92,10 +96,6 @@ Run
.B \-\-list\-capture\-options
to list available capture sources.
.PP
Run
.B \-\-list\-v4l2\-devices
to list available camera devices (V4L2).
.PP
Additional options can be passed to each capture source by splitting capture source with
.B ;
for example
@@ -209,7 +209,7 @@ A value of 0 means to use the best option available.
.BI \-region " WxH+X+Y"
Specify region to capture when using
.BR \-w " region."
Format is width x height + X offset + Y offset. Use 0x0 for full monitor.
Format is width x height + X offset + Y offset. Set width and height to 0 to capture the whole monitor that contains the position.
It is compatible with tools such as slop (X11) and slurp (Wayland).
.TP
@@ -283,10 +283,12 @@ Video codec:
Quality preset (medium, high, very_high, ultra) for QP/VBR mode, or bitrate (kbps) for CBR mode (default: very_high).
.TP
.BI \-bm " auto|qp|vbr|cbr"
Bitrate mode (default: auto → qp). CBR recommended for replay buffer.
Bitrate mode (default: auto → qp). CBR recommended for replay buffer and live streaming.
QP means to capture with constant quality, even in motion, while VBR and CBR means to capture with constant size.
.TP
.BI \-fm " cfr|vfr|content"
Frame rate mode: constant, variable, or match content (default: vfr). Content mode only on X11 or portal.
Frame rate mode: cfr (constant), vfr (variable), or content (match content) (default: vfr). Content mode is only available on X11 or portal.
Content mode syncs video to the captured content and is recommended for smoothest video when the game is running
at the same frame rate or lower than what you are trying to record at.
@@ -379,7 +381,7 @@ When enabled, writes a timestamp file with extra extension \fI.ts\fR next to the
monotonic_microsec realtime_microsec
<monotonic_microsec> <realtime_microsec>
.fi
(default: no). Ignored for livestreaming and when output is piped.
(default: no). Ignored for live streaming and when output is piped.
.SS Output Options
.TP
.BI \-o " output"
@@ -401,6 +403,9 @@ Show system info (codecs, capture options).
.B \-\-list\-capture\-options
List available capture sources (window, monitors, portal, v4l2 device path).
.TP
.B \-\-list\-monitors
List available monitors.
.TP
.B \-\-list\-v4l2\-devices
List available cameras devices (V4L2).
.TP
@@ -445,7 +450,7 @@ Save last 30 minutes (replay mode).
Use
.B pkill
to send signals (e.g.,
.BR "pkill -SIGUSR1 -f gpu-screen-recorder" ).
.BR "pkill -SIGUSR1 -f ""^gpu-screen-recorder""" ).
.SH EXAMPLES
Record monitor at 60 FPS with desktop audio:
.PP
@@ -544,7 +549,7 @@ gpu-screen-recorder -w "DP-1|DP-2;x=1920" -o video.mp4
.fi
.SH FILES
.TP
.I ~/.config/gpu-screen-recorder.env
.I ~/.config/gpu-screen-recorder/gpu-screen-recorder.env
Environment variables for systemd service (optional).
.TP
.I /usr/lib/modprobe.d/gsr-nvidia.conf
@@ -601,8 +606,16 @@ ShadowPlay-style UI
Developed by dec05eba and contributors.
.SH COPYRIGHT
Copyright © dec05eba. Licensed under GPL3-only.
.SH BUGS
.SH REPORTING BUGS
Report bugs at
.UR mailto:dec05eba@protonmail.com
dec05eba@protonmail.com
.UE .
.UR https://git.dec05eba.com/?p=about
See more information about reporting bugs at the gpu-screen-recorder website
.UE .
.br
.UR https://git.dec05eba.com/gpu-screen-recorder/about/
Before reporting a bug or an issue, please take a look at FAQ part of the README
.UE .
The bug or issue may have been previously reported or may not be related to gpu-screen-recorder.

View File

@@ -63,6 +63,7 @@ typedef struct {
void (*list_application_audio)(void *userdata);
void (*list_v4l2_devices)(void *userdata);
void (*list_capture_options)(const char *card_path, void *userdata);
void (*list_monitors)(void *userdata);
} args_handlers;
typedef struct {

View File

@@ -112,6 +112,7 @@ typedef struct {
bool paused;
double paused_start_secs;
bool streaming;
gsr_monitor_rotation rotation;
} gsr_pipewire_video;

View File

@@ -378,6 +378,7 @@ int gsr_kms_client_init(gsr_kms_client *self, const char *card_path) {
goto err;
}
}
poll_fd.revents = 0;
}
fprintf(stderr, "gsr info: gsr_kms_client_init: server connected\n");

View File

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

View File

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

View File

@@ -196,7 +196,8 @@ static void usage_header(void) {
"[-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] "
"[-fallback-cpu-encoding yes|no] [-o <output_file>] [-ro <output_directory>] [-ffmpeg-opts <options>] [--list-capture-options [card_path]] "
"[--list-audio-devices] [--list-application-audio] [--list-v4l2-devices] [-write-first-frame-ts yes|no] [-low-power yes|no] [-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
"[--list-monitors] [--list-audio-devices] [--list-application-audio] [--list-v4l2-devices] [-write-first-frame-ts yes|no] [-low-power yes|no] "
"[-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
fflush(stdout);
}
@@ -500,6 +501,11 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
}
}
if(strcmp(argv[1], "--list-monitors") == 0) {
arg_handlers->list_monitors(userdata);
return true;
}
if(argc == 2 && strcmp(argv[1], "--version") == 0) {
arg_handlers->version(userdata);
return true;

View File

@@ -458,6 +458,7 @@ static bool gsr_capture_v4l2_map_buffer(gsr_capture_v4l2 *self, const struct v4l
if(!self->dma_image[i]) {
self->yuyv_conversion_fallback = true;
// This doesn't work for everybody on nvidia. On pop os 24.04 when egl_display is NULL (when capturing monitor on x11) then this fails with EGL_BAD_DISPLAY
self->dma_image[i] = self->params.egl->eglCreateImage(self->params.egl->egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, (intptr_t[]) {
EGL_WIDTH, fmt->fmt.pix.width,
EGL_HEIGHT, fmt->fmt.pix.height,

View File

@@ -187,6 +187,7 @@ static void gsr_capture_xcomposite_on_event(gsr_capture *cap, gsr_egl *egl) {
break;
}
case ConfigureNotify: {
// TODO: Use PresentConfigureNotify instead
self->window_pos.x = xev->xconfigure.x;
self->window_pos.y = xev->xconfigure.y;

View File

@@ -1888,23 +1888,31 @@ static void camera_query_callback(const char *path, const gsr_capture_v4l2_suppo
printf("%s|%ux%u@%uhz|%s\n", path, setup->resolution.width, setup->resolution.height, gsr_capture_v4l2_framerate_to_number(setup->framerate), gsr_capture_v4l2_pixfmt_to_string(setup->pixfmt));
}
static void list_supported_capture_options(const gsr_window *window, const char *card_path, bool list_monitors) {
// Returns the number of monitors found
static int list_monitors(const gsr_window *window, const char *card_path) {
capture_options_callback options;
options.window = window;
options.num_monitors = 0;
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);
return options.num_monitors;
}
static void list_supported_capture_options(const gsr_window *window, const char *card_path, bool do_list_monitors) {
const bool wayland = gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_WAYLAND;
if(!wayland) {
puts("window");
puts("focused");
}
capture_options_callback options;
options.window = window;
options.num_monitors = 0;
if(list_monitors) {
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);
}
int num_monitors = 0;
if(do_list_monitors)
num_monitors = list_monitors(window, card_path);
if(options.num_monitors > 0)
if(num_monitors > 0)
puts("region");
gsr_capture_v4l2_list_devices(camera_query_callback, NULL);
@@ -1933,11 +1941,20 @@ static void version_command(void *userdata) {
_exit(0);
}
static void info_command(void *userdata) {
(void)userdata;
struct WindowingSetup {
Display *dpy;
gsr_window *window;
gsr_egl egl;
bool list_monitors;
};
static WindowingSetup setup_windowing(bool setup_egl) {
WindowingSetup setup;
memset(&setup, 0, sizeof(setup));
bool wayland = false;
Display *dpy = XOpenDisplay(nullptr);
if (!dpy) {
setup.dpy = XOpenDisplay(nullptr);
if (!setup.dpy) {
wayland = true;
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
}
@@ -1946,7 +1963,7 @@ static void info_command(void *userdata) {
XSetIOErrorHandler(x11_io_error_handler);
if(!wayland)
wayland = is_xwayland(dpy);
wayland = is_xwayland(setup.dpy);
if(!wayland && is_using_prime_run()) {
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
@@ -1956,46 +1973,56 @@ static void info_command(void *userdata) {
disable_prime_run();
}
gsr_window *window = gsr_window_create(dpy, wayland);
if(!window) {
setup.window = gsr_window_create(setup.dpy, wayland);
if(!setup.window) {
fprintf(stderr, "gsr error: failed to create window\n");
_exit(1);
}
gsr_egl egl;
if(!gsr_egl_load(&egl, window, false, false)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(22);
}
setup.list_monitors = true;
bool list_monitors = true;
egl.card_path[0] = '\0';
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) {
// TODO: Allow specifying another card, and in other places
if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) {
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
list_monitors = false;
if(setup_egl) {
if(!gsr_egl_load(&setup.egl, setup.window, false, false)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(22);
}
setup.egl.card_path[0] = '\0';
if(monitor_capture_use_drm(setup.window, setup.egl.gpu_info.vendor)) {
// TODO: Allow specifying another card, and in other places
if(!gsr_get_valid_card_path(&setup.egl, setup.egl.card_path, true)) {
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
setup.list_monitors = false;
}
}
}
return setup;
}
static void info_command(void *userdata) {
(void)userdata;
WindowingSetup windowing_setup = setup_windowing(true);
const bool wayland = gsr_window_get_display_server(windowing_setup.window) == GSR_DISPLAY_SERVER_WAYLAND;
av_log_set_level(AV_LOG_FATAL);
puts("section=system_info");
list_system_info(wayland);
if(egl.gpu_info.is_steam_deck)
if(windowing_setup.egl.gpu_info.is_steam_deck)
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);
list_gpu_info(&windowing_setup.egl);
puts("section=video_codecs");
list_supported_video_codecs(&egl, wayland);
list_supported_video_codecs(&windowing_setup.egl, wayland);
puts("section=image_formats");
puts("jpeg");
puts("png");
puts("section=capture_options");
list_supported_capture_options(window, egl.card_path, list_monitors);
list_supported_capture_options(windowing_setup.window, windowing_setup.egl.card_path, windowing_setup.list_monitors);
fflush(stdout);
@@ -2058,53 +2085,30 @@ static void list_v4l2_devices(void *userdata) {
// |card_path| can be NULL. If not NULL then |vendor| has to be valid
static void list_capture_options_command(const char *card_path, void *userdata) {
(void)userdata;
bool wayland = false;
Display *dpy = XOpenDisplay(nullptr);
if (!dpy) {
wayland = true;
fprintf(stderr, "gsr warning: failed to connect to the X server. Assuming wayland is running without Xwayland\n");
}
WindowingSetup windowing_setup = setup_windowing(card_path != nullptr);
XSetErrorHandler(x11_error_handler);
XSetIOErrorHandler(x11_io_error_handler);
if(card_path)
list_supported_capture_options(windowing_setup.window, card_path, true);
else
list_supported_capture_options(windowing_setup.window, windowing_setup.egl.card_path, windowing_setup.list_monitors);
if(!wayland)
wayland = is_xwayland(dpy);
fflush(stdout);
if(!wayland && is_using_prime_run()) {
// Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device.
// This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected
// to the drm device.
fprintf(stderr, "gsr warning: use of prime-run on X11 is not supported. Disabling prime-run\n");
disable_prime_run();
}
// Not needed as this will just slow down shutdown
//gsr_egl_unload(&egl);
//gsr_window_destroy(&window);
//if(dpy)
// XCloseDisplay(dpy);
gsr_window *window = gsr_window_create(dpy, wayland);
if(!window) {
fprintf(stderr, "gsr error: failed to create window\n");
_exit(1);
}
_exit(0);
}
if(card_path) {
list_supported_capture_options(window, card_path, true);
} else {
gsr_egl egl;
if(!gsr_egl_load(&egl, window, false, false)) {
fprintf(stderr, "gsr error: failed to load opengl\n");
_exit(1);
}
static void list_monitors_command(void *userdata) {
(void)userdata;
WindowingSetup windowing_setup = setup_windowing(true);
bool list_monitors = true;
egl.card_path[0] = '\0';
if(monitor_capture_use_drm(window, egl.gpu_info.vendor)) {
// TODO: Allow specifying another card, and in other places
if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) {
fprintf(stderr, "gsr error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n");
list_monitors = false;
}
}
list_supported_capture_options(window, egl.card_path, list_monitors);
}
if(windowing_setup.list_monitors)
list_monitors(windowing_setup.window, windowing_setup.egl.card_path);
fflush(stdout);
@@ -3263,7 +3267,7 @@ static const AVCodec* pick_video_codec(gsr_egl *egl, args_parser *args_parser, b
if(!video_codec_f && use_fallback_codec && args_parser->video_encoder != GSR_VIDEO_ENCODER_HW_CPU) {
switch(args_parser->video_codec) {
case GSR_VIDEO_CODEC_H264: {
fprintf(stderr, "gsr error: selected video codec h264 is not supported\n");
fprintf(stderr, "gsr error: selected video codec h264 is not supported by your hardware\n");
if(args_parser->fallback_cpu_encoding) {
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");
force_cpu_encoding(args_parser);
@@ -3273,14 +3277,14 @@ static const AVCodec* pick_video_codec(gsr_egl *egl, args_parser *args_parser, b
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");
fprintf(stderr, "gsr warning: selected video codec hevc is not supported by your hardware, trying h264 instead\n");
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
return pick_video_codec(egl, args_parser, true, low_power, supported_video_codecs);
}
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");
fprintf(stderr, "gsr warning: selected video codec av1 is not supported by your hardware, trying h264 instead\n");
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
return pick_video_codec(egl, args_parser, true, low_power, supported_video_codecs);
}
@@ -3289,7 +3293,7 @@ static const AVCodec* pick_video_codec(gsr_egl *egl, args_parser *args_parser, b
// TODO: Cant fallback to other codec because webm only supports vp8/vp9
break;
case GSR_VIDEO_CODEC_H264_VULKAN: {
fprintf(stderr, "gsr warning: selected video codec h264_vulkan is not supported, trying h264 instead\n");
fprintf(stderr, "gsr warning: selected video codec h264_vulkan is not supported by your hardware, trying h264 instead\n");
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, args_parser->video_codec, false, true, supported_video_codecs)) {
@@ -3300,7 +3304,7 @@ static const AVCodec* pick_video_codec(gsr_egl *egl, args_parser *args_parser, b
return pick_video_codec(egl, args_parser, true, low_power, supported_video_codecs);
}
case GSR_VIDEO_CODEC_HEVC_VULKAN: {
fprintf(stderr, "gsr warning: selected video codec hevc_vulkan is not supported, trying hevc instead\n");
fprintf(stderr, "gsr warning: selected video codec hevc_vulkan is not supported by your hardware, trying hevc instead\n");
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, args_parser->video_codec, false, true, supported_video_codecs)) {
@@ -3560,7 +3564,7 @@ static void set_display_server_environment_variables() {
const char *wayland_display = getenv("WAYLAND_DISPLAY");
if(!wayland_display) {
wayland_display = "wayland-1";
wayland_display = "wayland-0";
setenv("WAYLAND_DISPLAY", wayland_display, true);
}
}
@@ -3682,6 +3686,7 @@ int main(int argc, char **argv) {
arg_handlers.list_application_audio = list_application_audio_command;
arg_handlers.list_v4l2_devices = list_v4l2_devices;
arg_handlers.list_capture_options = list_capture_options_command;
arg_handlers.list_monitors = list_monitors_command;
args_parser arg_parser;
if(!args_parser_parse(&arg_parser, argc, argv, &arg_handlers, NULL))

View File

@@ -134,7 +134,7 @@ static void on_process_cb(void *user_data) {
}
struct spa_buffer *buffer = pw_buf->buffer;
const bool has_buffer = buffer->datas[0].chunk->size != 0;
const bool has_buffer = buffer->n_datas > 0 && buffer->datas[0].chunk->size != 0;
pthread_mutex_lock(&self->mutex);
@@ -348,6 +348,7 @@ static void on_state_changed_cb(void *user_data, enum pw_stream_state prev_state
} else {
self->paused = false;
}
self->streaming = (new_state == PW_STREAM_STATE_STREAMING);
pthread_mutex_unlock(&self->mutex);
}
@@ -532,31 +533,36 @@ static bool spa_video_format_get_modifiers(gsr_pipewire_video *self, const enum
static void gsr_pipewire_video_init_modifiers(gsr_pipewire_video *self) {
for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) {
self->supported_video_formats[i].format = video_formats[i];
int32_t num_modifiers = 0;
spa_video_format_get_modifiers(self, self->supported_video_formats[i].format, self->modifiers + self->num_modifiers, GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS - self->num_modifiers, &num_modifiers);
int32_t num_modifiers_video_format = 0;
spa_video_format_get_modifiers(self, self->supported_video_formats[i].format, self->modifiers + self->num_modifiers, GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS - self->num_modifiers, &num_modifiers_video_format);
self->supported_video_formats[i].modifiers_index = self->num_modifiers;
self->supported_video_formats[i].modifiers_size = num_modifiers;
self->num_modifiers += num_modifiers;
self->supported_video_formats[i].modifiers_size = num_modifiers_video_format;
self->num_modifiers += num_modifiers_video_format;
}
}
static void gsr_pipewire_video_format_remove_modifier(gsr_pipewire_video *self, gsr_video_format *video_format, uint64_t modifier) {
/* Returns the number of modifiers */
static size_t gsr_pipewire_video_format_remove_modifier(gsr_pipewire_video *self, gsr_video_format *video_format, uint64_t modifier) {
for(size_t i = 0; i < video_format->modifiers_size; ++i) {
if(self->modifiers[video_format->modifiers_index + i] != modifier)
continue;
if(self->modifiers[video_format->modifiers_index + i] == modifier) {
for(size_t j = i + 1; j < video_format->modifiers_size; ++j) {
self->modifiers[video_format->modifiers_index + j - 1] = self->modifiers[video_format->modifiers_index + j];
}
for(size_t j = i + 1; j < video_format->modifiers_size; ++j) {
self->modifiers[j - 1] = self->modifiers[j];
--video_format->modifiers_size;
break;
}
--video_format->modifiers_size;
return;
}
return video_format->modifiers_size;
}
static void gsr_pipewire_video_remove_modifier(gsr_pipewire_video *self, uint64_t modifier) {
self->num_modifiers = 0;
for(size_t i = 0; i < GSR_PIPEWIRE_VIDEO_NUM_VIDEO_FORMATS; i++) {
gsr_video_format *video_format = &self->supported_video_formats[i];
gsr_pipewire_video_format_remove_modifier(self, video_format, modifier);
const size_t num_modifiers_video_format = gsr_pipewire_video_format_remove_modifier(self, video_format, modifier);
video_format->modifiers_index = self->num_modifiers;
self->num_modifiers += num_modifiers_video_format;
}
}
@@ -793,7 +799,7 @@ static EGLImage gsr_pipewire_video_create_egl_image_with_fallback(gsr_pipewire_v
self->no_modifiers_fallback = true;
image = gsr_pipewire_video_create_egl_image(self, fds, offsets, pitches, modifiers, false);
} else {
fprintf(stderr, "gsr error: gsr_pipewire_video_create_egl_image_with_fallback: failed to create egl image with modifiers, renegotiating with a different modifier\n");
fprintf(stderr, "gsr error: gsr_pipewire_video_create_egl_image_with_fallback: failed to create egl image with modifier 0x%" PRIx64 ", renegotiating with a different modifier\n", self->format.info.raw.modifier);
self->negotiated = false;
pw_thread_loop_lock(self->thread_loop);
gsr_pipewire_video_remove_modifier(self, self->format.info.raw.modifier);
@@ -853,7 +859,7 @@ bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map te
output->rotation = GSR_MONITOR_ROT_0;
pthread_mutex_lock(&self->mutex);
if(!self->negotiated || self->dmabuf_data[0].fd <= 0) {
if(!self->negotiated || !self->streaming || self->dmabuf_data[0].fd <= 0) {
pthread_mutex_unlock(&self->mutex);
return false;
}

View File

@@ -340,11 +340,18 @@ static bool pa_sound_device_handle_reconnect(pa_handle *p, char *device_name, si
return false;
}
if(!(p->stream = pa_stream_new(p->context, p->stream_name, &p->ss, NULL))) {
pa_proplist *proplist = pa_proplist_new();
// This prevents microphone recording indicator from being shown on KDE
pa_proplist_sets(proplist, "node.virtual", "true");
if(!(p->stream = pa_stream_new_with_proplist(p->context, p->stream_name, &p->ss, NULL, proplist))) {
//pa_context_errno(p->context);
pa_proplist_free(proplist);
return false;
}
pa_proplist_free(proplist);
const int r = pa_stream_connect_record(p->stream, device_name, &p->attr,
(pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_ADJUST_LATENCY|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_DONT_MOVE));