FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/avtextformat.c
Date: 2025-06-01 09:29:47
Exec Total Coverage
Lines: 248 372 66.7%
Functions: 19 24 79.2%
Branches: 139 253 54.9%

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