FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/tf_json.c
Date: 2025-04-25 22:50:00
Exec Total Coverage
Lines: 86 88 97.7%
Functions: 7 8 87.5%
Branches: 41 56 73.2%

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
31 #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
32 #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
33 #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
34
35 #define DEFINE_FORMATTER_CLASS(name) \
36 static const char *name##_get_name(void *ctx) \
37 { \
38 return #name ; \
39 } \
40 static const AVClass name##_class = { \
41 .class_name = #name, \
42 .item_name = name##_get_name, \
43 .option = name##_options \
44 }
45
46
47 /* JSON output */
48
49 typedef struct JSONContext {
50 const AVClass *class;
51 int indent_level;
52 int compact;
53 const char *item_sep, *item_start_end;
54 } JSONContext;
55
56 #undef OFFSET
57 #define OFFSET(x) offsetof(JSONContext, x)
58
59 static const AVOption json_options[]= {
60 { "compact", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
61 { "c", "enable compact output", OFFSET(compact), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
62 { NULL }
63 };
64
65 DEFINE_FORMATTER_CLASS(json);
66
67 4 static av_cold int json_init(AVTextFormatContext *wctx)
68 {
69 4 JSONContext *json = wctx->priv;
70
71
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 json->item_sep = json->compact ? ", " : ",\n";
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 json->item_start_end = json->compact ? " " : "\n";
73
74 4 return 0;
75 }
76
77 1066 static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx)
78 {
79 static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0};
80 static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0};
81 const char *p;
82
83
2/2
✓ Branch 0 taken 8991 times.
✓ Branch 1 taken 1066 times.
10057 for (p = src; *p; p++) {
84 8991 char *s = strchr(json_escape, *p);
85
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 8989 times.
8991 if (s) {
86 2 av_bprint_chars(dst, '\\', 1);
87 2 av_bprint_chars(dst, json_subst[s - json_escape], 1);
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8989 times.
8989 } else if ((unsigned char)*p < 32) {
89 av_bprintf(dst, "\\u00%02x", *p & 0xff);
90 } else {
91 8989 av_bprint_chars(dst, *p, 1);
92 }
93 }
94 1066 return dst->str;
95 }
96
97 #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ')
98
99 70 static void json_print_section_header(AVTextFormatContext *wctx, const void *data)
100 {
101 70 JSONContext *json = wctx->priv;
102 AVBPrint buf;
103 70 const struct AVTextFormatSection *section = wctx->section[wctx->level];
104 140 const struct AVTextFormatSection *parent_section = wctx->level ?
105
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 4 times.
70 wctx->section[wctx->level-1] : NULL;
106
107
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])
108 53 writer_put_str(wctx, ",\n");
109
110
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) {
111 4 writer_put_str(wctx, "{\n");
112 4 json->indent_level++;
113 } else {
114 66 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
115 66 json_escape_str(&buf, section->name, wctx);
116 66 JSON_INDENT();
117
118 66 json->indent_level++;
119
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
66 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
120 15 writer_printf(wctx, "\"%s\": [\n", buf.str);
121
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)) {
122 12 writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end);
123 } else {
124 39 writer_printf(wctx, "{%s", json->item_start_end);
125
126 /* this is required so the parser can distinguish between packets and frames */
127
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) {
128
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (!json->compact)
129 28 JSON_INDENT();
130 28 writer_printf(wctx, "\"type\": \"%s\"", section->name);
131 28 wctx->nb_item[wctx->level]++;
132 }
133 }
134 66 av_bprint_finalize(&buf, NULL);
135 }
136 70 }
137
138 70 static void json_print_section_footer(AVTextFormatContext *wctx)
139 {
140 70 JSONContext *json = wctx->priv;
141 70 const struct AVTextFormatSection *section = wctx->section[wctx->level];
142
143
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 66 times.
70 if (wctx->level == 0) {
144 4 json->indent_level--;
145 4 writer_put_str(wctx, "\n}\n");
146
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 51 times.
66 } else if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY) {
147 15 writer_w8(wctx, '\n');
148 15 json->indent_level--;
149 15 JSON_INDENT();
150 15 writer_w8(wctx, ']');
151 } else {
152 51 writer_put_str(wctx, json->item_start_end);
153 51 json->indent_level--;
154
1/2
✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
51 if (!json->compact)
155 51 JSON_INDENT();
156 51 writer_w8(wctx, '}');
157 }
158 70 }
159
160 316 static inline void json_print_item_str(AVTextFormatContext *wctx,
161 const char *key, const char *value)
162 {
163 AVBPrint buf;
164
165 316 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
166 316 writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key, wctx));
167 316 av_bprint_clear(&buf);
168 316 writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx));
169 316 av_bprint_finalize(&buf, NULL);
170 316 }
171
172 316 static void json_print_str(AVTextFormatContext *wctx, const char *key, const char *value)
173 {
174 316 JSONContext *json = wctx->priv;
175 632 const struct AVTextFormatSection *parent_section = wctx->level ?
176
1/2
✓ Branch 0 taken 316 times.
✗ Branch 1 not taken.
316 wctx->section[wctx->level-1] : NULL;
177
178
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))
179 307 writer_put_str(wctx, json->item_sep);
180
1/2
✓ Branch 0 taken 316 times.
✗ Branch 1 not taken.
316 if (!json->compact)
181 316 JSON_INDENT();
182 316 json_print_item_str(wctx, key, value);
183 316 }
184
185 368 static void json_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
186 {
187 368 JSONContext *json = wctx->priv;
188 736 const struct AVTextFormatSection *parent_section = wctx->level ?
189
1/2
✓ Branch 0 taken 368 times.
✗ Branch 1 not taken.
368 wctx->section[wctx->level-1] : NULL;
190 AVBPrint buf;
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 368 }
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
214