FFmpeg coverage


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