22 files changed, 163 insertions(+), 23 deletions(-)
diff --git a/Changelog b/Changelog
index f388d41b02..0ba3a77c08 100644
--- a/Changelog
+++ b/Changelog
@@ -30,6 +30,7 @@ version <next>:
- Change the default bitrate control method from VBR to CQP for QSV encoders.
- removed deprecated ffmpeg CLI options -psnr and -map_channel
- DVD-Video demuxer, powered by libdvdnav and libdvdread
+- ffprobe -show_stream_groups option
version 6.1:
diff --git a/doc/ffprobe.xsd b/doc/ffprobe.xsd
index 9490a20ce8..dc04ce7142 100644
--- a/doc/ffprobe.xsd
+++ b/doc/ffprobe.xsd
@@ -337,6 +337,7 @@
<xsd:attribute name="filename" type="xsd:string" use="required"/>
<xsd:attribute name="nb_streams" type="xsd:int" use="required"/>
<xsd:attribute name="nb_programs" type="xsd:int" use="required"/>
+ <xsd:attribute name="nb_stream_groups" type="xsd:int" use="required"/>
<xsd:attribute name="format_name" type="xsd:string" use="required"/>
<xsd:attribute name="format_long_name" type="xsd:string"/>
<xsd:attribute name="start_time" type="xsd:float"/>
diff --git a/fftools/ffprobe.c b/fftools/ffprobe.c
index 8390377159..ff05c4c1db 100644
--- a/fftools/ffprobe.c
+++ b/fftools/ffprobe.c
@@ -112,8 +112,10 @@ static int do_show_format = 0;
static int do_show_frames = 0;
static int do_show_packets = 0;
static int do_show_programs = 0;
+static int do_show_stream_groups = 0;
static int do_show_streams = 0;
static int do_show_stream_disposition = 0;
+static int do_show_stream_group_disposition = 0;
static int do_show_data = 0;
static int do_show_program_version = 0;
static int do_show_library_versions = 0;
@@ -126,6 +128,7 @@ static int do_show_chapter_tags = 0;
static int do_show_format_tags = 0;
static int do_show_frame_tags = 0;
static int do_show_program_tags = 0;
+static int do_show_stream_group_tags = 0;
static int do_show_stream_tags = 0;
static int do_show_packet_tags = 0;
@@ -159,7 +162,7 @@ static int find_stream_info = 1;
/* section structure definition */
-#define SECTION_MAX_NB_CHILDREN 10
+#define SECTION_MAX_NB_CHILDREN 11
typedef enum {
SECTION_ID_NONE = -1,
@@ -203,6 +206,14 @@ typedef enum {
SECTION_ID_PROGRAM_TAGS,
SECTION_ID_PROGRAM_VERSION,
SECTION_ID_PROGRAMS,
+ SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
+ SECTION_ID_STREAM_GROUP_STREAM_TAGS,
+ SECTION_ID_STREAM_GROUP,
+ SECTION_ID_STREAM_GROUP_STREAMS,
+ SECTION_ID_STREAM_GROUP_STREAM,
+ SECTION_ID_STREAM_GROUP_DISPOSITION,
+ SECTION_ID_STREAM_GROUP_TAGS,
+ SECTION_ID_STREAM_GROUPS,
SECTION_ID_ROOT,
SECTION_ID_STREAM,
SECTION_ID_STREAM_DISPOSITION,
@@ -249,6 +260,12 @@ static const char *get_raw_string_type(const void *data)
return data;
}
+static const char *get_stream_group_type(const void *data)
+{
+ const AVStreamGroup *stg = (const AVStreamGroup *)data;
+ return av_x_if_null(avformat_stream_group_name(stg->type), "unknown");
+}
+
static struct section sections[] = {
[SECTION_ID_CHAPTERS] = { SECTION_ID_CHAPTERS, "chapters",
SECTION_FLAG_IS_ARRAY, { SECTION_ID_CHAPTER, -1 } },
[SECTION_ID_CHAPTER] = { SECTION_ID_CHAPTER, "chapter", 0, {
SECTION_ID_CHAPTER_TAGS, -1 } },
@@ -290,8 +307,16 @@ static struct section sections[] = {
[SECTION_ID_PROGRAM_TAGS] = { SECTION_ID_PROGRAM_TAGS, "tags",
SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name =
"program_tags" },
[SECTION_ID_PROGRAM_VERSION] = { SECTION_ID_PROGRAM_VERSION,
"program_version", 0, { -1 } },
[SECTION_ID_PROGRAMS] = { SECTION_ID_PROGRAMS,
"programs", SECTION_FLAG_IS_ARRAY, { SECTION_ID_PROGRAM, -1 } },
+ [SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION] = { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
"disposition", 0, { -1 }, .unique_name = "stream_group_stream_disposition" },
+ [SECTION_ID_STREAM_GROUP_STREAM_TAGS] = { SECTION_ID_STREAM_GROUP_STREAM_TAGS, "tags",
SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name =
"stream_group_stream_tags" },
+ [SECTION_ID_STREAM_GROUP] = { SECTION_ID_STREAM_GROUP,
"stream_group", SECTION_FLAG_HAS_TYPE, { SECTION_ID_STREAM_GROUP_TAGS,
SECTION_ID_STREAM_GROUP_DISPOSITION, SECTION_ID_STREAM_GROUP_STREAMS, -1 }, .get_type =
get_stream_group_type },
+ [SECTION_ID_STREAM_GROUP_STREAMS] = { SECTION_ID_STREAM_GROUP_STREAMS,
"streams", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP_STREAM, -1 }, .unique_name =
"stream_group_streams" },
+ [SECTION_ID_STREAM_GROUP_STREAM] = { SECTION_ID_STREAM_GROUP_STREAM,
"stream", 0, { SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
SECTION_ID_STREAM_GROUP_STREAM_TAGS, -1 }, .unique_name = "stream_group_stream" },
+ [SECTION_ID_STREAM_GROUP_DISPOSITION] = { SECTION_ID_STREAM_GROUP_DISPOSITION,
"disposition", 0, { -1 }, .unique_name = "stream_group_disposition" },
+ [SECTION_ID_STREAM_GROUP_TAGS] = { SECTION_ID_STREAM_GROUP_TAGS, "tags",
SECTION_FLAG_HAS_VARIABLE_FIELDS, { -1 }, .element_name = "tag", .unique_name =
"stream_group_tags" },
+ [SECTION_ID_STREAM_GROUPS] = { SECTION_ID_STREAM_GROUPS,
"stream_groups", SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM_GROUP, -1 } },
[SECTION_ID_ROOT] = { SECTION_ID_ROOT, "root",
SECTION_FLAG_IS_WRAPPER,
- { SECTION_ID_CHAPTERS,
SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS, SECTION_ID_STREAMS,
+ { SECTION_ID_CHAPTERS,
SECTION_ID_FORMAT, SECTION_ID_FRAMES, SECTION_ID_PROGRAMS,
SECTION_ID_STREAM_GROUPS, SECTION_ID_STREAMS,
SECTION_ID_PACKETS,
SECTION_ID_ERROR, SECTION_ID_PROGRAM_VERSION, SECTION_ID_LIBRARY_VERSIONS,
SECTION_ID_PIXEL_FORMATS, -1} },
[SECTION_ID_STREAMS] = { SECTION_ID_STREAMS, "streams",
SECTION_FLAG_IS_ARRAY, { SECTION_ID_STREAM, -1 } },
@@ -3071,7 +3096,10 @@ static void print_dispositions(WriterContext *w,
uint32_t disposition, SectionID
writer_print_section_footer(w);
}
-static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_idx, InputStream *ist, int in_program)
+#define IN_PROGRAM 1
+#define IN_STREAM_GROUP 2
+
+static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int
stream_idx, InputStream *ist, int container)
{
AVStream *stream = ist->st;
AVCodecParameters *par;
@@ -3081,12 +3109,29 @@ static int show_stream(WriterContext *w,
AVFormatContext *fmt_ctx, int stream_id
AVRational sar, dar;
AVBPrint pbuf;
const AVCodecDescriptor *cd;
+ const SectionID section_header[] = {
+ SECTION_ID_STREAM,
+ SECTION_ID_PROGRAM_STREAM,
+ SECTION_ID_STREAM_GROUP_STREAM,
+ };
+ const SectionID section_disposition[] = {
+ SECTION_ID_STREAM_DISPOSITION,
+ SECTION_ID_PROGRAM_STREAM_DISPOSITION,
+ SECTION_ID_STREAM_GROUP_STREAM_DISPOSITION,
+ };
+ const SectionID section_tags[] = {
+ SECTION_ID_STREAM_TAGS,
+ SECTION_ID_PROGRAM_STREAM_TAGS,
+ SECTION_ID_STREAM_GROUP_STREAM_TAGS,
+ };
int ret = 0;
const char *profile = NULL;
+ av_assert0(container < FF_ARRAY_ELEMS(section_header));
+
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
- writer_print_section_header(w, NULL, in_program ? SECTION_ID_PROGRAM_STREAM : SECTION_ID_STREAM);
+ writer_print_section_header(w, NULL, section_header[container]);
print_int("index", stream->index);
@@ -3248,13 +3293,14 @@ static int show_stream(WriterContext *w, AVFormatContext *fmt_ctx, int stream_id
/* Print disposition information */
if (do_show_stream_disposition) {
- print_dispositions(w, stream->disposition,
- in_program ? SECTION_ID_PROGRAM_STREAM_DISPOSITION
- : SECTION_ID_STREAM_DISPOSITION);
+ av_assert0(container < FF_ARRAY_ELEMS(section_disposition));
+ print_dispositions(w, stream->disposition,
section_disposition[container]);
}
- if (do_show_stream_tags)
- ret = show_tags(w, stream->metadata, in_program ?
SECTION_ID_PROGRAM_STREAM_TAGS : SECTION_ID_STREAM_TAGS);
+ if (do_show_stream_tags) {
+ av_assert0(container < FF_ARRAY_ELEMS(section_tags));
+ ret = show_tags(w, stream->metadata, section_tags[container]);
+ }
if (stream->codecpar->nb_coded_side_data) {
writer_print_section_header(w, NULL,
SECTION_ID_STREAM_SIDE_DATA_LIST);
@@ -3309,7 +3355,7 @@ static int show_program(WriterContext *w, InputFile
*ifile, AVProgram *program)
writer_print_section_header(w, NULL, SECTION_ID_PROGRAM_STREAMS);
for (i = 0; i < program->nb_stream_indexes; i++) {
if (selected_streams[program->stream_index[i]]) {
- ret = show_stream(w, fmt_ctx, program->stream_index[i],
&ifile->streams[program->stream_index[i]], 1);
+ ret = show_stream(w, fmt_ctx, program->stream_index[i],
&ifile->streams[program->stream_index[i]], IN_PROGRAM);
if (ret < 0)
break;
}
@@ -3339,6 +3385,72 @@ static int show_programs(WriterContext *w, InputFile
*ifile)
return ret;
}
+static void print_stream_group_params(WriterContext *w, AVStreamGroup *stg)
+{
+ const char *unknown = "unknown";
+ if (stg->type != AV_STREAM_GROUP_PARAMS_NONE)
+ print_str("type", av_x_if_null(avformat_stream_group_name(stg->type),
unknown));
+ else
+ print_str_opt("type", unknown);
+}
+
+static int show_stream_group(WriterContext *w, InputFile *ifile, AVStreamGroup
*stg)
+{
+ AVFormatContext *fmt_ctx = ifile->fmt_ctx;
+ AVBPrint pbuf;
+ int i, ret = 0;
+
+ av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
+ writer_print_section_header(w, stg, SECTION_ID_STREAM_GROUP);
+ print_int("index", stg->index);
+ if (fmt_ctx->iformat->flags & AVFMT_SHOW_IDS) print_fmt ("id",
"0x%"PRIx64, stg->id);
+ else print_str_opt("id", "N/A");
+ print_int("nb_streams", stg->nb_streams);
+ print_stream_group_params(w, stg);
+
+ /* Print disposition information */
+ if (do_show_stream_group_disposition)
+ print_dispositions(w, stg->disposition,
SECTION_ID_STREAM_GROUP_DISPOSITION);
+
+ if (do_show_stream_group_tags)
+ ret = show_tags(w, stg->metadata, SECTION_ID_STREAM_GROUP_TAGS);
+ if (ret < 0)
+ goto end;
+
+ writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUP_STREAMS);
+ for (i = 0; i < stg->nb_streams; i++) {
+ if (selected_streams[stg->streams[i]->index]) {
+ ret = show_stream(w, fmt_ctx, stg->streams[i]->index,
&ifile->streams[stg->streams[i]->index], IN_STREAM_GROUP);
+ if (ret < 0)
+ break;
+ }
+ }
+ writer_print_section_footer(w);
+
+end:
+ av_bprint_finalize(&pbuf, NULL);
+ writer_print_section_footer(w);
+ return ret;
+}
+
+static int show_stream_groups(WriterContext *w, InputFile *ifile)
+{
+ AVFormatContext *fmt_ctx = ifile->fmt_ctx;
+ int i, ret = 0;
+
+ writer_print_section_header(w, NULL, SECTION_ID_STREAM_GROUPS);