X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=ffprobe.c;h=5b703486dbb5d177bc130fe8b78772794f1af3e6;hb=043c75a989a16334cf58099d2fff687dece5444d;hp=c98bdac3ffa3d1a31a3e702f253211ed59d75e85;hpb=f7d5272456ff67d6af262bb220d2af68c85cbb92;p=ffmpeg diff --git a/ffprobe.c b/ffprobe.c index c98bdac3ffa..5b703486dbb 100644 --- a/ffprobe.c +++ b/ffprobe.c @@ -53,6 +53,7 @@ static int do_show_frames = 0; static AVDictionary *fmt_entries_to_show = NULL; static int do_show_packets = 0; static int do_show_streams = 0; +static int do_show_data = 0; static int do_show_program_version = 0; static int do_show_library_versions = 0; @@ -165,6 +166,7 @@ typedef struct Writer { void (*print_section_header)(WriterContext *wctx, const char *); void (*print_section_footer)(WriterContext *wctx, const char *); void (*print_integer) (WriterContext *wctx, const char *, long long int); + void (*print_rational) (WriterContext *wctx, AVRational *q, char *sep); void (*print_string) (WriterContext *wctx, const char *, const char *); void (*show_tags) (WriterContext *wctx, AVDictionary *dict); int flags; ///< a combination or WRITER_FLAG_* @@ -177,9 +179,14 @@ struct WriterContext { void *priv; ///< private data for use by the filter unsigned int nb_item; ///< number of the item printed in the given section, starting at 0 unsigned int nb_section; ///< number of the section printed in the given section sequence, starting at 0 + unsigned int nb_section_packet; ///< number of the packet section in case we are in "packets_and_frames" section + unsigned int nb_section_frame; ///< number of the frame section in case we are in "packets_and_frames" section + unsigned int nb_section_packet_frame; ///< nb_section_packet or nb_section_frame according if is_packets_and_frames unsigned int nb_chapter; ///< number of the chapter, starting at 0 + int multiple_sections; ///< tells if the current chapter can contain multiple sections int is_fmt_chapter; ///< tells if the current chapter is "format", required by the print_format_entry option + int is_packets_and_frames; ///< tells if the current section is "packets_and_frames" }; static const char *writer_get_name(void *p) @@ -251,11 +258,17 @@ static inline void writer_print_footer(WriterContext *wctx) static inline void writer_print_chapter_header(WriterContext *wctx, const char *chapter) { + wctx->nb_section = + wctx->nb_section_packet = wctx->nb_section_frame = + wctx->nb_section_packet_frame = 0; + wctx->is_packets_and_frames = !strcmp(chapter, "packets_and_frames"); + wctx->multiple_sections = !strcmp(chapter, "packets") || !strcmp(chapter, "frames" ) || + wctx->is_packets_and_frames || + !strcmp(chapter, "streams") || !strcmp(chapter, "library_versions"); + wctx->is_fmt_chapter = !strcmp(chapter, "format"); + if (wctx->writer->print_chapter_header) wctx->writer->print_chapter_header(wctx, chapter); - wctx->nb_section = 0; - - wctx->is_fmt_chapter = !strcmp(chapter, "format"); } static inline void writer_print_chapter_footer(WriterContext *wctx, @@ -269,6 +282,9 @@ static inline void writer_print_chapter_footer(WriterContext *wctx, static inline void writer_print_section_header(WriterContext *wctx, const char *section) { + if (wctx->is_packets_and_frames) + wctx->nb_section_packet_frame = !strcmp(section, "packet") ? wctx->nb_section_packet + : wctx->nb_section_frame; if (wctx->writer->print_section_header) wctx->writer->print_section_header(wctx, section); wctx->nb_item = 0; @@ -279,6 +295,10 @@ static inline void writer_print_section_footer(WriterContext *wctx, { if (wctx->writer->print_section_footer) wctx->writer->print_section_footer(wctx, section); + if (wctx->is_packets_and_frames) { + if (!strcmp(section, "packet")) wctx->nb_section_packet++; + else wctx->nb_section_frame++; + } wctx->nb_section++; } @@ -291,6 +311,16 @@ static inline void writer_print_integer(WriterContext *wctx, } } +static inline void writer_print_rational(WriterContext *wctx, + const char *key, AVRational q, char sep) +{ + AVBPrint buf; + av_bprint_init(&buf, 0, AV_BPRINT_SIZE_AUTOMATIC); + av_bprintf(&buf, "%d%c%d", q.num, sep, q.den); + wctx->writer->print_string(wctx, key, buf.str); + wctx->nb_item++; +} + static inline void writer_print_string(WriterContext *wctx, const char *key, const char *val, int opt) { @@ -303,12 +333,12 @@ static inline void writer_print_string(WriterContext *wctx, } static void writer_print_time(WriterContext *wctx, const char *key, - int64_t ts, const AVRational *time_base) + int64_t ts, const AVRational *time_base, int is_duration) { char buf[128]; if (!wctx->is_fmt_chapter || !fmt_entries_to_show || av_dict_get(fmt_entries_to_show, key, NULL, 0)) { - if (ts == AV_NOPTS_VALUE) { + if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) { writer_print_string(wctx, key, "N/A", 1); } else { double d = ts * av_q2d(*time_base); @@ -318,9 +348,9 @@ static void writer_print_time(WriterContext *wctx, const char *key, } } -static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts) +static void writer_print_ts(WriterContext *wctx, const char *key, int64_t ts, int is_duration) { - if (ts == AV_NOPTS_VALUE) { + if ((!is_duration && ts == AV_NOPTS_VALUE) || (is_duration && ts == 0)) { writer_print_string(wctx, key, "N/A", 1); } else { writer_print_integer(wctx, key, ts); @@ -332,6 +362,34 @@ static inline void writer_show_tags(WriterContext *wctx, AVDictionary *dict) wctx->writer->show_tags(wctx, dict); } +static void writer_print_data(WriterContext *wctx, const char *name, + uint8_t *data, int size) +{ + AVBPrint bp; + int offset = 0, l, i; + + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); + av_bprintf(&bp, "\n"); + while (size) { + av_bprintf(&bp, "%08x: ", offset); + l = FFMIN(size, 16); + for (i = 0; i < l; i++) { + av_bprintf(&bp, "%02x", data[i]); + if (i & 1) + av_bprintf(&bp, " "); + } + av_bprint_chars(&bp, ' ', 41 - 2 * i - i / 2); + for (i = 0; i < l; i++) + av_bprint_chars(&bp, data[i] - 32U < 95 ? data[i] : '.', 1); + av_bprintf(&bp, "\n"); + offset += l; + data += l; + size -= l; + } + writer_print_string(wctx, name, bp.str, 0); + av_bprint_finalize(&bp, NULL); +} + #define MAX_REGISTERED_WRITERS_NB 64 static const Writer *registered_writers[MAX_REGISTERED_WRITERS_NB + 1]; @@ -497,15 +555,16 @@ static const Writer default_writer = { /* Compact output */ /** - * Escape \n, \r, \\ and sep characters contained in s, and print the - * resulting string. + * Apply C-language-like string escaping. */ static const char *c_escape_str(AVBPrint *dst, const char *src, const char sep, void *log_ctx) { const char *p; for (p = src; *p; p++) { - switch (*src) { + switch (*p) { + case '\b': av_bprintf(dst, "%s", "\\b"); break; + case '\f': av_bprintf(dst, "%s", "\\f"); break; case '\n': av_bprintf(dst, "%s", "\\n"); break; case '\r': av_bprintf(dst, "%s", "\\r"); break; case '\\': av_bprintf(dst, "%s", "\\\\"); break; @@ -662,19 +721,17 @@ static void compact_show_tags(WriterContext *wctx, AVDictionary *dict) AVDictionaryEntry *tag = NULL; AVBPrint buf; + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { if (wctx->nb_item) printf("%c", compact->item_sep); - if (!compact->nokey) { - av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_clear(&buf); 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); + av_bprint_clear(&buf); printf("%s", compact->escape_str(&buf, tag->value, compact->item_sep, wctx)); - av_bprint_finalize(&buf, NULL); } + av_bprint_finalize(&buf, NULL); } static const Writer compact_writer = { @@ -710,12 +767,330 @@ static const Writer csv_writer = { .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS, }; +/* Flat output */ + +typedef struct FlatContext { + const AVClass *class; + const char *section, *chapter; + const char *sep_str; + char sep; + int hierarchical; +} FlatContext; + +#undef OFFSET +#define OFFSET(x) offsetof(FlatContext, x) + +static const AVOption flat_options[]= { + {"sep_char", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, CHAR_MIN, CHAR_MAX }, + {"s", "set separator", OFFSET(sep_str), AV_OPT_TYPE_STRING, {.str="."}, CHAR_MIN, CHAR_MAX }, + {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 }, + {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 }, + {NULL}, +}; + +static const char *flat_get_name(void *ctx) +{ + return "flat"; +} + +static const AVClass flat_class = { + "FlatContext", + flat_get_name, + flat_options +}; + +static av_cold int flat_init(WriterContext *wctx, const char *args, void *opaque) +{ + FlatContext *flat = wctx->priv; + int err; + + flat->class = &flat_class; + av_opt_set_defaults(flat); + + if (args && + (err = (av_set_options_string(flat, args, "=", ":"))) < 0) { + av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args); + return err; + } + if (strlen(flat->sep_str) != 1) { + av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n", + flat->sep_str); + return AVERROR(EINVAL); + } + flat->sep = flat->sep_str[0]; + return 0; +} + +static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep) +{ + const char *p; + + for (p = src; *p; p++) { + if (!((*p >= '0' && *p <= '9') || + (*p >= 'a' && *p <= 'z') || + (*p >= 'A' && *p <= 'Z'))) + av_bprint_chars(dst, '_', 1); + else + av_bprint_chars(dst, *p, 1); + } + return dst->str; +} + +static const char *flat_escape_value_str(AVBPrint *dst, const char *src) +{ + const char *p; + + for (p = src; *p; p++) { + switch (*p) { + case '\n': av_bprintf(dst, "%s", "\\n"); break; + case '\r': av_bprintf(dst, "%s", "\\r"); 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); break; + } + } + return dst->str; +} + +static void flat_print_chapter_header(WriterContext *wctx, const char *chapter) +{ + FlatContext *flat = wctx->priv; + flat->chapter = chapter; +} + +static void flat_print_section_header(WriterContext *wctx, const char *section) +{ + FlatContext *flat = wctx->priv; + flat->section = section; +} + +static void flat_print_section(WriterContext *wctx) +{ + FlatContext *flat = wctx->priv; + int n = wctx->is_packets_and_frames ? wctx->nb_section_packet_frame + : wctx->nb_section; + + if (flat->hierarchical && wctx->multiple_sections) + printf("%s%c", flat->chapter, flat->sep); + printf("%s%c", flat->section, flat->sep); + if (wctx->multiple_sections) + printf("%d%c", n, flat->sep); +} + +static void flat_print_int(WriterContext *wctx, const char *key, long long int value) +{ + flat_print_section(wctx); + printf("%s=%lld\n", key, value); +} + +static void flat_print_str(WriterContext *wctx, const char *key, const char *value) +{ + FlatContext *flat = wctx->priv; + AVBPrint buf; + + flat_print_section(wctx); + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + printf("%s=", flat_escape_key_str(&buf, key, flat->sep)); + av_bprint_clear(&buf); + printf("\"%s\"\n", flat_escape_value_str(&buf, value)); + av_bprint_finalize(&buf, NULL); +} + +static void flat_show_tags(WriterContext *wctx, AVDictionary *dict) +{ + FlatContext *flat = wctx->priv; + AVBPrint buf; + AVDictionaryEntry *tag = NULL; + + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { + flat_print_section(wctx); + av_bprint_clear(&buf); + printf("tags%c%s=", flat->sep, flat_escape_key_str(&buf, tag->key, flat->sep)); + av_bprint_clear(&buf); + printf("\"%s\"\n", flat_escape_value_str(&buf, tag->value)); + } + av_bprint_finalize(&buf, NULL); +} + +static const Writer flat_writer = { + .name = "flat", + .priv_size = sizeof(FlatContext), + .init = flat_init, + .print_chapter_header = flat_print_chapter_header, + .print_section_header = flat_print_section_header, + .print_integer = flat_print_int, + .print_string = flat_print_str, + .show_tags = flat_show_tags, + .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER, +}; + +/* INI format output */ + +typedef struct { + const AVClass *class; + AVBPrint chapter_name, section_name; + int hierarchical; +} INIContext; + +#undef OFFSET +#define OFFSET(x) offsetof(INIContext, x) + +static const AVOption ini_options[] = { + {"hierarchical", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 }, + {"h", "specify if the section specification should be hierarchical", OFFSET(hierarchical), AV_OPT_TYPE_INT, {.dbl=1}, 0, 1 }, + {NULL}, +}; + +static const char *ini_get_name(void *ctx) +{ + return "ini"; +} + +static const AVClass ini_class = { + "INIContext", + ini_get_name, + ini_options +}; + +static av_cold int ini_init(WriterContext *wctx, const char *args, void *opaque) +{ + INIContext *ini = wctx->priv; + int err; + + av_bprint_init(&ini->chapter_name, 1, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_init(&ini->section_name, 1, AV_BPRINT_SIZE_UNLIMITED); + + ini->class = &ini_class; + av_opt_set_defaults(ini); + + if (args && (err = av_set_options_string(ini, args, "=", ":")) < 0) { + av_log(wctx, AV_LOG_ERROR, "Error parsing options string: '%s'\n", args); + return err; + } + + return 0; +} + +static av_cold void ini_uninit(WriterContext *wctx) +{ + INIContext *ini = wctx->priv; + av_bprint_finalize(&ini->chapter_name, NULL); + av_bprint_finalize(&ini->section_name, NULL); +} + +static void ini_print_header(WriterContext *wctx) +{ + printf("# ffprobe output\n\n"); +} + +static char *ini_escape_str(AVBPrint *dst, const char *src) +{ + int i = 0; + char c = 0; + + while (c = src[i++]) { + switch (c) { + case '\b': av_bprintf(dst, "%s", "\\b"); break; + case '\f': av_bprintf(dst, "%s", "\\f"); break; + case '\n': av_bprintf(dst, "%s", "\\n"); break; + case '\r': av_bprintf(dst, "%s", "\\r"); break; + case '\t': av_bprintf(dst, "%s", "\\t"); break; + case '\\': + case '#' : + case '=' : + case ':' : av_bprint_chars(dst, '\\', 1); + default: + if ((unsigned char)c < 32) + av_bprintf(dst, "\\x00%02x", c & 0xff); + else + av_bprint_chars(dst, c, 1); + break; + } + } + return dst->str; +} + +static void ini_print_chapter_header(WriterContext *wctx, const char *chapter) +{ + INIContext *ini = wctx->priv; + + av_bprint_clear(&ini->chapter_name); + av_bprintf(&ini->chapter_name, "%s", chapter); + + if (wctx->nb_chapter) + printf("\n"); +} + +static void ini_print_section_header(WriterContext *wctx, const char *section) +{ + INIContext *ini = wctx->priv; + int n = wctx->is_packets_and_frames ? wctx->nb_section_packet_frame + : wctx->nb_section; + if (wctx->nb_section) + printf("\n"); + av_bprint_clear(&ini->section_name); + + if (ini->hierarchical && wctx->multiple_sections) + av_bprintf(&ini->section_name, "%s.", ini->chapter_name.str); + av_bprintf(&ini->section_name, "%s", section); + + if (wctx->multiple_sections) + av_bprintf(&ini->section_name, ".%d", n); + printf("[%s]\n", ini->section_name.str); +} + +static void ini_print_str(WriterContext *wctx, const char *key, const char *value) +{ + AVBPrint buf; + + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + printf("%s=", ini_escape_str(&buf, key)); + av_bprint_clear(&buf); + printf("%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); +} + +static void ini_show_tags(WriterContext *wctx, AVDictionary *dict) +{ + INIContext *ini = wctx->priv; + AVDictionaryEntry *tag = NULL; + int is_first = 1; + + while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { + if (is_first) { + printf("\n[%s.tags]\n", ini->section_name.str); + is_first = 0; + } + writer_print_string(wctx, tag->key, tag->value, 0); + } +} + +static const Writer ini_writer = { + .name = "ini", + .priv_size = sizeof(INIContext), + .init = ini_init, + .uninit = ini_uninit, + .print_header = ini_print_header, + .print_chapter_header = ini_print_chapter_header, + .print_section_header = ini_print_section_header, + .print_integer = ini_print_int, + .print_string = ini_print_str, + .show_tags = ini_show_tags, + .flags = WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS|WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER, +}; + /* JSON output */ typedef struct { const AVClass *class; - int multiple_entries; ///< tells if the given chapter requires multiple entries - int print_packets_and_frames; int indent_level; int compact; const char *item_sep, *item_start_end; @@ -805,15 +1180,11 @@ static void json_print_chapter_header(WriterContext *wctx, const char *chapter) if (wctx->nb_chapter) printf(","); printf("\n"); - json->multiple_entries = !strcmp(chapter, "packets") || !strcmp(chapter, "frames" ) || - !strcmp(chapter, "packets_and_frames") || - !strcmp(chapter, "streams") || !strcmp(chapter, "library_versions"); - if (json->multiple_entries) { + if (wctx->multiple_sections) { JSON_INDENT(); 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->indent_level++; } } @@ -822,7 +1193,7 @@ static void json_print_chapter_footer(WriterContext *wctx, const char *chapter) { JSONContext *json = wctx->priv; - if (json->multiple_entries) { + if (wctx->multiple_sections) { printf("\n"); json->indent_level--; JSON_INDENT(); @@ -837,12 +1208,12 @@ static void json_print_section_header(WriterContext *wctx, const char *section) if (wctx->nb_section) printf(",\n"); JSON_INDENT(); - if (!json->multiple_entries) + if (!wctx->multiple_sections) printf("\"%s\": ", section); printf("{%s", json->item_start_end); json->indent_level++; /* this is required so the parser can distinguish between packets and frames */ - if (json->print_packets_and_frames) { + if (wctx->is_packets_and_frames) { if (!json->compact) JSON_INDENT(); printf("\"type\": \"%s\"%s", section, json->item_sep); @@ -867,9 +1238,7 @@ static inline void json_print_item_str(WriterContext *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); + av_bprint_clear(&buf); printf(" \"%s\"", json_escape_str(&buf, value, wctx)); av_bprint_finalize(&buf, NULL); } @@ -945,7 +1314,6 @@ static const Writer json_writer = { typedef struct { const AVClass *class; int within_tag; - int multiple_entries; ///< tells if the given chapter requires multiple entries int indent_level; int fully_qualified; int xsd_strict; @@ -1060,11 +1428,7 @@ static void xml_print_chapter_header(WriterContext *wctx, const char *chapter) if (wctx->nb_chapter) printf("\n"); - xml->multiple_entries = !strcmp(chapter, "packets") || !strcmp(chapter, "frames") || - !strcmp(chapter, "packets_and_frames") || - !strcmp(chapter, "streams") || !strcmp(chapter, "library_versions"); - - if (xml->multiple_entries) { + if (wctx->multiple_sections) { XML_INDENT(); printf("<%s>\n", chapter); xml->indent_level++; } @@ -1074,7 +1438,7 @@ static void xml_print_chapter_footer(WriterContext *wctx, const char *chapter) { XMLContext *xml = wctx->priv; - if (xml->multiple_entries) { + if (wctx->multiple_sections) { xml->indent_level--; XML_INDENT(); printf("\n", chapter); } @@ -1124,6 +1488,7 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict) int is_first = 1; AVBPrint buf; + av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); xml->indent_level++; while ((tag = av_dict_get(dict, "", tag, AV_DICT_IGNORE_SUFFIX))) { if (is_first) { @@ -1134,14 +1499,12 @@ static void xml_show_tags(WriterContext *wctx, AVDictionary *dict) } XML_INDENT(); - av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_clear(&buf); printf("key, wctx)); - av_bprint_finalize(&buf, NULL); - - av_bprint_init(&buf, 1, AV_BPRINT_SIZE_UNLIMITED); + av_bprint_clear(&buf); printf(" value=\"%s\"/>\n", xml_escape_str(&buf, tag->value, wctx)); - av_bprint_finalize(&buf, NULL); } + av_bprint_finalize(&buf, NULL); xml->indent_level--; } @@ -1172,6 +1535,8 @@ static void writer_register_all(void) writer_register(&default_writer); writer_register(&compact_writer); writer_register(&csv_writer); + writer_register(&flat_writer); + writer_register(&ini_writer); writer_register(&json_writer); writer_register(&xml_writer); } @@ -1183,10 +1548,13 @@ static void writer_register_all(void) } while (0) #define print_int(k, v) writer_print_integer(w, k, v) +#define print_q(k, v, s) writer_print_rational(w, k, v, s) #define print_str(k, v) writer_print_string(w, k, v, 0) #define print_str_opt(k, v) writer_print_string(w, k, v, 1) -#define print_time(k, v, tb) writer_print_time(w, k, v, tb) -#define print_ts(k, v) writer_print_ts(w, k, v) +#define print_time(k, v, tb) writer_print_time(w, k, v, tb, 0) +#define print_ts(k, v) writer_print_ts(w, k, v, 0) +#define print_duration_time(k, v, tb) writer_print_time(w, k, v, tb, 1) +#define print_duration_ts(k, v) writer_print_ts(w, k, v, 1) #define print_val(k, v, u) writer_print_string(w, k, \ value_string(val_str, sizeof(val_str), (struct unit_value){.val.i = v, .unit=u}), 0) #define print_section_header(s) writer_print_section_header(w, s) @@ -1211,12 +1579,16 @@ static void show_packet(WriterContext *w, AVFormatContext *fmt_ctx, AVPacket *pk print_time("pts_time", pkt->pts, &st->time_base); print_ts ("dts", pkt->dts); print_time("dts_time", pkt->dts, &st->time_base); - print_ts ("duration", pkt->duration); - print_time("duration_time", pkt->duration, &st->time_base); + print_duration_ts("duration", pkt->duration); + print_duration_time("duration_time", pkt->duration, &st->time_base); + print_duration_ts("convergence_duration", pkt->convergence_duration); + print_duration_time("convergence_duration_time", pkt->convergence_duration, &st->time_base); print_val("size", pkt->size, unit_byte_str); if (pkt->pos != -1) print_fmt ("pos", "%"PRId64, pkt->pos); else print_str_opt("pos", "N/A"); print_fmt("flags", "%c", pkt->flags & AV_PKT_FLAG_KEY ? 'K' : '_'); + if (do_show_data) + writer_print_data(w, "data", pkt->data, pkt->size); print_section_footer("packet"); av_bprint_finalize(&pbuf, NULL); @@ -1240,6 +1612,8 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream) print_time("pkt_pts_time", frame->pkt_pts, &stream->time_base); print_ts ("pkt_dts", frame->pkt_dts); print_time("pkt_dts_time", frame->pkt_dts, &stream->time_base); + print_duration_ts ("pkt_duration", frame->pkt_duration); + print_duration_time("pkt_duration_time", frame->pkt_duration, &stream->time_base); if (frame->pkt_pos != -1) print_fmt ("pkt_pos", "%"PRId64, frame->pkt_pos); else print_str_opt("pkt_pos", "N/A"); @@ -1251,9 +1625,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream) if (s) print_str ("pix_fmt", s); else print_str_opt("pix_fmt", "unknown"); if (frame->sample_aspect_ratio.num) { - print_fmt("sample_aspect_ratio", "%d:%d", - frame->sample_aspect_ratio.num, - frame->sample_aspect_ratio.den); + print_q("sample_aspect_ratio", frame->sample_aspect_ratio, ':'); } else { print_str_opt("sample_aspect_ratio", "N/A"); } @@ -1273,6 +1645,7 @@ static void show_frame(WriterContext *w, AVFrame *frame, AVStream *stream) print_int("nb_samples", frame->nb_samples); break; } + show_tags(av_frame_get_metadata(frame)); print_section_footer("frame"); @@ -1288,14 +1661,16 @@ static av_always_inline int get_decoded_frame(AVFormatContext *fmt_ctx, int ret = 0; *got_frame = 0; - switch (dec_ctx->codec_type) { - case AVMEDIA_TYPE_VIDEO: - ret = avcodec_decode_video2(dec_ctx, frame, got_frame, pkt); - break; + if (dec_ctx->codec) { + switch (dec_ctx->codec_type) { + case AVMEDIA_TYPE_VIDEO: + ret = avcodec_decode_video2(dec_ctx, frame, got_frame, pkt); + break; - case AVMEDIA_TYPE_AUDIO: - ret = avcodec_decode_audio4(dec_ctx, frame, got_frame, pkt); - break; + case AVMEDIA_TYPE_AUDIO: + ret = avcodec_decode_audio4(dec_ctx, frame, got_frame, pkt); + break; + } } return ret; @@ -1381,7 +1756,7 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i s = av_get_media_type_string(dec_ctx->codec_type); if (s) print_str ("codec_type", s); else print_str_opt("codec_type", "unknown"); - print_fmt("codec_time_base", "%d/%d", dec_ctx->time_base.num, dec_ctx->time_base.den); + print_q("codec_time_base", dec_ctx->time_base, '/'); /* print AVI/FourCC tag */ av_get_codec_tag_string(val_str, sizeof(val_str), dec_ctx->codec_tag); @@ -1394,16 +1769,12 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i print_int("height", dec_ctx->height); print_int("has_b_frames", dec_ctx->has_b_frames); if (dec_ctx->sample_aspect_ratio.num) { - print_fmt("sample_aspect_ratio", "%d:%d", - dec_ctx->sample_aspect_ratio.num, - dec_ctx->sample_aspect_ratio.den); + print_q("sample_aspect_ratio", dec_ctx->sample_aspect_ratio, ':'); av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, dec_ctx->width * dec_ctx->sample_aspect_ratio.num, dec_ctx->height * dec_ctx->sample_aspect_ratio.den, 1024*1024); - print_fmt("display_aspect_ratio", "%d:%d", - display_aspect_ratio.num, - display_aspect_ratio.den); + print_q("display_aspect_ratio", display_aspect_ratio, ':'); } else { print_str_opt("sample_aspect_ratio", "N/A"); print_str_opt("display_aspect_ratio", "N/A"); @@ -1447,9 +1818,9 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id", "0x%x", stream->id); else print_str_opt("id", "N/A"); - print_fmt("r_frame_rate", "%d/%d", stream->r_frame_rate.num, stream->r_frame_rate.den); - print_fmt("avg_frame_rate", "%d/%d", stream->avg_frame_rate.num, stream->avg_frame_rate.den); - print_fmt("time_base", "%d/%d", stream->time_base.num, stream->time_base.den); + print_q("r_frame_rate", stream->r_frame_rate, '/'); + print_q("avg_frame_rate", stream->avg_frame_rate, '/'); + print_q("time_base", stream->time_base, '/'); print_time("start_time", stream->start_time, &stream->time_base); print_time("duration", stream->duration, &stream->time_base); if (dec_ctx->bit_rate > 0) print_val ("bit_rate", dec_ctx->bit_rate, unit_bit_per_second_str); @@ -1461,6 +1832,9 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i if (nb_streams_packets[stream_idx]) print_fmt ("nb_read_packets", "%"PRIu64, nb_streams_packets[stream_idx]); else print_str_opt("nb_read_packets", "N/A"); show_tags(stream->metadata); + if (do_show_data) + writer_print_data(w, "extradata", dec_ctx->extradata, + dec_ctx->extradata_size); print_section_footer("stream"); av_bprint_finalize(&pbuf, NULL); @@ -1541,7 +1915,11 @@ static int open_input_file(AVFormatContext **fmt_ctx_ptr, const char *filename) AVStream *stream = fmt_ctx->streams[i]; AVCodec *codec; - if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) { + if (stream->codec->codec_id == CODEC_ID_PROBE) { + av_log(NULL, AV_LOG_ERROR, + "Failed to probe codec for input stream %d\n", + stream->index); + } else if (!(codec = avcodec_find_decoder(stream->codec->codec_id))) { av_log(NULL, AV_LOG_ERROR, "Unsupported codec with id %d for input stream %d\n", stream->codec->codec_id, stream->index); @@ -1631,8 +2009,7 @@ static void ffprobe_show_program_version(WriterContext *w) program_birth_year, this_year); print_str("build_date", __DATE__); print_str("build_time", __TIME__); - print_str("compiler_type", CC_TYPE); - print_str("compiler_version", CC_VERSION); + print_str("compiler_ident", CC_IDENT); print_str("configuration", FFMPEG_CONFIGURATION); print_section_footer("program_version"); writer_print_chapter_footer(w, "program_version"); @@ -1738,7 +2115,9 @@ static const OptionDef options[] = { { "pretty", 0, {(void*)&opt_pretty}, "prettify the format of displayed values, make it more human readable" }, { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_format}, - "set the output printing format (available formats are: default, compact, csv, json, xml)", "format" }, + "set the output printing format (available formats are: default, compact, csv, flat, ini, json, xml)", "format" }, + { "of", OPT_STRING | HAS_ARG, {(void*)&print_format}, "alias for -print_format", "format" }, + { "show_data", OPT_BOOL, {(void*)&do_show_data}, "show packets data" }, { "show_error", OPT_BOOL, {(void*)&do_show_error} , "show probing error" }, { "show_format", OPT_BOOL, {(void*)&do_show_format} , "show format/container info" }, { "show_frames", OPT_BOOL, {(void*)&do_show_frames} , "show frames info" },