diff options
| author | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
|---|---|---|
| committer | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
| commit | 76424950e373d3b04ac3dd13019151bfba3e8423 (patch) | |
| tree | 4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/modules/codec/codec.c | |
Add the files
Diffstat (limited to 'src/modules/codec/codec.c')
| -rw-r--r-- | src/modules/codec/codec.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/modules/codec/codec.c b/src/modules/codec/codec.c new file mode 100644 index 0000000..4e6889e --- /dev/null +++ b/src/modules/codec/codec.c @@ -0,0 +1,268 @@ +#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_UNKNOWN: + default: + return "Unknown"; + } +} + +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) { + ffStrbufSetF(&key, "%s (%s - %s)", FF_CODEC_MODULE_NAME, direction, gpu->chars); + } else { + ffStrbufSetF(&key, "%s (%s)", FF_CODEC_MODULE_NAME, direction); + } + } else { + FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]){ + FF_ARG(index, "index"), + FF_ARG(*gpu, "gpu"), + FF_ARG(direction, "direction"), + FF_ARG(options->moduleArgs.keyIcon, "icon"), + })); + } + + if (options->moduleArgs.outputFormat.length == 0) { + FF_STRBUF_AUTO_DESTROY typesJoined = ffStrbufCreate(); + for (FFCodecType type = FF_CODEC_TYPE_H261; type <= FF_CODEC_TYPE_MAX; type <<= 1) { + if ((types & type) == 0) { + continue; + } + if (typesJoined.length > 0) { + ffStrbufAppendS(&typesJoined, ", "); + } + ffStrbufAppendS(&typesJoined, ffCodecTypeToString(type)); + } + ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY); + puts(typesJoined.length ? typesJoined.chars : "None"); + } else { + FF_LIST_AUTO_DESTROY typeList = ffListCreate(); // Use list instead of pre-joined string for qjs and lua + for (FFCodecType type = FF_CODEC_TYPE_H261; type <= FF_CODEC_TYPE_MAX; type <<= 1) { + if ((types & type) == 0) { + continue; + } + ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, typeList), ffCodecTypeToString(type)); + } + FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]){ + 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 + } +} + +bool ffPrintCodec(FFCodecOptions* options) { + FF_LIST_AUTO_DESTROY result = ffListCreate(); + const char* error = ffDetectCodec(options, &result); + + 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; + } + + 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); + if (options->showType & FF_CODEC_SHOW_TYPE_ENCODER) { + printCodecLine(options, index, &item->gpu, "Encoder", item->encoders, item->platformApi); + } + if (options->showType & FF_CODEC_SHOW_TYPE_DECODER) { + printCodecLine(options, index, &item->gpu, "Decoder", item->decoders, item->platformApi); + } + } + } 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; + merged.platformApi = item->platformApi; + } + if (options->showType & FF_CODEC_SHOW_TYPE_ENCODER) { + printCodecLine(options, 0, &merged.gpu, "Encoder", merged.encoders, merged.platformApi); + } + if (options->showType & FF_CODEC_SHOW_TYPE_DECODER) { + printCodecLine(options, 0, &merged.gpu, "Decoder", merged.decoders, merged.platformApi); + } + } + + FF_LIST_FOR_EACH (FFCodecResult, item, result) { + ffStrbufDestroy(&item->gpu); + } + 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; + } + + if (unsafe_yyjson_equals_str(key, "splitGPU")) { + options->splitGPU = yyjson_get_bool(val); + continue; + } + + if (unsafe_yyjson_equals_str(key, "useVulkan")) { + options->useVulkan = yyjson_get_bool(val); + continue; + } + + if (unsafe_yyjson_equals_str(key, "showType")) { + int value; + const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]){ + { "encoder", FF_CODEC_SHOW_TYPE_ENCODER }, + { "decoder", FF_CODEC_SHOW_TYPE_DECODER }, + { "both", FF_CODEC_SHOW_TYPE_BOTH }, + {}, + }); + if (error) { + ffPrintError(FF_CODEC_MODULE_NAME, 0, NULL, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error); + } else { + options->showType = (FFCodecShowType) value; + } + continue; + } + + 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); + yyjson_mut_obj_add_bool(doc, module, "splitGPU", options->splitGPU); + yyjson_mut_obj_add_bool(doc, module, "useVulkan", options->useVulkan); + switch (options->showType) { + case FF_CODEC_SHOW_TYPE_ENCODER: + yyjson_mut_obj_add_str(doc, module, "showType", "encoder"); + break; + case FF_CODEC_SHOW_TYPE_DECODER: + yyjson_mut_obj_add_str(doc, module, "showType", "decoder"); + break; + case FF_CODEC_SHOW_TYPE_BOTH: + yyjson_mut_obj_add_str(doc, module, "showType", "both"); + break; + default: + break; + } +} + +bool ffGenerateCodecJsonResult(FFCodecOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) { + FF_LIST_AUTO_DESTROY result = ffListCreate(); + const char* error = ffDetectCodec(options, &result); + + 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"); + yyjson_mut_val* decoders = yyjson_mut_obj_add_arr(doc, obj, "decoders"); + + for (FFCodecType type = FF_CODEC_TYPE_UNKNOWN; type <= FF_CODEC_TYPE_MAX; type <<= 1) { + if (item->encoders & type) { + yyjson_mut_arr_add_str(doc, encoders, ffCodecTypeToString(type)); + } + if (item->decoders & type) { + 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) { + ffStrbufDestroy(&item->gpu); + } + return true; +} + +void ffInitCodecOptions(FFCodecOptions* options) { + ffOptionInitModuleArg(&options->moduleArgs, ""); + options->splitGPU = false; + options->useVulkan = false; + options->showType = FF_CODEC_SHOW_TYPE_BOTH; +} + +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" }, + { "Decoder / Encoder", "direction" }, + { "Compatibility alias of codec types", "types" }, + { "Platform API used for detection", "platform-api" }, + })) +}; |