... | ... |
@@ -87,6 +87,10 @@ Use sexagesimal format HH:MM:SS.MICROSECONDS for time values. |
87 | 87 |
Prettify the format of the displayed values, it corresponds to the |
88 | 88 |
options "-unit -prefix -byte_binary_prefix -sexagesimal". |
89 | 89 |
|
90 |
+@item -print_format @var{format} |
|
91 |
+Set the output printing format. |
|
92 |
+Current available formats are "default" and "json". |
|
93 |
+ |
|
90 | 94 |
@item -show_format |
91 | 95 |
Show information about the container format of the input multimedia |
92 | 96 |
stream. |
... | ... |
@@ -135,6 +135,8 @@ struct writer { |
135 | 135 |
const char *items_sep; ///< separator between sets of key/value couples |
136 | 136 |
const char *section_sep; ///< separator between sections (streams, packets, ...) |
137 | 137 |
const char *header, *footer; |
138 |
+ void (*print_section_start)(const char *, int); |
|
139 |
+ void (*print_section_end) (const char *, int); |
|
138 | 140 |
void (*print_header)(const char *); |
139 | 141 |
void (*print_footer)(const char *); |
140 | 142 |
void (*print_int_f)(const char *, int); |
... | ... |
@@ -143,6 +145,84 @@ struct writer { |
143 | 143 |
}; |
144 | 144 |
|
145 | 145 |
|
146 |
+/* JSON output */ |
|
147 |
+ |
|
148 |
+static void json_print_header(const char *section) |
|
149 |
+{ |
|
150 |
+ printf("{\n"); |
|
151 |
+} |
|
152 |
+ |
|
153 |
+static char *json_escape_str(const char *s) |
|
154 |
+{ |
|
155 |
+ static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0}; |
|
156 |
+ static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0}; |
|
157 |
+ char *ret, *p; |
|
158 |
+ int i, len = 0; |
|
159 |
+ |
|
160 |
+ // compute the length of the escaped string |
|
161 |
+ for (i = 0; s[i]; i++) { |
|
162 |
+ if (strchr(json_escape, s[i])) len += 2; // simple escape |
|
163 |
+ else if ((unsigned char)s[i] < 32) len += 6; // handle non-printable chars |
|
164 |
+ else len += 1; // char copy |
|
165 |
+ } |
|
166 |
+ |
|
167 |
+ p = ret = av_malloc(len + 1); |
|
168 |
+ if (!p) |
|
169 |
+ return NULL; |
|
170 |
+ for (i = 0; s[i]; i++) { |
|
171 |
+ char *q = strchr(json_escape, s[i]); |
|
172 |
+ if (q) { |
|
173 |
+ *p++ = '\\'; |
|
174 |
+ *p++ = json_subst[q - json_escape]; |
|
175 |
+ } else if ((unsigned char)s[i] < 32) { |
|
176 |
+ snprintf(p, 7, "\\u00%02x", s[i] & 0xff); |
|
177 |
+ p += 6; |
|
178 |
+ } else { |
|
179 |
+ *p++ = s[i]; |
|
180 |
+ } |
|
181 |
+ } |
|
182 |
+ *p = 0; |
|
183 |
+ return ret; |
|
184 |
+} |
|
185 |
+ |
|
186 |
+static void json_print_str(const char *key, const char *value) |
|
187 |
+{ |
|
188 |
+ char *key_esc = json_escape_str(key); |
|
189 |
+ char *value_esc = json_escape_str(value); |
|
190 |
+ printf(" \"%s\": \"%s\"", |
|
191 |
+ key_esc ? key_esc : "", |
|
192 |
+ value_esc ? value_esc : ""); |
|
193 |
+ av_free(key_esc); |
|
194 |
+ av_free(value_esc); |
|
195 |
+} |
|
196 |
+ |
|
197 |
+static void json_print_int(const char *key, int value) |
|
198 |
+{ |
|
199 |
+ char *key_esc = json_escape_str(key); |
|
200 |
+ printf(" \"%s\": %d", key_esc ? key_esc : "", value); |
|
201 |
+ av_free(key_esc); |
|
202 |
+} |
|
203 |
+ |
|
204 |
+static void json_print_footer(const char *section) |
|
205 |
+{ |
|
206 |
+ printf("\n }"); |
|
207 |
+} |
|
208 |
+ |
|
209 |
+static void json_print_section_start(const char *section, int multiple_entries) |
|
210 |
+{ |
|
211 |
+ char *section_esc = json_escape_str(section); |
|
212 |
+ printf("\n \"%s\":%s", section_esc ? section_esc : "", |
|
213 |
+ multiple_entries ? " [" : " "); |
|
214 |
+ av_free(section_esc); |
|
215 |
+} |
|
216 |
+ |
|
217 |
+static void json_print_section_end(const char *section, int multiple_entries) |
|
218 |
+{ |
|
219 |
+ if (multiple_entries) |
|
220 |
+ printf("]"); |
|
221 |
+} |
|
222 |
+ |
|
223 |
+ |
|
146 | 224 |
/* Default output */ |
147 | 225 |
|
148 | 226 |
static void default_print_header(const char *section) |
... | ... |
@@ -262,6 +342,27 @@ static void show_packets(struct writer *w, AVFormatContext *fmt_ctx) |
262 | 262 |
show_packet(w, fmt_ctx, &pkt, i++); |
263 | 263 |
} |
264 | 264 |
|
265 |
+static void json_show_tags(struct writer *w, AVDictionary *dict) |
|
266 |
+{ |
|
267 |
+ AVDictionaryEntry *tag = NULL; |
|
268 |
+ struct print_buf pbuf = {.s = NULL}; |
|
269 |
+ int first = 1; |
|
270 |
+ |
|
271 |
+ if (!dict) |
|
272 |
+ return; |
|
273 |
+ printf(",\n \"tags\": {\n"); |
|
274 |
+ while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { |
|
275 |
+ if (first) { |
|
276 |
+ print_str0(tag->key, tag->value); |
|
277 |
+ first = 0; |
|
278 |
+ } else { |
|
279 |
+ print_str(tag->key, tag->value); |
|
280 |
+ } |
|
281 |
+ } |
|
282 |
+ printf("\n }"); |
|
283 |
+ av_free(pbuf.s); |
|
284 |
+} |
|
285 |
+ |
|
265 | 286 |
static void default_show_tags(struct writer *w, AVDictionary *dict) |
266 | 287 |
{ |
267 | 288 |
AVDictionaryEntry *tag = NULL; |
... | ... |
@@ -435,6 +536,16 @@ static struct writer writers[] = {{ |
435 | 435 |
.section_sep = "\n", |
436 | 436 |
.footer = "\n", |
437 | 437 |
WRITER_FUNC(default), |
438 |
+ },{ |
|
439 |
+ .name = "json", |
|
440 |
+ .header = "{", |
|
441 |
+ .item_sep = ",\n", |
|
442 |
+ .items_sep = ",", |
|
443 |
+ .section_sep = ",", |
|
444 |
+ .footer = "\n}\n", |
|
445 |
+ .print_section_start = json_print_section_start, |
|
446 |
+ .print_section_end = json_print_section_end, |
|
447 |
+ WRITER_FUNC(json), |
|
438 | 448 |
} |
439 | 449 |
}; |
440 | 450 |
|
... | ... |
@@ -449,9 +560,13 @@ static int get_writer(const char *name) |
449 | 449 |
return -1; |
450 | 450 |
} |
451 | 451 |
|
452 |
-#define SECTION_PRINT(name, left) do { \ |
|
452 |
+#define SECTION_PRINT(name, multiple_entries, left) do { \ |
|
453 | 453 |
if (do_show_ ## name) { \ |
454 |
+ if (w->print_section_start) \ |
|
455 |
+ w->print_section_start(#name, multiple_entries); \ |
|
454 | 456 |
show_ ## name (w, fmt_ctx); \ |
457 |
+ if (w->print_section_end) \ |
|
458 |
+ w->print_section_end(#name, multiple_entries); \ |
|
455 | 459 |
if (left) \ |
456 | 460 |
printf("%s", w->section_sep); \ |
457 | 461 |
} \ |
... | ... |
@@ -476,9 +591,9 @@ static int probe_file(const char *filename) |
476 | 476 |
if (w->header) |
477 | 477 |
printf("%s", w->header); |
478 | 478 |
|
479 |
- SECTION_PRINT(packets, do_show_streams || do_show_format); |
|
480 |
- SECTION_PRINT(streams, do_show_format); |
|
481 |
- SECTION_PRINT(format, 0); |
|
479 |
+ SECTION_PRINT(packets, 1, do_show_streams || do_show_format); |
|
480 |
+ SECTION_PRINT(streams, 1, do_show_format); |
|
481 |
+ SECTION_PRINT(format, 0, 0); |
|
482 | 482 |
|
483 | 483 |
if (w->footer) |
484 | 484 |
printf("%s", w->footer); |
... | ... |
@@ -548,6 +663,7 @@ static const OptionDef options[] = { |
548 | 548 |
"use sexagesimal format HOURS:MM:SS.MICROSECONDS for time units" }, |
549 | 549 |
{ "pretty", 0, {(void*)&opt_pretty}, |
550 | 550 |
"prettify the format of displayed values, make it more human readable" }, |
551 |
+ { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format}, "set the output printing format (available formats are: default, json)" }, |
|
551 | 552 |
{ "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" }, |
552 | 553 |
{ "show_packets", OPT_BOOL, {(void*)&do_show_packets}, "show packets info" }, |
553 | 554 |
{ "show_streams", OPT_BOOL, {(void*)&do_show_streams}, "show streams info" }, |