2026-05-31 13:16:24 +08:00
|
|
|
#include "common/printing.h"
|
|
|
|
|
#include "common/jsonconfig.h"
|
|
|
|
|
#include "detection/codec/codec.h"
|
|
|
|
|
#include "modules/codec/codec.h"
|
|
|
|
|
|
|
|
|
|
static const char* ffCodecTypeToString(FFCodecType type) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
case FF_CODEC_TYPE_H261:
|
|
|
|
|
return "H.261";
|
|
|
|
|
case FF_CODEC_TYPE_H263:
|
|
|
|
|
return "H.263";
|
|
|
|
|
case FF_CODEC_TYPE_MJPEG:
|
|
|
|
|
return "MJPEG";
|
|
|
|
|
case FF_CODEC_TYPE_MPEG1:
|
|
|
|
|
return "MPEG-1";
|
|
|
|
|
case FF_CODEC_TYPE_MPEG2:
|
|
|
|
|
return "MPEG-2";
|
|
|
|
|
case FF_CODEC_TYPE_DIVX_XVID:
|
|
|
|
|
return "DivX / Xvid";
|
|
|
|
|
case FF_CODEC_TYPE_H264:
|
|
|
|
|
return "H.264";
|
|
|
|
|
case FF_CODEC_TYPE_WMV8:
|
|
|
|
|
return "WMV-8";
|
|
|
|
|
case FF_CODEC_TYPE_WMV9:
|
|
|
|
|
return "WMV-9";
|
|
|
|
|
case FF_CODEC_TYPE_VC1:
|
|
|
|
|
return "VC-1";
|
|
|
|
|
case FF_CODEC_TYPE_VP8:
|
|
|
|
|
return "VP8";
|
|
|
|
|
case FF_CODEC_TYPE_HEVC:
|
|
|
|
|
return "HEVC / H.265";
|
|
|
|
|
case FF_CODEC_TYPE_VP9:
|
|
|
|
|
return "VP9";
|
|
|
|
|
case FF_CODEC_TYPE_AV1:
|
|
|
|
|
return "AV1";
|
|
|
|
|
case FF_CODEC_TYPE_VVC:
|
|
|
|
|
return "VVC / H.266";
|
|
|
|
|
case FF_CODEC_TYPE_DOLBY_VISION_HEVC:
|
|
|
|
|
return "Dolby Vision (HEVC)";
|
|
|
|
|
case FF_CODEC_TYPE_PRORES:
|
|
|
|
|
return "Apple ProRes";
|
|
|
|
|
case FF_CODEC_TYPE_PRORES_RAW:
|
|
|
|
|
return "Apple ProRes RAW";
|
|
|
|
|
case FF_CODEC_TYPE_JPEG_XL:
|
|
|
|
|
return "JPEG XL";
|
|
|
|
|
case FF_CODEC_TYPE_UNKNOWN:
|
|
|
|
|
default:
|
|
|
|
|
return "Unknown";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 19:08:30 +08:00
|
|
|
static void printCodecLine(const FFCodecOptions* options, uint8_t index, FFstrbuf* gpu, const char* direction, FFCodecType types, const char* platformApi) {
|
2026-05-31 13:16:24 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
|
|
|
|
|
if (options->moduleArgs.key.length == 0) {
|
2026-05-31 14:03:13 +08:00
|
|
|
if (gpu->length > 0) {
|
|
|
|
|
ffStrbufSetF(&key, "%s (%s - %s)", FF_CODEC_MODULE_NAME, direction, gpu->chars);
|
|
|
|
|
} else {
|
|
|
|
|
ffStrbufSetF(&key, "%s (%s)", FF_CODEC_MODULE_NAME, direction);
|
|
|
|
|
}
|
2026-05-31 13:16:24 +08:00
|
|
|
} else {
|
|
|
|
|
FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]){
|
|
|
|
|
FF_ARG(index, "index"),
|
2026-05-31 14:03:13 +08:00
|
|
|
FF_ARG(*gpu, "gpu"),
|
|
|
|
|
FF_ARG(direction, "direction"),
|
|
|
|
|
FF_ARG(options->moduleArgs.keyIcon, "icon"),
|
2026-05-31 13:16:24 +08:00
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->moduleArgs.outputFormat.length == 0) {
|
2026-05-31 14:03:13 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY typesJoined = ffStrbufCreate();
|
|
|
|
|
for (FFCodecType type = FF_CODEC_TYPE_UNKNOWN; type <= FF_CODEC_TYPE_MAX; type <<= 1) {
|
|
|
|
|
if ((types & type) == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (typesJoined.length > 0) {
|
|
|
|
|
ffStrbufAppendS(&typesJoined, ", ");
|
|
|
|
|
}
|
|
|
|
|
ffStrbufAppendS(&typesJoined, ffCodecTypeToString(type));
|
|
|
|
|
}
|
2026-05-31 13:16:24 +08:00
|
|
|
ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
|
2026-05-31 14:03:13 +08:00
|
|
|
puts(typesJoined.length ? typesJoined.chars : "None");
|
2026-05-31 13:16:24 +08:00
|
|
|
} else {
|
2026-05-31 14:03:13 +08:00
|
|
|
FF_LIST_AUTO_DESTROY typeList = ffListCreate(); // Use list instead of pre-joined string for qjs and lua
|
|
|
|
|
for (FFCodecType type = FF_CODEC_TYPE_UNKNOWN; type <= FF_CODEC_TYPE_MAX; type <<= 1) {
|
|
|
|
|
if ((types & type) == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, typeList), ffCodecTypeToString(type));
|
|
|
|
|
}
|
2026-05-31 13:16:24 +08:00
|
|
|
FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]){
|
2026-05-31 14:03:13 +08:00
|
|
|
FF_ARG(*gpu, "gpu"),
|
|
|
|
|
FF_ARG(direction, "direction"),
|
|
|
|
|
FF_ARG(typeList, "types"),
|
2026-05-31 19:08:30 +08:00
|
|
|
FF_ARG(platformApi, "platform-api"),
|
2026-05-31 13:16:24 +08:00
|
|
|
}));
|
|
|
|
|
// No need to destroy strings in lists, as they are static strings
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ffPrintCodec(FFCodecOptions* options) {
|
|
|
|
|
FF_LIST_AUTO_DESTROY result = ffListCreate();
|
2026-05-31 14:03:13 +08:00
|
|
|
const char* error = ffDetectCodec(options, &result);
|
2026-05-31 13:16:24 +08:00
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
ffPrintError(FF_CODEC_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (result.length == 0) {
|
|
|
|
|
ffPrintError(FF_CODEC_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No hardware video acceleration found");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 14:03:13 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
|
|
|
|
|
if (options->splitGPU) {
|
|
|
|
|
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);
|
2026-05-31 19:08:30 +08:00
|
|
|
printCodecLine(options, index, &item->gpu, "Encoder", item->encoders, item->platformApi);
|
|
|
|
|
printCodecLine(options, index, &item->gpu, "Decoder", item->decoders, item->platformApi);
|
2026-05-31 14:03:13 +08:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
FFCodecResult merged = {
|
|
|
|
|
.gpu = ffStrbufCreate(),
|
|
|
|
|
.decoders = FF_CODEC_TYPE_NONE,
|
|
|
|
|
.encoders = FF_CODEC_TYPE_NONE,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FF_LIST_FOR_EACH (FFCodecResult, item, result) {
|
|
|
|
|
merged.decoders |= item->decoders;
|
|
|
|
|
merged.encoders |= item->encoders;
|
2026-05-31 19:08:30 +08:00
|
|
|
merged.platformApi = item->platformApi;
|
2026-05-31 14:03:13 +08:00
|
|
|
}
|
2026-05-31 19:08:30 +08:00
|
|
|
printCodecLine(options, 0, &merged.gpu, "Encoder", merged.encoders, merged.platformApi);
|
|
|
|
|
printCodecLine(options, 0, &merged.gpu, "Decoder", merged.decoders, merged.platformApi);
|
2026-05-31 13:16:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-31 14:03:13 +08:00
|
|
|
FF_LIST_FOR_EACH (FFCodecResult, item, result) {
|
|
|
|
|
ffStrbufDestroy(&item->gpu);
|
|
|
|
|
}
|
2026-05-31 13:16:24 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ffParseCodecJsonObject(FFCodecOptions* options, yyjson_val* module) {
|
|
|
|
|
yyjson_val *key, *val;
|
|
|
|
|
size_t idx, max;
|
|
|
|
|
yyjson_obj_foreach (module, idx, max, key, val) {
|
|
|
|
|
if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 14:03:13 +08:00
|
|
|
if (unsafe_yyjson_equals_str(key, "splitGPU")) {
|
|
|
|
|
options->splitGPU = yyjson_get_bool(val);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 23:31:45 +08:00
|
|
|
if (unsafe_yyjson_equals_str(key, "useVulkan")) {
|
|
|
|
|
options->useVulkan = yyjson_get_bool(val);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-31 13:16:24 +08:00
|
|
|
ffPrintError(FF_CODEC_MODULE_NAME, 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ffGenerateCodecJsonConfig(FFCodecOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
|
|
|
|
|
ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
|
2026-05-31 14:03:13 +08:00
|
|
|
yyjson_mut_obj_add_bool(doc, module, "splitGPU", options->splitGPU);
|
2026-05-31 23:31:45 +08:00
|
|
|
yyjson_mut_obj_add_bool(doc, module, "useVulkan", options->useVulkan);
|
2026-05-31 13:16:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-31 23:31:45 +08:00
|
|
|
bool ffGenerateCodecJsonResult(FFCodecOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
|
2026-05-31 13:16:24 +08:00
|
|
|
FF_LIST_AUTO_DESTROY result = ffListCreate();
|
2026-05-31 23:31:45 +08:00
|
|
|
const char* error = ffDetectCodec(options, &result);
|
2026-05-31 13:16:24 +08:00
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
|
yyjson_mut_obj_add_str(doc, module, "error", error);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
|
|
|
|
|
|
|
|
|
|
FF_LIST_FOR_EACH (FFCodecResult, item, result) {
|
|
|
|
|
yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
|
|
|
|
|
yyjson_mut_obj_add_strbuf(doc, obj, "gpu", &item->gpu);
|
|
|
|
|
|
|
|
|
|
yyjson_mut_val* encoders = yyjson_mut_obj_add_arr(doc, obj, "encoders");
|
2026-05-31 14:03:13 +08:00
|
|
|
yyjson_mut_val* decoders = yyjson_mut_obj_add_arr(doc, obj, "decoders");
|
2026-05-31 13:16:24 +08:00
|
|
|
|
|
|
|
|
for (FFCodecType type = FF_CODEC_TYPE_UNKNOWN; type <= FF_CODEC_TYPE_MAX; type <<= 1) {
|
2026-05-31 14:03:13 +08:00
|
|
|
if (item->encoders & type) {
|
2026-05-31 13:16:24 +08:00
|
|
|
yyjson_mut_arr_add_str(doc, encoders, ffCodecTypeToString(type));
|
|
|
|
|
}
|
2026-05-31 14:03:13 +08:00
|
|
|
if (item->decoders & type) {
|
|
|
|
|
yyjson_mut_arr_add_str(doc, decoders, ffCodecTypeToString(type));
|
|
|
|
|
}
|
2026-05-31 13:16:24 +08:00
|
|
|
}
|
2026-05-31 19:08:30 +08:00
|
|
|
yyjson_mut_obj_add_str(doc, obj, "platformApi", item->platformApi);
|
2026-05-31 13:16:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-31 14:03:13 +08:00
|
|
|
FF_LIST_FOR_EACH (FFCodecResult, item, result) {
|
|
|
|
|
ffStrbufDestroy(&item->gpu);
|
|
|
|
|
}
|
2026-05-31 13:16:24 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ffInitCodecOptions(FFCodecOptions* options) {
|
2026-05-31 14:03:13 +08:00
|
|
|
ffOptionInitModuleArg(&options->moduleArgs, "");
|
|
|
|
|
options->splitGPU = false;
|
2026-05-31 23:31:45 +08:00
|
|
|
options->useVulkan = false;
|
2026-05-31 13:16:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ffDestroyCodecOptions(FFCodecOptions* options) {
|
|
|
|
|
ffOptionDestroyModuleArg(&options->moduleArgs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FFModuleBaseInfo ffCodecModuleInfo = {
|
|
|
|
|
.name = FF_CODEC_MODULE_NAME,
|
|
|
|
|
.description = "Print hardware video acceleration codec types (decode / encode)",
|
|
|
|
|
.initOptions = (void*) ffInitCodecOptions,
|
|
|
|
|
.destroyOptions = (void*) ffDestroyCodecOptions,
|
|
|
|
|
.parseJsonObject = (void*) ffParseCodecJsonObject,
|
|
|
|
|
.printModule = (void*) ffPrintCodec,
|
|
|
|
|
.generateJsonResult = (void*) ffGenerateCodecJsonResult,
|
|
|
|
|
.generateJsonConfig = (void*) ffGenerateCodecJsonConfig,
|
|
|
|
|
.formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]){
|
|
|
|
|
{ "GPU name", "gpu" },
|
2026-05-31 14:03:13 +08:00
|
|
|
{ "Decoder / Encoder", "direction" },
|
|
|
|
|
{ "Compatibility alias of codec types", "types" },
|
2026-05-31 19:08:30 +08:00
|
|
|
{ "Platform API used for detection", "platform-api" },
|
2026-05-31 13:16:24 +08:00
|
|
|
}))
|
|
|
|
|
};
|