History

fftools/textformat: Extract and generalize textformat api from ffprobe.c

softworkz committed 1 year ago

Commit 97d680d1 parent d79e4f612 files · +2411 −0Browse files

Unified · Split

12 files
  1. fftools/textformat/avtextformat.c +672 −0
  2. fftools/textformat/avtextformat.h +171 −0
  3. fftools/textformat/avtextwriters.h +68 −0
  4. fftools/textformat/tf_compact.c +282 −0
  5. fftools/textformat/tf_default.c +145 −0
  6. fftools/textformat/tf_flat.c +174 −0
  7. fftools/textformat/tf_ini.c +160 −0
  8. fftools/textformat/tf_json.c +215 −0
  9. fftools/textformat/tf_xml.c +221 −0
  10. fftools/textformat/tw_avio.c +129 −0
  11. fftools/textformat/tw_buffer.c +92 −0
  12. fftools/textformat/tw_stdout.c +82 −0
fftools/textformat/avtextformat.c+672−0
@@ -0,0 +1,672 @@
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
43static 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
57static 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
65static 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
78static void *textcontext_child_next(void *obj, void *prev)
79{
80 AVTextFormatContext *ctx = obj;
81 if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv)
82 return ctx->priv;
83 return NULL;
84}
85
86static 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
94static 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
102int avtext_context_close(AVTextFormatContext **ptctx)
103{
104 AVTextFormatContext *tctx = *ptctx;
105 int i;
106 int ret = 0;
107
108 if (!tctx)
109 return EINVAL;
110
111 av_hash_freep(&tctx->hash);
112
113 av_hash_freep(&tctx->hash);
114
115 if (tctx->formatter->uninit)
116 tctx->formatter->uninit(tctx);
117 for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
118 av_bprint_finalize(&tctx->section_pbuf[i], NULL);
119 if (tctx->formatter->priv_class)
120 av_opt_free(tctx->priv);
121 av_freep(&tctx->priv);
122 av_opt_free(tctx);
123 av_freep(ptctx);
124 return ret;
125}
126
127
128int 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 int i, ret = 0;
139
140 if (!(tctx = av_mallocz(sizeof(AVTextFormatContext)))) {
141 ret = AVERROR(ENOMEM);
142 goto fail;
143 }
144
145 if (!(tctx->priv = av_mallocz(formatter->priv_size))) {
146 ret = AVERROR(ENOMEM);
147 goto fail;
148 }
149
150 tctx->show_value_unit = show_value_unit;
151 tctx->use_value_prefix = use_value_prefix;
152 tctx->use_byte_value_binary_prefix = use_byte_value_binary_prefix;
153 tctx->use_value_sexagesimal_format = use_value_sexagesimal_format;
154 tctx->show_optional_fields = show_optional_fields;
155
156 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 tctx->class = &textcontext_class;
163 tctx->formatter = formatter;
164 tctx->level = -1;
165 tctx->sections = sections;
166 tctx->nb_sections = nb_sections;
167 tctx->writer = writer_context;
168
169 av_opt_set_defaults(tctx);
170
171 if (formatter->priv_class) {
172 void *priv_ctx = tctx->priv;
173 *(const AVClass **)priv_ctx = formatter->priv_class;
174 av_opt_set_defaults(priv_ctx);
175 }
176
177 /* convert options to dictionary */
178 if (args) {
179 AVDictionary *opts = NULL;
180 const AVDictionaryEntry *opt = NULL;
181
182 if ((ret = av_dict_parse_string(&opts, args, "=", ":", 0)) < 0) {
183 av_log(tctx, AV_LOG_ERROR, "Failed to parse option string '%s' provided to textformat context\n", args);
184 av_dict_free(&opts);
185 goto fail;
186 }
187
188 while ((opt = av_dict_iterate(opts, opt))) {
189 if ((ret = av_opt_set(tctx, opt->key, opt->value, AV_OPT_SEARCH_CHILDREN)) < 0) {
190 av_log(tctx, AV_LOG_ERROR, "Failed to set option '%s' with value '%s' provided to textformat context\n",
191 opt->key, opt->value);
192 av_dict_free(&opts);
193 goto fail;
194 }
195 }
196
197 av_dict_free(&opts);
198 }
199
200 if (show_data_hash) {
201 if ((ret = av_hash_alloc(&tctx->hash, show_data_hash)) < 0) {
202 if (ret == AVERROR(EINVAL)) {
203 const char *n;
204 av_log(NULL, AV_LOG_ERROR, "Unknown hash algorithm '%s'\nKnown algorithms:", show_data_hash);
205 for (i = 0; (n = av_hash_names(i)); i++)
206 av_log(NULL, AV_LOG_ERROR, " %s", n);
207 av_log(NULL, AV_LOG_ERROR, "\n");
208 }
209 return ret;
210 }
211 }
212
213 /* validate replace string */
214 {
215 const uint8_t *p = tctx->string_validation_replacement;
216 const uint8_t *endp = p + strlen(p);
217 while (*p) {
218 const uint8_t *p0 = p;
219 int32_t code;
220 ret = av_utf8_decode(&code, &p, endp, tctx->string_validation_utf8_flags);
221 if (ret < 0) {
222 AVBPrint bp;
223 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC);
224 bprint_bytes(&bp, p0, p-p0),
225 av_log(tctx, AV_LOG_ERROR,
226 "Invalid UTF8 sequence %s found in string validation replace '%s'\n",
227 bp.str, tctx->string_validation_replacement);
228 return ret;
229 }
230 }
231 }
232
233 for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
234 av_bprint_init(&tctx->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
235
236 if (tctx->formatter->init)
237 ret = tctx->formatter->init(tctx);
238 if (ret < 0)
239 goto fail;
240
241 *ptctx = tctx;
242
243 return 0;
244
245fail:
246 avtext_context_close(&tctx);
247 return ret;
248}
249
250/* Temporary definitions during refactoring */
251static const char unit_second_str[] = "s" ;
252static const char unit_hertz_str[] = "Hz" ;
253static const char unit_byte_str[] = "byte" ;
254static const char unit_bit_per_second_str[] = "bit/s";
255
256
257void avtext_print_section_header(AVTextFormatContext *tctx,
258 const void *data,
259 int section_id)
260{
261 tctx->level++;
262 av_assert0(tctx->level < SECTION_MAX_NB_LEVELS);
263
264 tctx->nb_item[tctx->level] = 0;
265 memset(tctx->nb_item_type[tctx->level], 0, sizeof(tctx->nb_item_type[tctx->level]));
266 tctx->section[tctx->level] = &tctx->sections[section_id];
267
268 if (tctx->formatter->print_section_header)
269 tctx->formatter->print_section_header(tctx, data);
270}
271
272void avtext_print_section_footer(AVTextFormatContext *tctx)
273{
274 int section_id = tctx->section[tctx->level]->id;
275 int parent_section_id = tctx->level ?
276 tctx->section[tctx->level-1]->id : SECTION_ID_NONE;
277
278 if (parent_section_id != SECTION_ID_NONE) {
279 tctx->nb_item[tctx->level - 1]++;
280 tctx->nb_item_type[tctx->level - 1][section_id]++;
281 }
282
283 if (tctx->formatter->print_section_footer)
284 tctx->formatter->print_section_footer(tctx);
285 tctx->level--;
286}
287
288void avtext_print_integer(AVTextFormatContext *tctx,
289 const char *key, int64_t val)
290{
291 const struct AVTextFormatSection *section = tctx->section[tctx->level];
292
293 if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
294 tctx->formatter->print_integer(tctx, key, val);
295 tctx->nb_item[tctx->level]++;
296 }
297}
298
299static inline int validate_string(AVTextFormatContext *tctx, char **dstp, const char *src)
300{
301 const uint8_t *p, *endp;
302 AVBPrint dstbuf;
303 int invalid_chars_nb = 0, ret = 0;
304
305 av_bprint_init(&dstbuf, 0, AV_BPRINT_SIZE_UNLIMITED);
306
307 endp = src + strlen(src);
308 for (p = src; *p;) {
309 uint32_t code;
310 int invalid = 0;
311 const uint8_t *p0 = p;
312
313 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 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 if (!invalid || tctx->string_validation == AV_TEXTFORMAT_STRING_VALIDATION_IGNORE)
340 av_bprint_append_data(&dstbuf, p0, p-p0);
341 }
342
343 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
349end:
350 av_bprint_finalize(&dstbuf, dstp);
351 return ret;
352}
353
354struct unit_value {
355 union { double d; int64_t i; } val;
356 const char *unit;
357};
358
359static char *value_string(AVTextFormatContext *tctx, char *buf, int buf_size, struct unit_value uv)
360{
361 double vald;
362 int64_t vali;
363 int show_float = 0;
364
365 if (uv.unit == unit_second_str) {
366 vald = uv.val.d;
367 show_float = 1;
368 } else {
369 vald = vali = uv.val.i;
370 }
371
372 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 const char *prefix_string = "";
383
384 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 if (show_float || (tctx->use_value_prefix && vald != (int64_t)vald))
402 snprintf(buf, buf_size, "%f", vald);
403 else
404 snprintf(buf, buf_size, "%"PRId64, vali);
405 av_strlcatf(buf, buf_size, "%s%s%s", *prefix_string || tctx->show_value_unit ? " " : "",
406 prefix_string, tctx->show_value_unit ? uv.unit : "");
407 }
408
409 return buf;
410}
411
412
413void 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 uv.val.i = value;
418 uv.unit = unit;
419 avtext_print_string(tctx, key, value_string(tctx, val_str, sizeof(val_str), uv), 0);
420}
421
422
423int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags)
424{
425 const struct AVTextFormatSection *section = tctx->section[tctx->level];
426 int ret = 0;
427
428 if (tctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_NEVER ||
429 (tctx->show_optional_fields == SHOW_OPTIONAL_FIELDS_AUTO
430 && (flags & AV_TEXTFORMAT_PRINT_STRING_OPTIONAL)
431 && !(tctx->formatter->flags & AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS)))
432 return 0;
433
434 if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
435 if (flags & AV_TEXTFORMAT_PRINT_STRING_VALIDATE) {
436 char *key1 = NULL, *val1 = NULL;
437 ret = validate_string(tctx, &key1, key);
438 if (ret < 0) goto end;
439 ret = validate_string(tctx, &val1, val);
440 if (ret < 0) goto end;
441 tctx->formatter->print_string(tctx, key1, val1);
442 end:
443 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 av_free(key1);
449 av_free(val1);
450 } else {
451 tctx->formatter->print_string(tctx, key, val);
452 }
453
454 tctx->nb_item[tctx->level]++;
455 }
456
457 return ret;
458}
459
460void avtext_print_rational(AVTextFormatContext *tctx,
461 const char *key, AVRational q, char sep)
462{
463 AVBPrint buf;
464 av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC);
465 av_bprintf(&buf, "%d%c%d", q.num, sep, q.den);
466 avtext_print_string(tctx, key, buf.str, 0);
467}
468
469void avtext_print_time(AVTextFormatContext *tctx, const char *key,
470 int64_t ts, const AVRational *time_base, int is_duration)
471{
472 char buf[128];
473
474 if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
475 avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
476 } else {
477 double d = ts * av_q2d(*time_base);
478 struct unit_value uv;
479 uv.val.d = d;
480 uv.unit = unit_second_str;
481 value_string(tctx, buf, sizeof(buf), uv);
482 avtext_print_string(tctx, key, buf, 0);
483 }
484}
485
486void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration)
487{
488 if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) {
489 avtext_print_string(tctx, key, "N/A", AV_TEXTFORMAT_PRINT_STRING_OPTIONAL);
490 } else {
491 avtext_print_integer(tctx, key, ts);
492 }
493}
494
495void avtext_print_data(AVTextFormatContext *tctx, const char *name,
496 const uint8_t *data, int size)
497{
498 AVBPrint bp;
499 int offset = 0, l, i;
500
501 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
502 av_bprintf(&bp, "\n");
503 while (size) {
504 av_bprintf(&bp, "%08x: ", offset);
505 l = FFMIN(size, 16);
506 for (i = 0; i < l; i++) {
507 av_bprintf(&bp, "%02x", data[i]);
508 if (i & 1)
509 av_bprintf(&bp, " ");
510 }
511 av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2);
512 for (i = 0; i < l; i++)
513 av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1);
514 av_bprintf(&bp, "\n");
515 offset += l;
516 data += l;
517 size -= l;
518 }
519 avtext_print_string(tctx, name, bp.str, 0);
520 av_bprint_finalize(&bp, NULL);
521}
522
523void avtext_print_data_hash(AVTextFormatContext *tctx, const char *name,
524 const uint8_t *data, int size)
525{
526 char *p, buf[AV_HASH_MAX_SIZE * 2 + 64] = { 0 };
527
528 if (!tctx->hash)
529 return;
530 av_hash_init(tctx->hash);
531 av_hash_update(tctx->hash, data, size);
532 snprintf(buf, sizeof(buf), "%s:", av_hash_get_name(tctx->hash));
533 p = buf + strlen(buf);
534 av_hash_final_hex(tctx->hash, p, buf + sizeof(buf) - p);
535 avtext_print_string(tctx, name, buf, 0);
536}
537
538void avtext_print_integers(AVTextFormatContext *tctx, const char *name,
539 uint8_t *data, int size, const char *format,
540 int columns, int bytes, int offset_add)
541{
542 AVBPrint bp;
543 int offset = 0, l, i;
544
545 av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED);
546 av_bprintf(&bp, "\n");
547 while (size) {
548 av_bprintf(&bp, "%08x: ", offset);
549 l = FFMIN(size, columns);
550 for (i = 0; i < l; i++) {
551 if (bytes == 1) av_bprintf(&bp, format, *data);
552 else if (bytes == 2) av_bprintf(&bp, format, AV_RN16(data));
553 else if (bytes == 4) av_bprintf(&bp, format, AV_RN32(data));
554 data += bytes;
555 size --;
556 }
557 av_bprintf(&bp, "\n");
558 offset += offset_add;
559 }
560 avtext_print_string(tctx, name, bp.str, 0);
561 av_bprint_finalize(&bp, NULL);
562}
563
564static const char *writercontext_get_writer_name(void *p)
565{
566 AVTextWriterContext *wctx = p;
567 return wctx->writer->name;
568}
569
570static void *writercontext_child_next(void *obj, void *prev)
571{
572 AVTextFormatContext *ctx = obj;
573 if (!prev && ctx->formatter && ctx->formatter->priv_class && ctx->priv)
574 return ctx->priv;
575 return NULL;
576}
577
578static const AVClass textwriter_class = {
579 .class_name = "AVTextWriterContext",
580 .item_name = writercontext_get_writer_name,
581 .version = LIBAVUTIL_VERSION_INT,
582 .child_next = writercontext_child_next,
583};
584
585
586int avtextwriter_context_close(AVTextWriterContext **pwctx)
587{
588 AVTextWriterContext *wctx = *pwctx;
589 int ret = 0;
590
591 if (!wctx)
592 return EINVAL;
593
594 if (wctx->writer->uninit)
595 wctx->writer->uninit(wctx);
596 if (wctx->writer->priv_class)
597 av_opt_free(wctx->priv);
598 av_freep(&wctx->priv);
599 av_freep(pwctx);
600 return ret;
601}
602
603
604int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer)
605{
606 AVTextWriterContext *wctx;
607 int ret = 0;
608
609 if (!(wctx = av_mallocz(sizeof(AVTextWriterContext)))) {
610 ret = AVERROR(ENOMEM);
611 goto fail;
612 }
613
614 if (!(wctx->priv = av_mallocz(writer->priv_size))) {
615 ret = AVERROR(ENOMEM);
616 goto fail;
617 }
618
619 if (writer->priv_class) {
620 void *priv_ctx = wctx->priv;
621 *(const AVClass **)priv_ctx = writer->priv_class;
622 av_opt_set_defaults(priv_ctx);
623 }
624
625 wctx->class = &textwriter_class;
626 wctx->writer = writer;
627
628 av_opt_set_defaults(wctx);
629
630
631 if (wctx->writer->init)
632 ret = wctx->writer->init(wctx);
633 if (ret < 0)
634 goto fail;
635
636 *pwctx = wctx;
637
638 return 0;
639
640fail:
641 avtextwriter_context_close(&wctx);
642 return ret;
643}
644
645static const AVTextFormatter *registered_formatters[7+1];
646static void formatters_register_all(void)
647{
648 static int initialized;
649
650 if (initialized)
651 return;
652 initialized = 1;
653
654 registered_formatters[0] = &avtextformatter_default;
655 registered_formatters[1] = &avtextformatter_compact;
656 registered_formatters[2] = &avtextformatter_csv;
657 registered_formatters[3] = &avtextformatter_flat;
658 registered_formatters[4] = &avtextformatter_ini;
659 registered_formatters[5] = &avtextformatter_json;
660 registered_formatters[6] = &avtextformatter_xml;
661}
662
663const AVTextFormatter *avtext_get_formatter_by_name(const char *name)
664{
665 formatters_register_all();
666
667 for (int i = 0; registered_formatters[i]; i++)
668 if (!strcmp(registered_formatters[i]->name, name))
669 return registered_formatters[i];
670
671 return NULL;
672}
fftools/textformat/avtextformat.h+171−0
@@ -0,0 +1,171 @@
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#ifndef FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H
22#define FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H
23
24#include <stddef.h>
25#include <stdint.h>
26#include "libavutil/attributes.h"
27#include "libavutil/dict.h"
28#include "libavformat/avio.h"
29#include "libavutil/bprint.h"
30#include "libavutil/rational.h"
31#include "libavutil/hash.h"
32#include "avtextwriters.h"
33
34#define SECTION_MAX_NB_CHILDREN 11
35
36
37typedef struct AVTextFormatSection {
38 int id; ///< unique id identifying a section
39 const char *name;
40
41#define AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER 1 ///< the section only contains other sections, but has no data at its own level
42#define AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY 2 ///< the section contains an array of elements of the same type
43#define AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
44 /// For these sections the element_name field is mandatory.
45#define AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE 8 ///< the section contains a type to distinguish multiple nested elements
46#define AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE 16 ///< the items in this array section should be numbered individually by type
47
48 int flags;
49 const int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
50 const char *element_name; ///< name of the contained element, if provided
51 const char *unique_name; ///< unique section name, in case the name is ambiguous
52 AVDictionary *entries_to_show;
53 const char *(* get_type)(const void *data); ///< function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined
54 int show_all_entries;
55} AVTextFormatSection;
56
57typedef struct AVTextFormatContext AVTextFormatContext;
58
59#define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS 1
60#define AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT 2
61
62typedef enum {
63 AV_TEXTFORMAT_STRING_VALIDATION_FAIL,
64 AV_TEXTFORMAT_STRING_VALIDATION_REPLACE,
65 AV_TEXTFORMAT_STRING_VALIDATION_IGNORE,
66 AV_TEXTFORMAT_STRING_VALIDATION_NB
67} StringValidation;
68
69typedef struct AVTextFormatter {
70 const AVClass *priv_class; ///< private class of the formatter, if any
71 int priv_size; ///< private size for the formatter context
72 const char *name;
73
74 int (*init) (AVTextFormatContext *tctx);
75 void (*uninit)(AVTextFormatContext *tctx);
76
77 void (*print_section_header)(AVTextFormatContext *tctx, const void *data);
78 void (*print_section_footer)(AVTextFormatContext *tctx);
79 void (*print_integer) (AVTextFormatContext *tctx, const char *, int64_t);
80 void (*print_rational) (AVTextFormatContext *tctx, AVRational *q, char *sep);
81 void (*print_string) (AVTextFormatContext *tctx, const char *, const char *);
82 int flags; ///< a combination or AV_TEXTFORMAT__FLAG_*
83} AVTextFormatter;
84
85#define SECTION_MAX_NB_LEVELS 12
86#define SECTION_MAX_NB_SECTIONS 100
87
88struct AVTextFormatContext {
89 const AVClass *class; ///< class of the formatter
90 const AVTextFormatter *formatter; ///< the AVTextFormatter of which this is an instance
91 AVTextWriterContext *writer; ///< the AVTextWriterContext
92
93 char *name; ///< name of this formatter instance
94 void *priv; ///< private data for use by the filter
95
96 const struct AVTextFormatSection *sections; ///< array containing all sections
97 int nb_sections; ///< number of sections
98
99 int level; ///< current level, starting from 0
100
101 /** number of the item printed in the given section, starting from 0 */
102 unsigned int nb_item[SECTION_MAX_NB_LEVELS];
103 unsigned int nb_item_type[SECTION_MAX_NB_LEVELS][SECTION_MAX_NB_SECTIONS];
104
105 /** section per each level */
106 const struct AVTextFormatSection *section[SECTION_MAX_NB_LEVELS];
107 AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
108 /// used by various formatters
109
110 int show_optional_fields;
111 int show_value_unit;
112 int use_value_prefix;
113 int use_byte_value_binary_prefix;
114 int use_value_sexagesimal_format;
115
116 struct AVHashContext *hash;
117
118 int string_validation;
119 char *string_validation_replacement;
120 unsigned int string_validation_utf8_flags;
121};
122
123#define AV_TEXTFORMAT_PRINT_STRING_OPTIONAL 1
124#define AV_TEXTFORMAT_PRINT_STRING_VALIDATE 2
125
126int avtext_context_open(AVTextFormatContext **ptctx, const AVTextFormatter *formatter, AVTextWriterContext *writer_context, const char *args,
127 const struct AVTextFormatSection *sections, int nb_sections,
128 int show_value_unit,
129 int use_value_prefix,
130 int use_byte_value_binary_prefix,
131 int use_value_sexagesimal_format,
132 int show_optional_fields,
133 char *show_data_hash);
134
135int avtext_context_close(AVTextFormatContext **tctx);
136
137
138void avtext_print_section_header(AVTextFormatContext *tctx, const void *data, int section_id);
139
140void avtext_print_section_footer(AVTextFormatContext *tctx);
141
142void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t val);
143
144int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags);
145
146void avtext_print_unit_int(AVTextFormatContext *tctx, const char *key, int value, const char *unit);
147
148void avtext_print_rational(AVTextFormatContext *tctx, const char *key, AVRational q, char sep);
149
150void avtext_print_time(AVTextFormatContext *tctx, const char *key, int64_t ts, const AVRational *time_base, int is_duration);
151
152void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration);
153
154void avtext_print_data(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size);
155
156void avtext_print_data_hash(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size);
157
158void avtext_print_integers(AVTextFormatContext *tctx, const char *name, uint8_t *data, int size,
159 const char *format, int columns, int bytes, int offset_add);
160
161const AVTextFormatter *avtext_get_formatter_by_name(const char *name);
162
163extern const AVTextFormatter avtextformatter_default;
164extern const AVTextFormatter avtextformatter_compact;
165extern const AVTextFormatter avtextformatter_csv;
166extern const AVTextFormatter avtextformatter_flat;
167extern const AVTextFormatter avtextformatter_ini;
168extern const AVTextFormatter avtextformatter_json;
169extern const AVTextFormatter avtextformatter_xml;
170
171#endif /* FFTOOLS_TEXTFORMAT_AVTEXTFORMAT_H */
fftools/textformat/avtextwriters.h+68−0
@@ -0,0 +1,68 @@
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#ifndef FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H
22#define FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H
23
24#include <stddef.h>
25#include <stdint.h>
26#include "libavutil/attributes.h"
27#include "libavutil/dict.h"
28#include "libavformat/avio.h"
29#include "libavutil/bprint.h"
30#include "libavutil/rational.h"
31#include "libavutil/hash.h"
32
33typedef struct AVTextWriterContext AVTextWriterContext;
34
35typedef struct AVTextWriter {
36 const AVClass *priv_class; ///< private class of the writer, if any
37 int priv_size; ///< private size for the writer private class
38 const char *name;
39
40 int (* init)(AVTextWriterContext *wctx);
41 void (* uninit)(AVTextWriterContext *wctx);
42 void (* writer_w8)(AVTextWriterContext *wctx, int b);
43 void (* writer_put_str)(AVTextWriterContext *wctx, const char *str);
44 void (* writer_printf)(AVTextWriterContext *wctx, const char *fmt, ...);
45} AVTextWriter;
46
47typedef struct AVTextWriterContext {
48 const AVClass *class; ///< class of the writer
49 const AVTextWriter *writer;
50 const char *name;
51 void *priv; ///< private data for use by the writer
52
53} AVTextWriterContext;
54
55
56int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer);
57
58int avtextwriter_context_close(AVTextWriterContext **pwctx);
59
60int avtextwriter_create_stdout(AVTextWriterContext **pwctx);
61
62int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit);
63
64int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename, int close_on_uninit);
65
66int avtextwriter_create_buffer(AVTextWriterContext **pwctx, AVBPrint *buffer);
67
68#endif /* FFTOOLS_TEXTFORMAT_AVTEXTWRITERS_H */
fftools/textformat/tf_compact.c+282−0
@@ -0,0 +1,282 @@
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 "avtextformat.h"
28#include <libavutil/mem.h>
29#include <libavutil/avassert.h>
30#include <libavutil/bprint.h>
31#include <libavutil/error.h>
32#include <libavutil/macros.h>
33#include <libavutil/opt.h>
34
35
36#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
37#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
38#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
39
40
41#define DEFINE_FORMATTER_CLASS(name) \
42static const char *name##_get_name(void *ctx) \
43{ \
44 return #name ; \
45} \
46static const AVClass name##_class = { \
47 .class_name = #name, \
48 .item_name = name##_get_name, \
49 .option = name##_options \
50}
51
52
53/* Compact output */
54
55/**
56 * Apply C-language-like string escaping.
57 */
58static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
59{
60 const char *p;
61
62 for (p = src; *p; p++) {
63 switch (*p) {
64 case '\b': av_bprintf(dst, "%s", "\\b"); break;
65 case '\f': av_bprintf(dst, "%s", "\\f"); break;
66 case '\n': av_bprintf(dst, "%s", "\\n"); break;
67 case '\r': av_bprintf(dst, "%s", "\\r"); break;
68 case '\\': av_bprintf(dst, "%s", "\\\\"); break;
69 default:
70 if (*p == sep)
71 av_bprint_chars(dst, '\\', 1);
72 av_bprint_chars(dst, *p, 1);
73 }
74 }
75 return dst->str;
76}
77
78/**
79 * Quote fields containing special characters, check RFC4180.
80 */
81static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
82{
83 char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
84 int needs_quoting = !!src[strcspn(src, meta_chars)];
85
86 if (needs_quoting)
87 av_bprint_chars(dst, '"', 1);
88
89 for (; *src; src++) {
90 if (*src == '"')
91 av_bprint_chars(dst, '"', 1);
92 av_bprint_chars(dst, *src, 1);
93 }
94 if (needs_quoting)
95 av_bprint_chars(dst, '"', 1);
96 return dst->str;
97}
98
99static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
100{
101 return src;
102}
103
104typedef struct CompactContext {
105 const AVClass *class;
106 char *item_sep_str;
107 char item_sep;
108 int nokey;
109 int print_section;
110 char *escape_mode_str;
111 const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
112 int nested_section[SECTION_MAX_NB_LEVELS];
113 int has_nested_elems[SECTION_MAX_NB_LEVELS];
114 int terminate_line[SECTION_MAX_NB_LEVELS];
115} CompactContext;
116
117#undef OFFSET
118#define OFFSET(x) offsetof(CompactContext, x)
119
120static const AVOption compact_options[]= {
121 {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 },
122 {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str="|"}, 0, 0 },
123 {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
124 {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
125 {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 },
126 {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="c"}, 0, 0 },
127 {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
128 {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
129 {NULL},
130};
131
132DEFINE_FORMATTER_CLASS(compact);
133
134static av_cold int compact_init(AVTextFormatContext *wctx)
135{
136 CompactContext *compact = wctx->priv;
137
138 if (strlen(compact->item_sep_str) != 1) {
139 av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
140 compact->item_sep_str);
141 return AVERROR(EINVAL);
142 }
143 compact->item_sep = compact->item_sep_str[0];
144
145 if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str;
146 else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str;
147 else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str;
148 else {
149 av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
150 return AVERROR(EINVAL);
151 }
152
153 return 0;
154}
155
156static void compact_print_section_header(AVTextFormatContext *wctx, const void *data)
157{
158 CompactContext *compact = wctx->priv;
159 const struct AVTextFormatSection *section = wctx->section[wctx->level];
160 const struct AVTextFormatSection *parent_section = wctx->level ?
161 wctx->section[wctx->level-1] : NULL;
162 compact->terminate_line[wctx->level] = 1;
163 compact->has_nested_elems[wctx->level] = 0;
164
165 av_bprint_clear(&wctx->section_pbuf[wctx->level]);
166 if (parent_section &&
167 (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE ||
168 (!(section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) &&
169 !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))))) {
170
171 /* define a prefix for elements not contained in an array or
172 in a wrapper, or for array elements with a type */
173 const char *element_name = (char *)av_x_if_null(section->element_name, section->name);
174 AVBPrint *section_pbuf = &wctx->section_pbuf[wctx->level];
175
176 compact->nested_section[wctx->level] = 1;
177 compact->has_nested_elems[wctx->level-1] = 1;
178
179 av_bprintf(section_pbuf, "%s%s",
180 wctx->section_pbuf[wctx->level-1].str, element_name);
181
182 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) {
183 // add /TYPE to prefix
184 av_bprint_chars(section_pbuf, '/', 1);
185
186 // normalize section type, replace special characters and lower case
187 for (const char *p = section->get_type(data); *p; p++) {
188 char c =
189 (*p >= '0' && *p <= '9') ||
190 (*p >= 'a' && *p <= 'z') ||
191 (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_';
192 av_bprint_chars(section_pbuf, c, 1);
193 }
194 }
195 av_bprint_chars(section_pbuf, ':', 1);
196
197 wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
198 } else {
199 if (parent_section && !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) &&
200 wctx->level && wctx->nb_item[wctx->level-1])
201 writer_w8(wctx, compact->item_sep);
202 if (compact->print_section &&
203 !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
204 writer_printf(wctx, "%s%c", section->name, compact->item_sep);
205 }
206}
207
208static void compact_print_section_footer(AVTextFormatContext *wctx)
209{
210 CompactContext *compact = wctx->priv;
211
212 if (!compact->nested_section[wctx->level] &&
213 compact->terminate_line[wctx->level] &&
214 !(wctx->section[wctx->level]->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
215 writer_w8(wctx, '\n');
216}
217
218static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
219{
220 CompactContext *compact = wctx->priv;
221 AVBPrint buf;
222
223 if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
224 if (!compact->nokey)
225 writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
226 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
227 writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx));
228 av_bprint_finalize(&buf, NULL);
229}
230
231static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
232{
233 CompactContext *compact = wctx->priv;
234
235 if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep);
236 if (!compact->nokey)
237 writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
238 writer_printf(wctx, "%"PRId64, value);
239}
240
241const AVTextFormatter avtextformatter_compact = {
242 .name = "compact",
243 .priv_size = sizeof(CompactContext),
244 .init = compact_init,
245 .print_section_header = compact_print_section_header,
246 .print_section_footer = compact_print_section_footer,
247 .print_integer = compact_print_int,
248 .print_string = compact_print_str,
249 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
250 .priv_class = &compact_class,
251};
252
253/* CSV output */
254
255#undef OFFSET
256#define OFFSET(x) offsetof(CompactContext, x)
257
258static const AVOption csv_options[] = {
259 {"item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 },
260 {"s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, {.str=","}, 0, 0 },
261 {"nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
262 {"nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
263 {"escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
264 {"e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, {.str="csv"}, 0, 0 },
265 {"print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
266 {"p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
267 {NULL},
268};
269
270DEFINE_FORMATTER_CLASS(csv);
271
272const AVTextFormatter avtextformatter_csv = {
273 .name = "csv",
274 .priv_size = sizeof(CompactContext),
275 .init = compact_init,
276 .print_section_header = compact_print_section_header,
277 .print_section_footer = compact_print_section_footer,
278 .print_integer = compact_print_int,
279 .print_string = compact_print_str,
280 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
281 .priv_class = &csv_class,
282};
fftools/textformat/tf_default.c+145−0
@@ -0,0 +1,145 @@
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 "avtextformat.h"
28#include <libavutil/mem.h>
29#include <libavutil/avassert.h>
30#include <libavutil/bprint.h>
31#include <libavutil/opt.h>
32
33#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
34#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
35#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
36
37#define DEFINE_FORMATTER_CLASS(name) \
38static const char *name##_get_name(void *ctx) \
39{ \
40 return #name ; \
41} \
42static const AVClass name##_class = { \
43 .class_name = #name, \
44 .item_name = name##_get_name, \
45 .option = name##_options \
46}
47
48/* Default output */
49
50typedef struct DefaultContext {
51 const AVClass *class;
52 int nokey;
53 int noprint_wrappers;
54 int nested_section[SECTION_MAX_NB_LEVELS];
55} DefaultContext;
56
57#undef OFFSET
58#define OFFSET(x) offsetof(DefaultContext, x)
59
60static const AVOption default_options[] = {
61 { "noprint_wrappers", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
62 { "nw", "do not print headers and footers", OFFSET(noprint_wrappers), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
63 { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
64 { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
65 {NULL},
66};
67
68DEFINE_FORMATTER_CLASS(default);
69
70/* lame uppercasing routine, assumes the string is lower case ASCII */
71static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
72{
73 int i;
74 for (i = 0; src[i] && i < dst_size-1; i++)
75 dst[i] = av_toupper(src[i]);
76 dst[i] = 0;
77 return dst;
78}
79
80static void default_print_section_header(AVTextFormatContext *wctx, const void *data)
81{
82 DefaultContext *def = wctx->priv;
83 char buf[32];
84 const struct AVTextFormatSection *section = wctx->section[wctx->level];
85 const struct AVTextFormatSection *parent_section = wctx->level ?
86 wctx->section[wctx->level-1] : NULL;
87
88 av_bprint_clear(&wctx->section_pbuf[wctx->level]);
89 if (parent_section &&
90 !(parent_section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY))) {
91 def->nested_section[wctx->level] = 1;
92 av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
93 wctx->section_pbuf[wctx->level-1].str,
94 upcase_string(buf, sizeof(buf),
95 av_x_if_null(section->element_name, section->name)));
96 }
97
98 if (def->noprint_wrappers || def->nested_section[wctx->level])
99 return;
100
101 if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
102 writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name));
103}
104
105static void default_print_section_footer(AVTextFormatContext *wctx)
106{
107 DefaultContext *def = wctx->priv;
108 const struct AVTextFormatSection *section = wctx->section[wctx->level];
109 char buf[32];
110
111 if (def->noprint_wrappers || def->nested_section[wctx->level])
112 return;
113
114 if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER|AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)))
115 writer_printf(wctx, "[/%s]\n", upcase_string(buf, sizeof(buf), section->name));
116}
117
118static void default_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
119{
120 DefaultContext *def = wctx->priv;
121
122 if (!def->nokey)
123 writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
124 writer_printf(wctx, "%s\n", value);
125}
126
127static void default_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
128{
129 DefaultContext *def = wctx->priv;
130
131 if (!def->nokey)
132 writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
133 writer_printf(wctx, "%"PRId64"\n", value);
134}
135
136const AVTextFormatter avtextformatter_default = {
137 .name = "default",
138 .priv_size = sizeof(DefaultContext),
139 .print_section_header = default_print_section_header,
140 .print_section_footer = default_print_section_footer,
141 .print_integer = default_print_int,
142 .print_string = default_print_str,
143 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS,
144 .priv_class = &default_class,
145};
fftools/textformat/tf_flat.c+174−0
@@ -0,0 +1,174 @@
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 "avtextformat.h"
28#include <libavutil/mem.h>
29#include <libavutil/avassert.h>
30#include <libavutil/bprint.h>
31#include <libavutil/error.h>
32#include <libavutil/macros.h>
33#include <libavutil/opt.h>
34
35#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
36#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
37#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
38
39#define DEFINE_FORMATTER_CLASS(name) \
40static const char *name##_get_name(void *ctx) \
41{ \
42 return #name ; \
43} \
44static const AVClass name##_class = { \
45 .class_name = #name, \
46 .item_name = name##_get_name, \
47 .option = name##_options \
48}
49
50
51/* Flat output */
52
53typedef struct FlatContext {
54 const AVClass *class;
55 const char *sep_str;
56 char sep;
57 int hierarchical;
58} FlatContext;
59
60#undef OFFSET
61#define OFFSET(x) offsetof(FlatContext, x)
62
63static const AVOption flat_options[]= {
64 {"sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 },
65 {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, 0, 0 },
66 {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
67 {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
68 {NULL},
69};
70
71DEFINE_FORMATTER_CLASS(flat);
72
73static av_cold int flat_init(AVTextFormatContext *wctx)
74{
75 FlatContext *flat = wctx->priv;
76
77 if (strlen(flat->sep_str) != 1) {
78 av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
79 flat->sep_str);
80 return AVERROR(EINVAL);
81 }
82 flat->sep = flat->sep_str[0];
83
84 return 0;
85}
86
87static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep)
88{
89 const char *p;
90
91 for (p = src; *p; p++) {
92 if (!((*p >= '0' && *p <= '9') ||
93 (*p >= 'a' && *p <= 'z') ||
94 (*p >= 'A' && *p <= 'Z')))
95 av_bprint_chars(dst, '_', 1);
96 else
97 av_bprint_chars(dst, *p, 1);
98 }
99 return dst->str;
100}
101
102static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
103{
104 const char *p;
105
106 for (p = src; *p; p++) {
107 switch (*p) {
108 case '\n': av_bprintf(dst, "%s", "\\n"); break;
109 case '\r': av_bprintf(dst, "%s", "\\r"); break;
110 case '\\': av_bprintf(dst, "%s", "\\\\"); break;
111 case '"': av_bprintf(dst, "%s", "\\\""); break;
112 case '`': av_bprintf(dst, "%s", "\\`"); break;
113 case '$': av_bprintf(dst, "%s", "\\$"); break;
114 default: av_bprint_chars(dst, *p, 1); break;
115 }
116 }
117 return dst->str;
118}
119
120static void flat_print_section_header(AVTextFormatContext *wctx, const void *data)
121{
122 FlatContext *flat = wctx->priv;
123 AVBPrint *buf = &wctx->section_pbuf[wctx->level];
124 const struct AVTextFormatSection *section = wctx->section[wctx->level];
125 const struct AVTextFormatSection *parent_section = wctx->level ?
126 wctx->section[wctx->level-1] : NULL;
127
128 /* build section header */
129 av_bprint_clear(buf);
130 if (!parent_section)
131 return;
132 av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
133
134 if (flat->hierarchical ||
135 !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) {
136 av_bprintf(buf, "%s%s", wctx->section[wctx->level]->name, flat->sep_str);
137
138 if (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
139 int n = parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE ?
140 wctx->nb_item_type[wctx->level-1][section->id] :
141 wctx->nb_item[wctx->level-1];
142 av_bprintf(buf, "%d%s", n, flat->sep_str);
143 }
144 }
145}
146
147static void flat_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
148{
149 writer_printf(wctx, "%s%s=%"PRId64"\n", wctx->section_pbuf[wctx->level].str, key, value);
150}
151
152static void flat_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
153{
154 FlatContext *flat = wctx->priv;
155 AVBPrint buf;
156
157 writer_put_str(wctx, wctx->section_pbuf[wctx->level].str);
158 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
159 writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep));
160 av_bprint_clear(&buf);
161 writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value));
162 av_bprint_finalize(&buf, NULL);
163}
164
165const AVTextFormatter avtextformatter_flat = {
166 .name = "flat",
167 .priv_size = sizeof(FlatContext),
168 .init = flat_init,
169 .print_section_header = flat_print_section_header,
170 .print_integer = flat_print_int,
171 .print_string = flat_print_str,
172 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
173 .priv_class = &flat_class,
174};
fftools/textformat/tf_ini.c+160−0
@@ -0,0 +1,160 @@
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 "avtextformat.h"
28#include <libavutil/mem.h>
29#include <libavutil/avassert.h>
30#include <libavutil/bprint.h>
31#include <libavutil/opt.h>
32
33#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
34#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
35#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
36
37#define DEFINE_FORMATTER_CLASS(name) \
38static const char *name##_get_name(void *ctx) \
39{ \
40 return #name ; \
41} \
42static const AVClass name##_class = { \
43 .class_name = #name, \
44 .item_name = name##_get_name, \
45 .option = name##_options \
46}
47
48/* Default output */
49
50typedef struct DefaultContext {
51 const AVClass *class;
52 int nokey;
53 int noprint_wrappers;
54 int nested_section[SECTION_MAX_NB_LEVELS];
55} DefaultContext;
56
57/* INI format output */
58
59typedef struct INIContext {
60 const AVClass *class;
61 int hierarchical;
62} INIContext;
63
64#undef OFFSET
65#define OFFSET(x) offsetof(INIContext, x)
66
67static const AVOption ini_options[] = {
68 {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
69 {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1 },
70 {NULL},
71};
72
73DEFINE_FORMATTER_CLASS(ini);
74
75static char *ini_escape_str(AVBPrint *dst, const char *src)
76{
77 int i = 0;
78 char c = 0;
79
80 while (c = src[i++]) {
81 switch (c) {
82 case '\b': av_bprintf(dst, "%s", "\\b"); break;
83 case '\f': av_bprintf(dst, "%s", "\\f"); break;
84 case '\n': av_bprintf(dst, "%s", "\\n"); break;
85 case '\r': av_bprintf(dst, "%s", "\\r"); break;
86 case '\t': av_bprintf(dst, "%s", "\\t"); break;
87 case '\\':
88 case '#' :
89 case '=' :
90 case ':' : av_bprint_chars(dst, '\\', 1);
91 default:
92 if ((unsigned char)c < 32)
93 av_bprintf(dst, "\\x00%02x", c & 0xff);
94 else
95 av_bprint_chars(dst, c, 1);
96 break;
97 }
98 }
99 return dst->str;
100}
101
102static void ini_print_section_header(AVTextFormatContext *wctx, const void *data)
103{
104 INIContext *ini = wctx->priv;
105 AVBPrint *buf = &wctx->section_pbuf[wctx->level];
106 const struct AVTextFormatSection *section = wctx->section[wctx->level];
107 const struct AVTextFormatSection *parent_section = wctx->level ?
108 wctx->section[wctx->level-1] : NULL;
109
110 av_bprint_clear(buf);
111 if (!parent_section) {
112 writer_put_str(wctx, "# ffprobe output\n\n");
113 return;
114 }
115
116 if (wctx->nb_item[wctx->level-1])
117 writer_w8(wctx, '\n');
118
119 av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
120 if (ini->hierarchical ||
121 !(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER))) {
122 av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
123
124 if (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
125 int n = parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE ?
126 wctx->nb_item_type[wctx->level-1][section->id] :
127 wctx->nb_item[wctx->level-1];
128 av_bprintf(buf, ".%d", n);
129 }
130 }
131
132 if (!(section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER)))
133 writer_printf(wctx, "[%s]\n", buf->str);
134}
135
136static void ini_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
137{
138 AVBPrint buf;
139
140 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
141 writer_printf(wctx, "%s=", ini_escape_str(&buf, key));
142 av_bprint_clear(&buf);
143 writer_printf(wctx, "%s\n", ini_escape_str(&buf, value));
144 av_bprint_finalize(&buf, NULL);
145}
146
147static void ini_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
148{
149 writer_printf(wctx, "%s=%"PRId64"\n", key, value);
150}
151
152const AVTextFormatter avtextformatter_ini = {
153 .name = "ini",
154 .priv_size = sizeof(INIContext),
155 .print_section_header = ini_print_section_header,
156 .print_integer = ini_print_int,
157 .print_string = ini_print_str,
158 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS|AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
159 .priv_class = &ini_class,
160};
fftools/textformat/tf_json.c+215−0
@@ -0,0 +1,215 @@
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 "avtextformat.h"
28#include <libavutil/mem.h>
29#include <libavutil/avassert.h>
30#include <libavutil/bprint.h>
31#include <libavutil/opt.h>
32
33#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
34#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
35#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
36
37#define DEFINE_FORMATTER_CLASS(name) \
38static const char *name##_get_name(void *ctx) \
39{ \
40 return #name ; \
41} \
42static const AVClass name##_class = { \
43 .class_name = #name, \
44 .item_name = name##_get_name, \
45 .option = name##_options \
46}
47
48
49/* JSON output */
50
51typedef struct JSONContext {
52 const AVClass *class;
53 int indent_level;
54 int compact;
55 const char *item_sep, *item_start_end;
56} JSONContext;
57
58#undef OFFSET
59#define OFFSET(x) offsetof(JSONContext, x)
60
61static const AVOption json_options[]= {
62 { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
63 { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
64 { NULL }
65};
66
67DEFINE_FORMATTER_CLASS(json);
68
69static av_cold int json_init(AVTextFormatContext *wctx)
70{
71 JSONContext *json = wctx->priv;
72
73 json->item_sep = json->compact ? ", " : ",\n";
74 json->item_start_end = json->compact ? " " : "\n";
75
76 return 0;
77}
78
79static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
80{
81 static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
82 static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0};
83 const char *p;
84
85 for (p = src; *p; p++) {
86 char *s = strchr(json_escape, *p);
87 if (s) {
88 av_bprint_chars(dst, '\\', 1);
89 av_bprint_chars(dst, json_subst[s - json_escape], 1);
90 } else if ((unsigned char)*p < 32) {
91 av_bprintf(dst, "\\u00%02x", *p & 0xff);
92 } else {
93 av_bprint_chars(dst, *p, 1);
94 }
95 }
96 return dst->str;
97}
98
99#define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
100
101static void json_print_section_header(AVTextFormatContext *wctx, const void *data)
102{
103 JSONContext *json = wctx->priv;
104 AVBPrint buf;
105 const struct AVTextFormatSection *section = wctx->section[wctx->level];
106 const struct AVTextFormatSection *parent_section = wctx->level ?
107 wctx->section[wctx->level-1] : NULL;
108
109 if (wctx->level && wctx->nb_item[wctx->level-1])
110 writer_put_str(wctx, ",\n");
111
112 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) {
113 writer_put_str(wctx, "{\n");
114 json->indent_level++;
115 } else {
116 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
117 json_escape_str(&buf, section->name, wctx);
118 JSON_INDENT();
119
120 json->indent_level++;
121 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
122 writer_printf(wctx, "\"%s\": [\n", buf.str);
123 } else if (parent_section && !(parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) {
124 writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end);
125 } else {
126 writer_printf(wctx, "{%s", json->item_start_end);
127
128 /* this is required so the parser can distinguish between packets and frames */
129 if (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE) {
130 if (!json->compact)
131 JSON_INDENT();
132 writer_printf(wctx, "\"type\": \"%s\"", section->name);
133 wctx->nb_item[wctx->level]++;
134 }
135 }
136 av_bprint_finalize(&buf, NULL);
137 }
138}
139
140static void json_print_section_footer(AVTextFormatContext *wctx)
141{
142 JSONContext *json = wctx->priv;
143 const struct AVTextFormatSection *section = wctx->section[wctx->level];
144
145 if (wctx->level == 0) {
146 json->indent_level--;
147 writer_put_str(wctx, "\n}\n");
148 } else if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
149 writer_w8(wctx, '\n');
150 json->indent_level--;
151 JSON_INDENT();
152 writer_w8(wctx, ']');
153 } else {
154 writer_put_str(wctx, json->item_start_end);
155 json->indent_level--;
156 if (!json->compact)
157 JSON_INDENT();
158 writer_w8(wctx, '}');
159 }
160}
161
162static inline void json_print_item_str(AVTextFormatContext *wctx,
163 const char *key, const char *value)
164{
165 AVBPrint buf;
166
167 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
168 writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx));
169 av_bprint_clear(&buf);
170 writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx));
171 av_bprint_finalize(&buf, NULL);
172}
173
174static void json_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
175{
176 JSONContext *json = wctx->priv;
177 const struct AVTextFormatSection *parent_section = wctx->level ?
178 wctx->section[wctx->level-1] : NULL;
179
180 if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
181 writer_put_str(wctx, json->item_sep);
182 if (!json->compact)
183 JSON_INDENT();
184 json_print_item_str(wctx, key, value);
185}
186
187static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
188{
189 JSONContext *json = wctx->priv;
190 const struct AVTextFormatSection *parent_section = wctx->level ?
191 wctx->section[wctx->level-1] : NULL;
192 AVBPrint buf;
193
194 if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
195 writer_put_str(wctx, json->item_sep);
196 if (!json->compact)
197 JSON_INDENT();
198
199 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
200 writer_printf(wctx, "\"%s\": %"PRId64, json_escape_str(&buf, key, wctx), value);
201 av_bprint_finalize(&buf, NULL);
202}
203
204const AVTextFormatter avtextformatter_json = {
205 .name = "json",
206 .priv_size = sizeof(JSONContext),
207 .init = json_init,
208 .print_section_header = json_print_section_header,
209 .print_section_footer = json_print_section_footer,
210 .print_integer = json_print_int,
211 .print_string = json_print_str,
212 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
213 .priv_class = &json_class,
214};
215
fftools/textformat/tf_xml.c+221−0
@@ -0,0 +1,221 @@
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 "avtextformat.h"
28#include <libavutil/mem.h>
29#include <libavutil/avassert.h>
30#include <libavutil/bprint.h>
31#include <libavutil/error.h>
32#include <libavutil/macros.h>
33#include <libavutil/opt.h>
34
35#define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
36#define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
37#define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
38
39#define DEFINE_FORMATTER_CLASS(name) \
40static const char *name##_get_name(void *ctx) \
41{ \
42 return #name ; \
43} \
44static const AVClass name##_class = { \
45 .class_name = #name, \
46 .item_name = name##_get_name, \
47 .option = name##_options \
48}
49
50/* XML output */
51
52typedef struct XMLContext {
53 const AVClass *class;
54 int within_tag;
55 int indent_level;
56 int fully_qualified;
57 int xsd_strict;
58} XMLContext;
59
60#undef OFFSET
61#define OFFSET(x) offsetof(XMLContext, x)
62
63static const AVOption xml_options[] = {
64 {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
65 {"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
66 {"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
67 {"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
68 {NULL},
69};
70
71DEFINE_FORMATTER_CLASS(xml);
72
73static av_cold int xml_init(AVTextFormatContext *wctx)
74{
75 XMLContext *xml = wctx->priv;
76
77 if (xml->xsd_strict) {
78 xml->fully_qualified = 1;
79#define CHECK_COMPLIANCE(opt, opt_name) \
80 if (opt) { \
81 av_log(wctx, AV_LOG_ERROR, \
82 "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
83 "You need to disable such option with '-no%s'\n", opt_name, opt_name); \
84 return AVERROR(EINVAL); \
85 }
86 ////CHECK_COMPLIANCE(show_private_data, "private");
87 CHECK_COMPLIANCE(wctx->show_value_unit, "unit");
88 CHECK_COMPLIANCE(wctx->use_value_prefix, "prefix");
89 }
90
91 return 0;
92}
93
94#define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ')
95
96static void xml_print_section_header(AVTextFormatContext *wctx, const void *data)
97{
98 XMLContext *xml = wctx->priv;
99 const struct AVTextFormatSection *section = wctx->section[wctx->level];
100 const struct AVTextFormatSection *parent_section = wctx->level ?
101 wctx->section[wctx->level-1] : NULL;
102
103 if (wctx->level == 0) {
104 const char *qual = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
105 "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" "
106 "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\"";
107
108 writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
109 writer_printf(wctx, "<%sffprobe%s>\n",
110 xml->fully_qualified ? "ffprobe:" : "",
111 xml->fully_qualified ? qual : "");
112 return;
113 }
114
115 if (xml->within_tag) {
116 xml->within_tag = 0;
117 writer_put_str(wctx, ">\n");
118 }
119
120 if (parent_section && (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) &&
121 wctx->level && wctx->nb_item[wctx->level-1])
122 writer_w8(wctx, '\n');
123 xml->indent_level++;
124
125 if (section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS)) {
126 XML_INDENT(); writer_printf(wctx, "<%s", section->name);
127
128 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) {
129 AVBPrint buf;
130 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
131 av_bprint_escape(&buf, section->get_type(data), NULL,
132 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
133 writer_printf(wctx, " type=\"%s\"", buf.str);
134 }
135 writer_printf(wctx, ">\n", section->name);
136 } else {
137 XML_INDENT(); writer_printf(wctx, "<%s ", section->name);
138 xml->within_tag = 1;
139 }
140}
141
142static void xml_print_section_footer(AVTextFormatContext *wctx)
143{
144 XMLContext *xml = wctx->priv;
145 const struct AVTextFormatSection *section = wctx->section[wctx->level];
146
147 if (wctx->level == 0) {
148 writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
149 } else if (xml->within_tag) {
150 xml->within_tag = 0;
151 writer_put_str(wctx, "/>\n");
152 xml->indent_level--;
153 } else {
154 XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name);
155 xml->indent_level--;
156 }
157}
158
159static void xml_print_value(AVTextFormatContext *wctx, const char *key,
160 const char *str, int64_t num, const int is_int)
161{
162 AVBPrint buf;
163 XMLContext *xml = wctx->priv;
164 const struct AVTextFormatSection *section = wctx->section[wctx->level];
165
166 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
167
168 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS) {
169 xml->indent_level++;
170 XML_INDENT();
171 av_bprint_escape(&buf, key, NULL,
172 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
173 writer_printf(wctx, "<%s key=\"%s\"",
174 section->element_name, buf.str);
175 av_bprint_clear(&buf);
176
177 if (is_int) {
178 writer_printf(wctx, " value=\"%"PRId64"\"/>\n", num);
179 } else {
180 av_bprint_escape(&buf, str, NULL,
181 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
182 writer_printf(wctx, " value=\"%s\"/>\n", buf.str);
183 }
184 xml->indent_level--;
185 } else {
186 if (wctx->nb_item[wctx->level])
187 writer_w8(wctx, ' ');
188
189 if (is_int) {
190 writer_printf(wctx, "%s=\"%"PRId64"\"", key, num);
191 } else {
192 av_bprint_escape(&buf, str, NULL,
193 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
194 writer_printf(wctx, "%s=\"%s\"", key, buf.str);
195 }
196 }
197
198 av_bprint_finalize(&buf, NULL);
199}
200
201static inline void xml_print_str(AVTextFormatContext *wctx, const char *key, const char *value) {
202 xml_print_value(wctx, key, value, 0, 0);
203}
204
205static void xml_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
206{
207 xml_print_value(wctx, key, NULL, value, 1);
208}
209
210const AVTextFormatter avtextformatter_xml = {
211 .name = "xml",
212 .priv_size = sizeof(XMLContext),
213 .init = xml_init,
214 .print_section_header = xml_print_section_header,
215 .print_section_footer = xml_print_section_footer,
216 .print_integer = xml_print_int,
217 .print_string = xml_print_str,
218 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
219 .priv_class = &xml_class,
220};
221
fftools/textformat/tw_avio.c+129−0
@@ -0,0 +1,129 @@
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
24#include "avtextwriters.h"
25#include "libavutil/opt.h"
26
27/* AVIO Writer */
28
29# define WRITER_NAME "aviowriter"
30
31typedef struct IOWriterContext {
32 const AVClass *class;
33 AVIOContext *avio_context;
34 int close_on_uninit;
35} IOWriterContext;
36
37static const char *iowriter_get_name(void *ctx)
38{
39 return WRITER_NAME;
40}
41
42static const AVClass iowriter_class = {
43 .class_name = WRITER_NAME,
44 .item_name = iowriter_get_name,
45};
46
47static av_cold void iowriter_uninit(AVTextWriterContext *wctx)
48{
49 IOWriterContext *ctx = wctx->priv;
50
51 if (ctx->close_on_uninit && ctx->avio_context) {
52 avio_flush(ctx->avio_context);
53 avio_close(ctx->avio_context);
54 }
55}
56
57static void io_w8(AVTextWriterContext *wctx, int b)
58{
59 IOWriterContext *ctx = wctx->priv;
60 avio_w8(ctx->avio_context, b);
61}
62
63static void io_put_str(AVTextWriterContext *wctx, const char *str)
64{
65 IOWriterContext *ctx = wctx->priv;
66 avio_write(ctx->avio_context, str, strlen(str));
67}
68
69static void io_printf(AVTextWriterContext *wctx, const char *fmt, ...)
70{
71 IOWriterContext *ctx = wctx->priv;
72 va_list ap;
73
74 va_start(ap, fmt);
75 avio_vprintf(ctx->avio_context, fmt, ap);
76 va_end(ap);
77}
78
79
80const AVTextWriter avtextwriter_avio = {
81 .name = WRITER_NAME,
82 .priv_size = sizeof(IOWriterContext),
83 .uninit = iowriter_uninit,
84 .priv_class = &iowriter_class,
85 .writer_put_str = io_put_str,
86 .writer_printf = io_printf,
87 .writer_w8 = io_w8
88};
89
90int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename, int close_on_uninit)
91{
92 IOWriterContext *ctx;
93 int ret;
94
95
96 ret = avtextwriter_context_open(pwctx, &avtextwriter_avio);
97 if (ret < 0)
98 return ret;
99
100 ctx = (*pwctx)->priv;
101
102 if ((ret = avio_open(&ctx->avio_context, output_filename, AVIO_FLAG_WRITE)) < 0) {
103 av_log(ctx, AV_LOG_ERROR,
104 "Failed to open output '%s' with error: %s\n", output_filename, av_err2str(ret));
105 avtextwriter_context_close(pwctx);
106 return ret;
107 }
108
109 ctx->close_on_uninit = close_on_uninit;
110
111 return ret;
112}
113
114
115int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit)
116{
117 IOWriterContext *ctx;
118 int ret;
119
120 ret = avtextwriter_context_open(pwctx, &avtextwriter_avio);
121 if (ret < 0)
122 return ret;
123
124 ctx = (*pwctx)->priv;
125 ctx->avio_context = avio_ctx;
126 ctx->close_on_uninit = close_on_uninit;
127
128 return ret;
129}
fftools/textformat/tw_buffer.c+92−0
@@ -0,0 +1,92 @@
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
24#include "avtextwriters.h"
25#include "libavutil/opt.h"
26#include "libavutil/bprint.h"
27
28/* Buffer Writer */
29
30# define WRITER_NAME "bufferwriter"
31
32typedef struct BufferWriterContext {
33 const AVClass *class;
34 AVBPrint *buffer;
35} BufferWriterContext;
36
37static const char *bufferwriter_get_name(void *ctx)
38{
39 return WRITER_NAME;
40}
41
42static const AVClass bufferwriter_class = {
43 .class_name = WRITER_NAME,
44 .item_name = bufferwriter_get_name,
45};
46
47static void buffer_w8(AVTextWriterContext *wctx, int b)
48{
49 BufferWriterContext *ctx = wctx->priv;
50 av_bprintf(ctx->buffer, "%c", b);
51}
52
53static void buffer_put_str(AVTextWriterContext *wctx, const char *str)
54{
55 BufferWriterContext *ctx = wctx->priv;
56 av_bprintf(ctx->buffer, "%s", str);
57}
58
59static void buffer_printf(AVTextWriterContext *wctx, const char *fmt, ...)
60{
61 BufferWriterContext *ctx = wctx->priv;
62
63 va_list vargs;
64 va_start(vargs, fmt);
65 av_vbprintf(ctx->buffer, fmt, vargs);
66 va_end(vargs);
67}
68
69
70const AVTextWriter avtextwriter_buffer = {
71 .name = WRITER_NAME,
72 .priv_size = sizeof(BufferWriterContext),
73 .priv_class = &bufferwriter_class,
74 .writer_put_str = buffer_put_str,
75 .writer_printf = buffer_printf,
76 .writer_w8 = buffer_w8
77};
78
79int avtextwriter_create_buffer(AVTextWriterContext **pwctx, AVBPrint *buffer)
80{
81 BufferWriterContext *ctx;
82 int ret;
83
84 ret = avtextwriter_context_open(pwctx, &avtextwriter_buffer);
85 if (ret < 0)
86 return ret;
87
88 ctx = (*pwctx)->priv;
89 ctx->buffer = buffer;
90
91 return ret;
92}
fftools/textformat/tw_stdout.c+82−0
@@ -0,0 +1,82 @@
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 <stdarg.h>
22#include <stdio.h>
23#include <string.h>
24
25#include "avtextwriters.h"
26#include "libavutil/opt.h"
27
28/* STDOUT Writer */
29
30# define WRITER_NAME "stdoutwriter"
31
32typedef struct StdOutWriterContext {
33 const AVClass *class;
34} StdOutWriterContext;
35
36static const char *stdoutwriter_get_name(void *ctx)
37{
38 return WRITER_NAME;
39}
40
41static const AVClass stdoutwriter_class = {
42 .class_name = WRITER_NAME,
43 .item_name = stdoutwriter_get_name,
44};
45
46static inline void stdout_w8(AVTextWriterContext *wctx, int b)
47{
48 printf("%c", b);
49}
50
51static inline void stdout_put_str(AVTextWriterContext *wctx, const char *str)
52{
53 printf("%s", str);
54}
55
56static inline void stdout_printf(AVTextWriterContext *wctx, const char *fmt, ...)
57{
58 va_list ap;
59
60 va_start(ap, fmt);
61 vprintf(fmt, ap);
62 va_end(ap);
63}
64
65
66static const AVTextWriter avtextwriter_stdout = {
67 .name = WRITER_NAME,
68 .priv_size = sizeof(StdOutWriterContext),
69 .priv_class = &stdoutwriter_class,
70 .writer_put_str = stdout_put_str,
71 .writer_printf = stdout_printf,
72 .writer_w8 = stdout_w8
73};
74
75int avtextwriter_create_stdout(AVTextWriterContext **pwctx)
76{
77 int ret;
78
79 ret = avtextwriter_context_open(pwctx, &avtextwriter_stdout);
80
81 return ret;
82}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t