FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/fftools/textformat/tf_xml.c
Date: 2025-06-05 01:40:37
Exec Total Coverage
Lines: 76 83 91.6%
Functions: 6 6 100.0%
Branches: 37 46 80.4%

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