FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/avtextformat.c
Date: 2026-06-16 12:54:33
Exec Total Coverage
Lines: 216 367 58.9%
Functions: 17 25 68.0%
Branches: 128 254 50.4%

Line Branch Exec Source
1 /*
2 * Copyright (c) The FFmpeg developers
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <limits.h>
22 #include <stdarg.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <string.h>
26
27 #include "libavutil/mem.h"
28 #include "libavutil/avassert.h"
29 #include "libavutil/base64.h"
30 #include "libavutil/bprint.h"
31 #include "libavutil/error.h"
32 #include "libavutil/hash.h"
33 #include "libavutil/intreadwrite.h"
34 #include "libavutil/macros.h"
35 #include "libavutil/opt.h"
36 #include "avtextformat.h"
37
38 #define SECTION_ID_NONE (-1)
39
40 #define SHOW_OPTIONAL_FIELDS_AUTO (-1)
41 #define SHOW_OPTIONAL_FIELDS_NEVER 0
42 #define SHOW_OPTIONAL_FIELDS_ALWAYS 1
43
44 static const struct {
45 double bin_val;
46 double dec_val;
47 char bin_str[4];
48 char dec_str[4];
49 } si_prefixes[] = {
50 { 1.0, 1.0, "", "" },
51 { 1.024e3, 1e3, "Ki", "K" },
52 { 1.048576e6, 1e6, "Mi", "M" },
53 { 1.073741824e9, 1e9, "Gi", "G" },
54 { 1.099511627776e12, 1e12, "Ti", "T" },
55 { 1.125899906842624e15, 1e15, "Pi", "P" },
56 };
57
58 static const char *textcontext_get_formatter_name(void *p)
59 {
60 AVTextFormatContext *tctx = p;
61 return tctx->formatter->name;
62 }
63
64 #define OFFSET(x) offsetof(AVTextFormatContext, x)
65
66 static const AVOption textcontext_options[] = {
67 { "string_validation", "set string validation mode",
68 OFFSET(string_validation), AV_OPT_TYPE_INT, { .i64 = AV_TEXTFORMAT_STRING_VALIDATION_REPLACE }, 0, AV_TEXTFORMAT_STRING_VALIDATION_NB - 1, .unit = "sv" },
69 { "sv", "set string validation mode",
70 OFFSET(string_validation), AV_OPT_TYPE_INT, { .i64 = AV_TEXTFORMAT_STRING_VALIDATION_REPLACE }, 0, AV_TEXTFORMAT_STRING_VALIDATION_NB - 1, .unit = "sv" },
71 { "ignore", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_TEXTFORMAT_STRING_VALIDATION_IGNORE }, .unit = "sv" },
72 { "replace", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_TEXTFORMAT_STRING_VALIDATION_REPLACE }, .unit = "sv" },
73 { "fail", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = AV_TEXTFORMAT_STRING_VALIDATION_FAIL }, .unit = "sv" },
74 { "string_validation_replacement", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, { .str = "" } },
75 { "svr", "set string validation replacement string", OFFSET(string_validation_replacement), AV_OPT_TYPE_STRING, { .str = "\xEF\xBF\xBD" } },
76 { NULL }
77 };
78
79 55 static void *textcontext_child_next(void *obj, void *prev)
80 {
81 55 AVTextFormatContext *ctx = obj;
82
4/8
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 55 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 55 times.
✗ Branch 7 not taken.
55 if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv)
83 55 return ctx->priv;
84 return NULL;
85 }
86
87 static const AVClass textcontext_class = {
88 .class_name = "AVTextContext",
89 .item_name = textcontext_get_formatter_name,
90 .option = textcontext_options,
91 .version = LIBAVUTIL_VERSION_INT,
92 .child_next = textcontext_child_next,
93 };
94
95 static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size)
96 {
97 av_bprintf(bp, "0X");
98 for (unsigned i = 0; i < ubuf_size; i++)
99 av_bprintf(bp, "%02X", ubuf[i]);
100 }
101
102 234 int avtext_context_close(AVTextFormatContext **ptctx)
103 {
104 234 AVTextFormatContext *tctx = *ptctx;
105 234 int ret = 0;
106
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (!tctx)
108 return AVERROR(EINVAL);
109
110 234 av_hash_freep(&tctx->hash);
111
112
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (tctx->formatter) {
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (tctx->formatter->uninit)
114 ret = tctx->formatter->uninit(tctx);
115
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (tctx->formatter->priv_class)
116 234 av_opt_free(tctx->priv);
117 }
118
2/2
✓ Branch 0 taken 2808 times.
✓ Branch 1 taken 234 times.
3042 for (int i = 0; i < SECTION_MAX_NB_LEVELS; i++)
119 2808 av_bprint_finalize(&tctx->section_pbuf[i], NULL);
120 234 av_freep(&tctx->priv);
121 234 av_opt_free(tctx);
122 234 av_freep(ptctx);
123 234 return ret;
124 }
125
126
127 234 int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer_context, const char *args,
128 const AVTextFormatSection *sections, int nb_sections, AVTextFormatOptions options, char *show_data_hash)
129 {
130 AVTextFormatContext *tctx;
131 234 int ret = 0;
132
133
2/4
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 234 times.
234 av_assert0(ptctx && formatter);
134
135
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 234 times.
234 if (!(tctx = av_mallocz(sizeof(AVTextFormatContext)))) {
136 ret = AVERROR(ENOMEM);
137 goto fail;
138 }
139
140
2/2
✓ Branch 0 taken 2808 times.
✓ Branch 1 taken 234 times.
3042 for (int i = 0; i < SECTION_MAX_NB_LEVELS; i++)
141 2808 av_bprint_init(&tctx->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
142
143 234 tctx->class = &textcontext_class;
144 234 av_opt_set_defaults(tctx);
145
146
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 234 times.
234 if (!(tctx->priv = av_mallocz(formatter->priv_size))) {
147 ret = AVERROR(ENOMEM);
148 goto fail;
149 }
150
151 234 tctx->opts = options;
152
153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (nb_sections > SECTION_MAX_NB_SECTIONS) {
154 av_log(tctx, AV_LOG_ERROR, "The number of section definitions (%d) is larger than the maximum allowed (%d)\n", nb_sections, SECTION_MAX_NB_SECTIONS);
155 ret = AVERROR(EINVAL);
156 goto fail;
157 }
158
159 234 tctx->formatter = formatter;
160 234 tctx->level = -1;
161 234 tctx->sections = sections;
162 234 tctx->nb_sections = nb_sections;
163 234 tctx->writer = writer_context;
164
165
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (formatter->priv_class) {
166 234 void *priv_ctx = tctx->priv;
167 234 *(const AVClass **)priv_ctx = formatter->priv_class;
168 234 av_opt_set_defaults(priv_ctx);
169 }
170
171 /* convert options to dictionary */
172
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 193 times.
234 if (args) {
173 41 AVDictionary *opts = NULL;
174 41 const AVDictionaryEntry *opt = NULL;
175
176
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 41 times.
41 if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) {
177 av_log(tctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to textformat context\n", args);
178 av_dict_free(&opts);
179 goto fail;
180 }
181
182
2/2
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 41 times.
96 while ((opt = av_dict_iterate(opts, opt))) {
183
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
55 if ((ret = av_opt_set(tctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) {
184 av_log(tctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to textformat context\n",
185 opt->key, opt->value);
186 av_dict_free(&opts);
187 goto fail;
188 }
189 }
190
191 41 av_dict_free(&opts);
192 }
193
194
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 220 times.
234 if (show_data_hash) {
195
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if ((ret = av_hash_alloc(&tctx->hash, show_data_hash)) < 0) {
196 if (ret == AVERROR(EINVAL)) {
197 const char *n;
198 av_log(NULL, AV_LOG_ERROR, "Unknown hash algorithm '%s'\nKnown algorithms:", show_data_hash);
199 for (unsigned i = 0; (n = av_hash_names(i)); i++)
200 av_log(NULL, AV_LOG_ERROR, " %s", n);
201 av_log(NULL, AV_LOG_ERROR, "\n");
202 }
203 goto fail;
204 }
205 }
206
207 /* validate replace string */
208 {
209 234 const uint8_t *p = (uint8_t *)tctx->string_validation_replacement;
210 234 const uint8_t *endp = p + strlen((const char *)p);
211
2/2
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 234 times.
468 while (*p) {
212 234 const uint8_t *p0 = p;
213 int32_t code;
214 234 ret = av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags);
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (ret < 0) {
216 AVBPrint bp;
217 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
218 bprint_bytes(&bp, p0, p - p0);
219 av_log(tctx, AV_LOG_ERROR,
220 "Invalid UTF8 sequence %s found in string validation replace '%s'\n",
221 bp.str, tctx->string_validation_replacement);
222 goto fail;
223 }
224 }
225 }
226
227
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 181 times.
234 if (tctx->formatter->init)
228 53 ret = tctx->formatter->init(tctx);
229
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (ret < 0)
230 goto fail;
231
232 234 *ptctx = tctx;
233
234 234 return 0;
235
236 fail:
237 avtext_context_close(&tctx);
238 return ret;
239 }
240
241 static const char unit_second_str[] = "s";
242
243 14627 void avtext_print_section_header(AVTextFormatContext *tctx, const void *data, int section_id)
244 {
245
2/4
✓ Branch 0 taken 14627 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14627 times.
14627 if (section_id < 0 || section_id >= tctx->nb_sections) {
246 av_log(tctx, AV_LOG_ERROR, "Invalid section_id for section_header: %d\n", section_id);
247 return;
248 }
249
250 14627 tctx->level++;
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14627 times.
14627 av_assert0(tctx->level < SECTION_MAX_NB_LEVELS);
252
253 14627 tctx->nb_item[tctx->level] = 0;
254 14627 memset(tctx->nb_item_type[tctx->level], 0, sizeof(tctx->nb_item_type[tctx->level]));
255 14627 tctx->section[tctx->level] = &tctx->sections[section_id];
256
257
1/2
✓ Branch 0 taken 14627 times.
✗ Branch 1 not taken.
14627 if (tctx->formatter->print_section_header)
258 14627 tctx->formatter->print_section_header(tctx, data);
259 }
260
261 14627 void avtext_print_section_footer(AVTextFormatContext *tctx)
262 {
263
2/4
✓ Branch 0 taken 14627 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 14627 times.
14627 if (tctx->level < 0 || tctx->level >= SECTION_MAX_NB_LEVELS) {
264 av_log(tctx, AV_LOG_ERROR, "Invalid level for section_footer: %d\n", tctx->level);
265 return;
266 }
267
268 14627 int section_id = tctx->section[tctx->level]->id;
269 29254 int parent_section_id = tctx->level ?
270
2/2
✓ Branch 0 taken 14393 times.
✓ Branch 1 taken 234 times.
14627 tctx->section[tctx->level - 1]->id : SECTION_ID_NONE;
271
272
2/2
✓ Branch 0 taken 14393 times.
✓ Branch 1 taken 234 times.
14627 if (parent_section_id != SECTION_ID_NONE) {
273 14393 tctx->nb_item[tctx->level - 1]++;
274 14393 tctx->nb_item_type[tctx->level - 1][section_id]++;
275 }
276
277
2/2
✓ Branch 0 taken 14537 times.
✓ Branch 1 taken 90 times.
14627 if (tctx->formatter->print_section_footer)
278 14537 tctx->formatter->print_section_footer(tctx);
279 14627 tctx->level--;
280 }
281
282 65227 void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t val, int flags)
283 {
284
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65227 times.
65227 av_assert0(tctx);
285
286
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65227 times.
65227 if (tctx->opts.show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER)
287 return;
288
289
1/2
✓ Branch 0 taken 65227 times.
✗ Branch 1 not taken.
65227 if (tctx->opts.show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
290
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 65227 times.
65227 && (flags & AV_TEXTFORMAT_PRINT_STRING_OPTIONAL)
291 && !(tctx->formatter->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS))
292 return;
293
294
3/6
✓ Branch 0 taken 65227 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 65227 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 65227 times.
65227 av_assert0(key && tctx->level >= 0 && tctx->level < SECTION_MAX_NB_LEVELS);
295
296
3/4
✓ Branch 0 taken 65227 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 41691 times.
✓ Branch 4 taken 23536 times.
65227 if (!tctx->opts.is_key_selected || tctx->opts.is_key_selected(tctx, key)) {
297 41691 tctx->formatter->print_integer(tctx, key, val);
298 41691 tctx->nb_item[tctx->level]++;
299 }
300 }
301
302 6824 static inline int validate_string(AVTextFormatContext *tctx, char **dstp, const char *src)
303 {
304 6824 const uint8_t *p, *endp, *srcp = (const uint8_t *)src;
305 AVBPrint dstbuf;
306 AVBPrint invalid_seq;
307 6824 int invalid_chars_nb = 0, ret = 0;
308
309 6824 *dstp = NULL;
310 6824 av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
311 6824 av_bprint_init(&invalid_seq, 0, AV_BPRINT_SIZE_UNLIMITED);
312
313 6824 endp = srcp + strlen(src);
314
2/2
✓ Branch 0 taken 227296 times.
✓ Branch 1 taken 6824 times.
234120 for (p = srcp; *p;) {
315 int32_t code;
316 227296 int invalid = 0;
317 227296 const uint8_t *p0 = p;
318
319
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 227296 times.
227296 if (av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags) < 0) {
320
321 av_bprint_clear(&invalid_seq);
322
323 bprint_bytes(&invalid_seq, p0, p - p0);
324
325 av_log(tctx, AV_LOG_DEBUG, "Invalid UTF-8 sequence '%s' found in string '%s'\n", invalid_seq.str, src);
326 invalid = 1;
327 }
328
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 227296 times.
227296 if (invalid) {
330 invalid_chars_nb++;
331
332 switch (tctx->string_validation) {
333 case AV_TEXTFORMAT_STRING_VALIDATION_FAIL:
334 av_log(tctx, AV_LOG_ERROR, "Invalid UTF-8 sequence found in string '%s'\n", src);
335 ret = AVERROR_INVALIDDATA;
336 goto end;
337
338 case AV_TEXTFORMAT_STRING_VALIDATION_REPLACE:
339 av_bprintf(&dstbuf, "%s", tctx->string_validation_replacement);
340 break;
341 }
342 }
343
344
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 227296 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
227296 if (!invalid || tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_IGNORE)
345 227296 av_bprint_append_data(&dstbuf, p0, p-p0);
346 }
347
348
1/4
✓ Branch 0 taken 6824 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
6824 if (invalid_chars_nb && tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_REPLACE)
349 av_log(tctx, AV_LOG_WARNING,
350 "%d invalid UTF-8 sequence(s) found in string '%s', replaced with '%s'\n",
351 invalid_chars_nb, src, tctx->string_validation_replacement);
352
353 6824 end:
354 6824 av_bprint_finalize(&dstbuf, dstp);
355 6824 av_bprint_finalize(&invalid_seq, NULL);
356 6824 return ret;
357 }
358
359 struct unit_value {
360 union {
361 double d;
362 int64_t i;
363 } val;
364
365 AVTextFormatValueFormat fmt;
366 const char *unit;
367 };
368
369 static const char float_fmt_full[] = "%f";
370 static const char float_fmt_singledigit[] = "%.1f";
371 40399 static char *value_string(const AVTextFormatContext *tctx, char *buf, int buf_size, struct unit_value uv)
372 {
373 double vald;
374 40399 int64_t vali = 0;
375 40399 const char *float_fmt = 0;
376
377
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40399 times.
40399 if (uv.fmt == AV_TEXTFORMAT_VALUE_FMT_DECIBEL) {
378 vald = 20 * log10(uv.val.d);
379 float_fmt = float_fmt_singledigit;
380
2/2
✓ Branch 0 taken 30802 times.
✓ Branch 1 taken 9597 times.
40399 } else if (uv.fmt >= AV_TEXTFORMAT_VALUE_FMT_DOUBLE) {
381 30802 vald = uv.val.d;
382 30802 float_fmt = float_fmt_full;
383 } else {
384 9597 vald = (double)uv.val.i;
385 9597 vali = uv.val.i;
386 }
387
388
3/4
✓ Branch 0 taken 30802 times.
✓ Branch 1 taken 9597 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 30802 times.
40399 if (uv.fmt == AV_TEXTFORMAT_VALUE_FMT_SECOND && tctx->opts.use_value_sexagesimal_format) {
389 double secs;
390 int hours, mins;
391 secs = vald;
392 mins = (int)secs / 60;
393 secs = secs - mins * 60;
394 hours = mins / 60;
395 mins %= 60;
396 snprintf(buf, buf_size, "%d:%02d:%09.6f", hours, mins, secs);
397 } else {
398 40399 const char *prefix_string = "";
399
400
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 40399 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
40399 if (tctx->opts.use_value_prefix && vald > 1) {
401 int64_t index;
402
403 if (uv.fmt == AV_TEXTFORMAT_VALUE_FMT_BYTE && tctx->opts.use_byte_value_binary_prefix) {
404 index = (int64_t)(log2(vald) / 10);
405 index = av_clip64(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
406 vald /= si_prefixes[index].bin_val;
407 prefix_string = si_prefixes[index].bin_str;
408 } else {
409 index = (int64_t)(log10(vald) / 3);
410 index = av_clip64(index, 0, FF_ARRAY_ELEMS(si_prefixes) - 1);
411 vald /= si_prefixes[index].dec_val;
412 prefix_string = si_prefixes[index].dec_str;
413 }
414 vali = (int64_t)vald;
415 }
416
417
3/6
✓ Branch 0 taken 9597 times.
✓ Branch 1 taken 30802 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9597 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
40399 if (float_fmt || (tctx->opts.use_value_prefix && vald != (int64_t)vald))
418
1/2
✓ Branch 0 taken 30802 times.
✗ Branch 1 not taken.
30802 snprintf(buf, buf_size, float_fmt ? float_fmt : "%f", vald);
419 else
420 9597 snprintf(buf, buf_size, "%"PRId64, vali);
421
422
2/8
✓ Branch 0 taken 40399 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 40399 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
40399 av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || tctx->opts.show_value_unit && uv.unit && *uv.unit ? " " : "",
423
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 40399 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
40399 prefix_string, tctx->opts.show_value_unit && uv.unit ? uv.unit : "");
424 }
425
426 40399 return buf;
427 }
428
429
430 9597 void avtext_print_unit_integer(AVTextFormatContext *tctx, const char *key, int64_t val, AVTextFormatValueFormat fmt, const char *unit)
431 {
432 char val_str[128];
433 struct unit_value uv;
434
435
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9597 times.
9597 av_assert0(fmt < AV_TEXTFORMAT_VALUE_FMT_DOUBLE);
436
437 9597 uv.val.i = val;
438 9597 uv.fmt = fmt;
439 9597 uv.unit = unit;
440 9597 avtext_print_string(tctx, key, value_string(tctx, val_str, sizeof(val_str), uv), 0);
441 9597 }
442
443
444 void avtext_print_unit_double(AVTextFormatContext *tctx, const char *key, double val, AVTextFormatValueFormat fmt, const char *unit)
445 {
446 char val_str[128];
447 struct unit_value uv;
448
449 av_assert0(fmt >= AV_TEXTFORMAT_VALUE_FMT_DOUBLE);
450
451 uv.val.d = val;
452 uv.fmt = fmt;
453 uv.unit = unit;
454 avtext_print_string(tctx, key, value_string(tctx, val_str, sizeof(val_str), uv), 0);
455 }
456
457 93926 int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags)
458 {
459 const AVTextFormatSection *section;
460 93926 int ret = 0;
461
462
4/8
✓ Branch 0 taken 93926 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 93926 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 93926 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 93926 times.
93926 av_assert0(key && val && tctx->level >= 0 && tctx->level < SECTION_MAX_NB_LEVELS);
463
464 93926 section = tctx->section[tctx->level];
465
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93926 times.
93926 if (tctx->opts.show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER)
467 return 0;
468
469
1/2
✓ Branch 0 taken 93926 times.
✗ Branch 1 not taken.
93926 if (tctx->opts.show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
470
2/2
✓ Branch 0 taken 8715 times.
✓ Branch 1 taken 85211 times.
93926 && (flags & AV_TEXTFORMAT_PRINT_STRING_OPTIONAL)
471
2/2
✓ Branch 0 taken 292 times.
✓ Branch 1 taken 8423 times.
8715 && !(tctx->formatter->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS))
472 292 return 0;
473
474
3/4
✓ Branch 0 taken 93634 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 42139 times.
✓ Branch 4 taken 51495 times.
93634 if (!tctx->opts.is_key_selected || tctx->opts.is_key_selected(tctx, key)) {
475
2/2
✓ Branch 0 taken 3412 times.
✓ Branch 1 taken 38727 times.
42139 if (flags & AV_TEXTFORMAT_PRINT_STRING_VALIDATE) {
476 3412 char *key1 = NULL, *val1 = NULL;
477 3412 ret = validate_string(tctx, &key1, key);
478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3412 times.
3412 if (ret < 0) goto end;
479 3412 ret = validate_string(tctx, &val1, val);
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3412 times.
3412 if (ret < 0) goto end;
481 3412 tctx->formatter->print_string(tctx, key1, val1);
482 3412 end:
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3412 times.
3412 if (ret < 0)
484 av_log(tctx, AV_LOG_ERROR,
485 "Invalid key=value string combination %s=%s in section %s\n",
486 key, val, section->unique_name);
487 3412 av_free(key1);
488 3412 av_free(val1);
489 } else {
490 38727 tctx->formatter->print_string(tctx, key, val);
491 }
492
493 42139 tctx->nb_item[tctx->level]++;
494 }
495
496 93634 return ret;
497 }
498
499 2760 void avtext_print_rational(AVTextFormatContext *tctx, const char *key, AVRational q, char sep)
500 {
501 char buf[44];
502 2760 snprintf(buf, sizeof(buf), "%d%c%d", q.num, sep, q.den);
503 2760 avtext_print_string(tctx, key, buf, 0);
504 2760 }
505
506 31585 void avtext_print_time(AVTextFormatContext *tctx, const char *key,
507 int64_t ts, const AVRational *time_base, int is_duration)
508 {
509
8/8
✓ Branch 0 taken 22470 times.
✓ Branch 1 taken 9115 times.
✓ Branch 2 taken 21693 times.
✓ Branch 3 taken 777 times.
✓ Branch 4 taken 9115 times.
✓ Branch 5 taken 21693 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 9109 times.
31585 if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
510 783 avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
511 } else {
512 char buf[128];
513 30802 double d = av_q2d(*time_base) * ts;
514 struct unit_value uv;
515 30802 uv.val.d = d;
516 30802 uv.fmt = AV_TEXTFORMAT_VALUE_FMT_SECOND;
517 30802 uv.unit = unit_second_str;
518 30802 value_string(tctx, buf, sizeof(buf), uv);
519 30802 avtext_print_string(tctx, key, buf, 0);
520 }
521 31585 }
522
523 31435 void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration)
524 {
525
8/8
✓ Branch 0 taken 22320 times.
✓ Branch 1 taken 9115 times.
✓ Branch 2 taken 21553 times.
✓ Branch 3 taken 767 times.
✓ Branch 4 taken 9115 times.
✓ Branch 5 taken 21553 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 9109 times.
31435 if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0))
526 773 avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
527 else
528 30662 avtext_print_integer(tctx, key, ts, 0);
529 31435 }
530
531 static void print_data_xxd(AVBPrint *bp, const uint8_t *data, int size)
532 {
533 unsigned offset = 0;
534 int i;
535
536 av_bprintf(bp, "\n");
537 while (size) {
538 av_bprintf(bp, "%08x: ", offset);
539 int l = FFMIN(size, 16);
540 for (i = 0; i < l; i++) {
541 av_bprintf(bp, "%02x", data[i]);
542 if (i & 1)
543 av_bprintf(bp, " ");
544 }
545 av_bprint_chars(bp, ' ', 41 - 2 * i - i / 2);
546 for (i = 0; i < l; i++)
547 av_bprint_chars(bp, data[i] - 32U < 95 ? data[i] : '.', 1);
548 av_bprintf(bp, "\n");
549 offset += l;
550 data += l;
551 size -= l;
552 }
553 }
554
555 static void print_data_base64(AVBPrint *bp, const uint8_t *data, int size)
556 {
557 char buf[AV_BASE64_SIZE(60)];
558
559 av_bprintf(bp, "\n");
560 while (size) {
561 int l = FFMIN(size, 60);
562 av_base64_encode(buf, sizeof(buf), data, l);
563 av_bprintf(bp, "%s\n", buf);
564 data += l;
565 size -= l;
566 }
567 }
568 void avtext_print_data(AVTextFormatContext *tctx, const char *key,
569 const uint8_t *data, int size)
570 {
571 AVBPrint bp;
572 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
573 switch (tctx->opts.data_dump_format) {
574 case AV_TEXTFORMAT_DATADUMP_XXD:
575 print_data_xxd(&bp, data, size);
576 break;
577 case AV_TEXTFORMAT_DATADUMP_BASE64:
578 print_data_base64(&bp, data, size);
579 break;
580 default:
581 av_unreachable("Invalid data dump type");
582 }
583 avtext_print_string(tctx, key, bp.str, 0);
584 av_bprint_finalize(&bp, NULL);
585 }
586
587 6167 void avtext_print_data_hash(AVTextFormatContext *tctx, const char *key,
588 const uint8_t *data, int size)
589 {
590 6167 char buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
591 int len;
592
593
2/2
✓ Branch 0 taken 4134 times.
✓ Branch 1 taken 2033 times.
6167 if (!tctx->hash)
594 4134 return;
595
596 2033 av_hash_init(tctx->hash);
597 2033 av_hash_update(tctx->hash, data, size);
598 2033 len = snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(tctx->hash));
599 2033 av_hash_final_hex(tctx->hash, (uint8_t *)&buf[len], (int)sizeof(buf) - len);
600 2033 avtext_print_string(tctx, key, buf, 0);
601 }
602
603 static const char *writercontext_get_writer_name(void *p)
604 {
605 AVTextWriterContext *wctx = p;
606 return wctx->writer->name;
607 }
608
609 static void *writercontext_child_next(void *obj, void *prev)
610 {
611 AVTextFormatContext *ctx = obj;
612 if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv)
613 return ctx->priv;
614 return NULL;
615 }
616
617 static const AVClass textwriter_class = {
618 .class_name = "AVTextWriterContext",
619 .item_name = writercontext_get_writer_name,
620 .version = LIBAVUTIL_VERSION_INT,
621 .child_next = writercontext_child_next,
622 };
623
624
625 234 int avtextwriter_context_close(AVTextWriterContext **pwctx)
626 {
627 234 AVTextWriterContext *wctx = *pwctx;
628 234 int ret = 0;
629
630
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (!wctx)
631 return AVERROR(EINVAL);
632
633
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (wctx->writer) {
634
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (wctx->writer->uninit)
635 ret = wctx->writer->uninit(wctx);
636
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (wctx->writer->priv_class)
637 234 av_opt_free(wctx->priv);
638 }
639 234 av_freep(&wctx->priv);
640 234 av_freep(pwctx);
641 234 return ret;
642 }
643
644
645 234 int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer)
646 {
647 AVTextWriterContext *wctx;
648 234 int ret = 0;
649
650
2/4
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 234 times.
234 if (!pwctx || !writer)
651 return AVERROR(EINVAL);
652
653
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 234 times.
234 if (!((wctx = av_mallocz(sizeof(AVTextWriterContext))))) {
654 ret = AVERROR(ENOMEM);
655 goto fail;
656 }
657
658
2/4
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 234 times.
234 if (writer->priv_size && !((wctx->priv = av_mallocz(writer->priv_size)))) {
659 ret = AVERROR(ENOMEM);
660 goto fail;
661 }
662
663
1/2
✓ Branch 0 taken 234 times.
✗ Branch 1 not taken.
234 if (writer->priv_class) {
664 234 void *priv_ctx = wctx->priv;
665 234 *(const AVClass **)priv_ctx = writer->priv_class;
666 234 av_opt_set_defaults(priv_ctx);
667 }
668
669 234 wctx->class = &textwriter_class;
670 234 wctx->writer = writer;
671
672 234 av_opt_set_defaults(wctx);
673
674
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (wctx->writer->init)
676 ret = wctx->writer->init(wctx);
677
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
234 if (ret < 0)
678 goto fail;
679
680 234 *pwctx = wctx;
681
682 234 return 0;
683
684 fail:
685 avtextwriter_context_close(&wctx);
686 return ret;
687 }
688
689 static const AVTextFormatter *const registered_formatters[] =
690 {
691 &avtextformatter_default,
692 &avtextformatter_compact,
693 &avtextformatter_csv,
694 &avtextformatter_flat,
695 &avtextformatter_ini,
696 &avtextformatter_json,
697 &avtextformatter_xml,
698 &avtextformatter_mermaid,
699 &avtextformatter_mermaidhtml,
700 NULL
701 };
702
703 234 const AVTextFormatter *avtext_get_formatter_by_name(const char *name)
704 {
705
1/2
✓ Branch 0 taken 324 times.
✗ Branch 1 not taken.
324 for (int i = 0; registered_formatters[i]; i++) {
706 const char *end;
707
2/2
✓ Branch 1 taken 234 times.
✓ Branch 2 taken 90 times.
324 if (av_strstart(name, registered_formatters[i]->name, &end) &&
708
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 234 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
234 (*end == '\0' || *end == '='))
709 234 return registered_formatters[i];
710 }
711
712 return NULL;
713 }
714