Prepare for video codec query, cleanup readme, add libva-drm as dependency

This commit is contained in:
dec05eba
2024-08-29 22:22:46 +02:00
parent 1b356677ea
commit dfe2b17648
9 changed files with 329 additions and 74 deletions

View File

@@ -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`). 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 # Dependencies
GPU Screen Recorder uses meson build system so you need to install `meson`. There are additional dependencies depending on your graphics card: GPU Screen Recorder uses meson build system so you need to install `meson` to build GPU Screen Recorder:
## AMD
libglvnd (which provides libgl and libegl)\ ## Build dependencies
mesa\ These are the dependencies needed to build GPU Screen Recorder:
ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)\
x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)\ * libglvnd (which provides libgl and libegl)
libpulse\ * ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)
vaapi (libva, libva-mesa-driver)\ * x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)
libdrm\ * libpulse
libcap\ * libva (and libva-drm)
wayland-client * libdrm
## Intel * libcap
libglvnd (which provides libgl and libegl)\ * wayland-client
mesa\
ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)\ ## Runtime dependencies
x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)\ There are also additional dependencies needed at runtime depending on your GPU vendor:
libpulse\
vaapi (libva, intel-media-driver/libva-intel-driver)\ ### AMD
libdrm\ * mesa
libcap\ * vaapi (libva-mesa-driver)
wayland-client
## NVIDIA ### Intel
libglvnd (which provides libgl and libegl)\ * mesa
ffmpeg (libavcodec, libavformat, libavutil, libswresample, libavfilter)\ * vaapi (intel-media-driver/libva-intel-driver/linux-firmware, depending on which intel iGPU you have)
x11 (libx11, libxcomposite, libxrandr, libxfixes, libxdamage, libxi)\
libpulse\ ### NVIDIA
cuda runtime (libcuda.so.1) (libnvidia-compute)\ * cuda runtime (libcuda.so.1) (libnvidia-compute)
nvenc (libnvidia-encode)\ * nvenc (libnvidia-encode)
libva\ * nvfbc (libnvidia-fbc1, when recording the screen on x11)
libdrm\ * xnvctrl (libxnvctrl0, when using the `-oc` option)
libcap\
wayland-client\ ## Optional dependencies
nvfbc (libnvidia-fbc1, when recording the screen on x11)\ When compiling GPU Screen Recorder with portal support (`-Dportal=true`, which is enabled by default) these dependencies are also needed:
xnvctrl (libxnvctrl0, when using the `-oc` option) * libdbus
## Optional dependencies when compiling with portal support (default option) * libpipewire (and libspa which is usually part of libpipewire)
dbus\
libpipewire (and libspa which is usually part of libpipewire)
# How to use # How to use
Run `gpu-screen-recorder --help` to see all options and also examples. Run `gpu-screen-recorder --help` to see all options and also examples.

View File

@@ -8,7 +8,20 @@ typedef struct gsr_video_encoder gsr_video_encoder;
typedef struct AVCodecContext AVCodecContext; typedef struct AVCodecContext AVCodecContext;
typedef struct AVFrame AVFrame; 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 { 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); 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 */ void (*copy_textures_to_frame)(gsr_video_encoder *encoder, AVFrame *frame); /* Can be NULL */
/* |textures| should be able to fit 2 elements */ /* |textures| should be able to fit 2 elements */
@@ -19,6 +32,7 @@ struct gsr_video_encoder {
bool started; 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); 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_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); void gsr_video_encoder_get_textures(gsr_video_encoder *encoder, unsigned int *textures, int *num_textures, gsr_destination_color *destination_color);

View File

@@ -46,6 +46,7 @@ dep = [
dependency('libswresample'), dependency('libswresample'),
dependency('libavfilter'), dependency('libavfilter'),
dependency('libva'), dependency('libva'),
dependency('libva-drm'),
dependency('libcap'), dependency('libcap'),
dependency('libdrm'), dependency('libdrm'),
dependency('wayland-egl'), dependency('wayland-egl'),

View File

@@ -25,10 +25,11 @@ libpulse = ">=13"
libswresample = ">=3" libswresample = ">=3"
libavfilter = ">=5" libavfilter = ">=5"
libva = ">=1" libva = ">=1"
libva-drm = ">=1"
libcap = ">=2" libcap = ">=2"
libdrm = ">=2" libdrm = ">=2"
wayland-egl = ">=15" wayland-egl = ">=15"
wayland-client = ">=1" wayland-client = ">=1"
dbus-1 = ">=1" dbus-1 = ">=1"
libpipewire-0.3 = ">=1" libpipewire-0.3 = ">=1"
libspa-0.2 = ">=0" libspa-0.2 = ">=0"

View File

@@ -122,6 +122,24 @@ static bool gsr_video_encoder_cuda_setup_textures(gsr_video_encoder_cuda *self,
return true; 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 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) { 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_cuda->params = *params;
*encoder = (gsr_video_encoder) { *encoder = (gsr_video_encoder) {
.get_supported_codecs = gsr_video_encoder_cuda_get_supported_codecs,
.start = gsr_video_encoder_cuda_start, .start = gsr_video_encoder_cuda_start,
.copy_textures_to_frame = gsr_video_encoder_cuda_copy_textures_to_frame, .copy_textures_to_frame = gsr_video_encoder_cuda_copy_textures_to_frame,
.get_textures = gsr_video_encoder_cuda_get_textures, .get_textures = gsr_video_encoder_cuda_get_textures,

View File

@@ -58,6 +58,22 @@ static bool gsr_video_encoder_software_setup_textures(gsr_video_encoder_software
return true; 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 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) { 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_software->params = *params;
*encoder = (gsr_video_encoder) { *encoder = (gsr_video_encoder) {
.get_supported_codecs = gsr_video_encoder_software_get_supported_codecs,
.start = gsr_video_encoder_software_start, .start = gsr_video_encoder_software_start,
.copy_textures_to_frame = gsr_video_encoder_software_copy_textures_to_frame, .copy_textures_to_frame = gsr_video_encoder_software_copy_textures_to_frame,
.get_textures = gsr_video_encoder_software_get_textures, .get_textures = gsr_video_encoder_software_get_textures,

View File

@@ -6,9 +6,11 @@
#include <libavutil/hwcontext_vaapi.h> #include <libavutil/hwcontext_vaapi.h>
#include <va/va_drmcommon.h> #include <va/va_drmcommon.h>
#include <va/va_drm.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <fcntl.h>
typedef struct { typedef struct {
gsr_video_encoder_vaapi_params params; 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 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) { 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_vaapi->params = *params;
*encoder = (gsr_video_encoder) { *encoder = (gsr_video_encoder) {
.get_supported_codecs = gsr_video_encoder_vaapi_get_supported_codecs,
.start = gsr_video_encoder_vaapi_start, .start = gsr_video_encoder_vaapi_start,
.copy_textures_to_frame = NULL, .copy_textures_to_frame = NULL,
.get_textures = gsr_video_encoder_vaapi_get_textures, .get_textures = gsr_video_encoder_vaapi_get_textures,

View File

@@ -1,6 +1,10 @@
#include "../../../include/encoder/video/video.h" #include "../../../include/encoder/video/video.h"
#include <assert.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) { bool gsr_video_encoder_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) {
assert(!encoder->started); assert(!encoder->started);
bool res = encoder->start(encoder, video_codec_context, frame); bool res = encoder->start(encoder, video_codec_context, frame);

View File

@@ -1686,6 +1686,39 @@ static int init_filter_graph(AVCodecContext *audio_codec_context, AVFilterGraph
return 0; 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) { static void xwayland_check_callback(const gsr_monitor *monitor, void *userdata) {
bool *xwayland_found = (bool*)userdata; bool *xwayland_found = (bool*)userdata;
if(monitor->name_len >= 8 && strncmp(monitor->name, "XWAYLAND", 8) == 0) 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) { static void list_supported_video_codecs(gsr_egl *egl, bool wayland) {
#if 1
if(find_h264_encoder(egl->gpu_info.vendor, egl->card_path)) if(find_h264_encoder(egl->gpu_info.vendor, egl->card_path))
puts("h264"); puts("h264");
if(find_h264_software_encoder()) if(find_h264_software_encoder())
@@ -1755,6 +1789,34 @@ static void list_supported_video_codecs(gsr_egl *egl, bool wayland) {
puts("vp8"); puts("vp8");
if(find_vp9_encoder(egl->gpu_info.vendor, egl->card_path)) if(find_vp9_encoder(egl->gpu_info.vendor, egl->card_path))
puts("vp9"); 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) { static bool monitor_capture_use_drm(gsr_egl *egl, bool wayland) {
@@ -1870,9 +1932,10 @@ static void info_command() {
fflush(stdout); fflush(stdout);
gsr_egl_unload(&egl); // Not needed as this will just slow down shutdown
if(dpy) //gsr_egl_unload(&egl);
XCloseDisplay(dpy); //if(dpy)
// XCloseDisplay(dpy);
_exit(0); _exit(0);
} }
@@ -2066,39 +2129,6 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre
return capture; 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) { static AVPixelFormat get_pixel_format(gsr_gpu_vendor vendor, bool use_software_video_encoder) {
if(use_software_video_encoder) { if(use_software_video_encoder) {
return AV_PIX_FMT_NV12; return AV_PIX_FMT_NV12;