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;
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
#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 {
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;
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 {
/** 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
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));
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);
(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)
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]++;
}
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]++;
}
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)
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;
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)));
}
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);
}
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,
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
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",
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;
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 &&
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);
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);
}
.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,
typedef struct FlatContext {
const AVClass *class;
- AVBPrint section_header[SECTION_MAX_NB_LEVELS];
const char *sep_str;
char sep;
int hierarchical;
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",
}
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;
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;
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))) {
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)
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);
.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,
typedef struct {
const AVClass *class;
int hierarchical;
- AVBPrint section_header[SECTION_MAX_NB_LEVELS];
} INIContext;
#undef OFFSET
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;
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;
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);
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,
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);
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);
}
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");
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);
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);
}
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;
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)
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;
}
return 0;
}
+static inline void mark_section_show_entries(SectionID section_id,
+ int show_all_entries, AVDictionary *entries)
+{
+ struct section *section = §ions[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(§ion->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 = §ions[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)
return 0;
}
+static void print_section(SectionID id, int level)
+{
+ const SectionID *pid;
+ const struct section *section = §ions[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" },
{ "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" },
{ NULL, },
};
+static inline int check_section_show_entries(int section_id)
+{
+ int *id;
+ struct section *section = §ions[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();
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 "
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;
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();