FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/tf_json.c
Date: 2025-06-01 09:29:47
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 <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/bprint.h"
29 #include "libavutil/opt.h"
30 #include "tf_internal.h"
31
32 /* JSON output */
33
34 typedef struct JSONContext {
35 const AVClass *class;
36 int indent_level;
37 int compact;
38 const char *item_sep, *item_start_end;
39 } JSONContext;
40
41 #undef OFFSET
42 #define OFFSET(x) offsetof(JSONContext, x)
43
44 static const AVOption json_options[] = {
45 { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
46 { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1 },
47 { NULL }
48 };
49
50 DEFINE_FORMATTER_CLASS(json);
51
52 4 static av_cold int json_init(AVTextFormatContext *wctx)
53 {
54 4 JSONContext *json = wctx->priv;
55
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 json->item_sep = json->compact ? ", " : ",\n";
57
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 json->item_start_end = json->compact ? " " : "\n";
58
59 4 return 0;
60 }
61
62 1066 static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
63 {
64 static const char json_escape[] = { '"', '\\', '\b', '\f', '\n', '\r', '\t', 0 };
65 static const char json_subst[] = { '"', '\\', 'b', 'f', 'n', 'r', 't', 0 };
66 const char *p;
67
68
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1066 times.
1066 if (!src) {
69 av_log(log_ctx, AV_LOG_WARNING, "Cannot escape NULL string, returning NULL\n");
70 return NULL;
71 }
72
73
2/2
✓ Branch 0 taken 8991 times.
✓ Branch 1 taken 1066 times.
10057 for (p = src; *p; p++) {
74 8991 char *s = strchr(json_escape, *p);
75
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8989 times.
8991 if (s) {
76 2 av_bprint_chars(dst, '\\', 1);
77 2 av_bprint_chars(dst, json_subst[s - json_escape], 1);
78
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8989 times.
8989 } else if ((unsigned char)*p < 32) {
79 av_bprintf(dst, "\\u00%02x", (unsigned char)*p);
80 } else {
81 8989 av_bprint_chars(dst, *p, 1);
82 }
83 }
84 1066 return dst->str;
85 }
86
87 #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
88
89 70 static void json_print_section_header(AVTextFormatContext *wctx, const void *data)
90 {
91 70 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
92 70 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
93 70 JSONContext *json = wctx->priv;
94 AVBPrint buf;
95
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (!section)
97 return;
98
99
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])
100 53 writer_put_str(wctx, ",\n");
101
102
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) {
103 4 writer_put_str(wctx, "{\n");
104 4 json->indent_level++;
105 } else {
106 66 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
107 66 json_escape_str(&buf, section->name, wctx);
108 66 JSON_INDENT();
109
110 66 json->indent_level++;
111
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
66 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
112 15 writer_printf(wctx, "\"%s\": [\n", buf.str);
113
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)) {
114 12 writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end);
115 } else {
116 39 writer_printf(wctx, "{%s", json->item_start_end);
117
118 /* this is required so the parser can distinguish between packets and frames */
119
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) {
120
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (!json->compact)
121 28 JSON_INDENT();
122 28 writer_printf(wctx, "\"type\": \"%s\"", section->name);
123 28 wctx->nb_item[wctx->level]++;
124 }
125 }
126 66 av_bprint_finalize(&buf, NULL);
127 }
128 }
129
130 70 static void json_print_section_footer(AVTextFormatContext *wctx)
131 {
132 70 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
133 70 JSONContext *json = wctx->priv;
134
135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (!section)
136 return;
137
138
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if (wctx->level == 0) {
139 4 json->indent_level--;
140 4 writer_put_str(wctx, "\n}\n");
141
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
66 } else if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
142 15 writer_w8(wctx, '\n');
143 15 json->indent_level--;
144 15 JSON_INDENT();
145 15 writer_w8(wctx, ']');
146 } else {
147 51 writer_put_str(wctx, json->item_start_end);
148 51 json->indent_level--;
149
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 if (!json->compact)
150 51 JSON_INDENT();
151 51 writer_w8(wctx, '}');
152 }
153 }
154
155 316 static inline void json_print_item_str(AVTextFormatContext *wctx,
156 const char *key, const char *value)
157 {
158 AVBPrint buf;
159
160 316 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
161 316 writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx));
162 316 av_bprint_clear(&buf);
163 316 writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx));
164 316 av_bprint_finalize(&buf, NULL);
165 316 }
166
167 316 static void json_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
168 {
169 316 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
170 316 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
171 316 JSONContext *json = wctx->priv;
172
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 316 times.
316 if (!section)
174 return;
175
176
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))
177 307 writer_put_str(wctx, json->item_sep);
178
1/2
✓ Branch 0 taken 316 times.
✗ Branch 1 not taken.
316 if (!json->compact)
179 316 JSON_INDENT();
180 316 json_print_item_str(wctx, key, value);
181 }
182
183 368 static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
184 {
185 368 const AVTextFormatSection *section = tf_get_section(wctx, wctx->level);
186 368 const AVTextFormatSection *parent_section = tf_get_parent_section(wctx, wctx->level);
187 368 JSONContext *json = wctx->priv;
188 AVBPrint buf;
189
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 368 times.
368 if (!section)
191 return;
192
193
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))
194 356 writer_put_str(wctx, json->item_sep);
195
1/2
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
368 if (!json->compact)
196 368 JSON_INDENT();
197
198 368 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
199 368 writer_printf(wctx, "\"%s\": %"PRId64, json_escape_str(&buf, key, wctx), value);
200 368 av_bprint_finalize(&buf, NULL);
201 }
202
203 const AVTextFormatter avtextformatter_json = {
204 .name = "json",
205 .priv_size = sizeof(JSONContext),
206 .init = json_init,
207 .print_section_header = json_print_section_header,
208 .print_section_footer = json_print_section_footer,
209 .print_integer = json_print_int,
210 .print_string = json_print_str,
211 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
212 .priv_class = &json_class,
213 };
214