mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-01 01:19:29 +09:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ed04830c1 | ||
|
|
755340454d | ||
|
|
2f8212b122 | ||
|
|
23782889be | ||
|
|
fd08cdb9b4 | ||
|
|
6079a0162d | ||
|
|
eff5d619eb | ||
|
|
309c4e5f2e | ||
|
|
0555cfde58 | ||
|
|
ff030ba63f | ||
|
|
0de75e5b7e | ||
|
|
c79fb1e5c9 | ||
|
|
4a4af85b6d | ||
|
|
8f7608e7ee | ||
|
|
f3235ed1bf | ||
|
|
3666bba518 | ||
|
|
5d8d14eeaf |
21
README.md
21
README.md
@@ -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
12
TODO
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -112,6 +112,7 @@ typedef struct {
|
||||
|
||||
bool paused;
|
||||
double paused_start_secs;
|
||||
bool streaming;
|
||||
|
||||
gsr_monitor_rotation rotation;
|
||||
} gsr_pipewire_video;
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gpu-screen-recorder"
|
||||
type = "executable"
|
||||
version = "5.12.3"
|
||||
version = "5.12.5"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
165
src/main.cpp
165
src/main.cpp
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user