FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/avtextformat.c
Date: 2026-01-23 14:02:51
Exec Total Coverage
Lines: 217 339 64.0%
Functions: 17 22 77.3%
Branches: 125 235 53.2%

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