Files
fastfetch/src/detection/codec/codec_linux.c
T

413 lines
15 KiB
C
Raw Normal View History

#include "codec.h"
2026-05-31 10:16:08 +08:00
2026-06-18 17:45:19 +08:00
#if FF_HAVE_VA || FF_HAVE_VDPAU
2026-05-31 19:08:30 +08:00
#include "common/library.h"
#include "common/mallocHelper.h"
2026-06-18 10:51:25 +08:00
#include "common/strutil.h"
2026-05-31 23:31:45 +08:00
#include "common/io.h"
2026-05-31 19:08:30 +08:00
2026-06-18 10:51:25 +08:00
#if FF_HAVE_VA
2026-05-31 19:08:30 +08:00
#include <fcntl.h>
#include <va/va.h>
#include <va/va_drm.h>
#endif
2026-05-31 10:16:08 +08:00
2026-05-31 19:08:30 +08:00
#include <string.h>
2026-06-02 09:10:55 +08:00
#if FF_HAVE_VDPAU
2026-05-31 19:08:30 +08:00
#include <stdlib.h>
#include <vdpau/vdpau.h>
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
2026-06-18 10:51:25 +08:00
#if FF_HAVE_VA
2026-05-31 10:16:08 +08:00
static FFCodecType ffCodecProfileToType(VAProfile profile) {
2026-05-31 10:16:08 +08:00
switch (profile) {
case 11: // VAProfileH263Baseline
return FF_CODEC_TYPE_H263;
2026-05-31 10:16:08 +08:00
case 12: // VAProfileJPEGBaseline
return FF_CODEC_TYPE_MJPEG;
2026-05-31 10:16:08 +08:00
case 0: // VAProfileMPEG2Simple
case 1: // VAProfileMPEG2Main
return FF_CODEC_TYPE_MPEG2;
2026-05-31 10:16:08 +08:00
case 2: // VAProfileMPEG4Simple
case 3: // VAProfileMPEG4AdvancedSimple
case 4: // VAProfileMPEG4Main
return FF_CODEC_TYPE_DIVX_XVID;
2026-05-31 10:16:08 +08:00
case 5: // VAProfileH264Baseline
case 6: // VAProfileH264Main
case 7: // VAProfileH264High
case 13: // VAProfileH264ConstrainedBaseline
case 15: // VAProfileH264MultiviewHigh
case 16: // VAProfileH264StereoHigh
case 36: // VAProfileH264High10
case 40: // VAProfileH264High422
return FF_CODEC_TYPE_H264;
2026-05-31 10:16:08 +08:00
case 8: // VAProfileVC1Simple
case 9: // VAProfileVC1Main
case 10: // VAProfileVC1Advanced
return FF_CODEC_TYPE_VC1;
2026-05-31 10:16:08 +08:00
case 14: // VAProfileVP8Version0_3
return FF_CODEC_TYPE_VP8;
2026-05-31 10:16:08 +08:00
case 17: // VAProfileHEVCMain
case 18: // VAProfileHEVCMain10
case 23: // VAProfileHEVCMain12
case 24: // VAProfileHEVCMain422_10
case 25: // VAProfileHEVCMain422_12
case 26: // VAProfileHEVCMain444
case 27: // VAProfileHEVCMain444_10
case 28: // VAProfileHEVCMain444_12
case 29: // VAProfileHEVCSccMain
case 30: // VAProfileHEVCSccMain10
case 31: // VAProfileHEVCSccMain444
case 34: // VAProfileHEVCSccMain444_10
return FF_CODEC_TYPE_HEVC;
2026-05-31 10:16:08 +08:00
case 19: // VAProfileVP9Profile0
case 20: // VAProfileVP9Profile1
case 21: // VAProfileVP9Profile2
case 22: // VAProfileVP9Profile3
return FF_CODEC_TYPE_VP9;
2026-05-31 10:16:08 +08:00
case 32: // VAProfileAV1Profile0
case 33: // VAProfileAV1Profile1
case 39: // VAProfileAV1Profile2
return FF_CODEC_TYPE_AV1;
2026-05-31 10:16:08 +08:00
case 37: // VAProfileVVCMain10
case 38: // VAProfileVVCMultilayerMain10
return FF_CODEC_TYPE_VVC;
2026-05-31 10:16:08 +08:00
default:
return FF_CODEC_TYPE_UNKNOWN;
2026-05-31 10:16:08 +08:00
}
}
2026-06-02 09:10:55 +08:00
static FFCodecShowType ffCodecGetEntrypointType(VAEntrypoint entrypoint) {
2026-05-31 10:16:08 +08:00
switch (entrypoint) {
case VAEntrypointVLD:
case VAEntrypointIDCT:
case VAEntrypointMoComp:
2026-06-02 09:10:55 +08:00
return FF_CODEC_SHOW_TYPE_DECODER;
case VAEntrypointEncSlice:
case VAEntrypointEncSliceLP:
2026-05-31 19:08:30 +08:00
case VAEntrypointFEI:
2026-06-02 09:10:55 +08:00
return FF_CODEC_SHOW_TYPE_ENCODER;
default:
2026-06-02 09:10:55 +08:00
return FF_CODEC_SHOW_TYPE_NONE;
}
}
static bool ffCodecProfileHasOutput(
2026-05-31 10:16:08 +08:00
VADisplay display,
__typeof__(vaQueryConfigEntrypoints)* ffvaQueryConfigEntrypoints,
__typeof__(vaGetConfigAttributes)* ffvaGetConfigAttributes,
int maxEntrypoints,
VAEntrypoint* entrypoints,
VAProfile profile,
2026-06-02 09:10:55 +08:00
FFCodecShowType entrypointType) {
2026-05-31 10:16:08 +08:00
int numEntrypoints = maxEntrypoints;
if (ffvaQueryConfigEntrypoints(display, profile, entrypoints, &numEntrypoints) != VA_STATUS_SUCCESS) {
return false;
}
for (int i = 0; i < numEntrypoints; ++i) {
2026-06-02 09:10:55 +08:00
if (ffCodecGetEntrypointType(entrypoints[i]) != entrypointType) {
2026-05-31 10:16:08 +08:00
continue;
}
VAConfigAttrib attrib = {
.type = VAConfigAttribRTFormat,
.value = 0,
};
if (ffvaGetConfigAttributes(display, profile, entrypoints[i], &attrib, 1) == VA_STATUS_SUCCESS &&
attrib.value != VA_ATTRIB_NOT_SUPPORTED &&
attrib.value != 0) {
return true;
}
}
return false;
}
2026-06-02 09:10:55 +08:00
static const char* ffDetectCodecByVa(FFCodecOptions* options, FFlist* result) {
2026-05-31 10:16:08 +08:00
FF_LIBRARY_LOAD_MESSAGE(libva, "libva" FF_LIBRARY_EXTENSION, 2)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaInitialize)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaTerminate)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaMaxNumProfiles)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaMaxNumEntrypoints)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaQueryConfigProfiles)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaQueryConfigEntrypoints)
2026-06-18 10:51:25 +08:00
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaQueryVendorString)
2026-05-31 10:16:08 +08:00
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libva, vaGetConfigAttributes)
FF_LIBRARY_LOAD_MESSAGE(libvaDrm, "libva-drm" FF_LIBRARY_EXTENSION, 2)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libvaDrm, vaGetDisplayDRM)
2026-06-02 09:10:55 +08:00
const char* error = "No DRM device could initialize VA-API";
2026-05-31 19:08:30 +08:00
2026-06-18 10:51:25 +08:00
FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/dev/dri/");
if (dirp == NULL) {
return "opendir(/dev/dri/) failed";
}
int drifd = dirfd(dirp);
2026-05-31 10:16:08 +08:00
2026-06-18 10:51:25 +08:00
struct dirent* entry;
while ((entry = readdir(dirp)) != NULL) {
if (entry->d_name[0] == '.' || !ffStrStartsWith(entry->d_name, "renderD")) {
2026-05-31 10:16:08 +08:00
continue;
}
2026-06-18 10:51:25 +08:00
FF_AUTO_CLOSE_FD int fd = openat(drifd, entry->d_name, O_RDONLY | O_CLOEXEC);
2026-05-31 10:16:08 +08:00
if (fd < 0) {
continue;
}
VADisplay display = ffvaGetDisplayDRM(fd);
if (!display) {
continue;
}
int major = 0, minor = 0;
if (ffvaInitialize(display, &major, &minor) != VA_STATUS_SUCCESS) {
2026-06-18 10:44:11 +08:00
ffvaTerminate(display);
2026-05-31 10:16:08 +08:00
continue;
}
2026-06-02 09:10:55 +08:00
error = NULL;
2026-05-31 10:16:08 +08:00
int maxProfiles = ffvaMaxNumProfiles(display);
int maxEntrypoints = ffvaMaxNumEntrypoints(display);
if (maxProfiles <= 0 || maxEntrypoints <= 0) {
ffvaTerminate(display);
continue;
}
FF_AUTO_FREE VAProfile* profiles = (VAProfile*) malloc(sizeof(VAProfile) * (size_t) maxProfiles);
FF_AUTO_FREE VAEntrypoint* entrypoints = (VAEntrypoint*) malloc(sizeof(VAEntrypoint) * (size_t) maxEntrypoints);
int numProfiles = maxProfiles;
if (ffvaQueryConfigProfiles(display, profiles, &numProfiles) != VA_STATUS_SUCCESS) {
ffvaTerminate(display);
continue;
}
FFCodecType decoderTypes = FF_CODEC_TYPE_NONE;
FFCodecType encoderTypes = FF_CODEC_TYPE_NONE;
2026-05-31 10:16:08 +08:00
for (int j = 0; j < numProfiles; ++j) {
2026-06-02 09:10:55 +08:00
FFCodecType type = ffCodecProfileToType(profiles[j]);
bool hasDecoder = (options->showType & FF_CODEC_SHOW_TYPE_DECODER) &&
2026-06-02 09:10:55 +08:00
!(decoderTypes & type) &&
ffCodecProfileHasOutput(
display,
ffvaQueryConfigEntrypoints,
ffvaGetConfigAttributes,
maxEntrypoints,
entrypoints,
profiles[j],
2026-06-02 09:10:55 +08:00
FF_CODEC_SHOW_TYPE_DECODER);
bool hasEncoder = (options->showType & FF_CODEC_SHOW_TYPE_ENCODER) &&
2026-06-02 09:10:55 +08:00
!(encoderTypes & type) &&
ffCodecProfileHasOutput(
display,
ffvaQueryConfigEntrypoints,
ffvaGetConfigAttributes,
maxEntrypoints,
entrypoints,
profiles[j],
2026-06-02 09:10:55 +08:00
FF_CODEC_SHOW_TYPE_ENCODER);
if (!hasDecoder && !hasEncoder) {
2026-05-31 10:16:08 +08:00
continue;
}
if (hasDecoder) {
decoderTypes |= type;
}
if (hasEncoder) {
encoderTypes |= type;
}
2026-05-31 10:16:08 +08:00
}
if (decoderTypes != FF_CODEC_TYPE_NONE || encoderTypes != FF_CODEC_TYPE_NONE) {
FFCodecResult* item = FF_LIST_ADD(FFCodecResult, *result);
2026-06-18 18:11:48 +08:00
ffStrbufInitS(&item->gpu, ffvaQueryVendorString(display));
item->decoders = decoderTypes;
item->encoders = encoderTypes;
2026-06-01 10:34:17 +08:00
item->platformApi = "VA-API";
2026-05-31 10:16:08 +08:00
}
ffvaTerminate(display);
}
2026-05-31 19:08:30 +08:00
return error;
}
#endif
2026-06-02 09:10:55 +08:00
#if FF_HAVE_VDPAU
2026-05-31 19:08:30 +08:00
static const struct FFCodecVdpauCodec {
VdpDecoderProfile profile;
FFCodecType type;
} FF_CODEC_VDPAU_CODECS[] = {
{ 0, FF_CODEC_TYPE_MPEG1 }, // VDP_DECODER_PROFILE_MPEG1
{ 1, FF_CODEC_TYPE_MPEG2 }, // VDP_DECODER_PROFILE_MPEG2_SIMPLE
{ 2, FF_CODEC_TYPE_MPEG2 }, // VDP_DECODER_PROFILE_MPEG2_MAIN
{ 12, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_MPEG4_PART2_SP
{ 13, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_MPEG4_PART2_ASP
{ 14, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_QMOBILE
{ 15, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_MOBILE
{ 16, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_HOME_THEATER
{ 17, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_HD_1080P
{ 18, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_QMOBILE
{ 19, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_MOBILE
{ 20, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_HOME_THEATER
{ 21, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_HD_1080P
{ 6, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_BASELINE
{ 7, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_MAIN
{ 8, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_HIGH
{ 22, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE
{ 23, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_EXTENDED
{ 24, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_PROGRESSIVE_HIGH
{ 25, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_CONSTRAINED_HIGH
{ 26, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE
{ 9, FF_CODEC_TYPE_VC1 }, // VDP_DECODER_PROFILE_VC1_SIMPLE
{ 10, FF_CODEC_TYPE_VC1 }, // VDP_DECODER_PROFILE_VC1_MAIN
{ 11, FF_CODEC_TYPE_VC1 }, // VDP_DECODER_PROFILE_VC1_ADVANCED
{ 100, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN
{ 101, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_10
{ 102, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_STILL
{ 103, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_12
{ 104, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_444
{ 105, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_444_10
{ 106, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_444_12
{ 27, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_0
{ 28, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_1
{ 29, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_2
{ 30, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_3
{ 107, FF_CODEC_TYPE_AV1 }, // VDP_DECODER_PROFILE_AV1_MAIN
{ 108, FF_CODEC_TYPE_AV1 }, // VDP_DECODER_PROFILE_AV1_HIGH
{ 109, FF_CODEC_TYPE_AV1 }, // VDP_DECODER_PROFILE_AV1_PROFESSIONAL
2026-05-31 19:08:30 +08:00
};
static const char* ffDetectCodecByVdpau(FFCodecOptions* options, FFlist* result) {
if (options->showType != FF_CODEC_SHOW_TYPE_DECODER) {
return "VDPAU only supports decoding";
}
2026-05-31 19:08:30 +08:00
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) {
2026-06-02 09:10:55 +08:00
return "XOpenDisplay() failed";
2026-05-31 19:08:30 +08:00
}
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);
2026-06-02 09:10:55 +08:00
return "vdp_device_create_x11() failed";
2026-05-31 19:08:30 +08:00
}
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);
2026-06-02 09:10:55 +08:00
return "ffvdp_get_proc_address() failed";
2026-05-31 19:08:30 +08:00
}
FFCodecType decoderTypes = FF_CODEC_TYPE_NONE;
for (uint32_t i = 0; i < ARRAY_SIZE(FF_CODEC_VDPAU_CODECS); ++i) {
2026-06-02 09:10:55 +08:00
const struct FFCodecVdpauCodec* codec = &FF_CODEC_VDPAU_CODECS[i];
if (decoderTypes & codec->type) {
continue;
}
2026-05-31 19:08:30 +08:00
VdpBool isSupported = VDP_FALSE;
uint32_t maxLevel = 0, maxMacroblocks = 0, maxWidth = 0, maxHeight = 0;
2026-06-02 09:10:55 +08:00
if (ffvdp_decoder_query_capabilities(device, codec->profile, &isSupported, &maxLevel, &maxMacroblocks, &maxWidth, &maxHeight) != VDP_STATUS_OK ||
2026-05-31 19:08:30 +08:00
!isSupported) {
continue;
}
2026-06-02 09:10:55 +08:00
decoderTypes |= codec->type;
2026-05-31 19:08:30 +08:00
}
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;
2026-06-01 10:34:17 +08:00
item->platformApi = "VDPAU";
2026-05-31 19:08:30 +08:00
2026-05-31 10:16:08 +08:00
return NULL;
}
2026-05-31 19:08:30 +08:00
#endif
const char* ffDetectCodecNative(FFCodecOptions* options, FFlist* result /* list of FFCodecResult */) {
2026-05-31 19:08:30 +08:00
FF_SUPPRESS_IO();
2026-06-18 17:45:19 +08:00
#if FF_HAVE_VA
2026-06-02 09:10:55 +08:00
if (ffDetectCodecByVa(options, result) == NULL) {
2026-05-31 19:08:30 +08:00
return NULL;
}
#endif
2026-06-02 09:10:55 +08:00
#if FF_HAVE_VDPAU
if (ffDetectCodecByVdpau(options, result) == NULL) {
return NULL;
}
2026-05-31 19:08:30 +08:00
#endif
2026-06-02 09:10:55 +08:00
return "Both libva and libvdpau fail to initialize";
2026-05-31 19:08:30 +08:00
}
2026-05-31 10:16:08 +08:00
#else
2026-05-31 23:31:45 +08:00
const char* ffDetectCodecNative(FFCodecOptions* options, FFlist* result /* list of FFCodecResult */) {
2026-05-31 10:16:08 +08:00
FF_UNUSED(options, result);
2026-05-31 19:08:30 +08:00
return "Fastfetch was built without DRM / VA-API / VDPAU headers";
2026-05-31 10:16:08 +08:00
}
#endif