FFmpeg
Loading...
Searching...
No Matches
tf_compact.c
Go to the documentation of this file.
1/*
2 * Copyright (c) The FFmpeg developers
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <inttypes.h>
22#include <limits.h>
23#include <stdarg.h>
24#include <string.h>
25
26#include "avtextformat.h"
27#include "libavutil/avutil.h"
28#include "libavutil/bprint.h"
29#include "libavutil/error.h"
30#include "libavutil/opt.h"
31#include "tf_internal.h"
32
33
34/* Compact output */
35
36/**
37 * Apply C-language-like string escaping.
38 */
39static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
40{
41 const char *p;
42
43 for (p = src; *p; p++) {
44 switch (*p) {
45 case '\b': av_bprintf(dst, "%s", "\\b"); break;
46 case '\f': av_bprintf(dst, "%s", "\\f"); break;
47 case '\n': av_bprintf(dst, "%s", "\\n"); break;
48 case '\r': av_bprintf(dst, "%s", "\\r"); break;
49 case '\\': av_bprintf(dst, "%s", "\\\\"); break;
50 default:
51 if (*p == sep)
52 av_bprint_chars(dst, '\\', 1);
53 av_bprint_chars(dst, *p, 1);
54 }
55 }
56 return dst->str;
57}
58
59/**
60 * Quote fields containing special characters, check RFC4180.
61 */
62static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
63{
64 char meta_chars[] = { sep, '"', '\n', '\r', '\0' };
65
66 int needs_quoting = !!src[strcspn(src, meta_chars)];
67
68 if (needs_quoting)
69 av_bprint_chars(dst, '"', 1);
70
71 for (; *src; src++) {
72 if (*src == '"')
73 av_bprint_chars(dst, '"', 1);
75 }
76 if (needs_quoting)
77 av_bprint_chars(dst, '"', 1);
78 return dst->str;
79}
80
81static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
82{
83 return src;
84}
85
86typedef struct CompactContext {
87 const AVClass *class;
90 int nokey;
93 const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
94 /** level of the row whose line the fields at this level belong to, -1 for none */
96 /** number of fields written to the row at this level */
98 /** the line of the row at this level */
100 /** the lines of the rows nested in the row at this level */
103
104#undef OFFSET
105#define OFFSET(x) offsetof(CompactContext, x)
106
107static const AVOption compact_options[] = {
108 { "item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "|" }, 0, 0 },
109 { "s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "|" }, 0, 0 },
110 { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
111 { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
112 { "escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "c" }, 0, 0 },
113 { "e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "c" }, 0, 0 },
114 { "print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
115 { "p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
116 { NULL },
117};
118
120
122{
123 CompactContext *compact = wctx->priv;
124
125 for (int i = 0; i < SECTION_MAX_NB_LEVELS; i++) {
128 }
129
130 if (strlen(compact->item_sep_str) != 1) {
131 av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
132 compact->item_sep_str);
133 return AVERROR(EINVAL);
134 }
135 compact->item_sep = compact->item_sep_str[0];
136
137 if (!strcmp(compact->escape_mode_str, "none")) {
138 compact->escape_str = none_escape_str;
139 } else if (!strcmp(compact->escape_mode_str, "c" )) {
140 compact->escape_str = c_escape_str;
141 } else if (!strcmp(compact->escape_mode_str, "csv" )) {
142 compact->escape_str = csv_escape_str;
143 } else {
144 av_log(wctx, AV_LOG_ERROR, "Unknown escape mode '%s'\n", compact->escape_mode_str);
145 return AVERROR(EINVAL);
146 }
147
148 return 0;
149}
150
152{
153 CompactContext *compact = wctx->priv;
154
155 for (int i = 0; i < SECTION_MAX_NB_LEVELS; i++) {
156 av_bprint_finalize(&compact->line[i], NULL);
157 av_bprint_finalize(&compact->rows[i], NULL);
158 }
159
160 return 0;
161}
162
163static void bprint_append_bprint(AVBPrint *dst, const AVBPrint *src)
164{
165 av_bprint_append_data(dst, src->str, FFMIN(src->len, src->size - 1));
166}
167
169{
170 CompactContext *compact = wctx->priv;
171 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
172 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
173 int level = wctx->level;
174
175 if (!section)
176 return;
177
179 compact->row_level[level] = level ? compact->row_level[level - 1] : -1;
180
181 if (parent_section &&
185
186 /* define a prefix for elements not contained in an array or
187 in a wrapper, or for array elements with a type, their
188 fields continue the line of the enclosing row */
189 const char *element_name = (char *)av_x_if_null(section->element_name, section->name);
190 AVBPrint *section_pbuf = &wctx->section_pbuf[level];
191
192 av_bprintf(section_pbuf, "%s%s",
193 wctx->section_pbuf[level - 1].str, element_name);
194
196 // add /TYPE to prefix
197 av_bprint_chars(section_pbuf, '/', 1);
198
199 // normalize section type, replace special characters and lower case
200 for (const char *p = section->get_type(data); *p; p++) {
201 char c =
202 (*p >= '0' && *p <= '9') ||
203 (*p >= 'a' && *p <= 'z') ||
204 (*p >= 'A' && *p <= 'Z') ? av_tolower(*p) : '_';
205 av_bprint_chars(section_pbuf, c, 1);
206 }
207 }
208 av_bprint_chars(section_pbuf, ':', 1);
209 return;
210 }
211
213 return;
214
215 /* a row, printed as one line once the section ends */
216 compact->row_level[level] = level;
217 compact->nb_fields[level] = 0;
218 av_bprint_clear(&compact->line[level]);
219 av_bprint_clear(&compact->rows[level]);
220 if (compact->print_section)
221 av_bprintf(&compact->line[level], "%s", section->name);
222}
223
225{
226 CompactContext *compact = wctx->priv;
227 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
228 int level = wctx->level;
229 int parent_row;
230
231 if (!section || compact->row_level[level] != level)
232 return;
233
234 /* the row's line, followed by the lines of the rows nested in it */
235 av_bprint_chars(&compact->line[level], '\n', 1);
236 bprint_append_bprint(&compact->line[level], &compact->rows[level]);
237
238 parent_row = level ? compact->row_level[level - 1] : -1;
239 if (parent_row >= 0)
240 bprint_append_bprint(&compact->rows[parent_row], &compact->line[level]);
241 else
242 writer_put_str(wctx, compact->line[level].str);
243}
244
245static void compact_print_field(AVTextFormatContext *wctx, const char *key, const char *value)
246{
247 CompactContext *compact = wctx->priv;
248 int row = compact->row_level[wctx->level];
249 AVBPrint tmp, *line = &tmp;
250
251 if (row >= 0) {
252 line = &compact->line[row];
253 if (compact->nb_fields[row]++ || compact->print_section)
254 av_bprint_chars(line, compact->item_sep, 1);
255 } else {
256 /* a field outside of any row */
258 }
259
260 if (!compact->nokey)
261 av_bprintf(line, "%s%s=", wctx->section_pbuf[wctx->level].str, key);
262 av_bprintf(line, "%s", value);
263
264 if (line == &tmp) {
265 writer_put_str(wctx, tmp.str);
267 }
268}
269
270static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
271{
272 CompactContext *compact = wctx->priv;
273 AVBPrint buf;
274
276 compact_print_field(wctx, key, compact->escape_str(&buf, value, compact->item_sep, wctx));
278}
279
280static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
281{
282 char buf[32];
283
284 snprintf(buf, sizeof(buf), "%"PRId64, value);
285 compact_print_field(wctx, key, buf);
286}
287
289 .name = "compact",
290 .priv_size = sizeof(CompactContext),
293 .print_section_header = compact_print_section_header,
294 .print_section_footer = compact_print_section_footer,
295 .print_integer = compact_print_int,
296 .print_string = compact_print_str,
298 .priv_class = &compact_class,
299};
300
301/* CSV output */
302
303#undef OFFSET
304#define OFFSET(x) offsetof(CompactContext, x)
305
306static const AVOption csv_options[] = {
307 { "item_sep", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "," }, 0, 0 },
308 { "s", "set item separator", OFFSET(item_sep_str), AV_OPT_TYPE_STRING, { .str = "," }, 0, 0 },
309 { "nokey", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
310 { "nk", "force no key printing", OFFSET(nokey), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
311 { "escape", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "csv" }, 0, 0 },
312 { "e", "set escape mode", OFFSET(escape_mode_str), AV_OPT_TYPE_STRING, { .str = "csv" }, 0, 0 },
313 { "print_section", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
314 { "p", "print section name", OFFSET(print_section), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1 },
315 { NULL },
316};
317
319
321 .name = "csv",
322 .priv_size = sizeof(CompactContext),
325 .print_section_header = compact_print_section_header,
326 .print_section_footer = compact_print_section_footer,
327 .print_integer = compact_print_int,
328 .print_string = compact_print_str,
330 .priv_class = &csv_class,
331};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:97
const AVTextFormatter avtextformatter_compact
Definition tf_compact.c:288
#define AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE
For these sections the element_name field is mandatory.
#define SECTION_MAX_NB_LEVELS
#define AV_TEXTFORMAT_FLAG_SUPPORTS_OPTIONAL_FIELDS
const AVTextFormatter avtextformatter_csv
Definition tf_compact.c:320
#define AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER
the section only contains other sections, but has no data at its own level
#define AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY
the section contains an array of elements of the same type
Convenience header that includes libavutil's core.
void av_bprintf(AVBPrint *buf, const char *fmt,...)
Definition bprint.c:121
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
Definition bprint.c:68
AVBPrint public header.
#define AV_BPRINT_SIZE_UNLIMITED
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
int(* init)(AVBSFContext *ctx)
Definition dts2pts.c:608
error code definitions
double value
Definition eval.c:102
const char * key
static void print_section(SectionID id, int level)
Definition ffprobe.c:3211
@ AV_OPT_TYPE_BOOL
Underlying C type is int.
Definition opt.h:326
@ AV_OPT_TYPE_STRING
Underlying C type is a uint8_t* that is either NULL or points to a C string allocated with the av_mal...
Definition opt.h:275
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
Finalize a print buffer.
Definition bprint.c:234
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
Append data to a print buffer.
Definition bprint.c:147
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
Append char c n times to a print buffer.
Definition bprint.c:129
void av_bprint_clear(AVBPrint *buf)
Reset the string to "" but keep internal allocated data.
Definition bprint.c:226
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
static void * av_x_if_null(const void *p, const void *x)
Return x default pointer in case p is NULL.
Definition avutil.h:311
static av_const int av_tolower(int c)
Locale-independent conversion of ASCII characters to lowercase.
Definition avstring.h:237
static av_cold void uninit(AVBitStreamFilterContext *ctx)
#define av_cold
Definition attributes.h:117
#define FFMIN(a, b)
Definition macros.h:49
const char data[16]
Definition mxf.c:149
AVOptions.
Describe the class of an AVClass context structure.
Definition log.h:76
AVOption.
Definition opt.h:428
void * priv
private data for use by the filter
int level
current level, starting from 0
AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]
generic print buffer dedicated to each section, used by various formatters
const char * element_name
name of the contained element, if provided
const char * name
const char *(* get_type)(const void *data)
function returning a type if defined, must be defined when SECTION_FLAG_HAS_TYPE is defined
AVBPrint rows[SECTION_MAX_NB_LEVELS]
the lines of the rows nested in the row at this level
Definition tf_compact.c:101
const char *(* escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Definition tf_compact.c:93
int row_level[SECTION_MAX_NB_LEVELS]
level of the row whose line the fields at this level belong to, -1 for none
Definition tf_compact.c:95
AVBPrint line[SECTION_MAX_NB_LEVELS]
the line of the row at this level
Definition tf_compact.c:99
char * item_sep_str
Definition tf_compact.c:88
char * escape_mode_str
Definition tf_compact.c:92
int nb_fields[SECTION_MAX_NB_LEVELS]
number of fields written to the row at this level
Definition tf_compact.c:97
uint8_t level
Definition svq3.c:208
#define av_log(a,...)
static uint8_t tmp[40]
Definition aes_ctr.c:52
#define src
Definition vp8dsp.c:248
static void compact_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
Definition tf_compact.c:270
static void compact_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
Definition tf_compact.c:280
static void bprint_append_bprint(AVBPrint *dst, const AVBPrint *src)
Definition tf_compact.c:163
static const AVOption csv_options[]
Definition tf_compact.c:306
static const AVOption compact_options[]
Definition tf_compact.c:107
static void compact_print_section_header(AVTextFormatContext *wctx, const void *data)
Definition tf_compact.c:168
static const char * csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Quote fields containing special characters, check RFC4180.
Definition tf_compact.c:62
static const char * c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Apply C-language-like string escaping.
Definition tf_compact.c:39
static void compact_print_field(AVTextFormatContext *wctx, const char *key, const char *value)
Definition tf_compact.c:245
static void compact_print_section_footer(AVTextFormatContext *wctx)
Definition tf_compact.c:224
static av_cold int compact_init(AVTextFormatContext *wctx)
Definition tf_compact.c:121
static av_cold int compact_uninit(AVTextFormatContext *wctx)
Definition tf_compact.c:151
#define OFFSET(x)
Definition tf_compact.c:105
static const char * none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx)
Definition tf_compact.c:81
Internal utilities for text formatters.
static const AVTextFormatSection * tf_get_section(AVTextFormatContext *tfc, int level)
Safely validate and access a section at a given level.
Definition tf_internal.h:42
static const AVTextFormatSection * tf_get_parent_section(AVTextFormatContext *tfc, int level)
Safely access the parent section.
Definition tf_internal.h:55
#define DEFINE_FORMATTER_CLASS(name)
Definition tf_internal.h:31
static void writer_put_str(AVTextFormatContext *wctx, const char *str)
Definition tf_internal.h:68
static double c[64]