]> git.sesse.net Git - ffmpeg/blobdiff - ffprobe.c
cafdec: fix seeking with adpcm
[ffmpeg] / ffprobe.c
index 9e5cd04101c02216f21ac592a43381512c758043..84dfa19643affc9d74048ceaa44b61ac059bfe07 100644 (file)
--- a/ffprobe.c
+++ b/ffprobe.c
@@ -55,9 +55,9 @@ static int do_read_packets = 0;
 static int do_show_error   = 0;
 static int do_show_format  = 0;
 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_stream_disposition = 0;
 static int do_show_data    = 0;
 static int do_show_program_version  = 0;
 static int do_show_library_versions = 0;
@@ -69,11 +69,14 @@ static int use_value_sexagesimal_format = 0;
 static int show_private_data            = 1;
 
 static char *print_format;
+static char *stream_specifier;
 
 /* section structure definition */
 
+#define SECTION_MAX_NB_CHILDREN 10
+
 struct section {
-    int id;             ///< unique id indentifying a section
+    int id;             ///< unique id identifying a section
     const char *name;
 
 #define SECTION_FLAG_IS_WRAPPER      1 ///< the section only contains other sections, but has no data at its own level
@@ -81,7 +84,11 @@ struct section {
 #define SECTION_FLAG_HAS_VARIABLE_FIELDS 4 ///< the section may contain a variable number of fields with variable keys.
                                            ///  For these sections the element_name field is mandatory.
     int flags;
+    int children_ids[SECTION_MAX_NB_CHILDREN+1]; ///< list of children section IDS, terminated by -1
     const char *element_name; ///< name of the contained element, if provided
+    const char *unique_name;  ///< unique section name, in case the name is ambiguous
+    AVDictionary *entries_to_show;
+    int show_all_entries;
 };
 
 typedef enum {
@@ -102,27 +109,29 @@ typedef enum {
     SECTION_ID_STREAM,
     SECTION_ID_STREAM_DISPOSITION,
     SECTION_ID_STREAMS,
-    SECTION_ID_STREAM_TAGS
+    SECTION_ID_STREAM_TAGS,
 } SectionID;
 
-static const struct section sections[] = {
-    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR,              "error" },
-    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT,             "format" },
-    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS,        "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, .element_name = "tag" },
-    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME,              "frame" },
-    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES,             "frames", SECTION_FLAG_IS_ARRAY },
-    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS,         "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, .element_name = "tag" },
-    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION,    "library_version" },
-    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS,   "library_versions", SECTION_FLAG_IS_ARRAY },
-    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET,             "packet" },
-    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS,            "packets", SECTION_FLAG_IS_ARRAY },
-    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY },
-    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION,    "program_version" },
-    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT,               "root", SECTION_FLAG_IS_WRAPPER },
-    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM,             "stream" },
-    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition" },
-    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS,            "streams", SECTION_FLAG_IS_ARRAY },
-    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS,        "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, .element_name = "tag" },
+static struct section sections[] = {
+    [SECTION_ID_ERROR] =              { SECTION_ID_ERROR, "error", 0, { -1 } },
+    [SECTION_ID_FORMAT] =             { SECTION_ID_FORMAT, "format", 0, { SECTION_ID_FORMAT_TAGS, -1 } },
+    [SECTION_ID_FORMAT_TAGS] =        { SECTION_ID_FORMAT_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "format_tags" },
+    [SECTION_ID_FRAMES] =             { SECTION_ID_FRAMES, "frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_FRAME, -1 } },
+    [SECTION_ID_FRAME] =              { SECTION_ID_FRAME, "frame", 0, { SECTION_ID_FRAME_TAGS, -1 } },
+    [SECTION_ID_FRAME_TAGS] =         { SECTION_ID_FRAME_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "frame_tags" },
+    [SECTION_ID_LIBRARY_VERSIONS] =   { SECTION_ID_LIBRARY_VERSIONS, "library_versions", SECTION_FLAG_IS_ARRAY, { SECTION_ID_LIBRARY_VERSION, -1 } },
+    [SECTION_ID_LIBRARY_VERSION] =    { SECTION_ID_LIBRARY_VERSION, "library_version", 0, { -1 } },
+    [SECTION_ID_PACKETS] =            { SECTION_ID_PACKETS, "packets", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
+    [SECTION_ID_PACKETS_AND_FRAMES] = { SECTION_ID_PACKETS_AND_FRAMES, "packets_and_frames", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PACKET, -1} },
+    [SECTION_ID_PACKET] =             { SECTION_ID_PACKET, "packet", 0, { -1 } },
+    [SECTION_ID_PROGRAM_VERSION] =    { SECTION_ID_PROGRAM_VERSION, "program_version", 0, { -1 } },
+    [SECTION_ID_ROOT] =               { SECTION_ID_ROOT, "root", SECTION_FLAG_IS_WRAPPER,
+                                        { SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_STREAMS, SECTION_ID_PACKETS,
+                                          SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS, -1} },
+    [SECTION_ID_STREAMS] =            { SECTION_ID_STREAMS, "streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
+    [SECTION_ID_STREAM] =             { SECTION_ID_STREAM, "stream", 0, { SECTION_ID_STREAM_DISPOSITION, SECTION_ID_STREAM_TAGS, -1 } },
+    [SECTION_ID_STREAM_DISPOSITION] = { SECTION_ID_STREAM_DISPOSITION, "disposition", 0, { -1 }, .unique_name = "stream_disposition" },
+    [SECTION_ID_STREAM_TAGS] =        { SECTION_ID_STREAM_TAGS, "tags", SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name = "stream_tags" },
 };
 
 static const OptionDef *options;
@@ -138,13 +147,16 @@ static const char unit_second_str[]         = "s"    ;
 static const char unit_hertz_str[]          = "Hz"   ;
 static const char unit_byte_str[]           = "byte" ;
 static const char unit_bit_per_second_str[] = "bit/s";
+
 static uint64_t *nb_streams_packets;
 static uint64_t *nb_streams_frames;
+static int *selected_streams;
 
-void av_noreturn exit_program(int ret)
+static void exit_program(void)
 {
-    av_dict_free(&fmt_entries_to_show);
-    exit(ret);
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
+        av_dict_free(&(sections[i].entries_to_show));
 }
 
 struct unit_value {
@@ -245,6 +257,8 @@ struct WriterContext {
 
     /** section per each level */
     const struct section *section[SECTION_MAX_NB_LEVELS];
+    AVBPrint section_pbuf[SECTION_MAX_NB_LEVELS]; ///< generic print buffer dedicated to each section,
+                                                  ///  used by various writers
 
     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
@@ -266,11 +280,15 @@ static const AVClass writer_class = {
 
 static void writer_close(WriterContext **wctx)
 {
+    int i;
+
     if (!*wctx)
         return;
 
     if ((*wctx)->writer->uninit)
         (*wctx)->writer->uninit(*wctx);
+    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
+        av_bprint_finalize(&(*wctx)->section_pbuf[i], NULL);
     if ((*wctx)->writer->priv_class)
         av_opt_free((*wctx)->priv);
     av_freep(&((*wctx)->priv));
@@ -280,7 +298,7 @@ static void writer_close(WriterContext **wctx)
 static int writer_open(WriterContext **wctx, const Writer *writer, const char *args,
                        const struct section *sections, int nb_sections)
 {
-    int ret = 0;
+    int i, ret = 0;
 
     if (!(*wctx = av_malloc(sizeof(WriterContext)))) {
         ret = AVERROR(ENOMEM);
@@ -307,6 +325,10 @@ static int writer_open(WriterContext **wctx, const Writer *writer, const char *a
             (ret = av_set_options_string(priv_ctx, args, "=", ":")) < 0)
             goto fail;
     }
+
+    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
+        av_bprint_init(&(*wctx)->section_pbuf[i], 1, AV_BPRINT_SIZE_UNLIMITED);
+
     if ((*wctx)->writer->init)
         ret = (*wctx)->writer->init(*wctx);
     if (ret < 0)
@@ -363,9 +385,9 @@ static inline void writer_print_section_footer(WriterContext *wctx)
 static inline void writer_print_integer(WriterContext *wctx,
                                         const char *key, long long int val)
 {
-    if ((wctx->section[wctx->level]->id != SECTION_ID_FORMAT
-         && wctx->section[wctx->level]->id != SECTION_ID_FORMAT_TAGS) ||
-        !fmt_entries_to_show || av_dict_get(fmt_entries_to_show, key, NULL, 0)) {
+    const struct section *section = wctx->section[wctx->level];
+
+    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
         wctx->writer->print_integer(wctx, key, val);
         wctx->nb_item[wctx->level]++;
     }
@@ -374,11 +396,12 @@ static inline void writer_print_integer(WriterContext *wctx,
 static inline void writer_print_string(WriterContext *wctx,
                                        const char *key, const char *val, int opt)
 {
+    const struct section *section = wctx->section[wctx->level];
+
     if (opt && !(wctx->writer->flags & WRITER_FLAG_DISPLAY_OPTIONAL_FIELDS))
         return;
-    if ((wctx->section[wctx->level]->id != SECTION_ID_FORMAT
-         && wctx->section[wctx->level]->id != SECTION_ID_FORMAT_TAGS) ||
-        !fmt_entries_to_show || av_dict_get(fmt_entries_to_show, key, NULL, 0)) {
+
+    if (section->show_all_entries || av_dict_get(section->entries_to_show, key, NULL, 0)) {
         wctx->writer->print_string(wctx, key, val);
         wctx->nb_item[wctx->level]++;
     }
@@ -494,7 +517,6 @@ typedef struct DefaultContext {
     int nokey;
     int noprint_wrappers;
     int nested_section[SECTION_MAX_NB_LEVELS];
-    AVBPrint prefix[SECTION_MAX_NB_LEVELS];
 } DefaultContext;
 
 #define OFFSET(x) offsetof(DefaultContext, x)
@@ -519,25 +541,6 @@ static inline char *upcase_string(char *dst, size_t dst_size, const char *src)
     return dst;
 }
 
-static int default_init(WriterContext *wctx)
-{
-    DefaultContext *def = wctx->priv;
-    int i;
-
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_init(&def->prefix[i], 1, AV_BPRINT_SIZE_UNLIMITED);
-    return 0;
-}
-
-static void default_uninit(WriterContext *wctx)
-{
-    DefaultContext *def = wctx->priv;
-    int i;
-
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_finalize(&def->prefix[i], NULL);
-}
-
 static void default_print_section_header(WriterContext *wctx)
 {
     DefaultContext *def = wctx->priv;
@@ -546,11 +549,12 @@ static void default_print_section_header(WriterContext *wctx)
     const struct section *parent_section = wctx->level ?
         wctx->section[wctx->level-1] : NULL;
 
-    av_bprint_clear(&def->prefix[wctx->level]);
+    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
     if (parent_section &&
         !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
         def->nested_section[wctx->level] = 1;
-        av_bprintf(&def->prefix[wctx->level], "%s%s:", def->prefix[wctx->level-1].str,
+        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
+                   wctx->section_pbuf[wctx->level-1].str,
                    upcase_string(buf, sizeof(buf),
                                  av_x_if_null(section->element_name, section->name)));
     }
@@ -580,7 +584,7 @@ static void default_print_str(WriterContext *wctx, const char *key, const char *
     DefaultContext *def = wctx->priv;
 
     if (!def->nokey)
-        printf("%s%s=", def->prefix[wctx->level].str, key);
+        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
     printf("%s\n", value);
 }
 
@@ -589,15 +593,13 @@ static void default_print_int(WriterContext *wctx, const char *key, long long in
     DefaultContext *def = wctx->priv;
 
     if (!def->nokey)
-        printf("%s%s=", def->prefix[wctx->level].str, key);
+        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
     printf("%lld\n", value);
 }
 
 static const Writer default_writer = {
     .name                  = "default",
     .priv_size             = sizeof(DefaultContext),
-    .init                  = default_init,
-    .uninit                = default_uninit,
     .print_section_header  = default_print_section_header,
     .print_section_footer  = default_print_section_footer,
     .print_integer         = default_print_int,
@@ -666,7 +668,6 @@ typedef struct CompactContext {
     char *escape_mode_str;
     const char * (*escape_str)(AVBPrint *dst, const char *src, const char sep, void *log_ctx);
     int nested_section[SECTION_MAX_NB_LEVELS];
-    AVBPrint prefix[SECTION_MAX_NB_LEVELS];
 } CompactContext;
 
 #undef OFFSET
@@ -689,7 +690,6 @@ DEFINE_WRITER_CLASS(compact);
 static av_cold int compact_init(WriterContext *wctx)
 {
     CompactContext *compact = wctx->priv;
-    int i;
 
     if (strlen(compact->item_sep_str) != 1) {
         av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
@@ -706,20 +706,9 @@ static av_cold int compact_init(WriterContext *wctx)
         return AVERROR(EINVAL);
     }
 
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_init(&compact->prefix[i], 1, AV_BPRINT_SIZE_UNLIMITED);
     return 0;
 }
 
-static void compact_uninit(WriterContext *wctx)
-{
-    CompactContext *compact = wctx->priv;
-    int i;
-
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_finalize(&compact->prefix[i], NULL);
-}
-
 static void compact_print_section_header(WriterContext *wctx)
 {
     CompactContext *compact = wctx->priv;
@@ -727,12 +716,12 @@ static void compact_print_section_header(WriterContext *wctx)
     const struct section *parent_section = wctx->level ?
         wctx->section[wctx->level-1] : NULL;
 
-    av_bprint_clear(&compact->prefix[wctx->level]);
+    av_bprint_clear(&wctx->section_pbuf[wctx->level]);
     if (parent_section &&
         !(parent_section->flags & (SECTION_FLAG_IS_WRAPPER|SECTION_FLAG_IS_ARRAY))) {
         compact->nested_section[wctx->level] = 1;
-        av_bprintf(&compact->prefix[wctx->level], "%s%s:",
-                   compact->prefix[wctx->level-1].str,
+        av_bprintf(&wctx->section_pbuf[wctx->level], "%s%s:",
+                   wctx->section_pbuf[wctx->level-1].str,
                    (char *)av_x_if_null(section->element_name, section->name));
         wctx->nb_item[wctx->level] = wctx->nb_item[wctx->level-1];
     } else if (compact->print_section &&
@@ -756,7 +745,7 @@ static void compact_print_str(WriterContext *wctx, const char *key, const char *
 
     if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
     if (!compact->nokey)
-        printf("%s%s=", compact->prefix[wctx->level].str, key);
+        printf("%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));
     av_bprint_finalize(&buf, NULL);
@@ -768,7 +757,7 @@ static void compact_print_int(WriterContext *wctx, const char *key, long long in
 
     if (wctx->nb_item[wctx->level]) printf("%c", compact->item_sep);
     if (!compact->nokey)
-        printf("%s%s=", compact->prefix[wctx->level].str, key);
+        printf("%s%s=", wctx->section_pbuf[wctx->level].str, key);
     printf("%lld", value);
 }
 
@@ -776,7 +765,6 @@ static const Writer compact_writer = {
     .name                 = "compact",
     .priv_size            = sizeof(CompactContext),
     .init                 = compact_init,
-    .uninit               = compact_uninit,
     .print_section_header = compact_print_section_header,
     .print_section_footer = compact_print_section_footer,
     .print_integer        = compact_print_int,
@@ -820,7 +808,6 @@ static const Writer csv_writer = {
 
 typedef struct FlatContext {
     const AVClass *class;
-    AVBPrint section_header[SECTION_MAX_NB_LEVELS];
     const char *sep_str;
     char sep;
     int hierarchical;
@@ -842,7 +829,6 @@ DEFINE_WRITER_CLASS(flat);
 static av_cold int flat_init(WriterContext *wctx)
 {
     FlatContext *flat = wctx->priv;
-    int i;
 
     if (strlen(flat->sep_str) != 1) {
         av_log(wctx, AV_LOG_ERROR, "Item separator '%s' specified, but must contain a single character\n",
@@ -851,20 +837,9 @@ static av_cold int flat_init(WriterContext *wctx)
     }
     flat->sep = flat->sep_str[0];
 
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_init(&flat->section_header[i], 1, AV_BPRINT_SIZE_UNLIMITED);
     return 0;
 }
 
-static void flat_uninit(WriterContext *wctx)
-{
-    FlatContext *flat = wctx->priv;
-    int i;
-
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_finalize(&flat->section_header[i], NULL);
-}
-
 static const char *flat_escape_key_str(AVBPrint *dst, const char *src, const char sep)
 {
     const char *p;
@@ -901,7 +876,7 @@ static const char *flat_escape_value_str(AVBPrint *dst, const char *src)
 static void flat_print_section_header(WriterContext *wctx)
 {
     FlatContext *flat = wctx->priv;
-    AVBPrint *buf = &flat->section_header[wctx->level];
+    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
     const struct section *section = wctx->section[wctx->level];
     const struct section *parent_section = wctx->level ?
         wctx->section[wctx->level-1] : NULL;
@@ -910,7 +885,7 @@ static void flat_print_section_header(WriterContext *wctx)
     av_bprint_clear(buf);
     if (!parent_section)
         return;
-    av_bprintf(buf, "%s", flat->section_header[wctx->level-1].str);
+    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
 
     if (flat->hierarchical ||
         !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
@@ -926,8 +901,7 @@ static void flat_print_section_header(WriterContext *wctx)
 
 static void flat_print_int(WriterContext *wctx, const char *key, long long int value)
 {
-    FlatContext *flat = wctx->priv;
-    printf("%s%s=%lld\n", flat->section_header[wctx->level].str, key, value);
+    printf("%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)
@@ -935,7 +909,7 @@ static void flat_print_str(WriterContext *wctx, const char *key, const char *val
     FlatContext *flat = wctx->priv;
     AVBPrint buf;
 
-    printf("%s", flat->section_header[wctx->level].str);
+    printf("%s", 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));
     av_bprint_clear(&buf);
@@ -947,7 +921,6 @@ static const Writer flat_writer = {
     .name                  = "flat",
     .priv_size             = sizeof(FlatContext),
     .init                  = flat_init,
-    .uninit                = flat_uninit,
     .print_section_header  = flat_print_section_header,
     .print_integer         = flat_print_int,
     .print_string          = flat_print_str,
@@ -960,7 +933,6 @@ static const Writer flat_writer = {
 typedef struct {
     const AVClass *class;
     int hierarchical;
-    AVBPrint section_header[SECTION_MAX_NB_LEVELS];
 } INIContext;
 
 #undef OFFSET
@@ -974,25 +946,6 @@ static const AVOption ini_options[] = {
 
 DEFINE_WRITER_CLASS(ini);
 
-static int ini_init(WriterContext *wctx)
-{
-    INIContext *ini = wctx->priv;
-    int i;
-
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_init(&ini->section_header[i], 1, AV_BPRINT_SIZE_UNLIMITED);
-    return 0;
-}
-
-static void ini_uninit(WriterContext *wctx)
-{
-    INIContext *ini = wctx->priv;
-    int i;
-
-    for (i = 0; i < SECTION_MAX_NB_LEVELS; i++)
-        av_bprint_finalize(&ini->section_header[i], NULL);
-}
-
 static char *ini_escape_str(AVBPrint *dst, const char *src)
 {
     int i = 0;
@@ -1023,7 +976,7 @@ static char *ini_escape_str(AVBPrint *dst, const char *src)
 static void ini_print_section_header(WriterContext *wctx)
 {
     INIContext *ini = wctx->priv;
-    AVBPrint *buf = &ini->section_header[wctx->level];
+    AVBPrint *buf = &wctx->section_pbuf[wctx->level];
     const struct section *section = wctx->section[wctx->level];
     const struct section *parent_section = wctx->level ?
         wctx->section[wctx->level-1] : NULL;
@@ -1037,7 +990,7 @@ static void ini_print_section_header(WriterContext *wctx)
     if (wctx->nb_item[wctx->level-1])
         printf("\n");
 
-    av_bprintf(buf, "%s", ini->section_header[wctx->level-1].str);
+    av_bprintf(buf, "%s", wctx->section_pbuf[wctx->level-1].str);
     if (ini->hierarchical ||
         !(section->flags & (SECTION_FLAG_IS_ARRAY|SECTION_FLAG_IS_WRAPPER))) {
         av_bprintf(buf, "%s%s", buf->str[0] ? "." : "", wctx->section[wctx->level]->name);
@@ -1072,8 +1025,6 @@ static void ini_print_int(WriterContext *wctx, const char *key, long long int va
 static const Writer ini_writer = {
     .name                  = "ini",
     .priv_size             = sizeof(INIContext),
-    .init                  = ini_init,
-    .uninit                = ini_uninit,
     .print_section_header  = ini_print_section_header,
     .print_integer         = ini_print_int,
     .print_string          = ini_print_str,
@@ -1155,13 +1106,13 @@ static void json_print_section_header(WriterContext *wctx)
         json->indent_level++;
         if (section->flags & SECTION_FLAG_IS_ARRAY) {
             printf("\"%s\": [\n", buf.str);
-        } else if (!(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
+        } else if (parent_section && !(parent_section->flags & SECTION_FLAG_IS_ARRAY)) {
             printf("\"%s\": {%s", buf.str, json->item_start_end);
         } else {
             printf("{%s", json->item_start_end);
 
             /* this is required so the parser can distinguish between packets and frames */
-            if (parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
+            if (parent_section && parent_section->id == SECTION_ID_PACKETS_AND_FRAMES) {
                 if (!json->compact)
                     JSON_INDENT();
                 printf("\"type\": \"%s\"%s", section->name, json->item_sep);
@@ -1615,14 +1566,16 @@ static void read_packets(WriterContext *w, AVFormatContext *fmt_ctx)
     av_init_packet(&pkt);
 
     while (!av_read_frame(fmt_ctx, &pkt)) {
-        if (do_read_packets) {
-            if (do_show_packets)
-                show_packet(w, fmt_ctx, &pkt, i++);
-            nb_streams_packets[pkt.stream_index]++;
-        }
-        if (do_read_frames) {
-            pkt1 = pkt;
-            while (pkt1.size && process_frame(w, fmt_ctx, &frame, &pkt1) > 0);
+        if (selected_streams[pkt.stream_index]) {
+            if (do_read_packets) {
+                if (do_show_packets)
+                    show_packet(w, fmt_ctx, &pkt, i++);
+                nb_streams_packets[pkt.stream_index]++;
+            }
+            if (do_read_frames) {
+                pkt1 = pkt;
+                while (pkt1.size && process_frame(w, fmt_ctx, &frame, &pkt1) > 0);
+            }
         }
         av_free_packet(&pkt);
     }
@@ -1764,6 +1717,7 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i
         print_int(name, !!(stream->disposition & AV_DISPOSITION_##flagname)); \
     } while (0)
 
+    if (do_show_stream_disposition) {
     writer_print_section_header(w, SECTION_ID_STREAM_DISPOSITION);
     PRINT_DISPOSITION(DEFAULT,          "default");
     PRINT_DISPOSITION(DUB,              "dub");
@@ -1777,6 +1731,7 @@ static void show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_i
     PRINT_DISPOSITION(CLEAN_EFFECTS,    "clean_effects");
     PRINT_DISPOSITION(ATTACHED_PIC,     "attached_pic");
     writer_print_section_footer(w);
+    }
 
     show_tags(w, stream->metadata, SECTION_ID_STREAM_TAGS);
 
@@ -1790,7 +1745,8 @@ static void show_streams(WriterContext *w, AVFormatContext *fmt_ctx)
     int i;
     writer_print_section_header(w, SECTION_ID_STREAMS);
     for (i = 0; i < fmt_ctx->nb_streams; i++)
-        show_stream(w, fmt_ctx, i);
+        if (selected_streams[i])
+            show_stream(w, fmt_ctx, i);
     writer_print_section_footer(w);
 }
 
@@ -1897,7 +1853,7 @@ static void close_input_file(AVFormatContext **ctx_ptr)
 static int probe_file(WriterContext *wctx, const char *filename)
 {
     AVFormatContext *fmt_ctx;
-    int ret;
+    int ret, i;
     int section_id;
 
     do_read_frames = do_show_frames || do_count_frames;
@@ -1907,6 +1863,22 @@ static int probe_file(WriterContext *wctx, const char *filename)
     if (ret >= 0) {
         nb_streams_frames  = av_calloc(fmt_ctx->nb_streams, sizeof(*nb_streams_frames));
         nb_streams_packets = av_calloc(fmt_ctx->nb_streams, sizeof(*nb_streams_packets));
+        selected_streams   = av_calloc(fmt_ctx->nb_streams, sizeof(*selected_streams));
+
+        for (i = 0; i < fmt_ctx->nb_streams; i++) {
+            if (stream_specifier) {
+                ret = avformat_match_stream_specifier(fmt_ctx,
+                                                      fmt_ctx->streams[i],
+                                                      stream_specifier);
+                if (ret < 0)
+                    goto end;
+                else
+                    selected_streams[i] = ret;
+            } else {
+                selected_streams[i] = 1;
+            }
+        }
+
         if (do_read_frames || do_read_packets) {
             if (do_show_frames && do_show_packets &&
                 wctx->writer->flags & WRITER_FLAG_PUT_PACKETS_AND_FRAMES_IN_SAME_CHAPTER)
@@ -1926,9 +1898,11 @@ static int probe_file(WriterContext *wctx, const char *filename)
         if (do_show_format)
             show_format(wctx, fmt_ctx);
 
+    end:
         close_input_file(&fmt_ctx);
         av_freep(&nb_streams_frames);
         av_freep(&nb_streams_packets);
+        av_freep(&selected_streams);
     }
     return ret;
 }
@@ -1997,11 +1971,101 @@ static int opt_format(void *optctx, const char *opt, const char *arg)
     return 0;
 }
 
+static inline void mark_section_show_entries(SectionID section_id,
+                                             int show_all_entries, AVDictionary *entries)
+{
+    struct section *section = &sections[section_id];
+
+    section->show_all_entries = show_all_entries;
+    if (show_all_entries) {
+        SectionID *id;
+        for (id = section->children_ids; *id != -1; id++)
+            mark_section_show_entries(*id, show_all_entries, entries);
+    } else {
+        av_dict_copy(&section->entries_to_show, entries, 0);
+    }
+}
+
+static int match_section(const char *section_name,
+                         int show_all_entries, AVDictionary *entries)
+{
+    int i, ret = 0;
+
+    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++) {
+        const struct section *section = &sections[i];
+        if (!strcmp(section_name, section->name) ||
+            (section->unique_name && !strcmp(section_name, section->unique_name))) {
+            av_log(NULL, AV_LOG_DEBUG,
+                   "'%s' matches section with unique name '%s'\n", section_name,
+                   (char *)av_x_if_null(section->unique_name, section->name));
+            ret++;
+            mark_section_show_entries(section->id, show_all_entries, entries);
+        }
+    }
+    return ret;
+}
+
+static int opt_show_entries(void *optctx, const char *opt, const char *arg)
+{
+    const char *p = arg;
+    int ret = 0;
+
+    while (*p) {
+        AVDictionary *entries = NULL;
+        char *section_name = av_get_token(&p, "=:");
+        int show_all_entries = 0;
+
+        if (!section_name) {
+            av_log(NULL, AV_LOG_ERROR,
+                   "Missing section name for option '%s'\n", opt);
+            return AVERROR(EINVAL);
+        }
+
+        if (*p == '=') {
+            p++;
+            while (*p && *p != ':') {
+                char *entry = av_get_token(&p, ",:");
+                if (!entry)
+                    break;
+                av_log(NULL, AV_LOG_VERBOSE,
+                       "Adding '%s' to the entries to show in section '%s'\n",
+                       entry, section_name);
+                av_dict_set(&entries, entry, "", AV_DICT_DONT_STRDUP_KEY);
+                if (*p == ',')
+                    p++;
+            }
+        } else {
+            show_all_entries = 1;
+        }
+
+        ret = match_section(section_name, show_all_entries, entries);
+        if (ret == 0) {
+            av_log(NULL, AV_LOG_ERROR, "No match for section '%s'\n", section_name);
+            ret = AVERROR(EINVAL);
+        }
+        av_dict_free(&entries);
+        av_free(section_name);
+
+        if (ret <= 0)
+            break;
+        if (*p)
+            p++;
+    }
+
+    return ret;
+}
+
 static int opt_show_format_entry(void *optctx, const char *opt, const char *arg)
 {
-    do_show_format = 1;
-    av_dict_set(&fmt_entries_to_show, arg, "", 0);
-    return 0;
+    char *buf = av_asprintf("format=%s", arg);
+    int ret;
+
+    av_log(NULL, AV_LOG_WARNING,
+           "Option '%s' is deprecated, use '-show_entries format=%s' instead\n",
+           opt, arg);
+    ret = opt_show_entries(optctx, opt, buf);
+    av_free(buf);
+    return ret;
 }
 
 static void opt_input_file(void *optctx, const char *arg)
@@ -2042,13 +2106,57 @@ static int opt_pretty(void *optctx, const char *opt, const char *arg)
     return 0;
 }
 
+static void print_section(SectionID id, int level)
+{
+    const SectionID *pid;
+    const struct section *section = &sections[id];
+    printf("%c%c%c",
+           section->flags & SECTION_FLAG_IS_WRAPPER           ? 'W' : '.',
+           section->flags & SECTION_FLAG_IS_ARRAY             ? 'A' : '.',
+           section->flags & SECTION_FLAG_HAS_VARIABLE_FIELDS  ? 'V' : '.');
+    printf("%*c  %s", level * 4, ' ', section->name);
+    if (section->unique_name)
+        printf("/%s", section->unique_name);
+    printf("\n");
+
+    for (pid = section->children_ids; *pid != -1; pid++)
+        print_section(*pid, level+1);
+}
+
+static int opt_sections(void *optctx, const char *opt, const char *arg)
+{
+    printf("Sections:\n"
+           "W.. = Section is a wrapper (contains other sections, no local entries)\n"
+           ".A. = Section contains an array of elements of the same type\n"
+           "..V = Section may contain a variable number of fields with variable keys\n"
+           "FLAGS NAME/UNIQUE_NAME\n"
+           "---\n");
+    print_section(SECTION_ID_ROOT, 0);
+    return 0;
+}
+
 static int opt_show_versions(const char *opt, const char *arg)
 {
-    do_show_program_version  = 1;
-    do_show_library_versions = 1;
+    mark_section_show_entries(SECTION_ID_PROGRAM_VERSION, 1, NULL);
+    mark_section_show_entries(SECTION_ID_LIBRARY_VERSION, 1, NULL);
     return 0;
 }
 
+#define DEFINE_OPT_SHOW_SECTION(section, target_section_id)             \
+    static int opt_show_##section(const char *opt, const char *arg)     \
+    {                                                                   \
+        mark_section_show_entries(SECTION_ID_##target_section_id, 1, NULL); \
+        return 0;                                                       \
+    }
+
+DEFINE_OPT_SHOW_SECTION(error,            ERROR);
+DEFINE_OPT_SHOW_SECTION(format,           FORMAT);
+DEFINE_OPT_SHOW_SECTION(frames,           FRAMES);
+DEFINE_OPT_SHOW_SECTION(library_versions, LIBRARY_VERSIONS);
+DEFINE_OPT_SHOW_SECTION(packets,          PACKETS);
+DEFINE_OPT_SHOW_SECTION(program_version,  PROGRAM_VERSION);
+DEFINE_OPT_SHOW_SECTION(streams,          STREAMS);
+
 static const OptionDef real_options[] = {
 #include "cmdutils_common_opts.h"
     { "f", HAS_ARG, {.func_arg = opt_format}, "force format", "format" },
@@ -2063,18 +2171,22 @@ static const OptionDef real_options[] = {
     { "print_format", OPT_STRING | HAS_ARG, {(void*)&print_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" },
+    { "select_streams", OPT_STRING | HAS_ARG, {(void*)&stream_specifier}, "select the specified streams", "stream_specifier" },
+    { "sections", OPT_EXIT, {.func_arg = opt_sections}, "print sections structure and section information, and exit" },
     { "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, {&do_show_format} , "show format/container info" },
-    { "show_frames",  OPT_BOOL, {(void*)&do_show_frames} , "show frames info" },
+    { "show_error",   0, {(void*)&opt_show_error},  "show probing error" },
+    { "show_format",  0, {(void*)&opt_show_format}, "show format/container info" },
+    { "show_frames",  0, {(void*)&opt_show_frames}, "show frames info" },
     { "show_format_entry", HAS_ARG, {.func_arg = opt_show_format_entry},
       "show a particular entry from the format/container info", "entry" },
-    { "show_packets", OPT_BOOL, {&do_show_packets}, "show packets info" },
-    { "show_streams", OPT_BOOL, {&do_show_streams}, "show streams info" },
+    { "show_entries", HAS_ARG, {.func_arg = opt_show_entries},
+      "show a set of specified entries", "entry_list" },
+    { "show_packets", 0, {(void*)&opt_show_packets}, "show packets info" },
+    { "show_streams", 0, {(void*)&opt_show_streams}, "show streams info" },
     { "count_frames", OPT_BOOL, {(void*)&do_count_frames}, "count the number of frames per stream" },
     { "count_packets", OPT_BOOL, {(void*)&do_count_packets}, "count the number of packets per stream" },
-    { "show_program_version",  OPT_BOOL, {(void*)&do_show_program_version},  "show ffprobe version" },
-    { "show_library_versions", OPT_BOOL, {(void*)&do_show_library_versions}, "show library versions" },
+    { "show_program_version",  0, {(void*)&opt_show_program_version},  "show ffprobe version" },
+    { "show_library_versions", 0, {(void*)&opt_show_library_versions}, "show library versions" },
     { "show_versions",         0, {(void*)&opt_show_versions}, "show program and library versions" },
     { "show_private_data", OPT_BOOL, {(void*)&show_private_data}, "show private data" },
     { "private",           OPT_BOOL, {(void*)&show_private_data}, "same as show_private_data" },
@@ -2084,15 +2196,34 @@ static const OptionDef real_options[] = {
     { NULL, },
 };
 
+static inline int check_section_show_entries(int section_id)
+{
+    int *id;
+    struct section *section = &sections[section_id];
+    if (sections[section_id].show_all_entries || sections[section_id].entries_to_show)
+        return 1;
+    for (id = section->children_ids; *id != -1; id++)
+        if (check_section_show_entries(*id))
+            return 1;
+    return 0;
+}
+
+#define SET_DO_SHOW(id, varname) do {                                   \
+        if (check_section_show_entries(SECTION_ID_##id))                \
+            do_show_##varname = 1;                                      \
+    } while (0)
+
 int main(int argc, char **argv)
 {
     const Writer *w;
     WriterContext *wctx;
     char *buf;
     char *w_name = NULL, *w_args = NULL;
-    int ret;
+    int ret, i;
 
     av_log_set_flags(AV_LOG_SKIP_REPEATED);
+    atexit(exit_program);
+
     options = real_options;
     parse_loglevel(argc, argv, options);
     av_register_all();
@@ -2105,6 +2236,16 @@ int main(int argc, char **argv)
     show_banner(argc, argv, options);
     parse_options(NULL, argc, argv, options, opt_input_file);
 
+    /* mark things to show, based on -show_entries */
+    SET_DO_SHOW(ERROR, error);
+    SET_DO_SHOW(FORMAT, format);
+    SET_DO_SHOW(FRAMES, frames);
+    SET_DO_SHOW(LIBRARY_VERSIONS, library_versions);
+    SET_DO_SHOW(PACKETS, packets);
+    SET_DO_SHOW(PROGRAM_VERSION, program_version);
+    SET_DO_SHOW(STREAMS, streams);
+    SET_DO_SHOW(STREAM_DISPOSITION, stream_disposition);
+
     if (do_bitexact && (do_show_program_version || do_show_library_versions)) {
         av_log(NULL, AV_LOG_ERROR,
                "-bitexact and -show_program_version or -show_library_versions "
@@ -2117,6 +2258,10 @@ int main(int argc, char **argv)
 
     if (!print_format)
         print_format = av_strdup("default");
+    if (!print_format) {
+        ret = AVERROR(ENOMEM);
+        goto end;
+    }
     w_name = av_strtok(print_format, "=", &buf);
     w_args = buf;
 
@@ -2157,7 +2302,8 @@ end:
     av_freep(&print_format);
 
     uninit_opts();
-    av_dict_free(&fmt_entries_to_show);
+    for (i = 0; i < FF_ARRAY_ELEMS(sections); i++)
+        av_dict_free(&(sections[i].entries_to_show));
 
     avformat_network_deinit();