FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/tf_xml.c
Date: 2025-04-25 22:50:00
Exec Total Coverage
Lines: 70 75 93.3%
Functions: 6 7 85.7%
Branches: 36 42 85.7%

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/error.h"
30 #include "libavutil/opt.h"
31
32 #define writer_w8(wctx_, b_) (wctx_)->writer->writer->writer_w8((wctx_)->writer, b_)
33 #define writer_put_str(wctx_, str_) (wctx_)->writer->writer->writer_put_str((wctx_)->writer, str_)
34 #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer->writer->writer_printf((wctx_)->writer, fmt_, __VA_ARGS__)
35
36 #define DEFINE_FORMATTER_CLASS(name) \
37 static const char *name##_get_name(void *ctx) \
38 { \
39 return #name ; \
40 } \
41 static const AVClass name##_class = { \
42 .class_name = #name, \
43 .item_name = name##_get_name, \
44 .option = name##_options \
45 }
46
47 /* XML output */
48
49 typedef struct XMLContext {
50 const AVClass *class;
51 int within_tag;
52 int indent_level;
53 int fully_qualified;
54 int xsd_strict;
55 } XMLContext;
56
57 #undef OFFSET
58 #define OFFSET(x) offsetof(XMLContext, x)
59
60 static const AVOption xml_options[] = {
61 {"fully_qualified", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
62 {"q", "specify if the output should be fully qualified", OFFSET(fully_qualified), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
63 {"xsd_strict", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
64 {"x", "ensure that the output is XSD compliant", OFFSET(xsd_strict), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1 },
65 {NULL},
66 };
67
68 DEFINE_FORMATTER_CLASS(xml);
69
70 2 static av_cold int xml_init(AVTextFormatContext *wctx)
71 {
72 2 XMLContext *xml = wctx->priv;
73
74
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (xml->xsd_strict) {
75 1 xml->fully_qualified = 1;
76 #define CHECK_COMPLIANCE(opt, opt_name) \
77 if (opt) { \
78 av_log(wctx, AV_LOG_ERROR, \
79 "XSD-compliant output selected but option '%s' was selected, XML output may be non-compliant.\n" \
80 "You need to disable such option with '-no%s'\n", opt_name, opt_name); \
81 return AVERROR(EINVAL); \
82 }
83 ////CHECK_COMPLIANCE(show_private_data, "private");
84
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 CHECK_COMPLIANCE(wctx->show_value_unit, "unit");
85
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 CHECK_COMPLIANCE(wctx->use_value_prefix, "prefix");
86 }
87
88 2 return 0;
89 }
90
91 #define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ')
92
93 84 static void xml_print_section_header(AVTextFormatContext *wctx, const void *data)
94 {
95 84 XMLContext *xml = wctx->priv;
96 84 const struct AVTextFormatSection *section = wctx->section[wctx->level];
97 168 const struct AVTextFormatSection *parent_section = wctx->level ?
98
2/2
✓ Branch 0 taken 82 times.
✓ Branch 1 taken 2 times.
84 wctx->section[wctx->level-1] : NULL;
99
100
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 82 times.
84 if (wctx->level == 0) {
101 2 const char *qual = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
102 "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" "
103 "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\"";
104
105 2 writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
106
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
2 writer_printf(wctx, "<%sffprobe%s>\n",
107 xml->fully_qualified ? "ffprobe:" : "",
108 xml->fully_qualified ? qual : "");
109 2 return;
110 }
111
112
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 74 times.
82 if (xml->within_tag) {
113 8 xml->within_tag = 0;
114 8 writer_put_str(wctx, ">\n");
115 }
116
117
3/4
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 76 times.
82 if (parent_section && (parent_section->flags & AV_TEXTFORMAT_SECTION_FLAG_IS_WRAPPER) &&
118
3/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
6 wctx->level && wctx->nb_item[wctx->level-1])
119 4 writer_w8(wctx, '\n');
120 82 xml->indent_level++;
121
122
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 70 times.
82 if (section->flags & (AV_TEXTFORMAT_SECTION_FLAG_IS_ARRAY|AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS)) {
123 12 XML_INDENT(); writer_printf(wctx, "<%s", section->name);
124
125
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_TYPE) {
126 AVBPrint buf;
127 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
128 av_bprint_escape(&buf, section->get_type(data), NULL,
129 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
130 writer_printf(wctx, " type=\"%s\"", buf.str);
131 }
132 12 writer_printf(wctx, ">\n", section->name);
133 } else {
134 70 XML_INDENT(); writer_printf(wctx, "<%s ", section->name);
135 70 xml->within_tag = 1;
136 }
137 }
138
139 84 static void xml_print_section_footer(AVTextFormatContext *wctx)
140 {
141 84 XMLContext *xml = wctx->priv;
142 84 const struct AVTextFormatSection *section = wctx->section[wctx->level];
143
144
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 82 times.
84 if (wctx->level == 0) {
145
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : "");
146
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 20 times.
82 } else if (xml->within_tag) {
147 62 xml->within_tag = 0;
148 62 writer_put_str(wctx, "/>\n");
149 62 xml->indent_level--;
150 } else {
151 20 XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name);
152 20 xml->indent_level--;
153 }
154 84 }
155
156 1192 static void xml_print_value(AVTextFormatContext *wctx, const char *key,
157 const char *str, int64_t num, const int is_int)
158 {
159 AVBPrint buf;
160 1192 XMLContext *xml = wctx->priv;
161 1192 const struct AVTextFormatSection *section = wctx->section[wctx->level];
162
163 1192 av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED);
164
165
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1174 times.
1192 if (section->flags & AV_TEXTFORMAT_SECTION_FLAG_HAS_VARIABLE_FIELDS) {
166 18 xml->indent_level++;
167 18 XML_INDENT();
168 18 av_bprint_escape(&buf, key, NULL,
169 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
170 18 writer_printf(wctx, "<%s key=\"%s\"",
171 section->element_name, buf.str);
172 18 av_bprint_clear(&buf);
173
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (is_int) {
175 writer_printf(wctx, " value=\"%"PRId64"\"/>\n", num);
176 } else {
177 18 av_bprint_escape(&buf, str, NULL,
178 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
179 18 writer_printf(wctx, " value=\"%s\"/>\n", buf.str);
180 }
181 18 xml->indent_level--;
182 } else {
183
2/2
✓ Branch 0 taken 1104 times.
✓ Branch 1 taken 70 times.
1174 if (wctx->nb_item[wctx->level])
184 1104 writer_w8(wctx, ' ');
185
186
2/2
✓ Branch 0 taken 632 times.
✓ Branch 1 taken 542 times.
1174 if (is_int) {
187 632 writer_printf(wctx, "%s=\"%"PRId64"\"", key, num);
188 } else {
189 542 av_bprint_escape(&buf, str, NULL,
190 AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES);
191 542 writer_printf(wctx, "%s=\"%s\"", key, buf.str);
192 }
193 }
194
195 1192 av_bprint_finalize(&buf, NULL);
196 1192 }
197
198 560 static inline void xml_print_str(AVTextFormatContext *wctx, const char *key, const char *value) {
199 560 xml_print_value(wctx, key, value, 0, 0);
200 560 }
201
202 632 static void xml_print_int(AVTextFormatContext *wctx, const char *key, int64_t value)
203 {
204 632 xml_print_value(wctx, key, NULL, value, 1);
205 632 }
206
207 const AVTextFormatter avtextformatter_xml = {
208 .name = "xml",
209 .priv_size = sizeof(XMLContext),
210 .init = xml_init,
211 .print_section_header = xml_print_section_header,
212 .print_section_footer = xml_print_section_footer,
213 .print_integer = xml_print_int,
214 .print_string = xml_print_str,
215 .flags = AV_TEXTFORMAT_FLAG_SUPPORTS_MIXED_ARRAY_CONTENT,
216 .priv_class = &xml_class,
217 };
218
219