| @@ -29,6 +29,7 @@ | |||||
| #include "libavformat/avformat.h" | #include "libavformat/avformat.h" | ||||
| #include "libavcodec/avcodec.h" | #include "libavcodec/avcodec.h" | ||||
| #include "libavutil/avstring.h" | #include "libavutil/avstring.h" | ||||
| #include "libavutil/bprint.h" | |||||
| #include "libavutil/opt.h" | #include "libavutil/opt.h" | ||||
| #include "libavutil/pixdesc.h" | #include "libavutil/pixdesc.h" | ||||
| #include "libavutil/dict.h" | #include "libavutil/dict.h" | ||||
| @@ -384,31 +385,6 @@ fail: | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| #define ESCAPE_INIT_BUF_SIZE 256 | |||||
| #define ESCAPE_CHECK_SIZE(src, size, max_size) \ | |||||
| if (size > max_size) { \ | |||||
| char buf[64]; \ | |||||
| snprintf(buf, sizeof(buf), "%s", src); \ | |||||
| av_log(log_ctx, AV_LOG_WARNING, \ | |||||
| "String '%s...' is too big\n", buf); \ | |||||
| return "FFPROBE_TOO_BIG_STRING"; \ | |||||
| } | |||||
| #define ESCAPE_REALLOC_BUF(dst_size_p, dst_p, src, size) \ | |||||
| if (*dst_size_p < size) { \ | |||||
| char *q = av_realloc(*dst_p, size); \ | |||||
| if (!q) { \ | |||||
| char buf[64]; \ | |||||
| snprintf(buf, sizeof(buf), "%s", src); \ | |||||
| av_log(log_ctx, AV_LOG_WARNING, \ | |||||
| "String '%s...' could not be escaped\n", buf); \ | |||||
| return "FFPROBE_THIS_STRING_COULD_NOT_BE_ESCAPED"; \ | |||||
| } \ | |||||
| *dst_size_p = size; \ | |||||
| *dst = q; \ | |||||
| } | |||||
| /* WRITERS */ | /* WRITERS */ | ||||
| /* Default output */ | /* Default output */ | ||||
| @@ -487,81 +463,51 @@ static const Writer default_writer = { | |||||
| * Escape \n, \r, \\ and sep characters contained in s, and print the | * Escape \n, \r, \\ and sep characters contained in s, and print the | ||||
| * resulting string. | * resulting string. | ||||
| */ | */ | ||||
| static const char *c_escape_str(char **dst, size_t *dst_size, | |||||
| const char *src, const char sep, void *log_ctx) | |||||
| static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) | |||||
| { | { | ||||
| const char *p; | const char *p; | ||||
| char *q; | |||||
| size_t size = 1; | |||||
| /* precompute size */ | |||||
| for (p = src; *p; p++, size++) { | |||||
| ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-2); | |||||
| if (*p == '\n' || *p == '\r' || *p == '\\') | |||||
| size++; | |||||
| } | |||||
| ESCAPE_REALLOC_BUF(dst_size, dst, src, size); | |||||
| q = *dst; | |||||
| for (p = src; *p; p++) { | for (p = src; *p; p++) { | ||||
| switch (*src) { | switch (*src) { | ||||
| case '\n': *q++ = '\\'; *q++ = 'n'; break; | |||||
| case '\r': *q++ = '\\'; *q++ = 'r'; break; | |||||
| case '\\': *q++ = '\\'; *q++ = '\\'; break; | |||||
| case '\n': av_bprintf(dst, "%s", "\\n"); break; | |||||
| case '\r': av_bprintf(dst, "%s", "\\r"); break; | |||||
| case '\\': av_bprintf(dst, "%s", "\\\\"); break; | |||||
| default: | default: | ||||
| if (*p == sep) | if (*p == sep) | ||||
| *q++ = '\\'; | |||||
| *q++ = *p; | |||||
| av_bprint_chars(dst, '\\', 1); | |||||
| av_bprint_chars(dst, *p, 1); | |||||
| } | } | ||||
| } | } | ||||
| *q = 0; | |||||
| return *dst; | |||||
| return dst->str; | |||||
| } | } | ||||
| /** | /** | ||||
| * Quote fields containing special characters, check RFC4180. | * Quote fields containing special characters, check RFC4180. | ||||
| */ | */ | ||||
| static const char *csv_escape_str(char **dst, size_t *dst_size, | |||||
| const char *src, const char sep, void *log_ctx) | |||||
| static const char *csv_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) | |||||
| { | { | ||||
| const char *p; | const char *p; | ||||
| char *q; | |||||
| size_t size = 1; | |||||
| int quote = 0; | int quote = 0; | ||||
| /* precompute size */ | |||||
| for (p = src; *p; p++, size++) { | |||||
| ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-4); | |||||
| /* check if input needs quoting */ | |||||
| for (p = src; *p; p++) | |||||
| if (*p == '"' || *p == sep || *p == '\n' || *p == '\r') | if (*p == '"' || *p == sep || *p == '\n' || *p == '\r') | ||||
| if (!quote) { | |||||
| quote = 1; | |||||
| size += 2; | |||||
| } | |||||
| if (*p == '"') | |||||
| size++; | |||||
| } | |||||
| ESCAPE_REALLOC_BUF(dst_size, dst, src, size); | |||||
| quote = 1; | |||||
| q = *dst; | |||||
| p = src; | |||||
| if (quote) | if (quote) | ||||
| *q++ = '\"'; | |||||
| while (*p) { | |||||
| av_bprint_chars(dst, '\"', 1); | |||||
| for (p = src; *p; p++) { | |||||
| if (*p == '"') | if (*p == '"') | ||||
| *q++ = '\"'; | |||||
| *q++ = *p++; | |||||
| av_bprint_chars(dst, '\"', 1); | |||||
| av_bprint_chars(dst, *p, 1); | |||||
| } | } | ||||
| if (quote) | if (quote) | ||||
| *q++ = '\"'; | |||||
| *q = 0; | |||||
| return *dst; | |||||
| av_bprint_chars(dst, '\"', 1); | |||||
| return dst->str; | |||||
| } | } | ||||
| static const char *none_escape_str(char **dst, size_t *dst_size, | |||||
| const char *src, const char sep, void *log_ctx) | |||||
| static const char *none_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) | |||||
| { | { | ||||
| return src; | return src; | ||||
| } | } | ||||
| @@ -571,11 +517,8 @@ typedef struct CompactContext { | |||||
| char *item_sep_str; | char *item_sep_str; | ||||
| char item_sep; | char item_sep; | ||||
| int nokey; | int nokey; | ||||
| char *buf; | |||||
| size_t buf_size; | |||||
| char *escape_mode_str; | char *escape_mode_str; | ||||
| const char * (*escape_str)(char **dst, size_t *dst_size, | |||||
| const char *src, const char sep, void *log_ctx); | |||||
| const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx); | |||||
| } CompactContext; | } CompactContext; | ||||
| #define OFFSET(x) offsetof(CompactContext, x) | #define OFFSET(x) offsetof(CompactContext, x) | ||||
| @@ -621,10 +564,6 @@ static av_cold int compact_init(WriterContext *wctx, const char *args, void *opa | |||||
| } | } | ||||
| compact->item_sep = compact->item_sep_str[0]; | compact->item_sep = compact->item_sep_str[0]; | ||||
| compact->buf_size = ESCAPE_INIT_BUF_SIZE; | |||||
| if (!(compact->buf = av_malloc(compact->buf_size))) | |||||
| return AVERROR(ENOMEM); | |||||
| if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str; | if (!strcmp(compact->escape_mode_str, "none")) compact->escape_str = none_escape_str; | ||||
| else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str; | else if (!strcmp(compact->escape_mode_str, "c" )) compact->escape_str = c_escape_str; | ||||
| else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str; | else if (!strcmp(compact->escape_mode_str, "csv" )) compact->escape_str = csv_escape_str; | ||||
| @@ -641,7 +580,6 @@ static av_cold void compact_uninit(WriterContext *wctx) | |||||
| CompactContext *compact = wctx->priv; | CompactContext *compact = wctx->priv; | ||||
| av_freep(&compact->item_sep_str); | av_freep(&compact->item_sep_str); | ||||
| av_freep(&compact->buf); | |||||
| av_freep(&compact->escape_mode_str); | av_freep(&compact->escape_mode_str); | ||||
| } | } | ||||
| @@ -660,12 +598,14 @@ static void compact_print_section_footer(WriterContext *wctx, const char *sectio | |||||
| static void compact_print_str(WriterContext *wctx, const char *key, const char *value) | static void compact_print_str(WriterContext *wctx, const char *key, const char *value) | ||||
| { | { | ||||
| CompactContext *compact = wctx->priv; | CompactContext *compact = wctx->priv; | ||||
| AVBPrint buf; | |||||
| if (wctx->nb_item) printf("%c", compact->item_sep); | if (wctx->nb_item) printf("%c", compact->item_sep); | ||||
| if (!compact->nokey) | if (!compact->nokey) | ||||
| printf("%s=", key); | printf("%s=", key); | ||||
| printf("%s", compact->escape_str(&compact->buf, &compact->buf_size, | |||||
| value, compact->item_sep, wctx)); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("%s", compact->escape_str(&buf, value, compact->item_sep, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| } | } | ||||
| static void compact_print_int(WriterContext *wctx, const char *key, long long int value) | static void compact_print_int(WriterContext *wctx, const char *key, long long int value) | ||||
| @@ -682,14 +622,20 @@ static void compact_show_tags(WriterContext *wctx, AVDictionary *dict) | |||||
| { | { | ||||
| CompactContext *compact = wctx->priv; | CompactContext *compact = wctx->priv; | ||||
| AVDictionaryEntry *tag = NULL; | AVDictionaryEntry *tag = NULL; | ||||
| AVBPrint buf; | |||||
| while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { | while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { | ||||
| if (wctx->nb_item) printf("%c", compact->item_sep); | if (wctx->nb_item) printf("%c", compact->item_sep); | ||||
| if (!compact->nokey) | |||||
| printf("tag:%s=", compact->escape_str(&compact->buf, &compact->buf_size, | |||||
| tag->key, compact->item_sep, wctx)); | |||||
| printf("%s", compact->escape_str(&compact->buf, &compact->buf_size, | |||||
| tag->value, compact->item_sep, wctx)); | |||||
| if (!compact->nokey) { | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("tag:%s=", compact->escape_str(&buf, tag->key, compact->item_sep, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| } | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("%s", compact->escape_str(&buf, tag->value, compact->item_sep, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| } | } | ||||
| } | } | ||||
| @@ -731,8 +677,6 @@ static const Writer csv_writer = { | |||||
| typedef struct { | typedef struct { | ||||
| const AVClass *class; | const AVClass *class; | ||||
| int multiple_entries; ///< tells if the given chapter requires multiple entries | int multiple_entries; ///< tells if the given chapter requires multiple entries | ||||
| char *buf; | |||||
| size_t buf_size; | |||||
| int print_packets_and_frames; | int print_packets_and_frames; | ||||
| int indent_level; | int indent_level; | ||||
| int compact; | int compact; | ||||
| @@ -776,52 +720,27 @@ static av_cold int json_init(WriterContext *wctx, const char *args, void *opaque | |||||
| json->item_sep = json->compact ? ", " : ",\n"; | json->item_sep = json->compact ? ", " : ",\n"; | ||||
| json->item_start_end = json->compact ? " " : "\n"; | json->item_start_end = json->compact ? " " : "\n"; | ||||
| json->buf_size = ESCAPE_INIT_BUF_SIZE; | |||||
| if (!(json->buf = av_malloc(json->buf_size))) | |||||
| return AVERROR(ENOMEM); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| static av_cold void json_uninit(WriterContext *wctx) | |||||
| { | |||||
| JSONContext *json = wctx->priv; | |||||
| av_freep(&json->buf); | |||||
| } | |||||
| static const char *json_escape_str(char **dst, size_t *dst_size, const char *src, | |||||
| void *log_ctx) | |||||
| static const char *json_escape_str(AVBPrint *dst, const char *src, void *log_ctx) | |||||
| { | { | ||||
| static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0}; | static const char json_escape[] = {'"', '\\', '\b', '\f', '\n', '\r', '\t', 0}; | ||||
| static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0}; | static const char json_subst[] = {'"', '\\', 'b', 'f', 'n', 'r', 't', 0}; | ||||
| const char *p; | const char *p; | ||||
| char *q; | |||||
| size_t size = 1; | |||||
| // compute the length of the escaped string | |||||
| for (p = src; *p; p++) { | |||||
| ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-6); | |||||
| if (strchr(json_escape, *p)) size += 2; // simple escape | |||||
| else if ((unsigned char)*p < 32) size += 6; // handle non-printable chars | |||||
| else size += 1; // char copy | |||||
| } | |||||
| ESCAPE_REALLOC_BUF(dst_size, dst, src, size); | |||||
| q = *dst; | |||||
| for (p = src; *p; p++) { | for (p = src; *p; p++) { | ||||
| char *s = strchr(json_escape, *p); | char *s = strchr(json_escape, *p); | ||||
| if (s) { | if (s) { | ||||
| *q++ = '\\'; | |||||
| *q++ = json_subst[s - json_escape]; | |||||
| av_bprint_chars(dst, '\\', 1); | |||||
| av_bprint_chars(dst, json_subst[s - json_escape], 1); | |||||
| } else if ((unsigned char)*p < 32) { | } else if ((unsigned char)*p < 32) { | ||||
| snprintf(q, 7, "\\u00%02x", *p & 0xff); | |||||
| q += 6; | |||||
| av_bprintf(dst, "\\u00%02x", *p & 0xff); | |||||
| } else { | } else { | ||||
| *q++ = *p; | |||||
| av_bprint_chars(dst, *p, 1); | |||||
| } | } | ||||
| } | } | ||||
| *q = 0; | |||||
| return *dst; | |||||
| return dst->str; | |||||
| } | } | ||||
| static void json_print_header(WriterContext *wctx) | static void json_print_header(WriterContext *wctx) | ||||
| @@ -843,6 +762,7 @@ static void json_print_footer(WriterContext *wctx) | |||||
| static void json_print_chapter_header(WriterContext *wctx, const char *chapter) | static void json_print_chapter_header(WriterContext *wctx, const char *chapter) | ||||
| { | { | ||||
| JSONContext *json = wctx->priv; | JSONContext *json = wctx->priv; | ||||
| AVBPrint buf; | |||||
| if (wctx->nb_chapter) | if (wctx->nb_chapter) | ||||
| printf(","); | printf(","); | ||||
| @@ -852,7 +772,9 @@ static void json_print_chapter_header(WriterContext *wctx, const char *chapter) | |||||
| !strcmp(chapter, "streams") || !strcmp(chapter, "library_versions"); | !strcmp(chapter, "streams") || !strcmp(chapter, "library_versions"); | ||||
| if (json->multiple_entries) { | if (json->multiple_entries) { | ||||
| JSON_INDENT(); | JSON_INDENT(); | ||||
| printf("\"%s\": [\n", json_escape_str(&json->buf, &json->buf_size, chapter, wctx)); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("\"%s\": [\n", json_escape_str(&buf, chapter, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| json->print_packets_and_frames = !strcmp(chapter, "packets_and_frames"); | json->print_packets_and_frames = !strcmp(chapter, "packets_and_frames"); | ||||
| json->indent_level++; | json->indent_level++; | ||||
| } | } | ||||
| @@ -903,10 +825,15 @@ static void json_print_section_footer(WriterContext *wctx, const char *section) | |||||
| static inline void json_print_item_str(WriterContext *wctx, | static inline void json_print_item_str(WriterContext *wctx, | ||||
| const char *key, const char *value) | const char *key, const char *value) | ||||
| { | { | ||||
| JSONContext *json = wctx->priv; | |||||
| AVBPrint buf; | |||||
| printf("\"%s\":", json_escape_str(&json->buf, &json->buf_size, key, wctx)); | |||||
| printf(" \"%s\"", json_escape_str(&json->buf, &json->buf_size, value, wctx)); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("\"%s\":", json_escape_str(&buf, key, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf(" \"%s\"", json_escape_str(&buf, value, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| } | } | ||||
| static void json_print_str(WriterContext *wctx, const char *key, const char *value) | static void json_print_str(WriterContext *wctx, const char *key, const char *value) | ||||
| @@ -922,12 +849,15 @@ static void json_print_str(WriterContext *wctx, const char *key, const char *val | |||||
| static void json_print_int(WriterContext *wctx, const char *key, long long int value) | static void json_print_int(WriterContext *wctx, const char *key, long long int value) | ||||
| { | { | ||||
| JSONContext *json = wctx->priv; | JSONContext *json = wctx->priv; | ||||
| AVBPrint buf; | |||||
| if (wctx->nb_item) printf("%s", json->item_sep); | if (wctx->nb_item) printf("%s", json->item_sep); | ||||
| if (!json->compact) | if (!json->compact) | ||||
| JSON_INDENT(); | JSON_INDENT(); | ||||
| printf("\"%s\": %lld", | |||||
| json_escape_str(&json->buf, &json->buf_size, key, wctx), value); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("\"%s\": %lld", json_escape_str(&buf, key, wctx), value); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| } | } | ||||
| static void json_show_tags(WriterContext *wctx, AVDictionary *dict) | static void json_show_tags(WriterContext *wctx, AVDictionary *dict) | ||||
| @@ -960,7 +890,6 @@ static const Writer json_writer = { | |||||
| .name = "json", | .name = "json", | ||||
| .priv_size = sizeof(JSONContext), | .priv_size = sizeof(JSONContext), | ||||
| .init = json_init, | .init = json_init, | ||||
| .uninit = json_uninit, | |||||
| .print_header = json_print_header, | .print_header = json_print_header, | ||||
| .print_footer = json_print_footer, | .print_footer = json_print_footer, | ||||
| .print_chapter_header = json_print_chapter_header, | .print_chapter_header = json_print_chapter_header, | ||||
| @@ -982,8 +911,6 @@ typedef struct { | |||||
| int indent_level; | int indent_level; | ||||
| int fully_qualified; | int fully_qualified; | ||||
| int xsd_strict; | int xsd_strict; | ||||
| char *buf; | |||||
| size_t buf_size; | |||||
| } XMLContext; | } XMLContext; | ||||
| #undef OFFSET | #undef OFFSET | ||||
| @@ -1043,61 +970,25 @@ static av_cold int xml_init(WriterContext *wctx, const char *args, void *opaque) | |||||
| } | } | ||||
| } | } | ||||
| xml->buf_size = ESCAPE_INIT_BUF_SIZE; | |||||
| if (!(xml->buf = av_malloc(xml->buf_size))) | |||||
| return AVERROR(ENOMEM); | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| static av_cold void xml_uninit(WriterContext *wctx) | |||||
| { | |||||
| XMLContext *xml = wctx->priv; | |||||
| av_freep(&xml->buf); | |||||
| } | |||||
| static const char *xml_escape_str(char **dst, size_t *dst_size, const char *src, | |||||
| void *log_ctx) | |||||
| static const char *xml_escape_str(AVBPrint *dst, const char *src, void *log_ctx) | |||||
| { | { | ||||
| const char *p; | const char *p; | ||||
| char *q; | |||||
| size_t size = 1; | |||||
| /* precompute size */ | |||||
| for (p = src; *p; p++, size++) { | |||||
| ESCAPE_CHECK_SIZE(src, size, SIZE_MAX-10); | |||||
| switch (*p) { | |||||
| case '&' : size += strlen("&"); break; | |||||
| case '<' : size += strlen("<"); break; | |||||
| case '>' : size += strlen(">"); break; | |||||
| case '\"': size += strlen("""); break; | |||||
| case '\'': size += strlen("'"); break; | |||||
| default: size++; | |||||
| } | |||||
| } | |||||
| ESCAPE_REALLOC_BUF(dst_size, dst, src, size); | |||||
| #define COPY_STR(str) { \ | |||||
| const char *s = str; \ | |||||
| while (*s) \ | |||||
| *q++ = *s++; \ | |||||
| } | |||||
| p = src; | |||||
| q = *dst; | |||||
| while (*p) { | |||||
| for (p = src; *p; p++) { | |||||
| switch (*p) { | switch (*p) { | ||||
| case '&' : COPY_STR("&"); break; | |||||
| case '<' : COPY_STR("<"); break; | |||||
| case '>' : COPY_STR(">"); break; | |||||
| case '\"': COPY_STR("""); break; | |||||
| case '\'': COPY_STR("'"); break; | |||||
| default: *q++ = *p; | |||||
| case '&' : av_bprintf(dst, "%s", "&"); break; | |||||
| case '<' : av_bprintf(dst, "%s", "<"); break; | |||||
| case '>' : av_bprintf(dst, "%s", ">"); break; | |||||
| case '\"': av_bprintf(dst, "%s", """); break; | |||||
| case '\'': av_bprintf(dst, "%s", "'"); break; | |||||
| default: av_bprint_chars(dst, *p, 1); | |||||
| } | } | ||||
| p++; | |||||
| } | } | ||||
| *q = 0; | |||||
| return *dst; | |||||
| return dst->str; | |||||
| } | } | ||||
| static void xml_print_header(WriterContext *wctx) | static void xml_print_header(WriterContext *wctx) | ||||
| @@ -1172,11 +1063,13 @@ static void xml_print_section_footer(WriterContext *wctx, const char *section) | |||||
| static void xml_print_str(WriterContext *wctx, const char *key, const char *value) | static void xml_print_str(WriterContext *wctx, const char *key, const char *value) | ||||
| { | { | ||||
| XMLContext *xml = wctx->priv; | |||||
| AVBPrint buf; | |||||
| if (wctx->nb_item) | if (wctx->nb_item) | ||||
| printf(" "); | printf(" "); | ||||
| printf("%s=\"%s\"", key, xml_escape_str(&xml->buf, &xml->buf_size, value, wctx)); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("%s=\"%s\"", key, xml_escape_str(&buf, value, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| } | } | ||||
| static void xml_print_int(WriterContext *wctx, const char *key, long long int value) | static void xml_print_int(WriterContext *wctx, const char *key, long long int value) | ||||
| @@ -1191,6 +1084,7 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict) | |||||
| XMLContext *xml = wctx->priv; | XMLContext *xml = wctx->priv; | ||||
| AVDictionaryEntry *tag = NULL; | AVDictionaryEntry *tag = NULL; | ||||
| int is_first = 1; | int is_first = 1; | ||||
| AVBPrint buf; | |||||
| xml->indent_level++; | xml->indent_level++; | ||||
| while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { | while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { | ||||
| @@ -1201,10 +1095,14 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict) | |||||
| is_first = 0; | is_first = 0; | ||||
| } | } | ||||
| XML_INDENT(); | XML_INDENT(); | ||||
| printf("<tag key=\"%s\"", | |||||
| xml_escape_str(&xml->buf, &xml->buf_size, tag->key, wctx)); | |||||
| printf(" value=\"%s\"/>\n", | |||||
| xml_escape_str(&xml->buf, &xml->buf_size, tag->value, wctx)); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf("<tag key=\"%s\"", xml_escape_str(&buf, tag->key, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); | |||||
| printf(" value=\"%s\"/>\n", xml_escape_str(&buf, tag->value, wctx)); | |||||
| av_bprint_finalize(&buf, NULL); | |||||
| } | } | ||||
| xml->indent_level--; | xml->indent_level--; | ||||
| } | } | ||||
| @@ -1213,7 +1111,6 @@ static Writer xml_writer = { | |||||
| .name = "xml", | .name = "xml", | ||||
| .priv_size = sizeof(XMLContext), | .priv_size = sizeof(XMLContext), | ||||
| .init = xml_init, | .init = xml_init, | ||||
| .uninit = xml_uninit, | |||||
| .print_header = xml_print_header, | .print_header = xml_print_header, | ||||
| .print_footer = xml_print_footer, | .print_footer = xml_print_footer, | ||||
| .print_chapter_header = xml_print_chapter_header, | .print_chapter_header = xml_print_chapter_header, | ||||