FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/avtextformat.c
Date: 2025-07-12 22:17:06
Exec Total Coverage
Lines: 234 357 65.5%
Functions: 18 23 78.3%
Branches: 139 255 54.5%

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