ohmu
·
2026-10-15
1#include "codec.h"
2
3#if FF_HAVE_VADRM || FF_HAVE_VAX11
4 #define FF_HAVE_VA 1
5#endif
6
7#if FF_HAVE_VA || FF_HAVE_VDPAU
8
9 #include "common/debug.h"
10 #include "common/library.h"
11 #include "common/mallocHelper.h"
12 #include "common/strutil.h"
13 #include "common/io.h"
14
15 #if FF_HAVE_VA
16
17 #include <fcntl.h>
18 #include <string.h>
19 #include <va/va.h>
20
21typedef struct FFVAData {
22 FF_LIBRARY_SYMBOL(vaInitialize)
23 FF_LIBRARY_SYMBOL(vaTerminate)
24 FF_LIBRARY_SYMBOL(vaMaxNumProfiles)
25 FF_LIBRARY_SYMBOL(vaMaxNumEntrypoints)
26 FF_LIBRARY_SYMBOL(vaQueryConfigProfiles)
27 FF_LIBRARY_SYMBOL(vaQueryConfigEntrypoints)
28 FF_LIBRARY_SYMBOL(vaQueryVendorString)
29 FF_LIBRARY_SYMBOL(vaGetConfigAttributes)
30} FFVAData;
31
32static FFCodecType vaProfileToType(VAProfile profile) {
33 switch (profile) {
34 case 11: // VAProfileH263Baseline
35 return FF_CODEC_TYPE_H263;
36
37 case 12: // VAProfileJPEGBaseline
38 return FF_CODEC_TYPE_MJPEG;
39
40 case 0: // VAProfileMPEG2Simple
41 case 1: // VAProfileMPEG2Main
42 return FF_CODEC_TYPE_MPEG2;
43
44 case 2: // VAProfileMPEG4Simple
45 case 3: // VAProfileMPEG4AdvancedSimple
46 case 4: // VAProfileMPEG4Main
47 return FF_CODEC_TYPE_DIVX_XVID;
48
49 case 5: // VAProfileH264Baseline
50 case 6: // VAProfileH264Main
51 case 7: // VAProfileH264High
52 case 13: // VAProfileH264ConstrainedBaseline
53 case 15: // VAProfileH264MultiviewHigh
54 case 16: // VAProfileH264StereoHigh
55 case 36: // VAProfileH264High10
56 case 40: // VAProfileH264High422
57 return FF_CODEC_TYPE_H264;
58
59 case 8: // VAProfileVC1Simple
60 case 9: // VAProfileVC1Main
61 case 10: // VAProfileVC1Advanced
62 return FF_CODEC_TYPE_VC1;
63
64 case 14: // VAProfileVP8Version0_3
65 return FF_CODEC_TYPE_VP8;
66
67 case 17: // VAProfileHEVCMain
68 case 18: // VAProfileHEVCMain10
69 case 23: // VAProfileHEVCMain12
70 case 24: // VAProfileHEVCMain422_10
71 case 25: // VAProfileHEVCMain422_12
72 case 26: // VAProfileHEVCMain444
73 case 27: // VAProfileHEVCMain444_10
74 case 28: // VAProfileHEVCMain444_12
75 case 29: // VAProfileHEVCSccMain
76 case 30: // VAProfileHEVCSccMain10
77 case 31: // VAProfileHEVCSccMain444
78 case 34: // VAProfileHEVCSccMain444_10
79 return FF_CODEC_TYPE_HEVC;
80
81 case 19: // VAProfileVP9Profile0
82 case 20: // VAProfileVP9Profile1
83 case 21: // VAProfileVP9Profile2
84 case 22: // VAProfileVP9Profile3
85 return FF_CODEC_TYPE_VP9;
86
87 case 32: // VAProfileAV1Profile0
88 case 33: // VAProfileAV1Profile1
89 case 39: // VAProfileAV1Profile2
90 return FF_CODEC_TYPE_AV1;
91
92 case 37: // VAProfileVVCMain10
93 case 38: // VAProfileVVCMultilayerMain10
94 return FF_CODEC_TYPE_VVC;
95
96 default:
97 return FF_CODEC_TYPE_UNKNOWN;
98 }
99}
100
101static FFCodecShowType vaGetEntrypointType(VAEntrypoint entrypoint) {
102 switch (entrypoint) {
103 case VAEntrypointVLD:
104 case VAEntrypointIDCT:
105 case VAEntrypointMoComp:
106 return FF_CODEC_SHOW_TYPE_DECODER;
107 case VAEntrypointEncSlice:
108 case VAEntrypointEncSliceLP:
109 case VAEntrypointFEI:
110 return FF_CODEC_SHOW_TYPE_ENCODER;
111 default:
112 return FF_CODEC_SHOW_TYPE_NONE;
113 }
114}
115
116static bool vaProfileHasOutput(
117 FFVAData* vaData,
118 VADisplay display,
119 int maxEntrypoints,
120 VAEntrypoint* entrypoints,
121 VAProfile profile,
122 FFCodecShowType entrypointType) {
123 int numEntrypoints = maxEntrypoints;
124 if (vaData->ffvaQueryConfigEntrypoints(display, profile, entrypoints, &numEntrypoints) != VA_STATUS_SUCCESS) {
125 return false;
126 }
127
128 for (int i = 0; i < numEntrypoints; ++i) {
129 if (vaGetEntrypointType(entrypoints[i]) != entrypointType) {
130 continue;
131 }
132
133 VAConfigAttrib attrib = {
134 .type = VAConfigAttribRTFormat,
135 .value = 0,
136 };
137
138 if (vaData->ffvaGetConfigAttributes(display, profile, entrypoints[i], &attrib, 1) == VA_STATUS_SUCCESS &&
139 attrib.value != VA_ATTRIB_NOT_SUPPORTED &&
140 attrib.value != 0) {
141 return true;
142 }
143 }
144
145 return false;
146}
147
148static bool vaDetectDisplay(VADisplay display, FFVAData* vaData, FFCodecOptions* options, FFlist* result, const char* api) {
149 int major = 0, minor = 0;
150 if (vaData->ffvaInitialize(display, &major, &minor) != VA_STATUS_SUCCESS) {
151 vaData->ffvaTerminate(display);
152 return false;
153 }
154
155 int maxProfiles = vaData->ffvaMaxNumProfiles(display);
156 int maxEntrypoints = vaData->ffvaMaxNumEntrypoints(display);
157 if (maxProfiles <= 0 || maxEntrypoints <= 0) {
158 vaData->ffvaTerminate(display);
159 return false;
160 }
161
162 FF_AUTO_FREE VAProfile* profiles = (VAProfile*) malloc(sizeof(VAProfile) * (size_t) maxProfiles);
163 FF_AUTO_FREE VAEntrypoint* entrypoints = (VAEntrypoint*) malloc(sizeof(VAEntrypoint) * (size_t) maxEntrypoints);
164
165 int numProfiles = maxProfiles;
166 if (vaData->ffvaQueryConfigProfiles(display, profiles, &numProfiles) != VA_STATUS_SUCCESS) {
167 vaData->ffvaTerminate(display);
168 return false;
169 }
170
171 FFCodecType decoderTypes = FF_CODEC_TYPE_NONE;
172 FFCodecType encoderTypes = FF_CODEC_TYPE_NONE;
173 for (int j = 0; j < numProfiles; ++j) {
174 FFCodecType type = vaProfileToType(profiles[j]);
175
176 bool hasDecoder = (options->showType & FF_CODEC_SHOW_TYPE_DECODER) &&
177 !(decoderTypes & type) &&
178 vaProfileHasOutput(
179 vaData,
180 display,
181 maxEntrypoints,
182 entrypoints,
183 profiles[j],
184 FF_CODEC_SHOW_TYPE_DECODER);
185
186 bool hasEncoder = (options->showType & FF_CODEC_SHOW_TYPE_ENCODER) &&
187 !(encoderTypes & type) &&
188 vaProfileHasOutput(
189 vaData,
190 display,
191 maxEntrypoints,
192 entrypoints,
193 profiles[j],
194 FF_CODEC_SHOW_TYPE_ENCODER);
195
196 if (!hasDecoder && !hasEncoder) {
197 continue;
198 }
199
200 if (hasDecoder) {
201 decoderTypes |= type;
202 }
203 if (hasEncoder) {
204 encoderTypes |= type;
205 }
206 }
207
208 if (decoderTypes != FF_CODEC_TYPE_NONE || encoderTypes != FF_CODEC_TYPE_NONE) {
209 FFCodecResult* item = FF_LIST_ADD(FFCodecResult, *result);
210 ffStrbufInitS(&item->gpu, vaData->ffvaQueryVendorString(display));
211 item->decoders = decoderTypes;
212 item->encoders = encoderTypes;
213 item->platformApi = api;
214 }
215
216 vaData->ffvaTerminate(display);
217
218 return true;
219}
220
221 #if FF_HAVE_VADRM
222 #include <va/va_drm.h>
223
224static const char* detectCodecByVaDrm(FFVAData* vaData, FFCodecOptions* options, FFlist* result) {
225 FF_LIBRARY_LOAD_MESSAGE(libvaDrm, "libva-drm" FF_LIBRARY_EXTENSION, 2)
226 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libvaDrm, vaGetDisplayDRM)
227
228 const char* error = "No DRM device could initialize VA-API";
229
230 FF_AUTO_CLOSE_DIR DIR* dirp = opendir("/dev/dri/");
231 if (dirp == nullptr) {
232 FF_DEBUG("opendir(/dev/dri/) failed: %s", strerror(errno));
233 return "opendir(/dev/dri/) failed";
234 }
235 int drifd = dirfd(dirp);
236
237 struct dirent* entry;
238 while ((entry = readdir(dirp)) != nullptr) {
239 if (entry->d_name[0] == '.' || !ffStrStartsWith(entry->d_name, "renderD")) {
240 continue;
241 }
242
243 FF_AUTO_CLOSE_FD int fd = openat(drifd, entry->d_name, O_RDWR | O_CLOEXEC);
244 if (fd < 0) {
245 continue;
246 }
247
248 VADisplay display = ffvaGetDisplayDRM(fd);
249 if (!display) {
250 continue;
251 }
252
253 if (vaDetectDisplay(display, vaData, options, result, "VA-API (DRM)")) {
254 error = nullptr;
255 }
256 }
257
258 return error;
259}
260 #endif
261 #if FF_HAVE_VAX11
262 #include <va/va_x11.h>
263
264static const char* detectCodecByVaX11(FFVAData* vaData, FFCodecOptions* options, FFlist* result) {
265 FF_LIBRARY_LOAD_MESSAGE(libX11, "libX11" FF_LIBRARY_EXTENSION, 6)
266 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XOpenDisplay)
267 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XCloseDisplay)
268
269 FF_LIBRARY_LOAD_MESSAGE(libvaX11, "libva-x11" FF_LIBRARY_EXTENSION, 2)
270 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libvaX11, vaGetDisplay)
271
272 Display* x11Display = ffXOpenDisplay(nullptr);
273 if (!x11Display) {
274 FF_DEBUG("XOpenDisplay() failed");
275 return "XOpenDisplay() failed";
276 }
277
278 VADisplay display = ffvaGetDisplay(x11Display);
279 if (!display) {
280 FF_DEBUG("vaGetDisplay() failed");
281 ffXCloseDisplay(x11Display);
282 return "vaGetDisplay() failed";
283 }
284
285 if (!vaDetectDisplay(display, vaData, options, result, "VA-API (X11)")) {
286 ffXCloseDisplay(x11Display);
287 return "VA-API could not detect any supported codec via X11";
288 }
289 ffXCloseDisplay(x11Display);
290
291 return nullptr;
292}
293 #endif
294
295static const char* detectCodecByVa(FFCodecOptions* options, FFlist* result) {
296 FFVAData vaData = {};
297
298 FF_LIBRARY_LOAD_MESSAGE(libva, "libva" FF_LIBRARY_EXTENSION, 2)
299 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaInitialize)
300 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaTerminate)
301 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaMaxNumProfiles)
302 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaMaxNumEntrypoints)
303 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaQueryConfigProfiles)
304 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaQueryConfigEntrypoints)
305 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaQueryVendorString)
306 FF_LIBRARY_LOAD_SYMBOL_VAR_MESSAGE(libva, vaData, vaGetConfigAttributes)
307
308 #if FF_HAVE_VADRM
309 if (detectCodecByVaDrm(&vaData, options, result) == nullptr) {
310 return nullptr;
311 }
312 #endif
313
314 #if FF_HAVE_VAX11
315 if (detectCodecByVaX11(&vaData, options, result) == nullptr) {
316 return nullptr;
317 }
318 #endif
319
320 return "VA-API could not detect any supported codec via DRM or X11";
321}
322 #endif
323
324 #if FF_HAVE_VDPAU
325 #include <vdpau/vdpau_x11.h>
326
327static const struct FFCodecVdpauCodec {
328 VdpDecoderProfile profile;
329 FFCodecType type;
330} FF_CODEC_VDPAU_CODECS[] = {
331 { 0, FF_CODEC_TYPE_MPEG1 }, // VDP_DECODER_PROFILE_MPEG1
332 { 1, FF_CODEC_TYPE_MPEG2 }, // VDP_DECODER_PROFILE_MPEG2_SIMPLE
333 { 2, FF_CODEC_TYPE_MPEG2 }, // VDP_DECODER_PROFILE_MPEG2_MAIN
334
335 { 6, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_BASELINE
336 { 7, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_MAIN
337 { 8, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_HIGH
338 { 9, FF_CODEC_TYPE_VC1 }, // VDP_DECODER_PROFILE_VC1_SIMPLE
339 { 10, FF_CODEC_TYPE_VC1 }, // VDP_DECODER_PROFILE_VC1_MAIN
340 { 11, FF_CODEC_TYPE_VC1 }, // VDP_DECODER_PROFILE_VC1_ADVANCED
341 { 12, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_MPEG4_PART2_SP
342 { 13, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_MPEG4_PART2_ASP
343 { 14, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_QMOBILE
344 { 15, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_MOBILE
345 { 16, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_HOME_THEATER
346 { 17, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX4_HD_1080P
347 { 18, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_QMOBILE
348 { 19, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_MOBILE
349 { 20, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_HOME_THEATER
350 { 21, FF_CODEC_TYPE_DIVX_XVID }, // VDP_DECODER_PROFILE_DIVX5_HD_1080P
351 { 22, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_CONSTRAINED_BASELINE
352 { 23, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_EXTENDED
353 { 24, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_PROGRESSIVE_HIGH
354 { 25, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_CONSTRAINED_HIGH
355 { 26, FF_CODEC_TYPE_H264 }, // VDP_DECODER_PROFILE_H264_HIGH_444_PREDICTIVE
356 { 27, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_0
357 { 28, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_1
358 { 29, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_2
359 { 30, FF_CODEC_TYPE_VP9 }, // VDP_DECODER_PROFILE_VP9_PROFILE_3
360
361 { 100, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN
362 { 101, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_10
363 { 102, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_STILL
364 { 103, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_12
365 { 104, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_444
366 { 105, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_444_10
367 { 106, FF_CODEC_TYPE_HEVC }, // VDP_DECODER_PROFILE_HEVC_MAIN_444_12
368 { 107, FF_CODEC_TYPE_AV1 }, // VDP_DECODER_PROFILE_AV1_MAIN
369 { 108, FF_CODEC_TYPE_AV1 }, // VDP_DECODER_PROFILE_AV1_HIGH
370 { 109, FF_CODEC_TYPE_AV1 }, // VDP_DECODER_PROFILE_AV1_PROFESSIONAL
371};
372
373static const char* detectCodecByVdpau(FFCodecOptions* options, FFlist* result) {
374 if (!(options->showType & FF_CODEC_SHOW_TYPE_DECODER)) {
375 return "VDPAU only supports decoding";
376 }
377
378 FF_LIBRARY_LOAD_MESSAGE(libX11, "libX11" FF_LIBRARY_EXTENSION, 6)
379 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XOpenDisplay)
380 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XCloseDisplay)
381 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libX11, XDefaultScreen)
382
383 FF_LIBRARY_LOAD_MESSAGE(libvdpau, "libvdpau" FF_LIBRARY_EXTENSION, 1)
384 FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libvdpau, vdp_device_create_x11)
385
386 Display* x11Display = ffXOpenDisplay(nullptr);
387 if (!x11Display) {
388 FF_DEBUG("XOpenDisplay() failed");
389 return "XOpenDisplay() failed";
390 }
391
392 VdpDevice device = VDP_INVALID_HANDLE;
393 VdpGetProcAddress* ffvdp_get_proc_address = nullptr;
394 VdpStatus ffvdp_device_create_x11_status = ffvdp_device_create_x11(x11Display, ffXDefaultScreen(x11Display), &device, &ffvdp_get_proc_address);
395 if (ffvdp_device_create_x11_status != VDP_STATUS_OK ||
396 device == VDP_INVALID_HANDLE ||
397 ffvdp_get_proc_address == nullptr) {
398 FF_DEBUG("vdp_device_create_x11() failed: status=%d, device=%x, getProcAddress=%p",
399 ffvdp_device_create_x11_status, (unsigned) device, (void*) ffvdp_get_proc_address);
400 ffXCloseDisplay(x11Display);
401 return "vdp_device_create_x11() failed";
402 }
403
404 VdpDeviceDestroy* ffvdp_device_destroy = nullptr;
405 VdpDecoderQueryCapabilities* ffvdp_decoder_query_capabilities = nullptr;
406 if (ffvdp_get_proc_address(device, VDP_FUNC_ID_DEVICE_DESTROY, (void**) &ffvdp_device_destroy) != VDP_STATUS_OK ||
407 ffvdp_get_proc_address(device, VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES, (void**) &ffvdp_decoder_query_capabilities) != VDP_STATUS_OK ||
408 ffvdp_device_destroy == nullptr ||
409 ffvdp_decoder_query_capabilities == nullptr) {
410 FF_DEBUG("vdp_device_create_x11() did not provide VDP_FUNC_ID_DEVICE_DESTROY / VDP_FUNC_ID_DECODER_QUERY_CAPABILITIES");
411 if (ffvdp_device_destroy) {
412 ffvdp_device_destroy(device);
413 }
414 ffXCloseDisplay(x11Display);
415 return "ffvdp_get_proc_address() failed";
416 }
417
418 FFCodecType decoderTypes = FF_CODEC_TYPE_NONE;
419
420 for (uint32_t i = 0; i < ARRAY_SIZE(FF_CODEC_VDPAU_CODECS); ++i) {
421 const struct FFCodecVdpauCodec* codec = &FF_CODEC_VDPAU_CODECS[i];
422 if (decoderTypes & codec->type) {
423 continue;
424 }
425 VdpBool isSupported = VDP_FALSE;
426 uint32_t maxLevel = 0, maxMacroblocks = 0, maxWidth = 0, maxHeight = 0;
427 if (ffvdp_decoder_query_capabilities(device, codec->profile, &isSupported, &maxLevel, &maxMacroblocks, &maxWidth, &maxHeight) != VDP_STATUS_OK ||
428 !isSupported) {
429 continue;
430 }
431
432 decoderTypes |= codec->type;
433 }
434
435 ffvdp_device_destroy(device);
436 ffXCloseDisplay(x11Display);
437
438 if (decoderTypes == FF_CODEC_TYPE_NONE) {
439 return nullptr;
440 }
441
442 FFCodecResult* item = FF_LIST_ADD(FFCodecResult, *result);
443 ffStrbufInit(&item->gpu);
444 const char* driver = getenv("VDPAU_DRIVER");
445 if (driver && *driver) {
446 ffStrbufSetS(&item->gpu, driver);
447 } else {
448 ffStrbufSetStatic(&item->gpu, "Default");
449 }
450 item->decoders = decoderTypes;
451 item->encoders = FF_CODEC_TYPE_NONE;
452 item->platformApi = "VDPAU";
453
454 return nullptr;
455}
456 #endif
457
458const char* ffDetectCodecNative(FFCodecOptions* options, FFlist* result /* list of FFCodecResult */) {
459 FF_SUPPRESS_IO();
460
461 #if FF_HAVE_VA
462 if (detectCodecByVa(options, result) == nullptr) {
463 return nullptr;
464 }
465 #endif
466 #if FF_HAVE_VDPAU
467 if (detectCodecByVdpau(options, result) == nullptr) {
468 return nullptr;
469 }
470 #endif
471
472 FF_DEBUG("Neither libva nor libvdpau could initialize a display");
473 return "Both libva and libvdpau fail to initialize";
474}
475
476#else
477
478const char* ffDetectCodecNative(FFCodecOptions* options, FFlist* result /* list of FFCodecResult */) {
479 FF_UNUSED(options, result);
480 return "Fastfetch was built without VA-API / VDPAU headers";
481}
482
483#endif