aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-08-29 22:22:46 +0200
committerdec05eba <dec05eba@protonmail.com>2024-08-29 22:22:46 +0200
commitdfe2b176487ee7937aec7ef3b9134ff7fa97dfa1 (patch)
tree6b885bb37d8088c3c35d870a2897bff086cf2860
parent1b356677ea281a10f89eb21365d7182ae6019360 (diff)
Prepare for video codec query, cleanup readme, add libva-drm as dependency
-rw-r--r--README.md72
-rw-r--r--include/encoder/video/video.h14
-rw-r--r--meson.build1
-rw-r--r--project.conf3
-rw-r--r--src/encoder/video/cuda.c19
-rw-r--r--src/encoder/video/software.c17
-rw-r--r--src/encoder/video/vaapi.c171
-rw-r--r--src/encoder/video/video.c4
-rw-r--r--src/main.cpp102
9 files changed, 329 insertions, 74 deletions
diff --git a/README.md b/README.md
index 1cd9eac..3ffc356 100644
--- a/README.md
+++ b/README.md
@@ -55,43 +55,41 @@ from one of the official sources before reporting it as an issue.
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`).
# Dependencies
-GPU Screen Recorder uses meson build system so you need to install `meson`. There are additional dependencies depending on your graphics card:
-## AMD
-libglvnd (which provides libgl and libegl)\
-mesa\
-ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)\
-x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)\
-libpulse\
-vaapi (libva, libva-mesa-driver)\
-libdrm\
-libcap\
-wayland-client
-## Intel
-libglvnd (which provides libgl and libegl)\
-mesa\
-ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)\
-x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)\
-libpulse\
-vaapi (libva, intel-media-driver/libva-intel-driver)\
-libdrm\
-libcap\
-wayland-client
-## NVIDIA
-libglvnd (which provides libgl and libegl)\
-ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)\
-x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)\
-libpulse\
-cuda runtime (libcuda.so.1) (libnvidia-compute)\
-nvenc (libnvidia-encode)\
-libva\
-libdrm\
-libcap\
-wayland-client\
-nvfbc (libnvidia-fbc1, when recording the screen on x11)\
-xnvctrl (libxnvctrl0, when using the `-oc` option)
-## Optional dependencies when compiling with portal support (default option)
-dbus\
-libpipewire (and libspa which is usually part of libpipewire)
+GPU Screen Recorder uses meson build system so you need to install `meson` to build GPU Screen Recorder:
+
+## Build dependencies
+These are the dependencies needed to build GPU Screen Recorder:
+
+* libglvnd (which provides libgl and libegl)
+* ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)
+* x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)
+* libpulse
+* libva (and libva-drm)
+* libdrm
+* libcap
+* wayland-client
+
+## Runtime dependencies
+There are also additional dependencies needed at runtime depending on your GPU vendor:
+
+### AMD
+* mesa
+* vaapi (libva-mesa-driver)
+
+### Intel
+* mesa
+* vaapi (intel-media-driver/libva-intel-driver/linux-firmware, depending on which intel iGPU you have)
+
+### NVIDIA
+* cuda runtime (libcuda.so.1) (libnvidia-compute)
+* nvenc (libnvidia-encode)
+* nvfbc (libnvidia-fbc1, when recording the screen on x11)
+* xnvctrl (libxnvctrl0, when using the `-oc` option)
+
+## Optional dependencies
+When compiling GPU Screen Recorder with portal support (`-Dportal=true`, which is enabled by default) these dependencies are also needed:
+* libdbus
+* libpipewire (and libspa which is usually part of libpipewire)
# How to use
Run `gpu-screen-recorder --help` to see all options and also examples.
diff --git a/include/encoder/video/video.h b/include/encoder/video/video.h
index 21338d6..899357a 100644
--- a/include/encoder/video/video.h
+++ b/include/encoder/video/video.h
@@ -8,7 +8,20 @@ typedef struct gsr_video_encoder gsr_video_encoder;
typedef struct AVCodecContext AVCodecContext;
typedef struct AVFrame AVFrame;
+typedef struct {
+ bool h264;
+ bool hevc;
+ bool hevc_hdr;
+ bool hevc_10bit;
+ bool av1;
+ bool av1_hdr;
+ bool av1_10bit;
+ bool vp8;
+ bool vp9;
+} gsr_supported_video_codecs;
+
struct gsr_video_encoder {
+ gsr_supported_video_codecs (*get_supported_codecs)(gsr_video_encoder *encoder, bool cleanup);
bool (*start)(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame);
void (*copy_textures_to_frame)(gsr_video_encoder *encoder, AVFrame *frame); /* Can be NULL */
/* |textures| should be able to fit 2 elements */
@@ -19,6 +32,7 @@ struct gsr_video_encoder {
bool started;
};
+gsr_supported_video_codecs gsr_video_encoder_get_supported_codecs(gsr_video_encoder *encoder, bool cleanup);
bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame);
void gsr_video_encoder_copy_textures_to_frame(gsr_video_encoder *encoder, AVFrame *frame);
void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color);
diff --git a/meson.build b/meson.build
index ce97d6d..083e4d8 100644
--- a/meson.build
+++ b/meson.build
@@ -46,6 +46,7 @@ dep = [
dependency('libswresample'),
dependency('libavfilter'),
dependency('libva'),
+ dependency('libva-drm'),
dependency('libcap'),
dependency('libdrm'),
dependency('wayland-egl'),
diff --git a/project.conf b/project.conf
index fa1e409..c5d9c12 100644
--- a/project.conf
+++ b/project.conf
@@ -25,10 +25,11 @@ libpulse = ">=13"
libswresample = ">=3"
libavfilter = ">=5"
libva = ">=1"
+libva-drm = ">=1"
libcap = ">=2"
libdrm = ">=2"
wayland-egl = ">=15"
wayland-client = ">=1"
dbus-1 = ">=1"
libpipewire-0.3 = ">=1"
-libspa-0.2 = ">=0"
+libspa-0.2 = ">=0" \ No newline at end of file
diff --git a/src/encoder/video/cuda.c b/src/encoder/video/cuda.c
index 4142e97..53abe78 100644
--- a/src/encoder/video/cuda.c
+++ b/src/encoder/video/cuda.c
@@ -122,6 +122,24 @@ static bool gsr_video_encoder_cuda_setup_textures(gsr_video_encoder_cuda *self,
return true;
}
+static gsr_supported_video_codecs gsr_video_encoder_cuda_get_supported_codecs(gsr_video_encoder *encoder, bool cleanup) {
+ (void)encoder;
+ (void)cleanup;
+ //gsr_video_encoder_cuda *encoder_cuda = encoder->priv;
+ // TODO: Query support
+ return (gsr_supported_video_codecs) {
+ .h264 = true,
+ .hevc = true,
+ .hevc_hdr = true,
+ .hevc_10bit = true,
+ .av1 = true,
+ .av1_hdr = true,
+ .av1_10bit = true,
+ .vp8 = false,
+ .vp9 = false
+ };
+}
+
static void gsr_video_encoder_cuda_stop(gsr_video_encoder_cuda *self, AVCodecContext *video_codec_context);
static bool gsr_video_encoder_cuda_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) {
@@ -225,6 +243,7 @@ gsr_video_encoder* gsr_video_encoder_cuda_create(const gsr_video_encoder_cuda_pa
encoder_cuda->params = *params;
*encoder = (gsr_video_encoder) {
+ .get_supported_codecs = gsr_video_encoder_cuda_get_supported_codecs,
.start = gsr_video_encoder_cuda_start,
.copy_textures_to_frame = gsr_video_encoder_cuda_copy_textures_to_frame,
.get_textures = gsr_video_encoder_cuda_get_textures,
diff --git a/src/encoder/video/software.c b/src/encoder/video/software.c
index 192be17..89b2cf0 100644
--- a/src/encoder/video/software.c
+++ b/src/encoder/video/software.c
@@ -58,6 +58,22 @@ static bool gsr_video_encoder_software_setup_textures(gsr_video_encoder_software
return true;
}
+static gsr_supported_video_codecs gsr_video_encoder_software_get_supported_codecs(gsr_video_encoder *encoder, bool cleanup) {
+ (void)encoder;
+ (void)cleanup;
+ return (gsr_supported_video_codecs) {
+ .h264 = true,
+ .hevc = false,
+ .hevc_hdr = false,
+ .hevc_10bit = false,
+ .av1 = false,
+ .av1_hdr = false,
+ .av1_10bit = false,
+ .vp8 = false,
+ .vp9 = false
+ };
+}
+
static void gsr_video_encoder_software_stop(gsr_video_encoder_software *self, AVCodecContext *video_codec_context);
static bool gsr_video_encoder_software_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) {
@@ -126,6 +142,7 @@ gsr_video_encoder* gsr_video_encoder_software_create(const gsr_video_encoder_sof
encoder_software->params = *params;
*encoder = (gsr_video_encoder) {
+ .get_supported_codecs = gsr_video_encoder_software_get_supported_codecs,
.start = gsr_video_encoder_software_start,
.copy_textures_to_frame = gsr_video_encoder_software_copy_textures_to_frame,
.get_textures = gsr_video_encoder_software_get_textures,
diff --git a/src/encoder/video/vaapi.c b/src/encoder/video/vaapi.c
index 9470e76..2185ebc 100644
--- a/src/encoder/video/vaapi.c
+++ b/src/encoder/video/vaapi.c
@@ -6,9 +6,11 @@
#include <libavutil/hwcontext_vaapi.h>
#include <va/va_drmcommon.h>
+#include <va/va_drm.h>
#include <stdlib.h>
#include <unistd.h>
+#include <fcntl.h>
typedef struct {
gsr_video_encoder_vaapi_params params;
@@ -147,6 +149,174 @@ static bool gsr_video_encoder_vaapi_setup_textures(gsr_video_encoder_vaapi *self
}
}
+static bool profile_is_h264(VAProfile profile) {
+ switch(profile) {
+ case 5: // VAProfileH264Baseline
+ case VAProfileH264Main:
+ case VAProfileH264High:
+ case VAProfileH264ConstrainedBaseline:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool profile_is_hevc_8bit(VAProfile profile) {
+ switch(profile) {
+ case VAProfileHEVCMain:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool profile_is_hevc_10bit(VAProfile profile) {
+ switch(profile) {
+ case VAProfileHEVCMain10:
+ //case VAProfileHEVCMain12:
+ //case VAProfileHEVCMain422_10:
+ //case VAProfileHEVCMain422_12:
+ //case VAProfileHEVCMain444:
+ //case VAProfileHEVCMain444_10:
+ //case VAProfileHEVCMain444_12:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool profile_is_av1(VAProfile profile) {
+ switch(profile) {
+ case VAProfileAV1Profile0:
+ case VAProfileAV1Profile1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool profile_is_vp8(VAProfile profile) {
+ switch(profile) {
+ case VAProfileVP8Version0_3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool profile_is_vp9(VAProfile profile) {
+ switch(profile) {
+ case VAProfileVP9Profile0:
+ case VAProfileVP9Profile1:
+ case VAProfileVP9Profile2:
+ case VAProfileVP9Profile3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool profile_supports_video_encoding(VADisplay va_dpy, VAProfile profile) {
+ int num_entrypoints = vaMaxNumEntrypoints(va_dpy);
+ if(num_entrypoints <= 0)
+ return false;
+
+ VAEntrypoint *entrypoint_list = calloc(num_entrypoints, sizeof(VAEntrypoint));
+ if(!entrypoint_list)
+ return false;
+
+ bool supported = false;
+ if(vaQueryConfigEntrypoints(va_dpy, profile, entrypoint_list, &num_entrypoints) == VA_STATUS_SUCCESS) {
+ for(int i = 0; i < num_entrypoints; ++i) {
+ if(entrypoint_list[i] == VAEntrypointEncSlice) {
+ supported = true;
+ break;
+ }
+ }
+ }
+
+ free(entrypoint_list);
+ return supported;
+}
+
+static bool get_supported_video_codecs(VADisplay va_dpy, gsr_supported_video_codecs *video_codecs, bool cleanup) {
+ *video_codecs = (gsr_supported_video_codecs){0};
+ bool success = false;
+ VAProfile *profile_list = NULL;
+
+ int va_major = 0;
+ int va_minor = 0;
+ if(vaInitialize(va_dpy, &va_major, &va_minor) != VA_STATUS_SUCCESS)
+ return false;
+
+ int num_profiles = vaMaxNumProfiles(va_dpy);
+ if(num_profiles <= 0)
+ goto done;
+
+ profile_list = calloc(num_profiles, sizeof(VAProfile));
+ if(!profile_list || vaQueryConfigProfiles(va_dpy, profile_list, &num_profiles) != VA_STATUS_SUCCESS)
+ goto done;
+
+ for(int i = 0; i < num_profiles; ++i) {
+ if(profile_is_h264(profile_list[i])) {
+ if(profile_supports_video_encoding(va_dpy, profile_list[i]))
+ video_codecs->h264 = true;
+ } else if(profile_is_hevc_8bit(profile_list[i])) {
+ if(profile_supports_video_encoding(va_dpy, profile_list[i]))
+ video_codecs->hevc = true;
+ } else if(profile_is_hevc_10bit(profile_list[i])) {
+ if(profile_supports_video_encoding(va_dpy, profile_list[i])) {
+ video_codecs->hevc_hdr = true;
+ video_codecs->hevc_10bit = true;
+ }
+ } else if(profile_is_av1(profile_list[i])) {
+ if(profile_supports_video_encoding(va_dpy, profile_list[i])) {
+ video_codecs->av1 = true;
+ video_codecs->av1_hdr = true;
+ video_codecs->av1_10bit = true;
+ }
+ } else if(profile_is_vp8(profile_list[i])) {
+ if(profile_supports_video_encoding(va_dpy, profile_list[i]))
+ video_codecs->vp8 = true;
+ } else if(profile_is_vp9(profile_list[i])) {
+ if(profile_supports_video_encoding(va_dpy, profile_list[i]))
+ video_codecs->vp9 = true;
+ }
+ }
+
+ success = true;
+ done:
+ if(profile_list)
+ free(profile_list);
+
+ if(cleanup)
+ vaTerminate(va_dpy);
+
+ return success;
+}
+
+static gsr_supported_video_codecs gsr_video_encoder_vaapi_get_supported_codecs(gsr_video_encoder *encoder, bool cleanup) {
+ gsr_video_encoder_vaapi *encoder_vaapi = encoder->priv;
+ gsr_supported_video_codecs supported_video_codecs = {0};
+
+ const int drm_fd = open(encoder_vaapi->params.egl->card_path, O_RDWR);
+ if(drm_fd == -1) {
+ fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: failed to open device %s\n", encoder_vaapi->params.egl->card_path);
+ return supported_video_codecs;
+ }
+
+ VADisplay va_dpy = vaGetDisplayDRM(drm_fd);
+ if(va_dpy) {
+ if(!get_supported_video_codecs(va_dpy, &supported_video_codecs, cleanup))
+ fprintf(stderr, "gsr error: gsr_video_encoder_vaapi_get_supported_codecs: failed to query supported video codecs for device %s\n", encoder_vaapi->params.egl->card_path);
+ }
+
+ if(cleanup)
+ close(drm_fd);
+
+ return supported_video_codecs;
+}
+
static void gsr_video_encoder_vaapi_stop(gsr_video_encoder_vaapi *self, AVCodecContext *video_codec_context);
static bool gsr_video_encoder_vaapi_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) {
@@ -234,6 +404,7 @@ gsr_video_encoder* gsr_video_encoder_vaapi_create(const gsr_video_encoder_vaapi_
encoder_vaapi->params = *params;
*encoder = (gsr_video_encoder) {
+ .get_supported_codecs = gsr_video_encoder_vaapi_get_supported_codecs,
.start = gsr_video_encoder_vaapi_start,
.copy_textures_to_frame = NULL,
.get_textures = gsr_video_encoder_vaapi_get_textures,
diff --git a/src/encoder/video/video.c b/src/encoder/video/video.c
index 9b0def0..daaf537 100644
--- a/src/encoder/video/video.c
+++ b/src/encoder/video/video.c
@@ -1,6 +1,10 @@
#include "../../../include/encoder/video/video.h"
#include <assert.h>
+gsr_supported_video_codecs gsr_video_encoder_get_supported_codecs(gsr_video_encoder *encoder, bool cleanup) {
+ return encoder->get_supported_codecs(encoder, cleanup);
+}
+
bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) {
assert(!encoder->started);
bool res = encoder->start(encoder, video_codec_context, frame);
diff --git a/src/main.cpp b/src/main.cpp
index 88eb536..20b3e85 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1686,6 +1686,39 @@ static int init_filter_graph(AVCodecContext *audio_codec_context, AVFilterGraph
return 0;
}
+static gsr_video_encoder* create_video_encoder(gsr_egl *egl, bool overclock, gsr_color_depth color_depth, bool use_software_video_encoder) {
+ gsr_video_encoder *video_encoder = nullptr;
+
+ if(use_software_video_encoder) {
+ gsr_video_encoder_software_params params;
+ params.egl = egl;
+ params.color_depth = color_depth;
+ video_encoder = gsr_video_encoder_software_create(&params);
+ return video_encoder;
+ }
+
+ switch(egl->gpu_info.vendor) {
+ case GSR_GPU_VENDOR_AMD:
+ case GSR_GPU_VENDOR_INTEL: {
+ gsr_video_encoder_vaapi_params params;
+ params.egl = egl;
+ params.color_depth = color_depth;
+ video_encoder = gsr_video_encoder_vaapi_create(&params);
+ break;
+ }
+ case GSR_GPU_VENDOR_NVIDIA: {
+ gsr_video_encoder_cuda_params params;
+ params.egl = egl;
+ params.overclock = overclock;
+ params.color_depth = color_depth;
+ video_encoder = gsr_video_encoder_cuda_create(&params);
+ break;
+ }
+ }
+
+ return video_encoder;
+}
+
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)
@@ -1735,6 +1768,7 @@ static void list_gpu_info(gsr_egl *egl) {
}
static void list_supported_video_codecs(gsr_egl *egl, bool wayland) {
+#if 1
if(find_h264_encoder(egl->gpu_info.vendor, egl->card_path))
puts("h264");
if(find_h264_software_encoder())
@@ -1755,6 +1789,34 @@ static void list_supported_video_codecs(gsr_egl *egl, bool wayland) {
puts("vp8");
if(find_vp9_encoder(egl->gpu_info.vendor, egl->card_path))
puts("vp9");
+#else
+ // Dont clean it up on purpose to increase shutdown speed
+ gsr_video_encoder *video_encoder = create_video_encoder(egl, false, GSR_COLOR_DEPTH_8_BITS, false);
+ if(!video_encoder)
+ return;
+
+ const gsr_supported_video_codecs supported_video_codecs = gsr_video_encoder_get_supported_codecs(video_encoder, false);
+ if(supported_video_codecs.h264)
+ puts("h264");
+ if(find_h264_software_encoder())
+ puts("h264_software");
+ if(supported_video_codecs.hevc) {
+ puts("hevc");
+ if(wayland)
+ puts("hevc_hdr"); // TODO: Verify if it's actually supported
+ puts("hevc_10bit"); // TODO: Verify if it's actually supported
+ }
+ if(supported_video_codecs.av1) {
+ puts("av1");
+ if(wayland)
+ puts("av1_hdr"); // TODO: Verify if it's actually supported
+ puts("av1_10bit"); // TODO: Verify if it's actually supported
+ }
+ if(supported_video_codecs.vp8)
+ puts("vp8");
+ if(supported_video_codecs.vp9)
+ puts("vp9");
+#endif
}
static bool monitor_capture_use_drm(gsr_egl *egl, bool wayland) {
@@ -1870,9 +1932,10 @@ static void info_command() {
fflush(stdout);
- gsr_egl_unload(&egl);
- if(dpy)
- XCloseDisplay(dpy);
+ // Not needed as this will just slow down shutdown
+ //gsr_egl_unload(&egl);
+ //if(dpy)
+ // XCloseDisplay(dpy);
_exit(0);
}
@@ -2066,39 +2129,6 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre
return capture;
}
-static gsr_video_encoder* create_video_encoder(gsr_egl *egl, bool overclock, gsr_color_depth color_depth, bool use_software_video_encoder) {
- gsr_video_encoder *video_encoder = nullptr;
-
- if(use_software_video_encoder) {
- gsr_video_encoder_software_params params;
- params.egl = egl;
- params.color_depth = color_depth;
- video_encoder = gsr_video_encoder_software_create(&params);
- return video_encoder;
- }
-
- switch(egl->gpu_info.vendor) {
- case GSR_GPU_VENDOR_AMD:
- case GSR_GPU_VENDOR_INTEL: {
- gsr_video_encoder_vaapi_params params;
- params.egl = egl;
- params.color_depth = color_depth;
- video_encoder = gsr_video_encoder_vaapi_create(&params);
- break;
- }
- case GSR_GPU_VENDOR_NVIDIA: {
- gsr_video_encoder_cuda_params params;
- params.egl = egl;
- params.overclock = overclock;
- params.color_depth = color_depth;
- video_encoder = gsr_video_encoder_cuda_create(&params);
- break;
- }
- }
-
- return video_encoder;
-}
-
static AVPixelFormat get_pixel_format(gsr_gpu_vendor vendor, bool use_software_video_encoder) {
if(use_software_video_encoder) {
return AV_PIX_FMT_NV12;