master ohmu/fastfetch-openbsd / src / modules / codec / codec.c
ohmu  ·  2026-10-15
  1#include "common/printing.h"
  2#include "common/jsonconfig.h"
  3#include "detection/codec/codec.h"
  4#include "modules/codec/codec.h"
  5
  6static const char* ffCodecTypeToString(FFCodecType type) {
  7    switch (type) {
  8        case FF_CODEC_TYPE_H261:
  9            return "H.261";
 10        case FF_CODEC_TYPE_H263:
 11            return "H.263";
 12        case FF_CODEC_TYPE_MJPEG:
 13            return "MJPEG";
 14        case FF_CODEC_TYPE_MPEG1:
 15            return "MPEG-1";
 16        case FF_CODEC_TYPE_MPEG2:
 17            return "MPEG-2";
 18        case FF_CODEC_TYPE_DIVX_XVID:
 19            return "DivX / Xvid";
 20        case FF_CODEC_TYPE_H264:
 21            return "H.264";
 22        case FF_CODEC_TYPE_WMV8:
 23            return "WMV-8";
 24        case FF_CODEC_TYPE_WMV9:
 25            return "WMV-9";
 26        case FF_CODEC_TYPE_VC1:
 27            return "VC-1";
 28        case FF_CODEC_TYPE_VP8:
 29            return "VP8";
 30        case FF_CODEC_TYPE_HEVC:
 31            return "HEVC / H.265";
 32        case FF_CODEC_TYPE_VP9:
 33            return "VP9";
 34        case FF_CODEC_TYPE_AV1:
 35            return "AV1";
 36        case FF_CODEC_TYPE_VVC:
 37            return "VVC / H.266";
 38        case FF_CODEC_TYPE_DOLBY_VISION_HEVC:
 39            return "Dolby Vision (HEVC)";
 40        case FF_CODEC_TYPE_PRORES:
 41            return "Apple ProRes";
 42        case FF_CODEC_TYPE_PRORES_RAW:
 43            return "Apple ProRes RAW";
 44        case FF_CODEC_TYPE_UNKNOWN:
 45        default:
 46            return "Unknown";
 47    }
 48}
 49
 50static void printCodecLine(const FFCodecOptions* options, uint8_t index, FFstrbuf* gpu, const char* direction, FFCodecType types, const char* platformApi) {
 51    FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
 52    if (options->moduleArgs.key.length == 0) {
 53        if (gpu->length > 0) {
 54            ffStrbufSetF(&key, "%s (%s - %s)", FF_MODULE_GET_DISPLAY_NAME(Codec), direction, gpu->chars);
 55        } else {
 56            ffStrbufSetF(&key, "%s (%s)", FF_MODULE_GET_DISPLAY_NAME(Codec), direction);
 57        }
 58    } else {
 59        FF_PARSE_FORMAT_STRING_CHECKED(&key, &options->moduleArgs.key, ((FFformatarg[]){
 60                                                                           FF_ARG(index, "index"),
 61                                                                           FF_ARG(*gpu, "gpu"),
 62                                                                           FF_ARG(direction, "direction"),
 63                                                                           FF_ARG(options->moduleArgs.keyIcon, "icon"),
 64                                                                           FF_ARG(FF_MODULE_GET_DISPLAY_NAME(Codec), "module-name"),
 65                                                                       }));
 66    }
 67
 68    if (options->moduleArgs.outputFormat.length == 0) {
 69        FF_STRBUF_AUTO_DESTROY typesJoined = ffStrbufCreate();
 70        for (FFCodecType type = FF_CODEC_TYPE_H261; type <= FF_CODEC_TYPE_MAX; type <<= 1) {
 71            if ((types & type) == 0) {
 72                continue;
 73            }
 74            if (typesJoined.length > 0) {
 75                ffStrbufAppendS(&typesJoined, ", ");
 76            }
 77            ffStrbufAppendS(&typesJoined, ffCodecTypeToString(type));
 78        }
 79        ffPrintLogoAndKey(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY);
 80        puts(typesJoined.length ? typesJoined.chars : "None");
 81    } else {
 82        FF_LIST_AUTO_DESTROY typeList = ffListCreate(); // Use list instead of pre-joined string for lua
 83        for (FFCodecType type = FF_CODEC_TYPE_H261; type <= FF_CODEC_TYPE_MAX; type <<= 1) {
 84            if ((types & type) == 0) {
 85                continue;
 86            }
 87            ffStrbufInitStatic(FF_LIST_ADD(FFstrbuf, typeList), ffCodecTypeToString(type));
 88        }
 89        FF_PRINT_FORMAT_CHECKED(key.chars, 0, &options->moduleArgs, FF_PRINT_TYPE_NO_CUSTOM_KEY, ((FFformatarg[]){
 90                                                                                                     FF_ARG(*gpu, "gpu"),
 91                                                                                                     FF_ARG(direction, "direction"),
 92                                                                                                     FF_ARG(typeList, "types"),
 93                                                                                                     FF_ARG(platformApi, "platform-api"),
 94                                                                                                 }));
 95        // No need to destroy strings in lists, as they are static strings
 96    }
 97}
 98
 99bool ffPrintCodec(FFCodecOptions* options) {
100    FF_LIST_AUTO_DESTROY result = ffListCreate();
101    const char* error = ffDetectCodec(options, &result);
102
103    if (error) {
104        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "%s", error);
105        return false;
106    }
107
108    if (result.length == 0) {
109        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "No hardware video acceleration found");
110        return false;
111    }
112
113    FF_STRBUF_AUTO_DESTROY key = ffStrbufCreate();
114    if (options->splitGPU) {
115        for (uint32_t i = 0; i < result.length; ++i) {
116            FFCodecResult* item = FF_LIST_GET(FFCodecResult, result, i);
117            uint8_t index = (uint8_t) (result.length == 1 ? 0 : i + 1);
118            if (options->showType & FF_CODEC_SHOW_TYPE_ENCODER) {
119                printCodecLine(options, index, &item->gpu, "Encoder", item->encoders, item->platformApi);
120            }
121            if (options->showType & FF_CODEC_SHOW_TYPE_DECODER) {
122                printCodecLine(options, index, &item->gpu, "Decoder", item->decoders, item->platformApi);
123            }
124        }
125    } else {
126        FFCodecResult merged = {
127            .gpu = ffStrbufCreate(),
128            .decoders = FF_CODEC_TYPE_NONE,
129            .encoders = FF_CODEC_TYPE_NONE,
130        };
131
132        FF_LIST_FOR_EACH (FFCodecResult, item, result) {
133            merged.decoders |= item->decoders;
134            merged.encoders |= item->encoders;
135            merged.platformApi = item->platformApi;
136        }
137        if (options->showType & FF_CODEC_SHOW_TYPE_ENCODER) {
138            printCodecLine(options, 0, &merged.gpu, "Encoder", merged.encoders, merged.platformApi);
139        }
140        if (options->showType & FF_CODEC_SHOW_TYPE_DECODER) {
141            printCodecLine(options, 0, &merged.gpu, "Decoder", merged.decoders, merged.platformApi);
142        }
143    }
144
145    FF_LIST_FOR_EACH (FFCodecResult, item, result) {
146        ffStrbufDestroy(&item->gpu);
147    }
148    return true;
149}
150
151void ffParseCodecJsonObject(FFCodecOptions* options, yyjson_val* module) {
152    yyjson_val *key, *val;
153    size_t idx, max;
154    yyjson_obj_foreach (module, idx, max, key, val) {
155        if (ffJsonConfigParseModuleArgs(key, val, &options->moduleArgs)) {
156            continue;
157        }
158
159        if (unsafe_yyjson_equals_str(key, "splitGPU")) {
160            options->splitGPU = yyjson_get_bool(val);
161            continue;
162        }
163
164        if (unsafe_yyjson_equals_str(key, "useVulkan")) {
165            options->useVulkan = yyjson_get_bool(val);
166            continue;
167        }
168
169        if (unsafe_yyjson_equals_str(key, "showType")) {
170            int value;
171            const char* error = ffJsonConfigParseEnum(val, &value, (FFKeyValuePair[]){
172                                                                       { "encoder", FF_CODEC_SHOW_TYPE_ENCODER },
173                                                                       { "decoder", FF_CODEC_SHOW_TYPE_DECODER },
174                                                                       { "both", FF_CODEC_SHOW_TYPE_BOTH },
175                                                                       {},
176                                                                   });
177            if (error) {
178                ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, nullptr, FF_PRINT_TYPE_NO_CUSTOM_KEY, "Invalid %s value: %s", unsafe_yyjson_get_str(key), error);
179            } else {
180                options->showType = (FFCodecShowType) value;
181            }
182            continue;
183        }
184
185        ffPrintError(FF_MODULE_GET_DISPLAY_NAME(Codec), 0, &options->moduleArgs, FF_PRINT_TYPE_DEFAULT, "Unknown JSON key %s", unsafe_yyjson_get_str(key));
186    }
187}
188
189[[gnu::cold]]
190void ffGenerateCodecJsonConfig(FFCodecOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
191    ffJsonConfigGenerateModuleArgsConfig(doc, module, &options->moduleArgs);
192    yyjson_mut_obj_add_bool(doc, module, "splitGPU", options->splitGPU);
193    yyjson_mut_obj_add_bool(doc, module, "useVulkan", options->useVulkan);
194    switch (options->showType) {
195        case FF_CODEC_SHOW_TYPE_ENCODER:
196            yyjson_mut_obj_add_str(doc, module, "showType", "encoder");
197            break;
198        case FF_CODEC_SHOW_TYPE_DECODER:
199            yyjson_mut_obj_add_str(doc, module, "showType", "decoder");
200            break;
201        case FF_CODEC_SHOW_TYPE_BOTH:
202            yyjson_mut_obj_add_str(doc, module, "showType", "both");
203            break;
204        default:
205            break;
206    }
207}
208
209bool ffGenerateCodecJsonResult(FFCodecOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module) {
210    FF_LIST_AUTO_DESTROY result = ffListCreate();
211    const char* error = ffDetectCodec(options, &result);
212
213    if (error) {
214        yyjson_mut_obj_add_str(doc, module, "error", error);
215        return false;
216    }
217
218    yyjson_mut_val* arr = yyjson_mut_obj_add_arr(doc, module, "result");
219
220    FF_LIST_FOR_EACH (FFCodecResult, item, result) {
221        yyjson_mut_val* obj = yyjson_mut_arr_add_obj(doc, arr);
222        yyjson_mut_obj_add_strbuf(doc, obj, "gpu", &item->gpu);
223
224        yyjson_mut_val* encoders = yyjson_mut_obj_add_arr(doc, obj, "encoders");
225        yyjson_mut_val* decoders = yyjson_mut_obj_add_arr(doc, obj, "decoders");
226
227        for (FFCodecType type = FF_CODEC_TYPE_UNKNOWN; type <= FF_CODEC_TYPE_MAX; type <<= 1) {
228            if (item->encoders & type) {
229                yyjson_mut_arr_add_str(doc, encoders, ffCodecTypeToString(type));
230            }
231            if (item->decoders & type) {
232                yyjson_mut_arr_add_str(doc, decoders, ffCodecTypeToString(type));
233            }
234        }
235        yyjson_mut_obj_add_str(doc, obj, "platformApi", item->platformApi);
236    }
237
238    FF_LIST_FOR_EACH (FFCodecResult, item, result) {
239        ffStrbufDestroy(&item->gpu);
240    }
241    return true;
242}
243
244void ffInitCodecOptions(FFCodecOptions* options) {
245    ffOptionInitModuleArg(&options->moduleArgs, "󰈫");
246    options->splitGPU = false;
247    options->useVulkan = false;
248    options->showType = FF_CODEC_SHOW_TYPE_BOTH;
249}
250
251void ffDestroyCodecOptions(FFCodecOptions* options) {
252    ffOptionDestroyModuleArg(&options->moduleArgs);
253}
254
255FFModuleBaseInfo ffCodecModuleInfo = {
256    .name = "Codec",
257    .description = "Print hardware video acceleration codec types (decode / encode)",
258    .displayName = {
259        .en = "Codec",
260        .ar = "الترميز",
261        .cs = "Kodek",
262        .de = "Codec",
263        .es = "Códec",
264        .fi = "Koodekki",
265        .fr = "Codec",
266        .gl = "Códec",
267        .he = "קודק",
268        .id = "Kodek",
269        .it = "Codec",
270        .ja = "コーデック",
271        .ko = "코덱",
272        .pl = "Kodek",
273        .pt = "Codec",
274        .ru = "Кодек",
275        .tr = "Kodek",
276        .uk = "Кодек",
277        .vi = "Codec",
278        .zh_CN = "编解码器",
279        .zh_TW = "編解碼器",
280    },
281    .initOptions = (void*) ffInitCodecOptions,
282    .destroyOptions = (void*) ffDestroyCodecOptions,
283    .parseJsonObject = (void*) ffParseCodecJsonObject,
284    .printModule = (void*) ffPrintCodec,
285    .generateJsonResult = (void*) ffGenerateCodecJsonResult,
286    .generateJsonConfig = (void*) ffGenerateCodecJsonConfig,
287    .formatArgs = FF_FORMAT_ARG_LIST(((FFModuleFormatArg[]){
288        { "GPU name *", "gpu" },
289        { "Decoder / Encoder *", "direction" },
290        { "Compatibility alias of codec types", "types" },
291        { "Platform API used for detection", "platform-api" },
292    })),
293    .defaultOrder = 38,
294};