FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/avtextformat.c
Date: 2025-04-25 22:50:00
Exec Total Coverage
Lines: 230 344 66.9%
Functions: 19 24 79.2%
Branches: 114 201 56.7%

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