diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c7239710..d71d5208a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ cmake_dependent_option(ENABLE_XCB_RANDR "Enable xcb-randr" ON "LINUX OR FreeBSD cmake_dependent_option(ENABLE_XRANDR "Enable xrandr" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_DRM "Enable libdrm" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_VA "Enable libva" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF) +cmake_dependent_option(ENABLE_VDPAU "Enable vdpau" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_DRM_AMDGPU "Enable libdrm_amdgpu" ON "LINUX OR FreeBSD OR GNU" OFF) cmake_dependent_option(ENABLE_GIO "Enable gio-2.0" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) cmake_dependent_option(ENABLE_DCONF "Enable dconf" ON "LINUX OR FreeBSD OR OpenBSD OR NetBSD OR ANDROID OR SunOS OR GNU" OFF) @@ -1669,6 +1670,10 @@ ff_lib_enable(VA "libva" "Libva" ) +ff_lib_enable(VDPAU + "vdpau" + "vdpau" +) ff_lib_enable(DRM_AMDGPU "libdrm_amdgpu" "Libdrm_Amdgpu" diff --git a/src/common/impl/init.c b/src/common/impl/init.c index fa034b27a..f0fc0a2f3 100644 --- a/src/common/impl/init.c +++ b/src/common/impl/init.c @@ -262,6 +262,12 @@ void ffListFeatures(void) { #if FF_HAVE_LIBZFS "libzfs\n" #endif +#if FF_HAVE_VA + "va\n" +#endif +#if FF_HAVE_VDPAU + "vdpau\n" +#endif #if FF_USE_SYSTEM_YYJSON "System yyjson\n" #endif diff --git a/src/detection/codec/codec.h b/src/detection/codec/codec.h index 1e7fa72d8..9377d8d34 100644 --- a/src/detection/codec/codec.h +++ b/src/detection/codec/codec.h @@ -33,6 +33,7 @@ typedef struct FFCodecResult { FFstrbuf gpu; FFCodecType decoders; FFCodecType encoders; + const char* platformApi; } FFCodecResult; const char* ffDetectCodec(FFCodecOptions* options, FFlist* result /*list of FFCodecResult*/); diff --git a/src/detection/codec/codec_linux.c b/src/detection/codec/codec_linux.c index 0db288818..b103e5334 100644 --- a/src/detection/codec/codec_linux.c +++ b/src/detection/codec/codec_linux.c @@ -1,17 +1,41 @@ #include "codec.h" #if FF_HAVE_DRM && FF_HAVE_VA + #define FF_CODEC_HAVE_VA 1 +#endif -#include "detection/gpu/gpu.h" -#include "common/io.h" -#include "common/library.h" -#include "common/mallocHelper.h" +#if FF_HAVE_VDPAU + #define FF_CODEC_HAVE_VDPAU 1 +#endif -#include -#include -#include -#include -#include +#if FF_CODEC_HAVE_VA || FF_CODEC_HAVE_VDPAU + + #include "common/library.h" + #include "common/mallocHelper.h" + + #if FF_CODEC_HAVE_VA + #include "detection/gpu/gpu.h" + #include "common/io.h" + + #include + #include + #include + #include + #endif + + #include + + #if FF_CODEC_HAVE_VDPAU + #include + #include + +VdpStatus vdp_device_create_x11(void* display, int screen, VdpDevice* device, VdpGetProcAddress** get_proc_address); +void* XOpenDisplay(const char* display_name); +int XCloseDisplay(void* display); +int XDefaultScreen(void* display); + #endif + + #if FF_CODEC_HAVE_VA static FFCodecType ffCodecProfileToType(VAProfile profile) { switch (profile) { @@ -98,7 +122,7 @@ static bool ffCodecEntrypointIsEncode(VAEntrypoint entrypoint) { case VAEntrypointEncSlice: case VAEntrypointEncPicture: case VAEntrypointEncSliceLP: - case VAEntrypointEncFEI: + case VAEntrypointFEI: return true; default: return false; @@ -112,8 +136,7 @@ static bool ffCodecProfileHasOutput( int maxEntrypoints, VAEntrypoint* entrypoints, VAProfile profile, - bool encode -) { + bool encode) { int numEntrypoints = maxEntrypoints; if (ffvaQueryConfigEntrypoints(display, profile, entrypoints, &numEntrypoints) != VA_STATUS_SUCCESS) { return false; @@ -180,7 +203,9 @@ static void ffCodecFillGpuName(const drmDevice* dev, const char* path, FFstrbuf* } } -const char* ffDetectCodec(FF_A_UNUSED FFCodecOptions* options, FFlist* result /* list of FFCodecResult */) { +static const char* ffDetectCodecByVa(FFlist* result, bool* initialized) { + *initialized = false; + FF_LIBRARY_LOAD_MESSAGE(libdrm, "libdrm" FF_LIBRARY_EXTENSION, 2) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmGetDevices) FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libdrm, drmFreeDevices) @@ -206,6 +231,8 @@ const char* ffDetectCodec(FF_A_UNUSED FFCodecOptions* options, FFlist* result /* return NULL; } + const char* error = NULL; + for (int i = 0; i < numDevices; ++i) { drmDevice* dev = devices[i]; @@ -235,6 +262,7 @@ const char* ffDetectCodec(FF_A_UNUSED FFCodecOptions* options, FFlist* result /* if (ffvaInitialize(display, &major, &minor) != VA_STATUS_SUCCESS) { continue; } + *initialized = true; int maxProfiles = ffvaMaxNumProfiles(display); int maxEntrypoints = ffvaMaxNumEntrypoints(display); @@ -247,7 +275,8 @@ const char* ffDetectCodec(FF_A_UNUSED FFCodecOptions* options, FFlist* result /* FF_AUTO_FREE VAEntrypoint* entrypoints = (VAEntrypoint*) malloc(sizeof(VAEntrypoint) * (size_t) maxEntrypoints); if (!profiles || !entrypoints) { ffvaTerminate(display); - return "malloc() failed"; + error = "malloc() failed"; + break; } int numProfiles = maxProfiles; @@ -295,20 +324,234 @@ const char* ffDetectCodec(FF_A_UNUSED FFCodecOptions* options, FFlist* result /* ffCodecFillGpuName(dev, path, &item->gpu); item->decoders = decoderTypes; item->encoders = encoderTypes; + item->platformApi = "vaapi"; } ffvaTerminate(display); } ffdrmFreeDevices(devices, numDevices); + return error; +} + #endif + + #if FF_CODEC_HAVE_VDPAU + +static const struct FFCodecVdpauCodec { + VdpDecoderProfile profile; + FFCodecType type; +} FF_CODEC_VDPAU_CODECS[] = { + #ifdef VDP_DECODER_PROFILE_MPEG1 + { VDP_DECODER_PROFILE_MPEG1, FF_CODEC_TYPE_MPEG1 }, + #endif + #ifdef VDP_DECODER_PROFILE_MPEG2_SIMPLE + { VDP_DECODER_PROFILE_MPEG2_SIMPLE, FF_CODEC_TYPE_MPEG2 }, + #endif + #ifdef VDP_DECODER_PROFILE_MPEG2_MAIN + { VDP_DECODER_PROFILE_MPEG2_MAIN, FF_CODEC_TYPE_MPEG2 }, + #endif + #ifdef VDP_DECODER_PROFILE_MPEG4_PART2_SP + { VDP_DECODER_PROFILE_MPEG4_PART2_SP, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_MPEG4_PART2_ASP + { VDP_DECODER_PROFILE_MPEG4_PART2_ASP, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX4_QMOBILE + { VDP_DECODER_PROFILE_DIVX4_QMOBILE, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX4_MOBILE + { VDP_DECODER_PROFILE_DIVX4_MOBILE, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX4_HOME_THEATER + { VDP_DECODER_PROFILE_DIVX4_HOME_THEATER, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX4_HD_1080P + { VDP_DECODER_PROFILE_DIVX4_HD_1080P, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX5_QMOBILE + { VDP_DECODER_PROFILE_DIVX5_QMOBILE, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX5_MOBILE + { VDP_DECODER_PROFILE_DIVX5_MOBILE, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX5_HOME_THEATER + { VDP_DECODER_PROFILE_DIVX5_HOME_THEATER, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_DIVX5_HD_1080P + { VDP_DECODER_PROFILE_DIVX5_HD_1080P, FF_CODEC_TYPE_DIVX_XVID }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_BASELINE + { VDP_DECODER_PROFILE_H264_BASELINE, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_MAIN + { VDP_DECODER_PROFILE_H264_MAIN, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_HIGH + { VDP_DECODER_PROFILE_H264_HIGH, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE + { VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_EXTENDED + { VDP_DECODER_PROFILE_H264_EXTENDED, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_PROGRESSIVE_HIGH + { VDP_DECODER_PROFILE_H264_PROGRESSIVE_HIGH, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_CONSTRAINED_HIGH + { VDP_DECODER_PROFILE_H264_CONSTRAINED_HIGH, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE + { VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE, FF_CODEC_TYPE_H264 }, + #endif + #ifdef VDP_DECODER_PROFILE_VC1_SIMPLE + { VDP_DECODER_PROFILE_VC1_SIMPLE, FF_CODEC_TYPE_VC1 }, + #endif + #ifdef VDP_DECODER_PROFILE_VC1_MAIN + { VDP_DECODER_PROFILE_VC1_MAIN, FF_CODEC_TYPE_VC1 }, + #endif + #ifdef VDP_DECODER_PROFILE_VC1_ADVANCED + { VDP_DECODER_PROFILE_VC1_ADVANCED, FF_CODEC_TYPE_VC1 }, + #endif + #ifdef VDP_DECODER_PROFILE_HEVC_MAIN + { VDP_DECODER_PROFILE_HEVC_MAIN, FF_CODEC_TYPE_HEVC }, + #endif + #ifdef VDP_DECODER_PROFILE_HEVC_MAIN_10 + { VDP_DECODER_PROFILE_HEVC_MAIN_10, FF_CODEC_TYPE_HEVC }, + #endif + #ifdef VDP_DECODER_PROFILE_HEVC_MAIN_STILL + { VDP_DECODER_PROFILE_HEVC_MAIN_STILL, FF_CODEC_TYPE_HEVC }, + #endif + #ifdef VDP_DECODER_PROFILE_HEVC_MAIN_12 + { VDP_DECODER_PROFILE_HEVC_MAIN_12, FF_CODEC_TYPE_HEVC }, + #endif + #ifdef VDP_DECODER_PROFILE_HEVC_MAIN_444 + { VDP_DECODER_PROFILE_HEVC_MAIN_444, FF_CODEC_TYPE_HEVC }, + #endif + #ifdef VDP_DECODER_PROFILE_HEVC_MAIN_444_10 + { VDP_DECODER_PROFILE_HEVC_MAIN_444_10, FF_CODEC_TYPE_HEVC }, + #endif + #ifdef VDP_DECODER_PROFILE_HEVC_MAIN_444_12 + { VDP_DECODER_PROFILE_HEVC_MAIN_444_12, FF_CODEC_TYPE_HEVC }, + #endif + #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_0 + { VDP_DECODER_PROFILE_VP9_PROFILE_0, FF_CODEC_TYPE_VP9 }, + #endif + #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_1 + { VDP_DECODER_PROFILE_VP9_PROFILE_1, FF_CODEC_TYPE_VP9 }, + #endif + #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_2 + { VDP_DECODER_PROFILE_VP9_PROFILE_2, FF_CODEC_TYPE_VP9 }, + #endif + #ifdef VDP_DECODER_PROFILE_VP9_PROFILE_3 + { VDP_DECODER_PROFILE_VP9_PROFILE_3, FF_CODEC_TYPE_VP9 }, + #endif + #ifdef VDP_DECODER_PROFILE_AV1_MAIN + { VDP_DECODER_PROFILE_AV1_MAIN, FF_CODEC_TYPE_AV1 }, + #endif + #ifdef VDP_DECODER_PROFILE_AV1_HIGH + { VDP_DECODER_PROFILE_AV1_HIGH, FF_CODEC_TYPE_AV1 }, + #endif + #ifdef VDP_DECODER_PROFILE_AV1_PROFESSIONAL + { VDP_DECODER_PROFILE_AV1_PROFESSIONAL, FF_CODEC_TYPE_AV1 }, + #endif +}; + +static const char* ffDetectCodecByVdpau(FFlist* result) { + FF_LIBRARY_LOAD_MESSAGE(libX11, "libX11" FF_LIBRARY_EXTENSION, 6) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XOpenDisplay) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XCloseDisplay) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XDefaultScreen) + + FF_LIBRARY_LOAD_MESSAGE(libvdpau, "libvdpau" FF_LIBRARY_EXTENSION, 1) + FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libvdpau, vdp_device_create_x11) + + void* x11Display = ffXOpenDisplay(NULL); + if (!x11Display) { + return NULL; + } + + VdpDevice device = VDP_INVALID_HANDLE; + VdpGetProcAddress* ffvdp_get_proc_address = NULL; + if (ffvdp_device_create_x11(x11Display, ffXDefaultScreen(x11Display), &device, &ffvdp_get_proc_address) != VDP_STATUS_OK || + device == VDP_INVALID_HANDLE || + ffvdp_get_proc_address == NULL) { + ffXCloseDisplay(x11Display); + return NULL; + } + + VdpDeviceDestroy* ffvdp_device_destroy = NULL; + VdpDecoderQueryCapabilities* ffvdp_decoder_query_capabilities = NULL; + if (ffvdp_get_proc_address(device, VDP_FUNC_ID_DEVICE_DESTROY, (void**) &ffvdp_device_destroy) != VDP_STATUS_OK || + ffvdp_get_proc_address(device, VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES, (void**) &ffvdp_decoder_query_capabilities) != VDP_STATUS_OK || + ffvdp_device_destroy == NULL || + ffvdp_decoder_query_capabilities == NULL) { + if (ffvdp_device_destroy) { + ffvdp_device_destroy(device); + } + ffXCloseDisplay(x11Display); + return NULL; + } + + FFCodecType decoderTypes = FF_CODEC_TYPE_NONE; + + for (uint32_t i = 0; i < ARRAY_SIZE(FF_CODEC_VDPAU_CODECS); ++i) { + VdpBool isSupported = VDP_FALSE; + uint32_t maxLevel = 0, maxMacroblocks = 0, maxWidth = 0, maxHeight = 0; + if (ffvdp_decoder_query_capabilities(device, FF_CODEC_VDPAU_CODECS[i].profile, &isSupported, &maxLevel, &maxMacroblocks, &maxWidth, &maxHeight) != VDP_STATUS_OK || + !isSupported) { + continue; + } + + decoderTypes |= FF_CODEC_VDPAU_CODECS[i].type; + } + + ffvdp_device_destroy(device); + ffXCloseDisplay(x11Display); + + if (decoderTypes == FF_CODEC_TYPE_NONE) { + return NULL; + } + + FFCodecResult* item = FF_LIST_ADD(FFCodecResult, *result); + ffStrbufInit(&item->gpu); + const char* driver = getenv("VDPAU_DRIVER"); + if (driver && *driver) { + ffStrbufSetS(&item->gpu, driver); + } else { + ffStrbufSetStatic(&item->gpu, "Default"); + } + item->decoders = decoderTypes; + item->encoders = FF_CODEC_TYPE_NONE; + item->platformApi = "vdpau"; + return NULL; } + #endif + +const char* ffDetectCodec(FF_A_UNUSED FFCodecOptions* options, FFlist* result /* list of FFCodecResult */) { + FF_SUPPRESS_IO(); + + #if FF_CODEC_HAVE_VA + bool vaInitialized = false; + const char* vaError = ffDetectCodecByVa(result, &vaInitialized); + + if (vaError == NULL && vaInitialized) { + return NULL; + } + #endif + #if FF_CODEC_HAVE_VDPAU + return ffDetectCodecByVdpau(result); + #endif + + return "Fastfetch was built without DRM / VA-API / VDPAU headers"; +} #else const char* ffDetectCodec(FFCodecOptions* options, FFlist* result /* list of FFCodecResult */) { FF_UNUSED(options, result); - return "Fastfetch was built without DRM / VA-API headers"; + return "Fastfetch was built without DRM / VA-API / VDPAU headers"; } #endif diff --git a/src/modules/codec/codec.c b/src/modules/codec/codec.c index e01d28985..91a703bbc 100644 --- a/src/modules/codec/codec.c +++ b/src/modules/codec/codec.c @@ -49,7 +49,7 @@ static const char* ffCodecTypeToString(FFCodecType type) { } } -static void printCodecLine(const FFCodecOptions* options, uint8_t index, FFstrbuf* gpu, const char* direction, FFCodecType types) { +static void printCodecLine(const FFCodecOptions* options, uint8_t index, FFstrbuf* gpu, const char* direction, FFCodecType types, const char* platformApi) { FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate(); if (options->moduleArgs.key.length == 0) { if (gpu->length > 0) { @@ -91,6 +91,7 @@ static void printCodecLine(const FFCodecOptions* options, uint8_t index, FFstrbu FF_ARG(*gpu, "gpu"), FF_ARG(direction, "direction"), FF_ARG(typeList, "types"), + FF_ARG(platformApi, "platform-api"), })); // No need to destroy strings in lists, as they are static strings } @@ -115,8 +116,8 @@ bool ffPrintCodec(FFCodecOptions* options) { for (uint32_t i = 0; i < result.length; ++i) { FFCodecResult* item = FF_LIST_GET(FFCodecResult, result, i); uint8_t index = (uint8_t) (result.length == 1 ? 0 : i + 1); - printCodecLine(options, index, &item->gpu, "Encoder", item->encoders); - printCodecLine(options, index, &item->gpu, "Decoder", item->decoders); + printCodecLine(options, index, &item->gpu, "Encoder", item->encoders, item->platformApi); + printCodecLine(options, index, &item->gpu, "Decoder", item->decoders, item->platformApi); } } else { FFCodecResult merged = { @@ -128,9 +129,10 @@ bool ffPrintCodec(FFCodecOptions* options) { FF_LIST_FOR_EACH (FFCodecResult, item, result) { merged.decoders |= item->decoders; merged.encoders |= item->encoders; + merged.platformApi = item->platformApi; } - printCodecLine(options, 0, &merged.gpu, "Encoder", merged.encoders); - printCodecLine(options, 0, &merged.gpu, "Decoder", merged.decoders); + printCodecLine(options, 0, &merged.gpu, "Encoder", merged.encoders, merged.platformApi); + printCodecLine(options, 0, &merged.gpu, "Decoder", merged.decoders, merged.platformApi); } FF_LIST_FOR_EACH (FFCodecResult, item, result) { @@ -187,6 +189,7 @@ bool ffGenerateCodecJsonResult(FF_A_UNUSED FFCodecOptions* options, yyjson_mut_d yyjson_mut_arr_add_str(doc, decoders, ffCodecTypeToString(type)); } } + yyjson_mut_obj_add_str(doc, obj, "platformApi", item->platformApi); } FF_LIST_FOR_EACH (FFCodecResult, item, result) { @@ -217,5 +220,6 @@ FFModuleBaseInfo ffCodecModuleInfo = { { "GPU name", "gpu" }, { "Decoder / Encoder", "direction" }, { "Compatibility alias of codec types", "types" }, + { "Platform API used for detection", "platform-api" }, })) };