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 |