FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/tf_json.c
Date: 2026-07-22 00:55:30
Exec Total Coverage
Lines: 86 93 92.5%
Functions: 7 7 100.0%
Branches: 42 60 70.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) The FFmpeg developers
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <inttypes.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <string.h>
25
26 #include "avtextformat.h"
27 #include "libavutil/bprint.h"
28 #include "libavutil/opt.h"
29 #include "tf_internal.h"
30
31 /* JSON output */
32
33 typedef struct JSONContext {
34 const AVClass *class;
35 int indent_level;
36 int compact;
37 const char *item_sep, *item_start_end;
38 } JSONContext;
39
40 #undef OFFSET
41 #define OFFSET(x) offsetof(JSONContext, x)
42
43 static const AVOption json_options[] = {
44 { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
45 { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
46 { NULL }
47 };
48
49 DEFINE_FORMATTER_CLASS(json);
50
51 4 static av_cold int json_init(AVTextFormatContext *wctx)
52 {
53 4 JSONContext *json = wctx->priv;
54
55
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 json->item_sep = json->compact ? ", " : ",\n";
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 json->item_start_end = json->compact ? " " : "\n";
57
58 4 return 0;
59 }
60
61 1066 static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
62 {
63 static const char json_escape[] = { '"', '\\', '\b', '\f', '\n', '\r', '\t', 0 };
64 static const char json_subst[] = { '"', '\\', 'b', 'f', 'n', 'r', 't', 0 };
65 const char *p;
66
67
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1066 times.
1066 if (!src) {
68 av_log(log_ctx, AV_LOG_WARNING, "Cannot escape NULL string, returning NULL\n");
69 return NULL;
70 }
71
72
2/2
✓ Branch 0 taken 8991 times.
✓ Branch 1 taken 1066 times.
10057 for (p = src; *p; p++) {
73 8991 const char *s = strchr(json_escape, *p);
74
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8989 times.
8991 if (s) {
75 2 av_bprint_chars(dst, '\\', 1);
76 2 av_bprint_chars(dst, json_subst[s - json_escape], 1);
77
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8989 times.
8989 } else if ((unsigned char)*p < 32) {
78 av_bprintf(dst, "\\u00%02x", (unsigned char)*p);
79 } else {
80 8989 av_bprint_chars(dst, *p, 1);
81 }
82 }
83 1066 return dst->str;
84 }
85
86 #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
87
88 70 static void json_print_section_header(AVTextFormatContext *wctx, const void *data)
89 {
90 70 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
91 70 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
92 70 JSONContext *json = wctx->priv;
93 AVBPrint buf;
94
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (!section)
96 return;
97
98
4/4
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 53 times.
✓ Branch 3 taken 13 times.
70 if (wctx->level && wctx->nb_item[wctx->level - 1])
99 53 writer_put_str(wctx, ",\n");
100
101
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) {
102 4 writer_put_str(wctx, "{\n");
103 4 json->indent_level++;
104 } else {
105 66 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
106 66 json_escape_str(&buf, section->name, wctx);
107 66 JSON_INDENT();
108
109 66 json->indent_level++;
110
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
66 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
111 15 writer_printf(wctx, "\"%s\": [\n", buf.str);
112
3/4
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 39 times.
51 } else if (parent_section && !(parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY)) {
113 12 writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end);
114 } else {
115 39 writer_printf(wctx, "{%s", json->item_start_end);
116
117 /* this is required so the parser can distinguish between packets and frames */
118
3/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 11 times.
39 if (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE) {
119
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (!json->compact)
120 28 JSON_INDENT();
121 28 writer_printf(wctx, "\"type\": \"%s\"", section->name);
122 28 wctx->nb_item[wctx->level]++;
123 }
124 }
125 66 av_bprint_finalize(&buf, NULL);
126 }
127 }
128
129 70 static void json_print_section_footer(AVTextFormatContext *wctx)
130 {
131 70 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
132 70 JSONContext *json = wctx->priv;
133
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (!section)
135 return;
136
137
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if (wctx->level == 0) {
138 4 json->indent_level--;
139 4 writer_put_str(wctx, "\n}\n");
140
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
66 } else if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
141 15 writer_w8(wctx, '\n');
142 15 json->indent_level--;
143 15 JSON_INDENT();
144 15 writer_w8(wctx, ']');
145 } else {
146 51 writer_put_str(wctx, json->item_start_end);
147 51 json->indent_level--;
148
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 if (!json->compact)
149 51 JSON_INDENT();
150 51 writer_w8(wctx, '}');
151 }
152 }
153
154 316 static inline void json_print_item_str(AVTextFormatContext *wctx,
155 const char *key, const char *value)
156 {
157 AVBPrint buf;
158
159 316 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
160 316 writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx));
161 316 av_bprint_clear(&buf);
162 316 writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx));
163 316 av_bprint_finalize(&buf, NULL);
164 316 }
165
166 316 static void json_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
167 {
168 316 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
169 316 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
170 316 JSONContext *json = wctx->priv;
171
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 316 times.
316 if (!section)
173 return;
174
175
4/6
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 307 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
316 if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
176 307 writer_put_str(wctx, json->item_sep);
177
1/2
✓ Branch 0 taken 316 times.
✗ Branch 1 not taken.
316 if (!json->compact)
178 316 JSON_INDENT();
179 316 json_print_item_str(wctx, key, value);
180 }
181
182 368 static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
183 {
184 368 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
185 368 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
186 368 JSONContext *json = wctx->priv;
187 AVBPrint buf;
188
189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 368 times.
368 if (!section)
190 return;
191
192
4/6
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 356 times.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 12 times.
368 if (wctx->nb_item[wctx->level] || (parent_section && parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_NUMBERING_BY_TYPE))
193 356 writer_put_str(wctx, json->item_sep);
194
1/2
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
368 if (!json->compact)
195 368 JSON_INDENT();
196
197 368 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
198 368 writer_printf(wctx, "\"%s\": %"PRId64, json_escape_str(&buf, key, wctx), value);
199 368 av_bprint_finalize(&buf, NULL);
200 }
201
202 const AVTextFormatter avtextformatter_json = {
203 .name = "json",
204 .priv_size = sizeof(JSONContext),
205 .init = json_init,
206 .print_section_header = json_print_section_header,
207 .print_section_footer = json_print_section_footer,
208 .print_integer = json_print_int,
209 .print_string = json_print_str,
210 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
211 .priv_class = &json_class,
212 };
213