../../../ffmpeg/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
219 / 367
59.7%
Branches:
134 / 254
52.8%
Functions:
17 / 25
68.0%

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