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+
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+static 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+
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+int 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+
128+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+ 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+
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+void 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+
272+void 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+
288+void 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+
299+static 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+
349+end:
350+ av_bprint_finalize(&dstbuf, dstp);
351+ return ret;
352+}
353+
354+struct unit_value {
355+ union { double d; int64_t i; } val;
356+ const char *unit;
357+};
358+
359+static 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+
413+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+ 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+
423+int 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+
460+void 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+
469+void 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+
486+void 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+
495+void 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+
523+void 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+
538+void 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+
564+static const char *writercontext_get_writer_name(void *p)
565+{
566+ AVTextWriterContext *wctx = p;
567+ return wctx->writer->name;
568+}
569+
570+static 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+
578+static 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+
586+int 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+
604+int 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+
640+fail:
641+ avtextwriter_context_close(&wctx);
642+ return ret;
643+}
644+
645+static const AVTextFormatter *registered_formatters[7+1];
646+static 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+
663+const 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+
37+typedef 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+
57+typedef struct AVTextFormatContext AVTextFormatContext;
58+
59+#define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS 1
60+#define AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT 2
61+
62+typedef 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+
69+typedef 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+
88+struct 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+
126+int 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+
135+int avtext_context_close(AVTextFormatContext **tctx);
136+
137+
138+void avtext_print_section_header(AVTextFormatContext *tctx, const void *data, int section_id);
139+
140+void avtext_print_section_footer(AVTextFormatContext *tctx);
141+
142+void avtext_print_integer(AVTextFormatContext *tctx, const char *key, int64_t val);
143+
144+int avtext_print_string(AVTextFormatContext *tctx, const char *key, const char *val, int flags);
145+
146+void avtext_print_unit_int(AVTextFormatContext *tctx, const char *key, int value, const char *unit);
147+
148+void avtext_print_rational(AVTextFormatContext *tctx, const char *key, AVRational q, char sep);
149+
150+void avtext_print_time(AVTextFormatContext *tctx, const char *key, int64_t ts, const AVRational *time_base, int is_duration);
151+
152+void avtext_print_ts(AVTextFormatContext *tctx, const char *key, int64_t ts, int is_duration);
153+
154+void avtext_print_data(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size);
155+
156+void avtext_print_data_hash(AVTextFormatContext *tctx, const char *name, const uint8_t *data, int size);
157+
158+void 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+
161+const AVTextFormatter *avtext_get_formatter_by_name(const char *name);
162+
163+extern const AVTextFormatter avtextformatter_default;
164+extern const AVTextFormatter avtextformatter_compact;
165+extern const AVTextFormatter avtextformatter_csv;
166+extern const AVTextFormatter avtextformatter_flat;
167+extern const AVTextFormatter avtextformatter_ini;
168+extern const AVTextFormatter avtextformatter_json;
169+extern 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+
33+typedef struct AVTextWriterContext AVTextWriterContext;
34+
35+typedef 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+
47+typedef 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+
56+int avtextwriter_context_open(AVTextWriterContext **pwctx, const AVTextWriter *writer);
57+
58+int avtextwriter_context_close(AVTextWriterContext **pwctx);
59+
60+int avtextwriter_create_stdout(AVTextWriterContext **pwctx);
61+
62+int avtextwriter_create_avio(AVTextWriterContext **pwctx, AVIOContext *avio_ctx, int close_on_uninit);
63+
64+int avtextwriter_create_file(AVTextWriterContext **pwctx, const char *output_filename, int close_on_uninit);
65+
66+int 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) \
42+static const char *name##_get_name(void *ctx) \
43+{ \
44+ return #name ; \
45+} \
46+static 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+ */
58+static 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+ */
81+static 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+
99+static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
100+{
101+ return src;
102+}
103+
104+typedef 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+
120+static 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+
132+DEFINE_FORMATTER_CLASS(compact);
133+
134+static 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+
156+static 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+
208+static 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+
218+static 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+
231+static 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+
241+const 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+
258+static 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+
270+DEFINE_FORMATTER_CLASS(csv);
271+
272+const 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) \
38+static const char *name##_get_name(void *ctx) \
39+{ \
40+ return #name ; \
41+} \
42+static const AVClass name##_class = { \
43+ .class_name = #name, \
44+ .item_name = name##_get_name, \
45+ .option = name##_options \
46+}
47+
48+/* Default output */
49+
50+typedef 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+
60+static 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+
68+DEFINE_FORMATTER_CLASS(default);
69+
70+/* lame uppercasing routine, assumes the string is lower case ASCII */
71+static 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+
80+static 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+
105+static 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+
118+static 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+
127+static 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+
136+const 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) \
40+static const char *name##_get_name(void *ctx) \
41+{ \
42+ return #name ; \
43+} \
44+static 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+
53+typedef 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+
63+static 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+
71+DEFINE_FORMATTER_CLASS(flat);
72+
73+static 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+
87+static 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+
102+static 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+
120+static 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+
147+static 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+
152+static 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+
165+const 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) \
38+static const char *name##_get_name(void *ctx) \
39+{ \
40+ return #name ; \
41+} \
42+static const AVClass name##_class = { \
43+ .class_name = #name, \
44+ .item_name = name##_get_name, \
45+ .option = name##_options \
46+}
47+
48+/* Default output */
49+
50+typedef 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+
59+typedef struct INIContext {
60+ const AVClass *class;
61+ int hierarchical;
62+} INIContext;
63+
64+#undef OFFSET
65+#define OFFSET(x) offsetof(INIContext, x)
66+
67+static 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+
73+DEFINE_FORMATTER_CLASS(ini);
74+
75+static 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+
102+static 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+
136+static 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+
147+static void ini_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
148+{
149+ writer_printf(wctx, "%s=%"PRId64"\n", key, value);
150+}
151+
152+const 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) \
38+static const char *name##_get_name(void *ctx) \
39+{ \
40+ return #name ; \
41+} \
42+static 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+
51+typedef 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+
61+static 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+
67+DEFINE_FORMATTER_CLASS(json);
68+
69+static 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+
79+static 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+
101+static 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+
140+static 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+
162+static 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+
174+static 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+
187+static 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+
204+const 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) \
40+static const char *name##_get_name(void *ctx) \
41+{ \
42+ return #name ; \
43+} \
44+static const AVClass name##_class = { \
45+ .class_name = #name, \
46+ .item_name = name##_get_name, \
47+ .option = name##_options \
48+}
49+
50+/* XML output */
51+
52+typedef 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+
63+static 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+
71+DEFINE_FORMATTER_CLASS(xml);
72+
73+static 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+
96+static 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+
142+static 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+
159+static 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+
201+static inline void xml_print_str(AVTextFormatContext *wctx, const char *key, const char *value) {
202+ xml_print_value(wctx, key, value, 0, 0);
203+}
204+
205+static void xml_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
206+{
207+ xml_print_value(wctx, key, NULL, value, 1);
208+}
209+
210+const 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+
31+typedef struct IOWriterContext {
32+ const AVClass *class;
33+ AVIOContext *avio_context;
34+ int close_on_uninit;
35+} IOWriterContext;
36+
37+static const char *iowriter_get_name(void *ctx)
38+{
39+ return WRITER_NAME;
40+}
41+
42+static const AVClass iowriter_class = {
43+ .class_name = WRITER_NAME,
44+ .item_name = iowriter_get_name,
45+};
46+
47+static 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+
57+static void io_w8(AVTextWriterContext *wctx, int b)
58+{
59+ IOWriterContext *ctx = wctx->priv;
60+ avio_w8(ctx->avio_context, b);
61+}
62+
63+static 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+
69+static 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+
80+const 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+
90+int 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+
115+int 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+
32+typedef struct BufferWriterContext {
33+ const AVClass *class;
34+ AVBPrint *buffer;
35+} BufferWriterContext;
36+
37+static const char *bufferwriter_get_name(void *ctx)
38+{
39+ return WRITER_NAME;
40+}
41+
42+static const AVClass bufferwriter_class = {
43+ .class_name = WRITER_NAME,
44+ .item_name = bufferwriter_get_name,
45+};
46+
47+static void buffer_w8(AVTextWriterContext *wctx, int b)
48+{
49+ BufferWriterContext *ctx = wctx->priv;
50+ av_bprintf(ctx->buffer, "%c", b);
51+}
52+
53+static void buffer_put_str(AVTextWriterContext *wctx, const char *str)
54+{
55+ BufferWriterContext *ctx = wctx->priv;
56+ av_bprintf(ctx->buffer, "%s", str);
57+}
58+
59+static 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+
70+const 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+
79+int 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+
32+typedef struct StdOutWriterContext {
33+ const AVClass *class;
34+} StdOutWriterContext;
35+
36+static const char *stdoutwriter_get_name(void *ctx)
37+{
38+ return WRITER_NAME;
39+}
40+
41+static const AVClass stdoutwriter_class = {
42+ .class_name = WRITER_NAME,
43+ .item_name = stdoutwriter_get_name,
44+};
45+
46+static inline void stdout_w8(AVTextWriterContext *wctx, int b)
47+{
48+ printf("%c", b);
49+}
50+
51+static inline void stdout_put_str(AVTextWriterContext *wctx, const char *str)
52+{
53+ printf("%s", str);
54+}
55+
56+static 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+
66+static 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+
75+int 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