You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	ffprobe: add -o option
This enables printing to a resource specified with -o OUTPUT. In case the output is not specified, prints to stdout as usual. Address issue: http://trac.ffmpeg.org/ticket/8024 Signed-off-by: Marton Balint <cus@passwd.hu>
This commit is contained in:
		
				
					committed by
					
						 Marton Balint
						Marton Balint
					
				
			
			
				
	
			
			
			
						parent
						
							7cae3d8b76
						
					
				
				
					commit
					ff07492cd8
				
			| @@ -19,6 +19,7 @@ version 5.1: | ||||
| - blurdetect filter | ||||
| - tiltshelf audio filter | ||||
| - QOI image format support | ||||
| - ffprobe -o option | ||||
|  | ||||
|  | ||||
| version 5.0: | ||||
|   | ||||
| @@ -28,6 +28,9 @@ If a url is specified in input, ffprobe will try to open and | ||||
| probe the url content. If the url cannot be opened or recognized as | ||||
| a multimedia file, a positive exit code is returned. | ||||
|  | ||||
| If no output is specified as output with @option{o} ffprobe will write | ||||
| to stdout. | ||||
|  | ||||
| ffprobe may be employed both as a standalone application or in | ||||
| combination with a textual filter, which may perform more | ||||
| sophisticated processing, e.g. statistical processing or plotting. | ||||
| @@ -348,6 +351,10 @@ on the specific build. | ||||
| @item -i @var{input_url} | ||||
| Read @var{input_url}. | ||||
|  | ||||
| @item -o @var{output_url} | ||||
| Write output to @var{output_url}. If not specified, the output is sent | ||||
| to stdout. | ||||
|  | ||||
| @end table | ||||
| @c man end | ||||
|  | ||||
|   | ||||
| @@ -281,6 +281,7 @@ static const OptionDef *options; | ||||
| static const char *input_filename; | ||||
| static const char *print_input_filename; | ||||
| static const AVInputFormat *iformat = NULL; | ||||
| static const char *output_filename = NULL; | ||||
|  | ||||
| static struct AVHashContext *hash; | ||||
|  | ||||
| @@ -476,6 +477,12 @@ typedef struct Writer { | ||||
| struct WriterContext { | ||||
|     const AVClass *class;           ///< class of the writer | ||||
|     const Writer *writer;           ///< the Writer of which this is an instance | ||||
|     AVIOContext *avio;              ///< the I/O context used to write | ||||
|  | ||||
|     void (* writer_w8)(WriterContext *wctx, int b); | ||||
|     void (* writer_put_str)(WriterContext *wctx, const char *str); | ||||
|     void (* writer_printf)(WriterContext *wctx, const char *fmt, ...); | ||||
|  | ||||
|     char *name;                     ///< name of this writer instance | ||||
|     void *priv;                     ///< private data for use by the filter | ||||
|  | ||||
| @@ -553,6 +560,10 @@ static void writer_close(WriterContext **wctx) | ||||
|         av_opt_free((*wctx)->priv); | ||||
|     av_freep(&((*wctx)->priv)); | ||||
|     av_opt_free(*wctx); | ||||
|     if ((*wctx)->avio) { | ||||
|         avio_flush((*wctx)->avio); | ||||
|         avio_close((*wctx)->avio); | ||||
|     } | ||||
|     av_freep(wctx); | ||||
| } | ||||
|  | ||||
| @@ -564,9 +575,46 @@ static void bprint_bytes(AVBPrint *bp, const uint8_t *ubuf, size_t ubuf_size) | ||||
|         av_bprintf(bp, "%02X", ubuf[i]); | ||||
| } | ||||
|  | ||||
| static inline void writer_w8_avio(WriterContext *wctx, int b) | ||||
| { | ||||
|     avio_w8(wctx->avio, b); | ||||
| } | ||||
|  | ||||
| static inline void writer_put_str_avio(WriterContext *wctx, const char *str) | ||||
| { | ||||
|     avio_write(wctx->avio, str, strlen(str)); | ||||
| } | ||||
|  | ||||
| static inline void writer_printf_avio(WriterContext *wctx, const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     va_start(ap, fmt); | ||||
|     avio_vprintf(wctx->avio, fmt, ap); | ||||
|     va_end(ap); | ||||
| } | ||||
|  | ||||
| static inline void writer_w8_printf(WriterContext *wctx, int b) | ||||
| { | ||||
|     printf("%c", b); | ||||
| } | ||||
|  | ||||
| static inline void writer_put_str_printf(WriterContext *wctx, const char *str) | ||||
| { | ||||
|     printf("%s", str); | ||||
| } | ||||
|  | ||||
| static inline void writer_printf_printf(WriterContext *wctx, const char *fmt, ...) | ||||
| { | ||||
|     va_list ap; | ||||
|  | ||||
|     va_start(ap, fmt); | ||||
|     vprintf(fmt, ap); | ||||
|     va_end(ap); | ||||
| } | ||||
|  | ||||
| static int writer_open(WriterContext **wctx, const Writer *writer, const char *args, | ||||
|                        const struct section *sections, int nb_sections) | ||||
|                        const struct section *sections, int nb_sections, const char *output) | ||||
| { | ||||
|     int i, ret = 0; | ||||
|  | ||||
| @@ -637,6 +685,21 @@ static int writer_open(WriterContext **wctx, const Writer *writer, const char *a | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (!output_filename) { | ||||
|         (*wctx)->writer_w8 = writer_w8_printf; | ||||
|         (*wctx)->writer_put_str = writer_put_str_printf; | ||||
|         (*wctx)->writer_printf = writer_printf_printf; | ||||
|     } else { | ||||
|         if ((ret = avio_open(&(*wctx)->avio, output, AVIO_FLAG_WRITE)) < 0) { | ||||
|             av_log(*wctx, AV_LOG_ERROR, | ||||
|                    "Failed to open output '%s' with error: %s\n", output, av_err2str(ret)); | ||||
|             goto fail; | ||||
|         } | ||||
|         (*wctx)->writer_w8 = writer_w8_avio; | ||||
|         (*wctx)->writer_put_str = writer_put_str_avio; | ||||
|         (*wctx)->writer_printf = writer_printf_avio; | ||||
|     } | ||||
|  | ||||
|     for (i = 0; i < SECTION_MAX_NB_LEVELS; i++) | ||||
|         av_bprint_init(&(*wctx)->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED); | ||||
|  | ||||
| @@ -904,6 +967,10 @@ static void writer_print_integers(WriterContext *wctx, const char *name, | ||||
|     av_bprint_finalize(&bp, NULL); | ||||
| } | ||||
|  | ||||
| #define writer_w8(wctx_, b_) (wctx_)->writer_w8(wctx_, b_) | ||||
| #define writer_put_str(wctx_, str_) (wctx_)->writer_put_str(wctx_, str_) | ||||
| #define writer_printf(wctx_, fmt_, ...) (wctx_)->writer_printf(wctx_, fmt_, __VA_ARGS__) | ||||
|  | ||||
| #define MAX_REGISTERED_WRITERS_NB 64 | ||||
|  | ||||
| static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1]; | ||||
| @@ -998,7 +1065,7 @@ static void default_print_section_header(WriterContext *wctx) | ||||
|         return; | ||||
|  | ||||
|     if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) | ||||
|         printf("[%s]\n", upcase_string(buf, sizeof(buf), section->name)); | ||||
|         writer_printf(wctx, "[%s]\n", upcase_string(buf, sizeof(buf), section->name)); | ||||
| } | ||||
|  | ||||
| static void default_print_section_footer(WriterContext *wctx) | ||||
| @@ -1011,7 +1078,7 @@ static void default_print_section_footer(WriterContext *wctx) | ||||
|         return; | ||||
|  | ||||
|     if (!(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) | ||||
|         printf("[/%s]\n", upcase_string(buf, sizeof(buf), section->name)); | ||||
|         writer_printf(wctx, "[/%s]\n", upcase_string(buf, sizeof(buf), section->name)); | ||||
| } | ||||
|  | ||||
| static void default_print_str(WriterContext *wctx, const char *key, const char *value) | ||||
| @@ -1019,8 +1086,8 @@ static void default_print_str(WriterContext *wctx, const char *key, const char * | ||||
|     DefaultContext *def = wctx->priv; | ||||
|  | ||||
|     if (!def->nokey) | ||||
|         printf("%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|     printf("%s\n", value); | ||||
|         writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|     writer_printf(wctx, "%s\n", value); | ||||
| } | ||||
|  | ||||
| static void default_print_int(WriterContext *wctx, const char *key, long long int value) | ||||
| @@ -1028,8 +1095,8 @@ static void default_print_int(WriterContext *wctx, const char *key, long long in | ||||
|     DefaultContext *def = wctx->priv; | ||||
|  | ||||
|     if (!def->nokey) | ||||
|         printf("%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|     printf("%lld\n", value); | ||||
|         writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|     writer_printf(wctx, "%lld\n", value); | ||||
| } | ||||
|  | ||||
| static const Writer default_writer = { | ||||
| @@ -1171,10 +1238,10 @@ static void compact_print_section_header(WriterContext *wctx) | ||||
|         } | ||||
|         if (parent_section && !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY)) && | ||||
|             wctx->level && wctx->nb_item[wctx->level-1]) | ||||
|             printf("%c", compact->item_sep); | ||||
|             writer_w8(wctx, compact->item_sep); | ||||
|         if (compact->print_section && | ||||
|             !(section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) | ||||
|             printf("%s%c", section->name, compact->item_sep); | ||||
|             writer_printf(wctx, "%s%c", section->name, compact->item_sep); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1185,7 +1252,7 @@ static void compact_print_section_footer(WriterContext *wctx) | ||||
|     if (!compact->nested_section[wctx->level] && | ||||
|         compact->terminate_line[wctx->level] && | ||||
|         !(wctx->section[wctx->level]->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) | ||||
|         printf("\n"); | ||||
|         writer_w8(wctx, '\n'); | ||||
| } | ||||
|  | ||||
| static void compact_print_str(WriterContext *wctx, const char *key, const char *value) | ||||
| @@ -1193,11 +1260,11 @@ static void compact_print_str(WriterContext *wctx, const char *key, const char * | ||||
|     CompactContext *compact = wctx->priv; | ||||
|     AVBPrint buf; | ||||
|  | ||||
|     if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep); | ||||
|     if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep); | ||||
|     if (!compact->nokey) | ||||
|         printf("%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|         writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|     av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||||
|     printf("%s", compact->escape_str(&buf, value, compact->item_sep, wctx)); | ||||
|     writer_put_str(wctx, compact->escape_str(&buf, value, compact->item_sep, wctx)); | ||||
|     av_bprint_finalize(&buf, NULL); | ||||
| } | ||||
|  | ||||
| @@ -1205,10 +1272,10 @@ static void compact_print_int(WriterContext *wctx, const char *key, long long in | ||||
| { | ||||
|     CompactContext *compact = wctx->priv; | ||||
|  | ||||
|     if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep); | ||||
|     if (wctx->nb_item[wctx->level]) writer_w8(wctx, compact->item_sep); | ||||
|     if (!compact->nokey) | ||||
|         printf("%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|     printf("%lld", value); | ||||
|         writer_printf(wctx, "%s%s=", wctx->section_pbuf[wctx->level].str, key); | ||||
|     writer_printf(wctx, "%lld", value); | ||||
| } | ||||
|  | ||||
| static const Writer compact_writer = { | ||||
| @@ -1351,7 +1418,7 @@ static void flat_print_section_header(WriterContext *wctx) | ||||
|  | ||||
| static void flat_print_int(WriterContext *wctx, const char *key, long long int value) | ||||
| { | ||||
|     printf("%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value); | ||||
|     writer_printf(wctx, "%s%s=%lld\n", wctx->section_pbuf[wctx->level].str, key, value); | ||||
| } | ||||
|  | ||||
| static void flat_print_str(WriterContext *wctx, const char *key, const char *value) | ||||
| @@ -1359,11 +1426,11 @@ static void flat_print_str(WriterContext *wctx, const char *key, const char *val | ||||
|     FlatContext *flat = wctx->priv; | ||||
|     AVBPrint buf; | ||||
|  | ||||
|     printf("%s", wctx->section_pbuf[wctx->level].str); | ||||
|     writer_put_str(wctx, wctx->section_pbuf[wctx->level].str); | ||||
|     av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||||
|     printf("%s=", flat_escape_key_str(&buf, key, flat->sep)); | ||||
|     writer_printf(wctx, "%s=", flat_escape_key_str(&buf, key, flat->sep)); | ||||
|     av_bprint_clear(&buf); | ||||
|     printf("\"%s\"\n", flat_escape_value_str(&buf, value)); | ||||
|     writer_printf(wctx, "\"%s\"\n", flat_escape_value_str(&buf, value)); | ||||
|     av_bprint_finalize(&buf, NULL); | ||||
| } | ||||
|  | ||||
| @@ -1433,12 +1500,12 @@ static void ini_print_section_header(WriterContext *wctx) | ||||
|  | ||||
|     av_bprint_clear(buf); | ||||
|     if (!parent_section) { | ||||
|         printf("# ffprobe output\n\n"); | ||||
|         writer_put_str(wctx, "# ffprobe output\n\n"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (wctx->nb_item[wctx->level-1]) | ||||
|         printf("\n"); | ||||
|         writer_w8(wctx, '\n'); | ||||
|  | ||||
|     av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str); | ||||
|     if (ini->hierarchical || | ||||
| @@ -1453,7 +1520,7 @@ static void ini_print_section_header(WriterContext *wctx) | ||||
|     } | ||||
|  | ||||
|     if (!(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) | ||||
|         printf("[%s]\n", buf->str); | ||||
|         writer_printf(wctx, "[%s]\n", buf->str); | ||||
| } | ||||
|  | ||||
| static void ini_print_str(WriterContext *wctx, const char *key, const char *value) | ||||
| @@ -1461,15 +1528,15 @@ static void ini_print_str(WriterContext *wctx, const char *key, const char *valu | ||||
|     AVBPrint buf; | ||||
|  | ||||
|     av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||||
|     printf("%s=", ini_escape_str(&buf, key)); | ||||
|     writer_printf(wctx, "%s=", ini_escape_str(&buf, key)); | ||||
|     av_bprint_clear(&buf); | ||||
|     printf("%s\n", ini_escape_str(&buf, value)); | ||||
|     writer_printf(wctx, "%s\n", ini_escape_str(&buf, value)); | ||||
|     av_bprint_finalize(&buf, NULL); | ||||
| } | ||||
|  | ||||
| static void ini_print_int(WriterContext *wctx, const char *key, long long int value) | ||||
| { | ||||
|     printf("%s=%lld\n", key, value); | ||||
|     writer_printf(wctx, "%s=%lld\n", key, value); | ||||
| } | ||||
|  | ||||
| static const Writer ini_writer = { | ||||
| @@ -1532,7 +1599,7 @@ static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx | ||||
|     return dst->str; | ||||
| } | ||||
|  | ||||
| #define JSON_INDENT() printf("%*c", json->indent_level * 4, ' ') | ||||
| #define JSON_INDENT() writer_printf(wctx, "%*c", json->indent_level * 4, ' ') | ||||
|  | ||||
| static void json_print_section_header(WriterContext *wctx) | ||||
| { | ||||
| @@ -1543,10 +1610,10 @@ static void json_print_section_header(WriterContext *wctx) | ||||
|         wctx->section[wctx->level-1] : NULL; | ||||
|  | ||||
|     if (wctx->level && wctx->nb_item[wctx->level-1]) | ||||
|         printf(",\n"); | ||||
|         writer_put_str(wctx, ",\n"); | ||||
|  | ||||
|     if (section->flags & SECTION_FLAG_IS_WRAPPER) { | ||||
|         printf("{\n"); | ||||
|         writer_put_str(wctx, "{\n"); | ||||
|         json->indent_level++; | ||||
|     } else { | ||||
|         av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||||
| @@ -1555,17 +1622,17 @@ static void json_print_section_header(WriterContext *wctx) | ||||
|  | ||||
|         json->indent_level++; | ||||
|         if (section->flags & SECTION_FLAG_IS_ARRAY) { | ||||
|             printf("\"%s\": [\n", buf.str); | ||||
|             writer_printf(wctx, "\"%s\": [\n", buf.str); | ||||
|         } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) { | ||||
|             printf("\"%s\": {%s", buf.str, json->item_start_end); | ||||
|             writer_printf(wctx, "\"%s\": {%s", buf.str, json->item_start_end); | ||||
|         } else { | ||||
|             printf("{%s", json->item_start_end); | ||||
|             writer_printf(wctx, "{%s", json->item_start_end); | ||||
|  | ||||
|             /* this is required so the parser can distinguish between packets and frames */ | ||||
|             if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) { | ||||
|                 if (!json->compact) | ||||
|                     JSON_INDENT(); | ||||
|                 printf("\"type\": \"%s\"", section->name); | ||||
|                 writer_printf(wctx, "\"type\": \"%s\"", section->name); | ||||
|                 wctx->nb_item[wctx->level]++; | ||||
|             } | ||||
|         } | ||||
| @@ -1580,18 +1647,18 @@ static void json_print_section_footer(WriterContext *wctx) | ||||
|  | ||||
|     if (wctx->level == 0) { | ||||
|         json->indent_level--; | ||||
|         printf("\n}\n"); | ||||
|         writer_put_str(wctx, "\n}\n"); | ||||
|     } else if (section->flags & SECTION_FLAG_IS_ARRAY) { | ||||
|         printf("\n"); | ||||
|         writer_w8(wctx, '\n'); | ||||
|         json->indent_level--; | ||||
|         JSON_INDENT(); | ||||
|         printf("]"); | ||||
|         writer_w8(wctx, ']'); | ||||
|     } else { | ||||
|         printf("%s", json->item_start_end); | ||||
|         writer_put_str(wctx, json->item_start_end); | ||||
|         json->indent_level--; | ||||
|         if (!json->compact) | ||||
|             JSON_INDENT(); | ||||
|         printf("}"); | ||||
|         writer_w8(wctx, '}'); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -1601,9 +1668,9 @@ static inline void json_print_item_str(WriterContext *wctx, | ||||
|     AVBPrint buf; | ||||
|  | ||||
|     av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||||
|     printf("\"%s\":", json_escape_str(&buf, key,   wctx)); | ||||
|     writer_printf(wctx, "\"%s\":", json_escape_str(&buf, key,   wctx)); | ||||
|     av_bprint_clear(&buf); | ||||
|     printf(" \"%s\"", json_escape_str(&buf, value, wctx)); | ||||
|     writer_printf(wctx, " \"%s\"", json_escape_str(&buf, value, wctx)); | ||||
|     av_bprint_finalize(&buf, NULL); | ||||
| } | ||||
|  | ||||
| @@ -1614,7 +1681,7 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val | ||||
|         wctx->section[wctx->level-1] : NULL; | ||||
|  | ||||
|     if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES)) | ||||
|         printf("%s", json->item_sep); | ||||
|         writer_put_str(wctx, json->item_sep); | ||||
|     if (!json->compact) | ||||
|         JSON_INDENT(); | ||||
|     json_print_item_str(wctx, key, value); | ||||
| @@ -1628,12 +1695,12 @@ static void json_print_int(WriterContext *wctx, const char *key, long long int v | ||||
|     AVBPrint buf; | ||||
|  | ||||
|     if (wctx->nb_item[wctx->level] || (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES)) | ||||
|         printf("%s", json->item_sep); | ||||
|         writer_put_str(wctx, json->item_sep); | ||||
|     if (!json->compact) | ||||
|         JSON_INDENT(); | ||||
|  | ||||
|     av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | ||||
|     printf("\"%s\": %lld", json_escape_str(&buf, key, wctx), value); | ||||
|     writer_printf(wctx, "\"%s\": %lld", json_escape_str(&buf, key, wctx), value); | ||||
|     av_bprint_finalize(&buf, NULL); | ||||
| } | ||||
|  | ||||
| @@ -1693,7 +1760,7 @@ static av_cold int xml_init(WriterContext *wctx) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #define XML_INDENT() printf("%*c", xml->indent_level * 4, ' ') | ||||
| #define XML_INDENT() writer_printf(wctx, "%*c", xml->indent_level * 4, ' ') | ||||
|  | ||||
| static void xml_print_section_header(WriterContext *wctx) | ||||
| { | ||||
| @@ -1707,8 +1774,8 @@ static void xml_print_section_header(WriterContext *wctx) | ||||
|             "xmlns:ffprobe=\"http://www.ffmpeg.org/schema/ffprobe\" " | ||||
|             "xsi:schemaLocation=\"http://www.ffmpeg.org/schema/ffprobe ffprobe.xsd\""; | ||||
|  | ||||
|         printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | ||||
|         printf("<%sffprobe%s>\n", | ||||
|         writer_put_str(wctx, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); | ||||
|         writer_printf(wctx, "<%sffprobe%s>\n", | ||||
|                xml->fully_qualified ? "ffprobe:" : "", | ||||
|                xml->fully_qualified ? qual : ""); | ||||
|         return; | ||||
| @@ -1716,20 +1783,20 @@ static void xml_print_section_header(WriterContext *wctx) | ||||
|  | ||||
|     if (xml->within_tag) { | ||||
|         xml->within_tag = 0; | ||||
|         printf(">\n"); | ||||
|         writer_put_str(wctx, ">\n"); | ||||
|     } | ||||
|     if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) { | ||||
|         xml->indent_level++; | ||||
|     } else { | ||||
|         if (parent_section && (parent_section->flags & SECTION_FLAG_IS_WRAPPER) && | ||||
|             wctx->level && wctx->nb_item[wctx->level-1]) | ||||
|             printf("\n"); | ||||
|             writer_w8(wctx, '\n'); | ||||
|         xml->indent_level++; | ||||
|  | ||||
|         if (section->flags & SECTION_FLAG_IS_ARRAY) { | ||||
|             XML_INDENT(); printf("<%s>\n", section->name); | ||||
|             XML_INDENT(); writer_printf(wctx, "<%s>\n", section->name); | ||||
|         } else { | ||||
|             XML_INDENT(); printf("<%s ", section->name); | ||||
|             XML_INDENT(); writer_printf(wctx, "<%s ", section->name); | ||||
|             xml->within_tag = 1; | ||||
|         } | ||||
|     } | ||||
| @@ -1741,15 +1808,15 @@ static void xml_print_section_footer(WriterContext *wctx) | ||||
|     const struct section *section = wctx->section[wctx->level]; | ||||
|  | ||||
|     if (wctx->level == 0) { | ||||
|         printf("</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : ""); | ||||
|         writer_printf(wctx, "</%sffprobe>\n", xml->fully_qualified ? "ffprobe:" : ""); | ||||
|     } else if (xml->within_tag) { | ||||
|         xml->within_tag = 0; | ||||
|         printf("/>\n"); | ||||
|         writer_put_str(wctx, "/>\n"); | ||||
|         xml->indent_level--; | ||||
|     } else if (section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS) { | ||||
|         xml->indent_level--; | ||||
|     } else { | ||||
|         XML_INDENT(); printf("</%s>\n", section->name); | ||||
|         XML_INDENT(); writer_printf(wctx, "</%s>\n", section->name); | ||||
|         xml->indent_level--; | ||||
|     } | ||||
| } | ||||
| @@ -1766,20 +1833,20 @@ static void xml_print_str(WriterContext *wctx, const char *key, const char *valu | ||||
|         XML_INDENT(); | ||||
|         av_bprint_escape(&buf, key, NULL, | ||||
|                          AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); | ||||
|         printf("<%s key=\"%s\"", | ||||
|                section->element_name, buf.str); | ||||
|         writer_printf(wctx, "<%s key=\"%s\"", | ||||
|                       section->element_name, buf.str); | ||||
|         av_bprint_clear(&buf); | ||||
|  | ||||
|         av_bprint_escape(&buf, value, NULL, | ||||
|                          AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); | ||||
|         printf(" value=\"%s\"/>\n", buf.str); | ||||
|         writer_printf(wctx, " value=\"%s\"/>\n", buf.str); | ||||
|     } else { | ||||
|         if (wctx->nb_item[wctx->level]) | ||||
|             printf(" "); | ||||
|             writer_w8(wctx, ' '); | ||||
|  | ||||
|         av_bprint_escape(&buf, value, NULL, | ||||
|                          AV_ESCAPE_MODE_XML, AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES); | ||||
|         printf("%s=\"%s\"", key, buf.str); | ||||
|         writer_printf(wctx, "%s=\"%s\"", key, buf.str); | ||||
|     } | ||||
|  | ||||
|     av_bprint_finalize(&buf, NULL); | ||||
| @@ -1788,8 +1855,8 @@ static void xml_print_str(WriterContext *wctx, const char *key, const char *valu | ||||
| static void xml_print_int(WriterContext *wctx, const char *key, long long int value) | ||||
| { | ||||
|     if (wctx->nb_item[wctx->level]) | ||||
|         printf(" "); | ||||
|     printf("%s=\"%lld\"", key, value); | ||||
|         writer_w8(wctx, ' '); | ||||
|     writer_printf(wctx, "%s=\"%lld\"", key, value); | ||||
| } | ||||
|  | ||||
| static Writer xml_writer = { | ||||
| @@ -3642,6 +3709,25 @@ static int opt_input_file_i(void *optctx, const char *opt, const char *arg) | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void opt_output_file(void *optctx, const char *arg) | ||||
| { | ||||
|     if (output_filename) { | ||||
|         av_log(NULL, AV_LOG_ERROR, | ||||
|                 "Argument '%s' provided as output filename, but '%s' was already specified.\n", | ||||
|                 arg, output_filename); | ||||
|         exit_program(1); | ||||
|     } | ||||
|     if (!strcmp(arg, "-")) | ||||
|         arg = "pipe:"; | ||||
|     output_filename = arg; | ||||
| } | ||||
|  | ||||
| static int opt_output_file_o(void *optctx, const char *opt, const char *arg) | ||||
| { | ||||
|     opt_output_file(optctx, arg); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int opt_print_filename(void *optctx, const char *opt, const char *arg) | ||||
| { | ||||
|     print_input_filename = arg; | ||||
| @@ -3904,6 +3990,7 @@ static const OptionDef real_options[] = { | ||||
|     { "bitexact", OPT_BOOL, {&do_bitexact}, "force bitexact output" }, | ||||
|     { "read_intervals", HAS_ARG, {.func_arg = opt_read_intervals}, "set read intervals", "read_intervals" }, | ||||
|     { "i", HAS_ARG, {.func_arg = opt_input_file_i}, "read specified file", "input_file"}, | ||||
|     { "o", HAS_ARG, {.func_arg = opt_output_file_o}, "write to specified output", "output_file"}, | ||||
|     { "print_filename", HAS_ARG, {.func_arg = opt_print_filename}, "override the printed input filename", "print_file"}, | ||||
|     { "find_stream_info", OPT_BOOL | OPT_INPUT | OPT_EXPERT, { &find_stream_info }, | ||||
|         "read and decode the streams to fill missing information with heuristics" }, | ||||
| @@ -4031,7 +4118,7 @@ int main(int argc, char **argv) | ||||
|     } | ||||
|  | ||||
|     if ((ret = writer_open(&wctx, w, w_args, | ||||
|                            sections, FF_ARRAY_ELEMS(sections))) >= 0) { | ||||
|                            sections, FF_ARRAY_ELEMS(sections), output_filename)) >= 0) { | ||||
|         if (w == &xml_writer) | ||||
|             wctx->string_validation_utf8_flags |= AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user