summaryrefslogtreecommitdiffstats
path: root/src/modules/codec
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules/codec')
-rw-r--r--src/modules/codec/codec.c268
-rw-r--r--src/modules/codec/codec.h11
-rw-r--r--src/modules/codec/option.h20
3 files changed, 299 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" },
+ }))
+};
diff --git a/src/modules/codec/codec.h b/src/modules/codec/codec.h
new file mode 100644
index 0000000..efd3d7c
--- /dev/null
+++ b/src/modules/codec/codec.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "option.h"
+
+#define FF_CODEC_MODULE_NAME "Codec"
+
+bool ffPrintCodec(FFCodecOptions* options);
+void ffInitCodecOptions(FFCodecOptions* options);
+void ffDestroyCodecOptions(FFCodecOptions* options);
+
+extern FFModuleBaseInfo ffCodecModuleInfo;
diff --git a/src/modules/codec/option.h b/src/modules/codec/option.h
new file mode 100644
index 0000000..bcc9201
--- /dev/null
+++ b/src/modules/codec/option.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "common/option.h"
+
+typedef enum FF_A_PACKED FFCodecShowType {
+ FF_CODEC_SHOW_TYPE_NONE = 0,
+ FF_CODEC_SHOW_TYPE_ENCODER = 1 << 0,
+ FF_CODEC_SHOW_TYPE_DECODER = 1 << 1,
+ FF_CODEC_SHOW_TYPE_BOTH = FF_CODEC_SHOW_TYPE_ENCODER | FF_CODEC_SHOW_TYPE_DECODER,
+ FF_CODEC_SHOW_TYPE_FORCE_UNSIGNED = UINT8_MAX,
+} FFCodecShowType;
+
+typedef struct FFCodecOptions {
+ FFModuleArgs moduleArgs;
+ bool splitGPU;
+ bool useVulkan;
+ FFCodecShowType showType;
+} FFCodecOptions;
+
+static_assert(sizeof(FFCodecOptions) <= FF_OPTION_MAX_SIZE, "FFCodecOptions size exceeds maximum allowed size");