mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-04-03 02:01:39 +09:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72c548c19e | ||
|
|
7123081e53 | ||
|
|
eba5f3f43d | ||
|
|
76dd8004e9 | ||
|
|
02c7a0bcce | ||
|
|
82177cd742 | ||
|
|
84e3b911d9 | ||
|
|
f9ab3ebd65 | ||
|
|
ab5988a2bb | ||
|
|
95c6fc84ea | ||
|
|
a10b882e82 | ||
|
|
92f4bd5f95 | ||
|
|
cc43ca0336 | ||
|
|
a3e1b2a896 | ||
|
|
80f0e483a4 | ||
|
|
739fd9cd72 | ||
|
|
ec90166c6e | ||
|
|
d34a1036ef | ||
|
|
bf6011ffcf | ||
|
|
20e101bfe9 | ||
|
|
e99605fec2 | ||
|
|
9e59f5f5cd | ||
|
|
96c62f2db2 | ||
|
|
dc15c69848 | ||
|
|
a545fe45c3 | ||
|
|
e623145de8 | ||
|
|
7d4a4068ff | ||
|
|
57ac400eab | ||
|
|
cf65b24d3d | ||
|
|
8c42c47627 | ||
|
|
dee37433af | ||
|
|
f9f0074f29 | ||
|
|
50fbee2b2f | ||
|
|
c60783fdcb | ||
|
|
313d3227d8 | ||
|
|
3c5514480c | ||
|
|
afd140f33b |
73
README.md
73
README.md
@@ -14,7 +14,7 @@ This is a cli-only tool, if you want an UI for this check out [GPU Screen Record
|
||||
Supported video codecs:
|
||||
* H264 (default)
|
||||
* HEVC (Optionally with HDR)
|
||||
* AV1 (Optionally with HDR. Not currently supported on NVIDIA if you use GPU Screen Recorder flatpak)
|
||||
* AV1 (Optionally with HDR. Not currently supported on NVIDIA in the flatpak version of GPU Screen Recorder)
|
||||
* VP8
|
||||
* VP9
|
||||
|
||||
@@ -31,9 +31,9 @@ This software works on X11 and Wayland on AMD, Intel and NVIDIA.
|
||||
# Installation
|
||||
If you are running an Arch Linux based distro then you can find gpu screen recorder on aur under the name gpu-screen-recorder (`yay -S gpu-screen-recorder`).\
|
||||
If you are running another distro then you can run `sudo ./install.sh`, but you need to manually install the dependencies, as described below.\
|
||||
You can also install gpu screen recorder ([the gtk gui version](https://git.dec05eba.com/gpu-screen-recorder-gtk/)) from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder), which is the easiest method
|
||||
You can also install gpu screen recorder ([the ui version](https://git.dec05eba.com/gpu-screen-recorder-gtk/)) from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder), which is the easiest method
|
||||
to install GPU Screen Recorder on non-arch based distros.\
|
||||
If you install GPU Screen Recorder flatpak, which is the gtk gui version then you can still run GPU Screen Recorder command line by using the flatpak command option, for example `flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder -w screen -f 60 -o video.mp4`. Note that if you want to record your monitor on AMD/Intel then you need to install the flatpak system-wide (like so: `flatpak install flathub --system com.dec05eba.gpu_screen_recorder`).
|
||||
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.\
|
||||
@@ -55,14 +55,14 @@ GPU Screen Recorder uses meson build system so you need to install `meson` to bu
|
||||
## Build dependencies
|
||||
These are the dependencies needed to build GPU Screen Recorder:
|
||||
|
||||
* vulkan-headers
|
||||
* ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)
|
||||
* x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage)
|
||||
* libpulse
|
||||
* wayland (wayland-client, wayland-egl, wayland-scanner)
|
||||
* ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)
|
||||
* libva (and libva-drm)
|
||||
* libpulse
|
||||
* libdrm
|
||||
* libcap
|
||||
* wayland (wayland-client, wayland-egl, wayland-scanner)
|
||||
* vulkan-headers
|
||||
|
||||
## Optional dependencies
|
||||
When building GPU Screen Recorder with portal support (`-Dportal=true` meson option, which is enabled by default) these dependencies are also needed:
|
||||
@@ -80,7 +80,7 @@ There are also additional dependencies needed at runtime depending on your GPU v
|
||||
|
||||
### Intel
|
||||
* mesa
|
||||
* vaapi (intel-media-driver/libva-intel-driver/linux-firmware-intel, depending on which intel iGPU you have)
|
||||
* vaapi (intel-media-driver/libva-intel-driver/linux-firmware-intel, depending on which intel GPU you have)
|
||||
|
||||
### NVIDIA
|
||||
* cuda runtime (libcuda.so.1) (libnvidia-compute)
|
||||
@@ -102,11 +102,8 @@ You can see a list of capture options to record if you run `gpu-screen-recorder
|
||||
```
|
||||
in this case you could record a window or a monitor with the name `DP-1`.\
|
||||
To list available audio devices that you can use you can run `gpu-screen-recorder --list-audio-devices` and the name to use is on the left size of the `|`.\
|
||||
To list available audio application names that you can use you can run `gpu-screen-recorder --list-application-audio`.
|
||||
## Streaming
|
||||
Streaming works the same way as recording, but the `-o` argument should be path to the live streaming service you want to use (including your live streaming key). Take a look at `scripts/twitch-stream.sh` to see an example of how to stream to twitch.\
|
||||
GPU Screen Recorder uses Ffmpeg so GPU Screen Recorder supports all protocols that Ffmpeg supports.\
|
||||
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.
|
||||
To list available audio application names that you can use you can run `gpu-screen-recorder --list-application-audio`.\
|
||||
You can run `gpu-screen-recorder --info` to list more information about the system, such as the device that is used for capture and video encoding and supported codecs. These commands can be parsed by scripts/programs.
|
||||
## Replay mode
|
||||
Run `gpu-screen-recorder` with the `-c mp4` and `-r` option, for example: `gpu-screen-recorder -w screen -f 60 -r 30 -c mp4 -o ~/Videos`. Note that in this case, `-o` should point to a directory.\
|
||||
If `-df yes` is set, replays are save in folders based on the date.
|
||||
@@ -117,6 +114,10 @@ This can be used for example to show a notification when a replay has been saved
|
||||
The replay buffer is stored in ram (as encoded video) by default, so don't use a too large replay time and/or video quality unless you have enough ram to store it.\
|
||||
You can use the `-replay-storage disk` option to store the replay buffer on disk instead of ram (in the same location as the output video).\
|
||||
By default videos are recorded with constant quality, but with replay mode you might want to record in constant bitrate mode instead for consistent ram/disk usage in high motion scenes. You can do that by using the `-bm cbr` option (along with `-q` option, for example `-bm cbr -q 20000`).
|
||||
## Streaming
|
||||
Streaming works the same way as recording, but the `-o` argument should be path to the live streaming service you want to use (including your live streaming key). Take a look at `scripts/twitch-stream.sh` to see an example of how to stream to twitch.\
|
||||
GPU Screen Recorder uses Ffmpeg so GPU Screen Recorder supports all protocols that Ffmpeg supports.\
|
||||
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.\
|
||||
To start/stop (and save) recording use the SIGRTMIN signal, for example `pkill -SIGRTMIN -f gpu-screen-recorder`. The name of the video will be displayed in stdout when saving the video.\
|
||||
@@ -150,22 +151,12 @@ If you record at your monitors refresh rate and enabled vsync in a game then the
|
||||
This is an issue in some games.
|
||||
If you experience this issue then you might want to either disable vsync in the game or use the `-fm content` option to sync capture to the content on the screen. For example: `gpu-screen-recorder -w screen -fm content -o video.mp4`.\
|
||||
Note that this option is currently only available on X11, or with desktop portal capture on Wayland (`-w portal`).
|
||||
# Issues
|
||||
## NVIDIA
|
||||
Nvidia drivers have an issue where CUDA breaks if CUDA is running when suspend/hibernation happens, and it remains broken until you reload the nvidia driver. `extra/gsr-nvidia.conf` will be installed by default when you install GPU Screen Recorder and that should fix this issue. If this doesn't fix the issue for you then your distro may use a different path for modprobe files. In that case you have to install that `extra/gsr-nvidia.conf` yourself into that location.
|
||||
You have to reboot your computer after installing GPU Screen Recorder for the first time for the fix to have any effect.
|
||||
# Examples
|
||||
Look at the [scripts](https://git.dec05eba.com/gpu-screen-recorder/tree/scripts) directory for script examples. For example if you want to automatically save a recording/replay into a folder with the same name as the game you are recording.
|
||||
|
||||
# AMD/Intel/Wayland root permission
|
||||
When recording a window or when using the `-w portal` option no special user permission is required,
|
||||
however when recording a monitor the program needs root permission (to access KMS).\
|
||||
This is safe in GPU Screen Recorder as the part that needs root access has been moved to its own small program that only does one thing.\
|
||||
For you as a user this only means that if you installed GPU Screen Recorder as a flatpak then a prompt asking for root password will show up once when you start recording.
|
||||
# Performance
|
||||
On a system with a i5 4690k CPU and a GTX 1080 GPU:\
|
||||
On a system with an i5 4690k CPU and a GTX 1080 GPU:\
|
||||
When recording Legend of Zelda Breath of the Wild at 4k, fps drops from 30 to 7 when using OBS Studio + nvenc, however when using this screen recorder the fps remains at 30.\
|
||||
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.\
|
||||
On a system with an AMD Ryzen 9 5900X CPU and an RX 7800XT GPU I dont see any fps drop at all, even when recording at 4k 60fps with AV1 codec with 10-bit colors.\
|
||||
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) and [https://www.youtube.com/watch?v=aK67RSZw2ZQ](https://www.youtube.com/watch?v=aK67RSZw2ZQ).\
|
||||
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.\
|
||||
@@ -175,6 +166,25 @@ NVIDIA driver has a "feature" (read: bug) where it will downclock memory transfe
|
||||
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!
|
||||
|
||||
# Issues
|
||||
## NVIDIA
|
||||
Nvidia drivers have an issue where CUDA breaks if CUDA is running when suspend/hibernation happens, and it remains broken until you reload the nvidia driver. `extra/gsr-nvidia.conf` will be installed by default when you install GPU Screen Recorder and that should fix this issue. If this doesn't fix the issue for you then your distro may use a different path for modprobe files. In that case you have to install that `extra/gsr-nvidia.conf` yourself into that location.
|
||||
You have to reboot your computer after installing GPU Screen Recorder for the first time for the fix to have any effect.
|
||||
|
||||
## TEMPORARY ISSUES
|
||||
1) Videos are in variable framerate format. Use MPV to play such videos, otherwise you might experience stuttering in the video if you are using a buggy video player. You can try saving the video into a .mkv file instead as some software may have better support for .mkv files (such as kdenlive). You can use the "-fm cfr" option to to use constant framerate mode.
|
||||
2) FLAC audio codec is disabled at the moment because of temporary issues.
|
||||
|
||||
# Examples
|
||||
Look at the [scripts](https://git.dec05eba.com/gpu-screen-recorder/tree/scripts) directory for script examples. For example if you want to automatically save a recording/replay into a folder with the same name as the game you are recording.
|
||||
|
||||
# AMD/Intel/Wayland root permission
|
||||
When recording a window or when using the `-w portal` option no special user permission is required,
|
||||
however when recording a monitor the program needs root permission (to access KMS).\
|
||||
This is safe in GPU Screen Recorder as the part that needs root access has been moved to its own small program that only does one thing.\
|
||||
For you as a user this only means that if you installed GPU Screen Recorder as a flatpak then a prompt asking for root password will show up once when you start recording.
|
||||
|
||||
# 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 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.
|
||||
@@ -185,10 +195,6 @@ See [https://git.dec05eba.com/?p=about](https://git.dec05eba.com/?p=about).
|
||||
# Demo
|
||||
[](https://www.youtube.com/watch?v=n5tm0g01n6A)
|
||||
|
||||
# TEMPORARY ISSUES
|
||||
1) Videos are in variable framerate format. Use MPV to play such videos, otherwise you might experience stuttering in the video if you are using a buggy video player. You can try saving the video into a .mkv file instead as some software may have better support for .mkv files (such as kdenlive). You can use the "-fm cfr" option to to use constant framerate mode.
|
||||
2) FLAC audio codec is disabled at the moment because of temporary issues.
|
||||
|
||||
# FAQ
|
||||
## It tells me that my AMD/Intel GPU is not supported or that my GPU doesn't support h264/hevc, but that's not true!
|
||||
Some linux distros (such as manjaro and fedora) disable hardware accelerated h264/hevc on AMD/Intel because of "patent license issues". If you are using an arch-based distro then you can install mesa-git instead of mesa and if you are using another distro then you may have to switch to a better distro. On fedora based distros you can follow this: [Hardware Accelerated Codec](https://rpmfusion.org/Howto/Multimedia).\
|
||||
@@ -202,7 +208,9 @@ Browsers and discord don't support hevc video codec at the moment. Choose h264 v
|
||||
Note that websites such as youtube support hevc so there is no need to choose h264 video codec if you intend to upload the video to youtube or if you want to play the video locally or if you intend to
|
||||
edit the video with a video editor. Hevc allows for better video quality (especially at lower file sizes) so hevc (or av1) is recommended for source videos.
|
||||
## I get a black bar/distorted colors on the sides in the video
|
||||
This is mostly an issue on AMD. For av1 it's a hardware issue, see: https://gitlab.freedesktop.org/mesa/mesa/-/issues/9185. For hevc it's a software issue in the AMD driver that hasn't been fixed yet. This issue happens at certain video resolutions. If you get this issue then a workaround is to record with h264 video codec instead (using the -k h264 option).
|
||||
This is mostly an issue on AMD. For av1 it's a hardware issue, see: https://gitlab.freedesktop.org/mesa/mesa/-/issues/9185. For hevc it's a software issue in ffmpeg that was fixed in ffmpeg version 8.\
|
||||
If your ffmpeg version is older than 8 then you can use the flatpak version of GPU Screen Recorder which comes with ffmpeg version >= 8.\
|
||||
Alternatively you can record with h264 codec (-k h264, which is also the default codec) to workaround this issue.
|
||||
## The video doesn't display or has a green/yellow overlay
|
||||
This can happen if your video player is missing the H264/HEVC video codecs. Either install the codecs or use mpv.
|
||||
## I get stutter in the video
|
||||
@@ -224,10 +232,13 @@ You have to use external software for that, such as Easy Effects or NoiseTorch.
|
||||
It's not really possible except in some cases. You can only record with the GPU that is displaying the graphics on your monitor.\
|
||||
Some laptops have display adapters that connect external monitors directly to the external GPU (if you have one)
|
||||
and on Wayland the external GPU will display the graphics for that monitor.
|
||||
In that case you can record the monitor with the external GPU by launching GPU Screen Recorder with [prime-run or by setting the DRI_PRIME environment variable](https://wiki.archlinux.org/title/PRIME) depending on your GPU brand.\
|
||||
In that case you can record the monitor with the external GPU by launching GPU Screen Recorder with [prime-run or by setting the DRI_PRIME environment variable](https://wiki.archlinux.org/title/PRIME) depending on your GPU brand.
|
||||
Alternatively you can capture with the desktop portal option (`-w portal`), which should allow you to capture any monitor.\
|
||||
However if you really want to change which GPU you want to record and encode with with then you can instead configure your display server (Xorg or Wayland compositor) to run with that GPU,
|
||||
then GPU Screen Recorder will automatically use that same GPU for recording and encoding.
|
||||
## The rotation of the video is incorrect when the monitor is rotated when using desktop portal capture
|
||||
This is a bug in kde plasma wayland. When using desktop portal capture and the monitor is rotated and a window is made fullscreen kde plasma wayland will give incorrect rotation to GPU Screen Recorder.
|
||||
This also affects other screen recording software such as obs studio.\
|
||||
Capture a monitor directly instead to workaround this issue until kde plasma devs fix it, or use another wayland compositor that doesn't have this issue.
|
||||
## System notifications get disabled when recording with desktop portal option
|
||||
Some desktop environments such as KDE Plasma turn off notifications when you record the screen with the desktop portal option. You can disable this by going into KDE Plasma settings -> search for notifications and then under "Do Not Disturb mode" untick "During screen sharing".
|
||||
|
||||
58
TODO
58
TODO
@@ -30,9 +30,6 @@ Window capture doesn't work properly in _control_ game after going from pause me
|
||||
Monitor capture on steam deck is slightly below the game fps, but only when capturing on the steam deck screen. If capturing on another monitor, there is no issue.
|
||||
Is this related to the dma buf rotation issue? different modifier being slow? does this always happen?
|
||||
|
||||
Fallback to vaapi copy in kms if opengl version fails. This can happen on steam deck for some reason (driver bug?). Also vaapi copy uses less gpu since it uses video codec unit to copy.
|
||||
Test if vaapi copy version uses less memory than opengl version.
|
||||
|
||||
Intel is a bit weird with monitor capture and multiple monitors. If one of the monitors is rotated then all the kms will be rotated as well.
|
||||
Is that only the case when the primary monitor is rotated? Also the primary monitor becomes position 0, 0 so crtc (x11 randr) position doesn't match the drm pos. Maybe get monitor position and size from drm instead.
|
||||
How about if multiple monitors are rotated?
|
||||
@@ -64,8 +61,6 @@ Remove follow focused option.
|
||||
|
||||
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.
|
||||
|
||||
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.-
|
||||
|
||||
@@ -84,8 +79,6 @@ When vulkan encode is added, mention minimum nvidia driver required. (550.54.14?
|
||||
Investigate if there is a way to do gpu->gpu copy directly without touching system ram to enable video encoding on a different gpu. On nvidia this is possible with cudaMemcpyPeer, but how about from an intel/amd gpu to an nvidia gpu or the other way around or any combination of iGPU and dedicated GPU?
|
||||
Maybe something with clEnqueueMigrateMemObjects? on AMD something with DirectGMA maybe?
|
||||
|
||||
Go back to using pure vaapi without opengl for video encoding? rotation (transpose) can be done if its done after (rgb to yuv) color conversion.
|
||||
|
||||
Use lanczos resampling for better scaling quality. Lanczos resampling can also be used for YUV chroma for better color quality on small text.
|
||||
|
||||
Flac is disabled because the frame sizes are too large which causes big audio/video desync.
|
||||
@@ -189,13 +182,6 @@ Use wayland color management protocol when it's available: https://gitlab.freede
|
||||
Use different exit codes for different errors. Use one for invalid -w option, another one for invalid -a option for audio devices, etc. This is to make UI error reporting better.
|
||||
Document these exit codes in an exit code .md file, or finally create a manpage where this can be documented.
|
||||
|
||||
Ffmpeg fixed black bars in videos on amd when using hevc and when recording at some resolutions, such as 1080p:
|
||||
https://github.com/FFmpeg/FFmpeg/commit/bcfbf2bac8f9eeeedc407b40596f5c7aaa0d5b47
|
||||
https://github.com/FFmpeg/FFmpeg/commit/d0facac679faf45d3356dff2e2cb382580d7a521
|
||||
Disable gpu screen recorder black bar handling when using hevc on amd when the libavcodec version is the one that comes after those commits.
|
||||
Also consider the mesa version, to see if the gpu supports this.
|
||||
The version is libavcodec >= 61.28.100
|
||||
|
||||
Use opengl compute shader instead of graphics shader. This might allow for better performance when games are using 100% of graphics unit which might fix issue with 100% gpu usage causing gpu screen recorder to run slow when not using vaapi to convert rgb to nv12(?).
|
||||
|
||||
Always disable prime run/dri prime and list all monitors to record from from all cards.
|
||||
@@ -209,8 +195,6 @@ Always disable prime run/dri prime and list all monitors to record from from all
|
||||
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.
|
||||
|
||||
Replay (and recording?) fails to save properly sometimes (especially for long videos). This is noticable with mp4 files since they get corrupt and become unplayable.
|
||||
The entire video does seem to get saved (it's a large video file) and it seems to have the correct headers but it's not playable.
|
||||
|
||||
@@ -273,7 +257,8 @@ Add the option to set audio track name, for example with -a "track-name:blabla|d
|
||||
Maybe disable qp/vbr for replay. In that case we can preallocate all replay data (for both ram and disk) and write to that directly when receiving packet (dont do that when also recording at the same time).
|
||||
That could improve performance/disk write optimization and maybe even reduce ram usage because of less blocks/fragmentation.
|
||||
|
||||
When rpc is added add the option to add/remove audio devices/app audio and also overlays (from new capture sources).
|
||||
When rpc is added add the option to add/remove audio devices/app audio and also overlays (from new capture sources). It should also be possible to save a replay of any duration (below duration set with -r).
|
||||
Have an rpc info/status command that just returns the status. This is to check if gpu screen recorder is running.
|
||||
|
||||
Support hdr screenshot.
|
||||
|
||||
@@ -314,7 +299,7 @@ It's possible for microphone audio to get desynced when recording together with
|
||||
|
||||
We can use dri2connect/dri3open to get the /dev/dri/card device. Note that this doesn't work on nvidia x11.
|
||||
|
||||
Add support for QVBR (QP with target bitrate).
|
||||
Add support for QVBR (QP with target bitrate). Maybe use VBR instead, since nvidia doesn't support QVBR and neither does vulkan.
|
||||
|
||||
KDE Plasma Wayland seems to use overlay planes now in non-fullscreen mode(limited to 1 overlay plane per gpu). Check if this is the case in the latest kde on arch linux.
|
||||
If it is, then support it in kms capture.
|
||||
@@ -334,3 +319,40 @@ Application audio capture isn't good enough. It creates a sink that for some aut
|
||||
However that causes pulseaudio to be unable to record from gsr-app-sink, and it ends up being stuck in pa_sound_device_handle_reconnect in the loop with pa_mainloop_iterate.
|
||||
|
||||
Add -k best/best_hdr/best_10bit option, to automatically choose the best codec (prefer av1, then hevc and then h264. For webm files choose vp9 and then vp8).
|
||||
|
||||
Check if region capture works properly with fractional scaling on wayland.
|
||||
|
||||
Add option to specify medium/high/very high/ultra for -bm cbr as well, which should automatically pick bitrate based on resolution and framerate.
|
||||
This should also be reflected in gsr ui.
|
||||
|
||||
Create a manpage and move --help text there and mention the manpage command to view it (and make it work in flatpak, maybe with man <link-to-manpage-file>).
|
||||
|
||||
Implement webcam support by using mjpeg with v4l2 and use ffmpeg mjpeg decoder.
|
||||
|
||||
After adding rpc, making recording while in replay/streaming work differently. Start recording should take audio as an argument, to optionally specify different audio for recording than replay/stream.
|
||||
|
||||
After adding rpc, make it possible to add/remove audio and video. The same number of audio tracks should remain, but the audio devices/app should be possible to configure. You should be able to configure the capture sources however you want.
|
||||
|
||||
It takes a while to shutdown gpu screen recorder when using replay with a long replay time (when the replay buffer is for example 6gb). Maybe this has to do with cleaning up so much memory. Try if _exit(0) is faster, ignoring cleanup of anything (assuming the gpu driver is happy with that, nvidia doesn't like such things. GPU drivers are often buggy when it comes to automatic cleanup).
|
||||
|
||||
Support desktop portal hdr capture when this gets merged: https://invent.kde.org/plasma/kwin/-/merge_requests/8293 note that it only works with pipewire >= 1.5.81. If codec is set to non-hdr codec when use desktop portal without hdr (convert to sdr, remove the code that forces non-hdr equivalent of the codec). Update gsr-gtk and gsr-ui accordingly.
|
||||
|
||||
Add option to save screenshot as .qoi. Use that then in gsr-ui for the background for the window (capture the monitor that the ui is going to show on). Do that on cosmic and hyprland, which are unable to display things behind the fullscreen window.
|
||||
|
||||
Support pausing recording when recording while replay/streaming.
|
||||
|
||||
Maybe use VK_VALVE_video_encode_rgb_conversion with vulkan encoding for shader-less rgb to yuv conversion. That would allow screen capture with no gpu processing.
|
||||
|
||||
Cursor sometimes doesn't have color when capturing region scaled (on kde plasma wayland at least).
|
||||
|
||||
Remove drm_monitor_get_display_server_data and do that work in the drm monitor query.
|
||||
|
||||
In gpu screen recorder --info output codec max resolutions. This allows for better error messages in ui frontends and easier and better error handling.
|
||||
|
||||
Set minimum fps for live stream or piping or always.
|
||||
|
||||
Support youtube sso.
|
||||
|
||||
Remove -fm content (support it but remove it from documentation and output deprecation notice when its used) and use it when using -fm vbr (which is the default option).
|
||||
But first -fm content needs to be support on wayland as well, by checking if there is a difference between frames (checksum the frame content).
|
||||
-fm content also needs to have a minimum fps to prevent live stream from timing out when nothing changes on the screen.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
typedef struct gsr_egl gsr_egl;
|
||||
|
||||
#define NUM_ARGS 31
|
||||
#define NUM_ARGS 32
|
||||
|
||||
typedef enum {
|
||||
ARG_TYPE_STRING,
|
||||
@@ -76,6 +76,7 @@ typedef struct {
|
||||
const char *recording_saved_script;
|
||||
bool verbose;
|
||||
bool gl_debug;
|
||||
bool fallback_cpu_encoding;
|
||||
bool record_cursor;
|
||||
bool date_folders;
|
||||
bool restore_portal_session;
|
||||
|
||||
@@ -14,13 +14,17 @@ typedef struct AVContentLightMetadata AVContentLightMetadata;
|
||||
typedef struct gsr_capture gsr_capture;
|
||||
|
||||
typedef struct {
|
||||
int width;
|
||||
int height;
|
||||
// Width and height of the video
|
||||
int video_width;
|
||||
int video_height;
|
||||
// Width and height of the frame at the start of capture, the target size
|
||||
int recording_width;
|
||||
int recording_height;
|
||||
int fps;
|
||||
} gsr_capture_metadata;
|
||||
|
||||
struct gsr_capture {
|
||||
/* These methods should not be called manually. Call gsr_capture_* instead. |capture_metdata->width| and |capture_metadata->height| should be set by this function */
|
||||
/* These methods should not be called manually. Call gsr_capture_* instead. |capture_metadata->video_width| and |capture_metadata->video_height| should be set by this function */
|
||||
int (*start)(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
|
||||
void (*on_event)(gsr_capture *cap, gsr_egl *egl); /* can be NULL */
|
||||
void (*tick)(gsr_capture *cap); /* can be NULL. If there is an event then |on_event| is called before this */
|
||||
|
||||
@@ -60,7 +60,7 @@ typedef struct {
|
||||
int gsr_color_conversion_init(gsr_color_conversion *self, const gsr_color_conversion_params *params);
|
||||
void gsr_color_conversion_deinit(gsr_color_conversion *self);
|
||||
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture, bool alpha_blending);
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture);
|
||||
void gsr_color_conversion_clear(gsr_color_conversion *self);
|
||||
void gsr_color_conversion_read_destination_texture(gsr_color_conversion *self, int destination_texture_index, int x, int y, int width, int height, unsigned int color_format, unsigned int data_format, void *pixels);
|
||||
|
||||
|
||||
@@ -10,23 +10,14 @@ typedef enum {
|
||||
GSR_IMAGE_FORMAT_PNG
|
||||
} gsr_image_format;
|
||||
|
||||
typedef enum {
|
||||
GSR_IMAGE_WRITER_SOURCE_OPENGL,
|
||||
GSR_IMAGE_WRITER_SOURCE_MEMORY
|
||||
} gsr_image_writer_source;
|
||||
|
||||
typedef struct {
|
||||
gsr_image_writer_source source;
|
||||
gsr_egl *egl;
|
||||
int width;
|
||||
int height;
|
||||
unsigned int texture;
|
||||
const void *memory; /* Reference */
|
||||
} gsr_image_writer;
|
||||
|
||||
bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height);
|
||||
/* |memory| is taken as a reference. The data is expected to be in rgba8 format (8 bit rgba) */
|
||||
bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height);
|
||||
void gsr_image_writer_deinit(gsr_image_writer *self);
|
||||
|
||||
/* Quality is between 1 and 100 where 100 is the max quality. Quality doesn't apply to lossless formats */
|
||||
|
||||
@@ -58,6 +58,8 @@ typedef struct {
|
||||
uint64_t modifiers;
|
||||
bool using_external_image;
|
||||
gsr_monitor_rotation rotation;
|
||||
int texture_width;
|
||||
int texture_height;
|
||||
} gsr_map_texture_output;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -58,4 +58,6 @@ vec2i scale_keep_aspect_ratio(vec2i from, vec2i to);
|
||||
|
||||
unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal_format, unsigned int format, int filter);
|
||||
|
||||
bool get_nvidia_driver_version(int *major, int *minor);
|
||||
|
||||
#endif /* GSR_UTILS_H */
|
||||
|
||||
@@ -59,8 +59,8 @@ struct gsr_kms_response_item {
|
||||
gsr_kms_rotation rotation;
|
||||
int x;
|
||||
int y;
|
||||
int crtc_w;
|
||||
int crtc_h;
|
||||
int src_w;
|
||||
int src_h;
|
||||
struct hdr_output_metadata hdr_metadata;
|
||||
};
|
||||
|
||||
|
||||
@@ -141,21 +141,21 @@ typedef enum {
|
||||
PLANE_PROPERTY_Y = 1 << 1,
|
||||
PLANE_PROPERTY_SRC_X = 1 << 2,
|
||||
PLANE_PROPERTY_SRC_Y = 1 << 3,
|
||||
PLANE_PROPERTY_CRTC_W = 1 << 4,
|
||||
PLANE_PROPERTY_CRTC_H = 1 << 5,
|
||||
PLANE_PROPERTY_SRC_W = 1 << 4,
|
||||
PLANE_PROPERTY_SRC_H = 1 << 5,
|
||||
PLANE_PROPERTY_IS_CURSOR = 1 << 6,
|
||||
PLANE_PROPERTY_IS_PRIMARY = 1 << 7,
|
||||
PLANE_PROPERTY_ROTATION = 1 << 8,
|
||||
} plane_property_mask;
|
||||
|
||||
/* Returns plane_property_mask */
|
||||
static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, int *x, int *y, int *src_x, int *src_y, int *crtc_w, int *crtc_h, gsr_kms_rotation *rotation) {
|
||||
static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, int *x, int *y, int *src_x, int *src_y, int *src_w, int *src_h, gsr_kms_rotation *rotation) {
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
*src_x = 0;
|
||||
*src_y = 0;
|
||||
*crtc_w = 0;
|
||||
*crtc_h = 0;
|
||||
*src_w = 0;
|
||||
*src_h = 0;
|
||||
*rotation = KMS_ROT_0;
|
||||
|
||||
plane_property_mask property_mask = 0;
|
||||
@@ -184,12 +184,12 @@ static uint32_t plane_get_properties(int drmfd, uint32_t plane_id, int *x, int *
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_Y") == 0) {
|
||||
*src_y = (int)(props->prop_values[i] >> 16);
|
||||
property_mask |= PLANE_PROPERTY_SRC_Y;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "CRTC_W") == 0) {
|
||||
*crtc_w = props->prop_values[i];
|
||||
property_mask |= PLANE_PROPERTY_CRTC_W;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "CRTC_H") == 0) {
|
||||
*crtc_h = props->prop_values[i];
|
||||
property_mask |= PLANE_PROPERTY_CRTC_H;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_W") == 0) {
|
||||
*src_w = (int)(props->prop_values[i] >> 16);
|
||||
property_mask |= PLANE_PROPERTY_SRC_W;
|
||||
} else if((type & DRM_MODE_PROP_RANGE) && strcmp(prop->name, "SRC_H") == 0) {
|
||||
*src_h = (int)(props->prop_values[i] >> 16);
|
||||
property_mask |= PLANE_PROPERTY_SRC_H;
|
||||
} else if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) {
|
||||
const uint64_t current_enum_value = props->prop_values[i];
|
||||
for(int j = 0; j < prop->count_enums; ++j) {
|
||||
@@ -351,9 +351,9 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
|
||||
// TODO: Check if dimensions have changed by comparing width and height to previous time this was called.
|
||||
// TODO: Support other plane formats than rgb (with multiple planes, such as direct YUV420 on wayland).
|
||||
|
||||
int x = 0, y = 0, src_x = 0, src_y = 0, crtc_w = 0, crtc_h = 0;
|
||||
int x = 0, y = 0, src_x = 0, src_y = 0, src_w = 0, src_h = 0;
|
||||
gsr_kms_rotation rotation = KMS_ROT_0;
|
||||
const uint32_t property_mask = plane_get_properties(drm->drmfd, plane->plane_id, &x, &y, &src_x, &src_y, &crtc_w, &crtc_h, &rotation);
|
||||
const uint32_t property_mask = plane_get_properties(drm->drmfd, plane->plane_id, &x, &y, &src_x, &src_y, &src_w, &src_h, &rotation);
|
||||
if(!(property_mask & PLANE_PROPERTY_IS_PRIMARY) && !(property_mask & PLANE_PROPERTY_IS_CURSOR))
|
||||
continue;
|
||||
|
||||
@@ -392,13 +392,13 @@ static int kms_get_fb(gsr_drm *drm, gsr_kms_response *response) {
|
||||
if(property_mask & PLANE_PROPERTY_IS_CURSOR) {
|
||||
response->items[item_index].x = x;
|
||||
response->items[item_index].y = y;
|
||||
response->items[item_index].crtc_w = 0;
|
||||
response->items[item_index].crtc_h = 0;
|
||||
response->items[item_index].src_w = 0;
|
||||
response->items[item_index].src_h = 0;
|
||||
} else {
|
||||
response->items[item_index].x = src_x;
|
||||
response->items[item_index].y = src_y;
|
||||
response->items[item_index].crtc_w = crtc_w;
|
||||
response->items[item_index].crtc_h = crtc_h;
|
||||
response->items[item_index].src_w = src_w;
|
||||
response->items[item_index].src_h = src_h;
|
||||
}
|
||||
++response->num_items;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.7.3', default_options : ['warning_level=2'])
|
||||
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.9.2', default_options : ['warning_level=2'])
|
||||
|
||||
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
|
||||
if get_option('buildtype') == 'debug'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gpu-screen-recorder"
|
||||
type = "executable"
|
||||
version = "5.7.3"
|
||||
version = "5.9.2"
|
||||
platforms = ["posix"]
|
||||
|
||||
[config]
|
||||
|
||||
@@ -195,7 +195,7 @@ static void usage_header() {
|
||||
"[-k h264|hevc|av1|vp8|vp9|hevc_hdr|av1_hdr|hevc_10bit|av1_10bit] [-ac aac|opus|flac] [-ab <bitrate>] [-oc yes|no] [-fm cfr|vfr|content] "
|
||||
"[-bm auto|qp|vbr|cbr] [-cr limited|full] [-tune performance|quality] [-df yes|no] [-sc <script_path>] [-p <plugin_path>] "
|
||||
"[-cursor yes|no] [-keyint <value>] [-restore-portal-session yes|no] [-portal-session-token-filepath filepath] [-encoder gpu|cpu] "
|
||||
"[-o <output_file>] [-ro <output_directory>] [--list-capture-options [card_path]] [--list-audio-devices] [--list-application-audio] "
|
||||
"[-fallback-cpu-encoding yes|no] [-o <output_file>] [-ro <output_directory>] [--list-capture-options [card_path]] [--list-audio-devices] [--list-application-audio] "
|
||||
"[-v yes|no] [-gl-debug yes|no] [--version] [-h|--help]\n", program_name);
|
||||
fflush(stdout);
|
||||
}
|
||||
@@ -293,8 +293,9 @@ static void usage_full() {
|
||||
printf(" Works only if your have \"Coolbits\" set to \"12\" in NVIDIA X settings, see README for more information. Note! use at your own risk! Optional, disabled by default.\n");
|
||||
printf("\n");
|
||||
printf(" -fm Framerate mode. Should be either 'cfr' (constant frame rate), 'vfr' (variable frame rate) or 'content'. Optional, set to 'vfr' by default.\n");
|
||||
printf(" 'vfr' is recommended for recording for less issue with very high system load but some applications such as video editors may not support it properly.\n");
|
||||
printf(" 'vfr' is recommended for general recording for less issue with very high system load but some applications such as video editors may not support it properly.\n");
|
||||
printf(" 'content' is currently only supported on X11 or when using portal capture option. The 'content' option matches the recording frame rate to the captured content.\n");
|
||||
printf(" 'content' is the best option to use when possible as it avoids capturing duplicate frames, which results in a smoother video.\n");
|
||||
printf("\n");
|
||||
printf(" -bm Bitrate mode. Should be either 'auto', 'qp' (constant quality), 'vbr' (variable bitrate) or 'cbr' (constant bitrate). Optional, set to 'auto' by default which defaults to 'qp' on all devices\n");
|
||||
printf(" except steam deck that has broken drivers and doesn't support qp.\n");
|
||||
@@ -341,6 +342,12 @@ static void usage_full() {
|
||||
printf(" Which device should be used for video encoding. Should either be 'gpu' or 'cpu'. 'cpu' option currently only work with h264 codec option (-k).\n");
|
||||
printf(" Optional, set to 'gpu' by default.\n");
|
||||
printf("\n");
|
||||
printf(" -fallback-cpu-encoding\n");
|
||||
printf(" If this is set to 'yes' and GPU encoding is not available on the system then CPU encoding will be used instead. Optional, set to 'no' by default.\n");
|
||||
printf(" If the fallback to CPU encoding happens then h264 codec will forcefully be used.\n");
|
||||
printf(" This should ideally not be used. Instead properly install vaapi on your system to encode the video with your GPU.\n");
|
||||
printf(" Some very old GPUs dont support video encoding in which case this option may be used.\n");
|
||||
printf("\n");
|
||||
printf(" --info\n");
|
||||
printf(" List info about the system. Lists the following information (prints them to stdout and exits):\n");
|
||||
printf(" Supported video codecs (h264, h264_software, hevc, hevc_hdr, hevc_10bit, av1, av1_hdr, av1_10bit, vp8, vp9) and image codecs (jpeg, png) (if supported).\n");
|
||||
@@ -469,6 +476,7 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
self->restore_portal_session = args_get_boolean_by_key(self->args, NUM_ARGS, "-restore-portal-session", false);
|
||||
self->restart_replay_on_save = args_get_boolean_by_key(self->args, NUM_ARGS, "-restart-replay-on-save", false);
|
||||
self->overclock = args_get_boolean_by_key(self->args, NUM_ARGS, "-oc", false);
|
||||
self->fallback_cpu_encoding = args_get_boolean_by_key(self->args, NUM_ARGS, "-fallback-cpu-encoding", false);
|
||||
|
||||
self->audio_bitrate = args_get_i64_by_key(self->args, NUM_ARGS, "-ab", 0);
|
||||
self->audio_bitrate *= 1000LL;
|
||||
@@ -586,8 +594,8 @@ static bool args_parser_set_values(args_parser *self) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(self->region_size.x < 0 || self->region_size.y < 0 || self->region_position.x < 0 || self->region_position.y < 0) {
|
||||
fprintf(stderr, "gsr error: invalid value for option -region '%s', expected width, height, x and y to be greater or equal to 0\n", region_str);
|
||||
if(self->region_size.x < 0 || self->region_size.y < 0) {
|
||||
fprintf(stderr, "gsr error: invalid value for option -region '%s', expected width and height to be greater or equal to 0\n", region_str);
|
||||
usage();
|
||||
return false;
|
||||
}
|
||||
@@ -755,6 +763,7 @@ bool args_parser_parse(args_parser *self, int argc, char **argv, const args_hand
|
||||
self->args[arg_index++] = (Arg){ .key = "-restore-portal-session", .optional = true, .list = false, .type = ARG_TYPE_BOOLEAN };
|
||||
self->args[arg_index++] = (Arg){ .key = "-portal-session-token-filepath", .optional = true, .list = false, .type = ARG_TYPE_STRING };
|
||||
self->args[arg_index++] = (Arg){ .key = "-encoder", .optional = true, .list = false, .type = ARG_TYPE_ENUM, .enum_values = video_encoder_enums, .num_enum_values = sizeof(video_encoder_enums)/sizeof(ArgEnum) };
|
||||
self->args[arg_index++] = (Arg){ .key = "-fallback-cpu-encoding", .optional = true, .list = false, .type = ARG_TYPE_BOOLEAN };
|
||||
self->args[arg_index++] = (Arg){ .key = "-replay-storage", .optional = true, .list = false, .type = ARG_TYPE_ENUM, .enum_values = replay_storage_enums, .num_enum_values = sizeof(replay_storage_enums)/sizeof(ArgEnum) };
|
||||
self->args[arg_index++] = (Arg){ .key = "-p", .optional = true, .list = true, .type = ARG_TYPE_STRING };
|
||||
assert(arg_index == NUM_ARGS);
|
||||
|
||||
@@ -209,14 +209,14 @@ static int gsr_capture_kms_start(gsr_capture *cap, gsr_capture_metadata *capture
|
||||
|
||||
if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
} else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) {
|
||||
capture_metadata->width = self->params.region_size.x;
|
||||
capture_metadata->height = self->params.region_size.y;
|
||||
capture_metadata->video_width = self->params.region_size.x;
|
||||
capture_metadata->video_height = self->params.region_size.y;
|
||||
} else {
|
||||
capture_metadata->width = self->capture_size.x;
|
||||
capture_metadata->height = self->capture_size.y;
|
||||
capture_metadata->video_width = self->capture_size.x;
|
||||
capture_metadata->video_height = self->capture_size.y;
|
||||
}
|
||||
|
||||
self->last_time_monitor_check = clock_get_monotonic_seconds();
|
||||
@@ -508,7 +508,7 @@ static void render_drm_cursor(gsr_capture_kms *self, gsr_color_conversion *color
|
||||
gsr_color_conversion_draw(color_conversion, self->cursor_texture_id,
|
||||
cursor_pos, (vec2i){cursor_size.x * scale.x, cursor_size.y * scale.y},
|
||||
(vec2i){0, 0}, cursor_size, cursor_size,
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, cursor_texture_id_is_external, true);
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, cursor_texture_id_is_external);
|
||||
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
@@ -536,13 +536,13 @@ static void render_x11_cursor(gsr_capture_kms *self, gsr_color_conversion *color
|
||||
gsr_color_conversion_draw(color_conversion, self->x11_cursor.texture_id,
|
||||
cursor_pos, (vec2i){self->x11_cursor.size.x * scale.x, self->x11_cursor.size.y * scale.y},
|
||||
(vec2i){0, 0}, self->x11_cursor.size, self->x11_cursor.size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
static void gsr_capture_kms_update_capture_size_change(gsr_capture_kms *self, gsr_color_conversion *color_conversion, vec2i target_pos, const gsr_kms_response_item *drm_fd) {
|
||||
if(target_pos.x != self->prev_target_pos.x || target_pos.y != self->prev_target_pos.y || drm_fd->crtc_w != self->prev_plane_size.x || drm_fd->crtc_h != self->prev_plane_size.y) {
|
||||
if(target_pos.x != self->prev_target_pos.x || target_pos.y != self->prev_target_pos.y || drm_fd->src_w != self->prev_plane_size.x || drm_fd->src_h != self->prev_plane_size.y) {
|
||||
self->prev_target_pos = target_pos;
|
||||
self->prev_plane_size = self->capture_size;
|
||||
gsr_color_conversion_clear(color_conversion);
|
||||
@@ -621,15 +621,12 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
if(drm_fd->has_hdr_metadata && self->params.hdr && hdr_metadata_is_supported_format(&drm_fd->hdr_metadata))
|
||||
gsr_kms_set_hdr_metadata(self, drm_fd);
|
||||
|
||||
self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->crtc_w, drm_fd->crtc_h });
|
||||
self->capture_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h });
|
||||
if(self->params.region_size.x > 0 && self->params.region_size.y > 0)
|
||||
self->capture_size = self->params.region_size;
|
||||
|
||||
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);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->width / 2 - output_size.x / 2), max_int(0, capture_metadata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->capture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
gsr_capture_kms_update_capture_size_change(self, color_conversion, target_pos, drm_fd);
|
||||
|
||||
vec2i capture_pos = self->capture_pos;
|
||||
@@ -649,12 +646,12 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
}
|
||||
|
||||
const gsr_monitor_rotation plane_rotation = kms_rotation_to_gsr_monitor_rotation(drm_fd->rotation);
|
||||
const gsr_monitor_rotation rotation = sub_rotations(self->monitor_rotation, plane_rotation);
|
||||
const gsr_monitor_rotation rotation = capture_is_combined_plane ? GSR_MONITOR_ROT_0 : sub_rotations(self->monitor_rotation, plane_rotation);
|
||||
|
||||
gsr_color_conversion_draw(color_conversion, self->external_texture_fallback ? self->external_input_texture_id : self->input_texture_id,
|
||||
target_pos, output_size,
|
||||
capture_pos, self->capture_size, (vec2i){ drm_fd->width, drm_fd->height },
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, self->external_texture_fallback, false);
|
||||
gsr_monitor_rotation_to_rotation(rotation), GSR_SOURCE_COLOR_RGB, self->external_texture_fallback);
|
||||
|
||||
if(self->params.record_cursor) {
|
||||
gsr_kms_response_item *cursor_drm_fd = find_cursor_drm_if_on_monitor(self, drm_fd->connector_id, capture_is_combined_plane);
|
||||
@@ -668,7 +665,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
cursor_monitor_offset.y += self->params.region_position.y;
|
||||
render_x11_cursor(self, color_conversion, cursor_monitor_offset, target_pos, output_size);
|
||||
} else if(cursor_drm_fd) {
|
||||
const vec2i framebuffer_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->crtc_w, drm_fd->crtc_h });
|
||||
const vec2i framebuffer_size = rotate_capture_size_if_rotated(self, (vec2i){ drm_fd->src_w, drm_fd->src_h });
|
||||
render_drm_cursor(self, color_conversion, cursor_drm_fd, target_pos, output_size, framebuffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,40 +55,6 @@ static uint32_t get_output_id_from_display_name(NVFBC_RANDR_OUTPUT_INFO *outputs
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: Test with optimus and open kernel modules */
|
||||
static bool get_driver_version(int *major, int *minor) {
|
||||
*major = 0;
|
||||
*minor = 0;
|
||||
|
||||
FILE *f = fopen("/proc/driver/nvidia/version", "rb");
|
||||
if(!f) {
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version (failed to read /proc/driver/nvidia/version)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, f);
|
||||
buffer[bytes_read] = '\0';
|
||||
|
||||
bool success = false;
|
||||
const char *p = strstr(buffer, "Kernel Module");
|
||||
if(p) {
|
||||
p += 13;
|
||||
int driver_major_version = 0, driver_minor_version = 0;
|
||||
if(sscanf(p, "%d.%d", &driver_major_version, &driver_minor_version) == 2) {
|
||||
*major = driver_major_version;
|
||||
*minor = driver_minor_version;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!success)
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version\n");
|
||||
|
||||
fclose(f);
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool version_at_least(int major, int minor, int expected_major, int expected_minor) {
|
||||
return major > expected_major || (major == expected_major && minor >= expected_minor);
|
||||
}
|
||||
@@ -288,7 +254,7 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
self->supports_direct_cursor = false;
|
||||
int driver_major_version = 0;
|
||||
int driver_minor_version = 0;
|
||||
if(self->params.direct_capture && get_driver_version(&driver_major_version, &driver_minor_version)) {
|
||||
if(self->params.direct_capture && get_nvidia_driver_version(&driver_major_version, &driver_minor_version)) {
|
||||
fprintf(stderr, "gsr info: detected nvidia version: %d.%d\n", driver_major_version, driver_minor_version);
|
||||
|
||||
// TODO:
|
||||
@@ -317,16 +283,16 @@ static int gsr_capture_nvfbc_start(gsr_capture *cap, gsr_capture_metadata *captu
|
||||
goto error_cleanup;
|
||||
}
|
||||
|
||||
capture_metadata->width = self->tracking_width;
|
||||
capture_metadata->height = self->tracking_height;
|
||||
capture_metadata->video_width = self->tracking_width;
|
||||
capture_metadata->video_height = self->tracking_height;
|
||||
|
||||
if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio((vec2i){capture_metadata->width, capture_metadata->height}, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
self->params.output_resolution = scale_keep_aspect_ratio((vec2i){capture_metadata->recording_width, capture_metadata->recording_height}, self->params.output_resolution);
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
} else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) {
|
||||
capture_metadata->width = self->params.region_size.x;
|
||||
capture_metadata->height = self->params.region_size.y;
|
||||
capture_metadata->video_width = self->params.region_size.x;
|
||||
capture_metadata->video_height = self->params.region_size.y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -367,11 +333,8 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, gsr_capture_metadata *cap
|
||||
if(self->params.region_size.x > 0 && self->params.region_size.y > 0)
|
||||
frame_size = self->params.region_size;
|
||||
|
||||
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 : frame_size;
|
||||
output_size = scale_keep_aspect_ratio(frame_size, output_size);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->width / 2 - output_size.x / 2), max_int(0, capture_metadata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(frame_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
|
||||
NVFBC_FRAME_GRAB_INFO frame_info;
|
||||
memset(&frame_info, 0, sizeof(frame_info));
|
||||
@@ -397,7 +360,7 @@ static int gsr_capture_nvfbc_capture(gsr_capture *cap, gsr_capture_metadata *cap
|
||||
gsr_color_conversion_draw(color_conversion, self->setup_params.dwTextures[grab_params.dwTextureIndex],
|
||||
target_pos, (vec2i){output_size.x, output_size.y},
|
||||
self->params.region_position, frame_size, original_frame_size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_BGR, false, false);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_BGR, false);
|
||||
|
||||
//self->params.egl->glFlush();
|
||||
//self->params.egl->glFinish();
|
||||
|
||||
@@ -293,12 +293,12 @@ static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capt
|
||||
}
|
||||
|
||||
if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) {
|
||||
capture_metadata->width = self->capture_size.x;
|
||||
capture_metadata->height = self->capture_size.y;
|
||||
capture_metadata->video_width = self->capture_size.x;
|
||||
capture_metadata->video_height = self->capture_size.y;
|
||||
} else {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -353,11 +353,10 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->width / 2 - output_size.x / 2), max_int(0, capture_metadata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->capture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
|
||||
const vec2i actual_texture_size = {self->pipewire_data.texture_width, self->pipewire_data.texture_height};
|
||||
|
||||
//self->params.egl->glFlush();
|
||||
//self->params.egl->glFinish();
|
||||
@@ -370,8 +369,8 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
|
||||
gsr_color_conversion_draw(color_conversion, self->pipewire_data.using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id,
|
||||
target_pos, output_size,
|
||||
(vec2i){self->pipewire_data.region.x, self->pipewire_data.region.y}, self->capture_size, self->capture_size,
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, self->pipewire_data.using_external_image, fourcc_alpha);
|
||||
(vec2i){self->pipewire_data.region.x, self->pipewire_data.region.y}, (vec2i){self->pipewire_data.region.width, self->pipewire_data.region.height}, actual_texture_size,
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, self->pipewire_data.using_external_image);
|
||||
|
||||
if(self->params.record_cursor && self->texture_map.cursor_texture_id > 0 && self->pipewire_data.cursor_region.width > 0) {
|
||||
const vec2d scale = {
|
||||
@@ -392,7 +391,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
(vec2i){0, 0},
|
||||
(vec2i){self->pipewire_data.cursor_region.width, self->pipewire_data.cursor_region.height},
|
||||
(vec2i){self->pipewire_data.cursor_region.width, self->pipewire_data.cursor_region.height},
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, false, true);
|
||||
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, false);
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
|
||||
@@ -104,11 +104,11 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, gsr_capture_metadata *
|
||||
self->texture_size.y = self->window_texture.window_height;
|
||||
|
||||
if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) {
|
||||
capture_metadata->width = self->texture_size.x;
|
||||
capture_metadata->height = self->texture_size.y;
|
||||
capture_metadata->video_width = self->texture_size.x;
|
||||
capture_metadata->video_height = self->texture_size.y;
|
||||
} else {
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
}
|
||||
|
||||
self->window_resize_timer = clock_get_monotonic_seconds();
|
||||
@@ -224,7 +224,7 @@ static bool gsr_capture_xcomposite_should_stop(gsr_capture *cap, bool *err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata *capture_metdata, gsr_color_conversion *color_conversion) {
|
||||
static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) {
|
||||
gsr_capture_xcomposite *self = cap->priv;
|
||||
|
||||
if(self->clear_background) {
|
||||
@@ -232,11 +232,8 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata
|
||||
gsr_color_conversion_clear(color_conversion);
|
||||
}
|
||||
|
||||
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->texture_size;
|
||||
output_size = scale_keep_aspect_ratio(self->texture_size, output_size);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metdata->width / 2 - output_size.x / 2), max_int(0, capture_metdata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->texture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
|
||||
//self->params.egl->glFlush();
|
||||
//self->params.egl->glFinish();
|
||||
@@ -244,7 +241,7 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata
|
||||
gsr_color_conversion_draw(color_conversion, window_texture_get_opengl_texture_id(&self->window_texture),
|
||||
target_pos, output_size,
|
||||
(vec2i){0, 0}, self->texture_size, self->texture_size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, false);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
if(self->params.record_cursor && self->cursor.visible) {
|
||||
const vec2d scale = {
|
||||
@@ -265,7 +262,7 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, gsr_capture_metadata
|
||||
gsr_color_conversion_draw(color_conversion, self->cursor.texture_id,
|
||||
cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y},
|
||||
(vec2i){0, 0}, self->cursor.size, self->cursor.size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
}
|
||||
|
||||
//self->params.egl->glFlush();
|
||||
|
||||
@@ -59,14 +59,14 @@ static int gsr_capture_ximage_start(gsr_capture *cap, gsr_capture_metadata *capt
|
||||
|
||||
if(self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0) {
|
||||
self->params.output_resolution = scale_keep_aspect_ratio(self->capture_size, self->params.output_resolution);
|
||||
capture_metadata->width = self->params.output_resolution.x;
|
||||
capture_metadata->height = self->params.output_resolution.y;
|
||||
capture_metadata->video_width = self->params.output_resolution.x;
|
||||
capture_metadata->video_height = self->params.output_resolution.y;
|
||||
} else if(self->params.region_size.x > 0 && self->params.region_size.y > 0) {
|
||||
capture_metadata->width = self->params.region_size.x;
|
||||
capture_metadata->height = self->params.region_size.y;
|
||||
capture_metadata->video_width = self->params.region_size.x;
|
||||
capture_metadata->video_height = self->params.region_size.y;
|
||||
} else {
|
||||
capture_metadata->width = self->capture_size.x;
|
||||
capture_metadata->height = self->capture_size.y;
|
||||
capture_metadata->video_width = self->capture_size.x;
|
||||
capture_metadata->video_height = self->capture_size.y;
|
||||
}
|
||||
|
||||
self->texture_id = gl_create_texture(self->params.egl, self->capture_size.x, self->capture_size.y, GL_RGB8, GL_RGB, GL_LINEAR);
|
||||
@@ -147,20 +147,17 @@ static bool gsr_capture_ximage_upload_to_texture(gsr_capture_ximage *self, int x
|
||||
return success;
|
||||
}
|
||||
|
||||
static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *capture_metdata, gsr_color_conversion *color_conversion) {
|
||||
static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) {
|
||||
gsr_capture_ximage *self = cap->priv;
|
||||
|
||||
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);
|
||||
|
||||
const vec2i target_pos = { max_int(0, capture_metdata->width / 2 - output_size.x / 2), max_int(0, capture_metdata->height / 2 - output_size.y / 2) };
|
||||
const vec2i output_size = scale_keep_aspect_ratio(self->capture_size, (vec2i){capture_metadata->recording_width, capture_metadata->recording_height});
|
||||
const vec2i target_pos = { max_int(0, capture_metadata->video_width / 2 - output_size.x / 2), max_int(0, capture_metadata->video_height / 2 - output_size.y / 2) };
|
||||
gsr_capture_ximage_upload_to_texture(self, self->capture_pos.x + self->params.region_position.x, self->capture_pos.y + self->params.region_position.y, self->capture_size.x, self->capture_size.y);
|
||||
|
||||
gsr_color_conversion_draw(color_conversion, self->texture_id,
|
||||
target_pos, output_size,
|
||||
(vec2i){0, 0}, self->capture_size, self->capture_size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, false);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
if(self->params.record_cursor && self->cursor.visible) {
|
||||
const vec2d scale = {
|
||||
@@ -181,7 +178,7 @@ static int gsr_capture_ximage_capture(gsr_capture *cap, gsr_capture_metadata *ca
|
||||
gsr_color_conversion_draw(color_conversion, self->cursor.texture_id,
|
||||
cursor_pos, (vec2i){self->cursor.size.x * scale.x, self->cursor.size.y * scale.y},
|
||||
(vec2i){0, 0}, self->cursor.size, self->cursor.size,
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
|
||||
self->params.egl->glDisable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
||||
@@ -39,10 +39,6 @@
|
||||
" 0.060118, 0.429412, -0.038049, 0.000000,\n" \
|
||||
" 0.062745, 0.500000, 0.500000, 1.000000);\n"
|
||||
|
||||
static int max_int(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
static const char* color_format_range_get_transform_matrix(gsr_destination_color color_format, gsr_color_range color_range) {
|
||||
switch(color_format) {
|
||||
case GSR_DESTINATION_COLOR_NV12: {
|
||||
@@ -483,6 +479,12 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
|
||||
const vec2i dest_texture_size = self->params.destination_textures_size[0];
|
||||
const int texture_target = external_texture ? GL_TEXTURE_EXTERNAL_OES : GL_TEXTURE_2D;
|
||||
|
||||
if(rotation == GSR_ROT_90 || rotation == GSR_ROT_270) {
|
||||
const float tmp = texture_size.x;
|
||||
texture_size.x = texture_size.y;
|
||||
texture_size.y = tmp;
|
||||
}
|
||||
|
||||
self->params.egl->glBindTexture(texture_target, texture_id);
|
||||
gsr_color_conversion_swizzle_texture_source(self, source_color);
|
||||
|
||||
@@ -501,18 +503,11 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
|
||||
(float)source_position.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y),
|
||||
};
|
||||
|
||||
vec2f texture_size_norm = {
|
||||
const vec2f texture_size_norm = {
|
||||
(float)source_size.x / (texture_size.x == 0 ? 1.0f : (float)texture_size.x),
|
||||
(float)source_size.y / (texture_size.y == 0 ? 1.0f : (float)texture_size.y),
|
||||
};
|
||||
|
||||
if(rotation == GSR_ROT_90 || rotation == GSR_ROT_270) {
|
||||
const float ratio_x = (double)source_size.x / (double)source_size.y;
|
||||
const float ratio_y = (double)source_size.y / (double)source_size.x;
|
||||
texture_size_norm.x *= ratio_y;
|
||||
texture_size_norm.y *= ratio_x;
|
||||
}
|
||||
|
||||
const float vertices[] = {
|
||||
-1.0f + 0.0f, -1.0f + 0.0f + size_norm.y, texture_pos_norm.x, texture_pos_norm.y + texture_size_norm.y,
|
||||
-1.0f + 0.0f, -1.0f + 0.0f, texture_pos_norm.x, texture_pos_norm.y,
|
||||
@@ -574,7 +569,7 @@ static void gsr_color_conversion_draw_graphics(gsr_color_conversion *self, unsig
|
||||
self->params.egl->glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture, bool alpha_blending) {
|
||||
void gsr_color_conversion_draw(gsr_color_conversion *self, unsigned int texture_id, vec2i destination_pos, vec2i destination_size, vec2i source_pos, vec2i source_size, vec2i texture_size, gsr_rotation rotation, gsr_source_color source_color, bool external_texture) {
|
||||
assert(!external_texture || self->params.load_external_image_shader);
|
||||
if(external_texture && !self->params.load_external_image_shader) {
|
||||
fprintf(stderr, "gsr error: gsr_color_conversion_draw: external texture not loaded\n");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <libavutil/hwcontext_vaapi.h>
|
||||
#include <libavutil/intreadwrite.h>
|
||||
|
||||
#include <va/va.h>
|
||||
#include <va/va_drm.h>
|
||||
#include <va/va_drmcommon.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -150,13 +152,102 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self
|
||||
|
||||
static void gsr_video_encoder_vaapi_stop(gsr_video_encoder_vaapi *self, AVCodecContext *video_codec_context);
|
||||
|
||||
static bool supports_hevc_without_padding(const char *card_path) {
|
||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(61, 28, 100) && VA_CHECK_VERSION(1, 21, 0)
|
||||
VAStatus va_status;
|
||||
VAConfigID va_config = 0;
|
||||
unsigned int num_surface_attr = 0;
|
||||
VASurfaceAttrib *surface_attr_list = NULL;
|
||||
bool supports_surface_attrib_alignment_size = false;
|
||||
int va_major = 0;
|
||||
int va_minor = 0;
|
||||
bool initialized = false;
|
||||
|
||||
char render_path[128];
|
||||
if(!gsr_card_path_get_render_path(card_path, render_path)) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to get /dev/dri/renderDXXX file from %s\n", card_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int drm_fd = open(render_path, O_RDWR);
|
||||
if(drm_fd == -1) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to open device %s\n", render_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const VADisplay va_dpy = vaGetDisplayDRM(drm_fd);
|
||||
if(!va_dpy) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to get vaapi display for device %s\n", render_path);
|
||||
goto done;
|
||||
}
|
||||
|
||||
vaSetInfoCallback(va_dpy, NULL, NULL);
|
||||
|
||||
if(vaInitialize(va_dpy, &va_major, &va_minor) != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: vaInitialize failed\n");
|
||||
goto done;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
va_status = vaCreateConfig(va_dpy, VAProfileHEVCMain, VAEntrypointEncSlice, NULL, 0, &va_config);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
va_status = vaCreateConfig(va_dpy, VAProfileHEVCMain, VAEntrypointEncSliceLP, NULL, 0, &va_config);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to create hevc vaapi config, error: %s (%d)\n", vaErrorStr(va_status), va_status);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
va_status = vaQuerySurfaceAttributes(va_dpy, va_config, 0, &num_surface_attr);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to query vaapi surface attributes size, error: %s (%d)\n", vaErrorStr(va_status), va_status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
surface_attr_list = malloc(num_surface_attr * sizeof(VASurfaceAttrib));
|
||||
if(!surface_attr_list) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to allocate memory for %u vaapi surface attributes, error: %s (%d)\n", num_surface_attr, vaErrorStr(va_status), va_status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
va_status = vaQuerySurfaceAttributes(va_dpy, va_config, surface_attr_list, &num_surface_attr);
|
||||
if(va_status != VA_STATUS_SUCCESS) {
|
||||
fprintf(stderr, "gsr error: supports_hevc_without_padding: failed to query vaapi surface attributes data, error: %s (%d)\n", vaErrorStr(va_status), va_status);
|
||||
goto done;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < num_surface_attr; ++i) {
|
||||
if(surface_attr_list[i].type == VASurfaceAttribAlignmentSize) {
|
||||
supports_surface_attrib_alignment_size = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
free(surface_attr_list);
|
||||
if(va_config > 0)
|
||||
vaDestroyConfig(va_dpy, va_config);
|
||||
if(initialized)
|
||||
vaTerminate(va_dpy);
|
||||
if(drm_fd > 0)
|
||||
close(drm_fd);
|
||||
return supports_surface_attrib_alignment_size;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool gsr_video_encoder_vaapi_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) {
|
||||
gsr_video_encoder_vaapi *self = encoder->priv;
|
||||
|
||||
if(self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && video_codec_context->codec_id == AV_CODEC_ID_HEVC) {
|
||||
// TODO: dont do this if using ffmpeg reports that this is not needed (AMD driver bug that was fixed recently)
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 64);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 16);
|
||||
if(supports_hevc_without_padding(self->params.egl->card_path)) {
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 2);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 2);
|
||||
} else {
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 256);
|
||||
video_codec_context->height = FFALIGN(video_codec_context->height, 256);
|
||||
}
|
||||
} else if(self->params.egl->gpu_info.vendor == GSR_GPU_VENDOR_AMD && video_codec_context->codec_id == AV_CODEC_ID_AV1) {
|
||||
// TODO: Dont do this for VCN 5 and forward which should fix this hardware bug
|
||||
video_codec_context->width = FFALIGN(video_codec_context->width, 64);
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
/* TODO: Support hdr/10-bit */
|
||||
bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int width, int height) {
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
|
||||
self->egl = egl;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
@@ -25,15 +24,6 @@ bool gsr_image_writer_init_opengl(gsr_image_writer *self, gsr_egl *egl, int widt
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gsr_image_writer_init_memory(gsr_image_writer *self, const void *memory, int width, int height) {
|
||||
memset(self, 0, sizeof(*self));
|
||||
self->source = GSR_IMAGE_WRITER_SOURCE_OPENGL;
|
||||
self->width = width;
|
||||
self->height = height;
|
||||
self->memory = memory;
|
||||
return true;
|
||||
}
|
||||
|
||||
void gsr_image_writer_deinit(gsr_image_writer *self) {
|
||||
if(self->texture) {
|
||||
self->egl->glDeleteTextures(1, &self->texture);
|
||||
@@ -64,7 +54,6 @@ static bool gsr_image_writer_write_memory_to_file(gsr_image_writer *self, const
|
||||
}
|
||||
|
||||
static bool gsr_image_writer_write_opengl_texture_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
|
||||
assert(self->source == GSR_IMAGE_WRITER_SOURCE_OPENGL);
|
||||
uint8_t *frame_data = malloc(self->width * self->height * 4);
|
||||
if(!frame_data) {
|
||||
fprintf(stderr, "gsr error: gsr_image_writer_write_to_file: failed to allocate memory for image frame\n");
|
||||
@@ -90,11 +79,5 @@ static bool gsr_image_writer_write_opengl_texture_to_file(gsr_image_writer *self
|
||||
}
|
||||
|
||||
bool gsr_image_writer_write_to_file(gsr_image_writer *self, const char *filepath, gsr_image_format image_format, int quality) {
|
||||
switch(self->source) {
|
||||
case GSR_IMAGE_WRITER_SOURCE_OPENGL:
|
||||
return gsr_image_writer_write_opengl_texture_to_file(self, filepath, image_format, quality);
|
||||
case GSR_IMAGE_WRITER_SOURCE_MEMORY:
|
||||
return gsr_image_writer_write_memory_to_file(self, filepath, image_format, quality, self->memory);
|
||||
}
|
||||
return false;
|
||||
return gsr_image_writer_write_opengl_texture_to_file(self, filepath, image_format, quality);
|
||||
}
|
||||
|
||||
296
src/main.cpp
296
src/main.cpp
@@ -48,7 +48,6 @@ extern "C" {
|
||||
extern "C" {
|
||||
#include <libavutil/pixfmt.h>
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavcodec/defs.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/opt.h>
|
||||
#include <libswresample/swresample.h>
|
||||
@@ -123,12 +122,12 @@ static void get_monitor_by_position_callback(const gsr_monitor *monitor, void *u
|
||||
std::swap(monitor_size.x, monitor_size.y);
|
||||
}
|
||||
|
||||
if(!data->output_name && data->position.x >= monitor_position.x && data->position.x <= monitor_position.x + monitor->size.x
|
||||
&& data->position.y >= monitor_position.y && data->position.y <= monitor_position.y + monitor->size.y)
|
||||
if(!data->output_name && data->position.x >= monitor_position.x && data->position.x <= monitor_position.x + monitor_size.x
|
||||
&& data->position.y >= monitor_position.y && data->position.y <= monitor_position.y + monitor_size.y)
|
||||
{
|
||||
data->output_name = strdup(monitor->name);
|
||||
data->monitor_pos = monitor_position;
|
||||
data->monitor_size = monitor->size;
|
||||
data->monitor_size = monitor_size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,8 +380,9 @@ static AVCodecContext *create_video_codec_context(AVPixelFormat pix_fmt, const A
|
||||
codec_context->colorspace = AVCOL_SPC_BT709;
|
||||
}
|
||||
//codec_context->chroma_sample_location = AVCHROMA_LOC_CENTER;
|
||||
if(codec->id == AV_CODEC_ID_HEVC)
|
||||
codec_context->codec_tag = MKTAG('h', 'v', 'c', '1'); // QuickTime on MacOS requires this or the video wont be playable
|
||||
// Can't use this because it's fucking broken in ffmpeg 8 or new mesa. It produces garbage output
|
||||
//if(codec->id == AV_CODEC_ID_HEVC)
|
||||
// codec_context->codec_tag = MKTAG('h', 'v', 'c', '1'); // QuickTime on MacOS requires this or the video wont be playable
|
||||
|
||||
if(arg_parser.bitrate_mode == GSR_BITRATE_MODE_CBR) {
|
||||
codec_context->bit_rate = arg_parser.video_bitrate;
|
||||
@@ -1268,6 +1268,11 @@ static RecordingStartAudio* get_recording_start_item_by_stream_index(RecordingSt
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct AudioPtsOffset {
|
||||
int64_t pts_offset = 0;
|
||||
int stream_index = 0;
|
||||
};
|
||||
|
||||
static void save_replay_async(AVCodecContext *video_codec_context, int video_stream_index, const std::vector<AudioTrack> &audio_tracks, gsr_replay_buffer *replay_buffer, std::string output_dir, const char *container_format, const std::string &file_extension, bool date_folders, bool hdr, gsr_capture *capture, int current_save_replay_seconds) {
|
||||
if(save_replay_thread.valid())
|
||||
return;
|
||||
@@ -1279,14 +1284,15 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
|
||||
return;
|
||||
}
|
||||
|
||||
const gsr_replay_buffer_iterator audio_start_iterator = gsr_replay_buffer_find_keyframe(replay_buffer, video_start_iterator, video_stream_index, true);
|
||||
// if(audio_start_index == (size_t)-1) {
|
||||
// fprintf(stderr, "gsr error: failed to save replay: failed to find an audio keyframe. perhaps replay was saved too fast, before anything has been recorded\n");
|
||||
// return;
|
||||
// }
|
||||
|
||||
const int64_t video_pts_offset = gsr_replay_buffer_iterator_get_packet(replay_buffer, video_start_iterator)->pts;
|
||||
const int64_t audio_pts_offset = audio_start_iterator.packet_index == (size_t)-1 ? 0 : gsr_replay_buffer_iterator_get_packet(replay_buffer, audio_start_iterator)->pts;
|
||||
|
||||
std::vector<AudioPtsOffset> audio_pts_offsets;
|
||||
audio_pts_offsets.reserve(audio_tracks.size());
|
||||
for(const AudioTrack &audio_track : audio_tracks) {
|
||||
const gsr_replay_buffer_iterator audio_start_iterator = gsr_replay_buffer_find_keyframe(replay_buffer, video_start_iterator, audio_track.stream_index, false);
|
||||
const int64_t audio_pts_offset = audio_start_iterator.packet_index == (size_t)-1 ? 0 : gsr_replay_buffer_iterator_get_packet(replay_buffer, audio_start_iterator)->pts;
|
||||
audio_pts_offsets.push_back(AudioPtsOffset{audio_pts_offset, audio_track.stream_index});
|
||||
}
|
||||
|
||||
gsr_replay_buffer *cloned_replay_buffer = gsr_replay_buffer_clone(replay_buffer);
|
||||
if(!cloned_replay_buffer) {
|
||||
@@ -1302,7 +1308,7 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
|
||||
|
||||
save_replay_output_filepath = std::move(output_filepath);
|
||||
|
||||
save_replay_thread = std::async(std::launch::async, [video_stream_index, recording_start_result, video_start_iterator, video_pts_offset, audio_pts_offset, video_codec_context, cloned_replay_buffer]() mutable {
|
||||
save_replay_thread = std::async(std::launch::async, [video_stream_index, recording_start_result, video_start_iterator, video_pts_offset, audio_pts_offsets{std::move(audio_pts_offsets)}, video_codec_context, cloned_replay_buffer]() mutable {
|
||||
gsr_replay_buffer_iterator replay_iterator = video_start_iterator;
|
||||
for(;;) {
|
||||
AVPacket *replay_packet = gsr_replay_buffer_iterator_get_packet(cloned_replay_buffer, replay_iterator);
|
||||
@@ -1350,8 +1356,10 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
|
||||
stream = recording_start_audio->stream;
|
||||
codec_context = audio_track->codec_context;
|
||||
|
||||
av_packet.pts -= audio_pts_offset;
|
||||
av_packet.dts -= audio_pts_offset;
|
||||
const AudioPtsOffset &audio_pts_offset = audio_pts_offsets[av_packet.stream_index - 1];
|
||||
assert(audio_pts_offset.stream_index == av_packet.stream_index);
|
||||
av_packet.pts -= audio_pts_offset.pts_offset;
|
||||
av_packet.dts -= audio_pts_offset.pts_offset;
|
||||
}
|
||||
|
||||
//av_packet.stream_index = stream->index;
|
||||
@@ -1637,6 +1645,41 @@ static bool get_supported_video_codecs(gsr_egl *egl, gsr_video_codec video_codec
|
||||
return false;
|
||||
}
|
||||
|
||||
static void force_cpu_encoding(args_parser *args_parser) {
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
|
||||
args_parser->video_encoder = GSR_VIDEO_ENCODER_HW_CPU;
|
||||
if(args_parser->bitrate_mode == GSR_BITRATE_MODE_VBR) {
|
||||
fprintf(stderr, "gsr warning: bitrate mode has been forcefully set to qp because software encoding option doesn't support vbr option\n");
|
||||
args_parser->bitrate_mode = GSR_BITRATE_MODE_QP;
|
||||
}
|
||||
}
|
||||
|
||||
static bool get_supported_video_codecs_with_cpu_fallback(gsr_egl *egl, args_parser *args_parser, bool cleanup, gsr_supported_video_codecs *video_codecs) {
|
||||
if(get_supported_video_codecs(egl, args_parser->video_codec, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, cleanup, video_codecs)) {
|
||||
if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU || !args_parser->fallback_cpu_encoding)
|
||||
return true;
|
||||
else if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_GPU && video_codecs->h264.supported && (args_parser->video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO || args_parser->video_codec == GSR_VIDEO_CODEC_H264))
|
||||
return true;
|
||||
}
|
||||
|
||||
if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU || !args_parser->fallback_cpu_encoding)
|
||||
return false;
|
||||
|
||||
fprintf(stderr, "gsr warning: gpu encoding is not available on your system, trying cpu encoding instead because -fallback-cpu-encoding is enabled. Install the proper vaapi drivers on your system (if supported) if you experience performance issues\n");
|
||||
|
||||
if(get_supported_video_codecs(egl, GSR_VIDEO_CODEC_H264, true, cleanup, video_codecs)) {
|
||||
if(args_parser->video_codec != (gsr_video_codec)GSR_VIDEO_CODEC_AUTO && args_parser->video_codec != GSR_VIDEO_CODEC_H264) {
|
||||
fprintf(stderr, "gsr warning: cpu encoding is used but video codec isn't set to h264. Forcing video codec to h264\n");
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
|
||||
}
|
||||
|
||||
force_cpu_encoding(args_parser);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void xwayland_check_callback(const gsr_monitor *monitor, void *userdata) {
|
||||
bool *xwayland_found = (bool*)userdata;
|
||||
if(monitor->name_len >= 8 && strncmp(monitor->name, "XWAYLAND", 8) == 0)
|
||||
@@ -2276,7 +2319,7 @@ static int video_quality_to_image_quality_value(gsr_video_quality video_quality)
|
||||
case GSR_VIDEO_QUALITY_HIGH:
|
||||
return 85;
|
||||
case GSR_VIDEO_QUALITY_VERY_HIGH:
|
||||
return 90;
|
||||
return 91; // Quality above 90 makes the jpeg image encoder (stb_image_writer) use yuv444 instead of yuv420, which greatly improves small colored text quality on dark background
|
||||
case GSR_VIDEO_QUALITY_ULTRA:
|
||||
return 97;
|
||||
}
|
||||
@@ -2292,8 +2335,10 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_ima
|
||||
gsr_capture *capture = create_capture_impl(arg_parser, egl, prefer_ximage);
|
||||
|
||||
gsr_capture_metadata capture_metadata;
|
||||
capture_metadata.width = 0;
|
||||
capture_metadata.height = 0;
|
||||
capture_metadata.video_width = 0;
|
||||
capture_metadata.video_height = 0;
|
||||
capture_metadata.recording_width = 0;
|
||||
capture_metadata.recording_height = 0;
|
||||
capture_metadata.fps = fps;
|
||||
|
||||
int capture_result = gsr_capture_start(capture, &capture_metadata);
|
||||
@@ -2302,8 +2347,11 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_ima
|
||||
_exit(capture_result);
|
||||
}
|
||||
|
||||
capture_metadata.recording_width = capture_metadata.video_width;
|
||||
capture_metadata.recording_height = capture_metadata.video_height;
|
||||
|
||||
gsr_image_writer image_writer;
|
||||
if(!gsr_image_writer_init_opengl(&image_writer, egl, capture_metadata.width, capture_metadata.height)) {
|
||||
if(!gsr_image_writer_init_opengl(&image_writer, egl, capture_metadata.video_width, capture_metadata.video_height)) {
|
||||
fprintf(stderr, "gsr error: capture_image_to_file_wayland: gsr_image_write_gl_init failed\n");
|
||||
_exit(1);
|
||||
}
|
||||
@@ -2315,7 +2363,7 @@ static void capture_image_to_file(args_parser &arg_parser, gsr_egl *egl, gsr_ima
|
||||
color_conversion_params.load_external_image_shader = gsr_capture_uses_external_image(capture);
|
||||
|
||||
color_conversion_params.destination_textures[0] = image_writer.texture;
|
||||
color_conversion_params.destination_textures_size[0] = { capture_metadata.width, capture_metadata.height };
|
||||
color_conversion_params.destination_textures_size[0] = { capture_metadata.video_width, capture_metadata.video_height };
|
||||
color_conversion_params.num_destination_textures = 1;
|
||||
color_conversion_params.destination_color = GSR_DESTINATION_COLOR_RGB8;
|
||||
|
||||
@@ -2706,37 +2754,49 @@ static bool codec_supports_resolution(vec2i codec_max_resolution, vec2i capture_
|
||||
return codec_max_resolution.x >= capture_resolution.x && codec_max_resolution.y >= capture_resolution.y;
|
||||
}
|
||||
|
||||
static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *egl, bool use_software_video_encoder, bool video_codec_auto, bool is_flv, bool *low_power, gsr_supported_video_codecs *supported_video_codecs) {
|
||||
static void print_codec_error(gsr_video_codec video_codec) {
|
||||
if(video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO)
|
||||
video_codec = GSR_VIDEO_CODEC_H264;
|
||||
|
||||
const char *video_codec_name = video_codec_to_string(video_codec);
|
||||
fprintf(stderr, "gsr error: your gpu does not support '%s' video codec. If you are sure that your gpu does support '%s' video encoding and you are using an AMD/Intel GPU,\n"
|
||||
" then make sure you have installed the GPU specific vaapi packages (intel-media-driver, libva-intel-driver, libva-mesa-driver and linux-firmware).\n"
|
||||
" It's also possible that your distro has disabled hardware accelerated video encoding for '%s' video codec.\n"
|
||||
" This may be the case on corporate distros such as Manjaro, Fedora or OpenSUSE.\n"
|
||||
" You can test this by running 'vainfo | grep VAEntrypointEncSlice' to see if it matches any H264/HEVC/AV1/VP8/VP9 profile.\n"
|
||||
" On such distros, you need to manually install mesa from source to enable H264/HEVC hardware acceleration, or use a more user friendly distro. Alternatively record with AV1 if supported by your GPU.\n"
|
||||
" You can alternatively use the flatpak version of GPU Screen Recorder (https://flathub.org/apps/com.dec05eba.gpu_screen_recorder) which bypasses system issues with patented H264/HEVC codecs.\n"
|
||||
" If your GPU doesn't support hardware accelerated video encoding then you can use '-fallback-cpu-encoding yes' option to encode with your cpu instead.\n", video_codec_name, video_codec_name, video_codec_name);
|
||||
}
|
||||
|
||||
static const AVCodec* pick_video_codec(gsr_egl *egl, args_parser *args_parser, bool use_fallback_codec, bool *low_power, gsr_supported_video_codecs *supported_video_codecs) {
|
||||
// TODO: software encoder for hevc, av1, vp8 and vp9
|
||||
*low_power = false;
|
||||
const AVCodec *video_codec_f = get_av_codec_if_supported(*video_codec, egl, use_software_video_encoder, supported_video_codecs);
|
||||
const AVCodec *video_codec_f = get_av_codec_if_supported(args_parser->video_codec, egl, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, supported_video_codecs);
|
||||
|
||||
if(!video_codec_auto && !video_codec_f && !is_flv) {
|
||||
switch(*video_codec) {
|
||||
if(!video_codec_f && 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 warning: selected video codec h264 is not supported, trying hevc instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
if(supported_video_codecs->hevc.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
fprintf(stderr, "gsr error: selected video codec h264 is not supported\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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GSR_VIDEO_CODEC_HEVC:
|
||||
case GSR_VIDEO_CODEC_HEVC_HDR:
|
||||
case GSR_VIDEO_CODEC_HEVC_10BIT: {
|
||||
fprintf(stderr, "gsr warning: selected video codec hevc is not supported, trying h264 instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
if(supported_video_codecs->h264.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
break;
|
||||
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");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
if(supported_video_codecs->h264.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
break;
|
||||
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_VP8:
|
||||
case GSR_VIDEO_CODEC_VP9:
|
||||
@@ -2744,54 +2804,44 @@ static const AVCodec* pick_video_codec(gsr_video_codec *video_codec, gsr_egl *eg
|
||||
break;
|
||||
case GSR_VIDEO_CODEC_H264_VULKAN: {
|
||||
fprintf(stderr, "gsr warning: selected video codec h264_vulkan is not supported, trying h264 instead\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
|
||||
// Need to do a query again because this time it's without vulkan
|
||||
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, supported_video_codecs)) {
|
||||
if(!get_supported_video_codecs_with_cpu_fallback(egl, args_parser, true, supported_video_codecs)) {
|
||||
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
|
||||
print_codec_error(args_parser->video_codec);
|
||||
_exit(11);
|
||||
}
|
||||
if(supported_video_codecs->h264.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
break;
|
||||
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");
|
||||
*video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
// Need to do a query again because this time it's without vulkan
|
||||
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, supported_video_codecs)) {
|
||||
if(!get_supported_video_codecs_with_cpu_fallback(egl, args_parser, true, supported_video_codecs)) {
|
||||
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
|
||||
print_codec_error(args_parser->video_codec);
|
||||
_exit(11);
|
||||
}
|
||||
if(supported_video_codecs->hevc.supported)
|
||||
video_codec_f = get_ffmpeg_video_codec(*video_codec, egl->gpu_info.vendor);
|
||||
break;
|
||||
return pick_video_codec(egl, args_parser, true, low_power, supported_video_codecs);
|
||||
}
|
||||
}
|
||||
|
||||
video_codec_f = get_av_codec_if_supported(args_parser->video_codec, egl, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, supported_video_codecs);
|
||||
}
|
||||
|
||||
if(!video_codec_f) {
|
||||
const char *video_codec_name = video_codec_to_string(*video_codec);
|
||||
fprintf(stderr, "gsr error: your gpu does not support '%s' video codec. If you are sure that your gpu does support '%s' video encoding and you are using an AMD/Intel GPU,\n"
|
||||
" then make sure you have installed the GPU specific vaapi packages (intel-media-driver, libva-intel-driver, libva-mesa-driver and linux-firmware).\n"
|
||||
" It's also possible that your distro has disabled hardware accelerated video encoding for '%s' video codec.\n"
|
||||
" This may be the case on corporate distros such as Manjaro, Fedora or OpenSUSE.\n"
|
||||
" You can test this by running 'vainfo | grep VAEntrypointEncSlice' to see if it matches any H264/HEVC/AV1/VP8/VP9 profile.\n"
|
||||
" On such distros, you need to manually install mesa from source to enable H264/HEVC hardware acceleration, or use a more user friendly distro. Alternatively record with AV1 if supported by your GPU.\n"
|
||||
" You can alternatively use the flatpak version of GPU Screen Recorder (https://flathub.org/apps/com.dec05eba.gpu_screen_recorder) which bypasses system issues with patented H264/HEVC codecs.\n"
|
||||
" Make sure you have mesa-extra freedesktop runtime installed when using the flatpak (this should be the default), which can be installed with this command:\n"
|
||||
" flatpak install --system org.freedesktop.Platform.GL.default//23.08-extra\n"
|
||||
" If your GPU doesn't support hardware accelerated video encoding then you can use '-encoder cpu' option to encode with your cpu instead.\n", video_codec_name, video_codec_name, video_codec_name);
|
||||
print_codec_error(args_parser->video_codec);
|
||||
_exit(54);
|
||||
}
|
||||
|
||||
*low_power = video_codec_only_supports_low_power_mode(*supported_video_codecs, *video_codec);
|
||||
*low_power = video_codec_only_supports_low_power_mode(*supported_video_codecs, args_parser->video_codec);
|
||||
|
||||
return video_codec_f;
|
||||
}
|
||||
|
||||
/* Returns -1 if none is available */
|
||||
static gsr_video_codec select_appropriate_video_codec_automatically(gsr_capture_metadata capture_metadata, const gsr_supported_video_codecs *supported_video_codecs) {
|
||||
const vec2i capture_size = {capture_metadata.width, capture_metadata.height};
|
||||
const vec2i capture_size = {capture_metadata.video_width, capture_metadata.video_height};
|
||||
if(supported_video_codecs->h264.supported && codec_supports_resolution(supported_video_codecs->h264.max_resolution, capture_size)) {
|
||||
fprintf(stderr, "gsr info: using h264 encoder because a codec was not specified\n");
|
||||
return GSR_VIDEO_CODEC_H264;
|
||||
@@ -2810,72 +2860,54 @@ static gsr_video_codec select_appropriate_video_codec_automatically(gsr_capture_
|
||||
}
|
||||
}
|
||||
|
||||
static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capture_metadata, gsr_video_codec *video_codec, const char *file_extension, bool use_software_video_encoder, gsr_egl *egl, bool *low_power) {
|
||||
static const AVCodec* select_video_codec_with_fallback(gsr_capture_metadata capture_metadata, args_parser *args_parser, const char *file_extension, gsr_egl *egl, bool *low_power) {
|
||||
gsr_supported_video_codecs supported_video_codecs;
|
||||
if(!get_supported_video_codecs(egl, *video_codec, use_software_video_encoder, true, &supported_video_codecs)) {
|
||||
fprintf(stderr, "gsr error: failed to query for supported video codecs\n");
|
||||
_exit(11);
|
||||
}
|
||||
get_supported_video_codecs(egl, args_parser->video_codec, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, true, &supported_video_codecs);
|
||||
// TODO: Use gsr_supported_video_codecs *supported_video_codecs_vulkan here to properly query vulkan video support
|
||||
set_supported_video_codecs_ffmpeg(&supported_video_codecs, nullptr, egl->gpu_info.vendor);
|
||||
|
||||
const bool video_codec_auto = *video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO;
|
||||
const bool video_codec_auto = args_parser->video_codec == (gsr_video_codec)GSR_VIDEO_CODEC_AUTO;
|
||||
if(video_codec_auto) {
|
||||
if(strcmp(file_extension, "webm") == 0) {
|
||||
fprintf(stderr, "gsr info: using vp8 encoder because a codec was not specified and the file extension is .webm\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_VP8;
|
||||
} else if(use_software_video_encoder) {
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_VP8;
|
||||
} else if(args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU) {
|
||||
fprintf(stderr, "gsr info: using h264 encoder because a codec was not specified\n");
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
} else {
|
||||
*video_codec = select_appropriate_video_codec_automatically(capture_metadata, &supported_video_codecs);
|
||||
if(*video_codec == (gsr_video_codec)-1) {
|
||||
fprintf(stderr, "gsr error: no video encoder was specified and neither h264, hevc nor av1 are supported on your system or you are trying to capture at a resolution higher than your system supports for each codec\n");
|
||||
_exit(52);
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
|
||||
} else if(args_parser->video_encoder != GSR_VIDEO_ENCODER_HW_CPU) {
|
||||
args_parser->video_codec = select_appropriate_video_codec_automatically(capture_metadata, &supported_video_codecs);
|
||||
if(args_parser->video_codec == (gsr_video_codec)-1) {
|
||||
if(args_parser->fallback_cpu_encoding) {
|
||||
fprintf(stderr, "gsr warning: gpu encoding is not available on your system or your gpu doesn't support recording at the resolution you are trying to record, 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);
|
||||
} else {
|
||||
fprintf(stderr, "gsr error: no video encoder was specified and neither h264, hevc nor av1 are supported on your system or you are trying to capture at a resolution higher than your system supports for each codec.\n");
|
||||
fprintf(stderr, " Ensure that you have installed the proper vaapi driver. If your gpu doesn't support video encoding then you can run gpu-screen-recorder with \"-fallback-cpu-encoding yes\" option to use cpu encoding.\n");
|
||||
_exit(52);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Allow hevc, vp9 and av1 in (enhanced) flv (supported since ffmpeg 6.1)
|
||||
const bool is_flv = strcmp(file_extension, "flv") == 0;
|
||||
if(is_flv) {
|
||||
if(*video_codec != GSR_VIDEO_CODEC_H264) {
|
||||
*video_codec = GSR_VIDEO_CODEC_H264;
|
||||
fprintf(stderr, "gsr warning: hevc/av1 is not compatible with flv, falling back to h264 instead.\n");
|
||||
if(LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(60, 10, 100) && strcmp(file_extension, "flv") == 0) {
|
||||
if(args_parser->video_codec != GSR_VIDEO_CODEC_H264) {
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_H264;
|
||||
fprintf(stderr, "gsr warning: hevc/av1 is not compatible with flv in your outdated version of ffmpeg, falling back to h264 instead.\n");
|
||||
}
|
||||
|
||||
// if(audio_codec != GSR_AUDIO_CODEC_AAC) {
|
||||
// audio_codec_to_use = "aac";
|
||||
// audio_codec = GSR_AUDIO_CODEC_AAC;
|
||||
// fprintf(stderr, "gsr warning: flv only supports aac, falling back to aac instead.\n");
|
||||
// }
|
||||
}
|
||||
|
||||
const bool is_hls = strcmp(file_extension, "m3u8") == 0;
|
||||
if(is_hls) {
|
||||
if(video_codec_is_av1(*video_codec)) {
|
||||
*video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
} else if(strcmp(file_extension, "m3u8") == 0) {
|
||||
if(video_codec_is_av1(args_parser->video_codec)) {
|
||||
args_parser->video_codec = GSR_VIDEO_CODEC_HEVC;
|
||||
fprintf(stderr, "gsr warning: av1 is not compatible with hls (m3u8), falling back to hevc instead.\n");
|
||||
}
|
||||
|
||||
// if(audio_codec != GSR_AUDIO_CODEC_AAC) {
|
||||
// audio_codec_to_use = "aac";
|
||||
// audio_codec = GSR_AUDIO_CODEC_AAC;
|
||||
// fprintf(stderr, "gsr warning: hls (m3u8) only supports aac, falling back to aac instead.\n");
|
||||
// }
|
||||
}
|
||||
|
||||
if(use_software_video_encoder && *video_codec != GSR_VIDEO_CODEC_H264) {
|
||||
fprintf(stderr, "gsr error: \"-encoder cpu\" option is currently only available when using h264 codec option (-k)\n");
|
||||
args_parser_print_usage();
|
||||
_exit(1);
|
||||
}
|
||||
const AVCodec *codec = pick_video_codec(egl, args_parser, true, low_power, &supported_video_codecs);
|
||||
|
||||
const AVCodec *codec = pick_video_codec(video_codec, egl, use_software_video_encoder, video_codec_auto, is_flv, low_power, &supported_video_codecs);
|
||||
|
||||
const vec2i codec_max_resolution = codec_get_max_resolution(*video_codec, use_software_video_encoder, &supported_video_codecs);
|
||||
const vec2i capture_size = {capture_metadata.width, capture_metadata.height};
|
||||
const vec2i codec_max_resolution = codec_get_max_resolution(args_parser->video_codec, args_parser->video_encoder == GSR_VIDEO_ENCODER_HW_CPU, &supported_video_codecs);
|
||||
const vec2i capture_size = {capture_metadata.video_width, capture_metadata.video_height};
|
||||
if(!codec_supports_resolution(codec_max_resolution, capture_size)) {
|
||||
const char *video_codec_name = video_codec_to_string(*video_codec);
|
||||
const char *video_codec_name = video_codec_to_string(args_parser->video_codec);
|
||||
fprintf(stderr, "gsr error: The max resolution for video codec %s is %dx%d while you are trying to capture at resolution %dx%d. Change capture resolution or video codec and try again\n",
|
||||
video_codec_name, codec_max_resolution.x, codec_max_resolution.y, capture_size.x, capture_size.y);
|
||||
_exit(53);
|
||||
@@ -3073,6 +3105,11 @@ int main(int argc, char **argv) {
|
||||
|
||||
set_display_server_environment_variables();
|
||||
|
||||
// Linux nvidia driver 580.105.08 added the environment variable CUDA_DISABLE_PERF_BOOST to disable the p2 power level issue,
|
||||
// where running cuda (which includes nvenc) causes the gpu to be forcefully set to p2 power level which on many nvidia gpus
|
||||
// decreases gpu performance in games. On my GTX 1080 it decreased game performance by 10% for absolutely no reason.
|
||||
// TODO: This only seems to allow the gpu to go to lower power level states, but not higher than p2.
|
||||
setenv("CUDA_DISABLE_PERF_BOOST", "1", true);
|
||||
// Stop nvidia driver from buffering frames
|
||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
||||
// If this is set to 1 then cuGraphicsGLRegisterImage will fail for egl context with error: invalid OpenGL or DirectX context,
|
||||
@@ -3110,6 +3147,15 @@ int main(int argc, char **argv) {
|
||||
if(!args_parser_parse(&arg_parser, argc, argv, &arg_handlers, NULL))
|
||||
_exit(1);
|
||||
|
||||
if(arg_parser.overclock) {
|
||||
int driver_major_version = 0;
|
||||
int driver_minor_version = 0;
|
||||
if(get_nvidia_driver_version(&driver_major_version, &driver_minor_version) && (driver_major_version > 580 || (driver_major_version == 580 && driver_minor_version >= 105))) {
|
||||
fprintf(stderr, "gsr info: overclocking was set by has been forcefully disabled since your gpu supports CUDA_DISABLE_PERF_BOOST to workaround driver issue (overclocking is not needed)\n");
|
||||
arg_parser.overclock = false;
|
||||
}
|
||||
}
|
||||
|
||||
//av_log_set_level(AV_LOG_TRACE);
|
||||
|
||||
const Arg *audio_input_arg = args_parser_get_arg(&arg_parser, "-a");
|
||||
@@ -3259,8 +3305,10 @@ int main(int argc, char **argv) {
|
||||
gsr_capture *capture = create_capture_impl(arg_parser, &egl, false);
|
||||
|
||||
gsr_capture_metadata capture_metadata;
|
||||
capture_metadata.width = 0;
|
||||
capture_metadata.height = 0;
|
||||
capture_metadata.video_width = 0;
|
||||
capture_metadata.video_height = 0;
|
||||
capture_metadata.recording_width = 0;
|
||||
capture_metadata.recording_height = 0;
|
||||
capture_metadata.fps = arg_parser.fps;
|
||||
|
||||
int capture_result = gsr_capture_start(capture, &capture_metadata);
|
||||
@@ -3281,11 +3329,16 @@ int main(int argc, char **argv) {
|
||||
AVStream *video_stream = nullptr;
|
||||
std::vector<AudioTrack> audio_tracks;
|
||||
|
||||
if(arg_parser.video_encoder == GSR_VIDEO_ENCODER_HW_CPU && arg_parser.video_codec != (gsr_video_codec)GSR_VIDEO_CODEC_AUTO && arg_parser.video_codec != GSR_VIDEO_CODEC_H264) {
|
||||
fprintf(stderr, "gsr error: -encoder cpu was specified but a codec other than h264 was specified. -encoder cpu supports only h264 at the moment\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
bool low_power = false;
|
||||
const AVCodec *video_codec_f = select_video_codec_with_fallback(capture_metadata, &arg_parser.video_codec, file_extension.c_str(), arg_parser.video_encoder == GSR_VIDEO_ENCODER_HW_CPU, &egl, &low_power);
|
||||
const AVCodec *video_codec_f = select_video_codec_with_fallback(capture_metadata, &arg_parser, file_extension.c_str(), &egl, &low_power);
|
||||
|
||||
const enum AVPixelFormat video_pix_fmt = get_pixel_format(arg_parser.video_codec, egl.gpu_info.vendor, arg_parser.video_encoder == GSR_VIDEO_ENCODER_HW_CPU);
|
||||
AVCodecContext *video_codec_context = create_video_codec_context(video_pix_fmt, video_codec_f, egl, arg_parser, capture_metadata.width, capture_metadata.height);
|
||||
AVCodecContext *video_codec_context = create_video_codec_context(video_pix_fmt, video_codec_f, egl, arg_parser, capture_metadata.video_width, capture_metadata.video_height);
|
||||
if(!is_replaying)
|
||||
video_stream = create_stream(av_format_context, video_codec_context);
|
||||
|
||||
@@ -3295,8 +3348,8 @@ int main(int argc, char **argv) {
|
||||
_exit(1);
|
||||
}
|
||||
video_frame->format = video_codec_context->pix_fmt;
|
||||
video_frame->width = capture_metadata.width;
|
||||
video_frame->height = capture_metadata.height;
|
||||
video_frame->width = capture_metadata.video_width;
|
||||
video_frame->height = capture_metadata.video_height;
|
||||
video_frame->color_range = video_codec_context->color_range;
|
||||
video_frame->color_primaries = video_codec_context->color_primaries;
|
||||
video_frame->color_trc = video_codec_context->color_trc;
|
||||
@@ -3321,9 +3374,12 @@ int main(int argc, char **argv) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
capture_metadata.recording_width = capture_metadata.video_width;
|
||||
capture_metadata.recording_height = capture_metadata.video_height;
|
||||
|
||||
// TODO: What if this updated resolution is above max resolution?
|
||||
capture_metadata.width = video_codec_context->width;
|
||||
capture_metadata.height = video_codec_context->height;
|
||||
capture_metadata.video_width = video_codec_context->width;
|
||||
capture_metadata.video_height = video_codec_context->height;
|
||||
|
||||
const Arg *plugin_arg = args_parser_get_arg(&arg_parser, "-p");
|
||||
assert(plugin_arg);
|
||||
@@ -3336,8 +3392,8 @@ int main(int argc, char **argv) {
|
||||
assert(color_depth == GSR_COLOR_DEPTH_8_BITS || color_depth == GSR_COLOR_DEPTH_10_BITS);
|
||||
|
||||
const gsr_plugin_init_params plugin_init_params = {
|
||||
(unsigned int)capture_metadata.width,
|
||||
(unsigned int)capture_metadata.height,
|
||||
(unsigned int)capture_metadata.video_width,
|
||||
(unsigned int)capture_metadata.video_height,
|
||||
(unsigned int)arg_parser.fps,
|
||||
color_depth == GSR_COLOR_DEPTH_8_BITS ? GSR_PLUGIN_COLOR_DEPTH_8_BITS : GSR_PLUGIN_COLOR_DEPTH_10_BITS,
|
||||
egl.context_type == GSR_GL_CONTEXT_TYPE_GLX ? GSR_PLUGIN_GRAPHICS_API_GLX : GSR_PLUGIN_GRAPHICS_API_EGL_ES,
|
||||
@@ -3777,9 +3833,9 @@ int main(int argc, char **argv) {
|
||||
if(plugins.num_plugins > 0) {
|
||||
gsr_plugins_draw(&plugins);
|
||||
gsr_color_conversion_draw(&color_conversion, plugins.texture,
|
||||
{0, 0}, {capture_metadata.width, capture_metadata.height},
|
||||
{0, 0}, {capture_metadata.width, capture_metadata.height},
|
||||
{capture_metadata.width, capture_metadata.height}, GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false, true);
|
||||
{0, 0}, {capture_metadata.video_width, capture_metadata.video_height},
|
||||
{0, 0}, {capture_metadata.video_width, capture_metadata.video_height},
|
||||
{capture_metadata.video_width, capture_metadata.video_height}, GSR_ROT_0, GSR_SOURCE_COLOR_RGB, false);
|
||||
}
|
||||
|
||||
if(capture_has_synchronous_task) {
|
||||
|
||||
@@ -871,6 +871,9 @@ bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map te
|
||||
|
||||
gsr_pipewire_video_update_cursor_texture(self, texture_map);
|
||||
|
||||
output->texture_width = self->format.info.raw.size.width;
|
||||
output->texture_height = self->format.info.raw.size.height;
|
||||
|
||||
output->region.x = 0;
|
||||
output->region.y = 0;
|
||||
|
||||
|
||||
36
src/utils.c
36
src/utils.c
@@ -148,6 +148,8 @@ int get_connector_type_by_name(const char *name) {
|
||||
return 3;
|
||||
else if(len >= 4 && strncmp(name, "eDP-", 4) == 0)
|
||||
return 4;
|
||||
else if(len >= 4 && strncmp(name, "DVI-", 4) == 0)
|
||||
return 5;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
@@ -606,3 +608,37 @@ unsigned int gl_create_texture(gsr_egl *egl, int width, int height, int internal
|
||||
egl->glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return texture_id;
|
||||
}
|
||||
|
||||
/* TODO: Test with optimus and open kernel modules */
|
||||
bool get_nvidia_driver_version(int *major, int *minor) {
|
||||
*major = 0;
|
||||
*minor = 0;
|
||||
|
||||
FILE *f = fopen("/proc/driver/nvidia/version", "rb");
|
||||
if(!f) {
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version (failed to read /proc/driver/nvidia/version)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char buffer[2048];
|
||||
size_t bytes_read = fread(buffer, 1, sizeof(buffer) - 1, f);
|
||||
buffer[bytes_read] = '\0';
|
||||
|
||||
bool success = false;
|
||||
const char *p = strstr(buffer, "Kernel Module");
|
||||
if(p) {
|
||||
p += 13;
|
||||
int driver_major_version = 0, driver_minor_version = 0;
|
||||
if(sscanf(p, "%d.%d", &driver_major_version, &driver_minor_version) == 2) {
|
||||
*major = driver_major_version;
|
||||
*minor = driver_minor_version;
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!success)
|
||||
fprintf(stderr, "gsr warning: failed to get nvidia driver version\n");
|
||||
|
||||
fclose(f);
|
||||
return success;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user