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};