---
doc/muxers.texi | 8 ++-
libavformat/dashenc.c | 161
+++++++++++++++++++++++++++++++++++-------
2 files changed, 140 insertions(+), 29 deletions(-)
diff --git a/doc/muxers.texi b/doc/muxers.texi
index 62f4091e31..4418b38c76 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -289,8 +289,12 @@ Set container format (mp4/webm) options using
a @code{:} separated list of
key=value parameters. Values containing @code{:} special
characters must be
escaped.
-@item dash_segment_type @var{dash_segment_type}
-Possible values:
+@item -dash_segment_type @var{dash_segment_type}
+Sets the container type for dash segment files. Syntax is "<type>
<type>:a,b,c <type>:d,e" where <type> is
+the container type and a, b, c, d and e are the indices of the
mapped streams. When no indices are specified,
+the container type is set for all streams.
+
+Possible container type values:
@item mp4
If this flag is set, the dash segment files will be in in
ISOBMFF format. This is the default format.
diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c
index f8b3d106d5..626dc76413 100644
--- a/libavformat/dashenc.c
+++ b/libavformat/dashenc.c
@@ -84,6 +84,8 @@ typedef struct OutputStream {
int64_t first_pts, start_pts, max_pts;
int64_t last_dts, last_pts;
int bit_rate;
+ SegmentType segment_type;
+ const char *format_name;
char codec_str[100];
int written_len;
@@ -131,8 +133,7 @@ typedef struct DASHContext {
int64_t timeout;
int index_correction;
char *format_options_str;
- SegmentType segment_type;
- const char *format_name;
+ const char *segment_types_str;
} DASHContext;
static struct codec_string {
@@ -188,14 +189,6 @@ static void dashenc_io_close(AVFormatContext
*s, AVIOContext **pb, char *filenam
}
}
-static const char *get_format_str(SegmentType segment_type) {
- int i;
- for (i = 0; i < SEGMENT_TYPE_NB; i++)
- if (formats[i].segment_type == segment_type)
- return formats[i].str;
- return NULL;
-}
-
static int check_file_extension(const char *filename, const char
*extension) {
char *dot;
if (!filename || !extension)
@@ -375,6 +368,8 @@ static void dash_free(AVFormatContext *s)
c->nb_as = 0;
}
+ av_freep(&c->segment_types_str);
+
if (!c->streams)
return;
for (i = 0; i < s->nb_streams; i++) {
@@ -621,13 +616,13 @@ static int
write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
if (as->media_type == AVMEDIA_TYPE_VIDEO) {
AVStream *st = s->streams[i];
avio_printf(out, "\t\t\t<Representation id=\"%d\"
mimeType=\"video/%s\" codecs=\"%s\"%s width=\"%d\" height=\"%d\"",
- i, c->format_name, os->codec_str, bandwidth_str,
s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
+ i, os->format_name, os->codec_str, bandwidth_str,
s->streams[i]->codecpar->width, s->streams[i]->codecpar->height);
if (st->avg_frame_rate.num)
avio_printf(out, " frameRate=\"%d/%d\"",
st->avg_frame_rate.num, st->avg_frame_rate.den);
avio_printf(out, ">\n");
} else {
avio_printf(out, "\t\t\t<Representation id=\"%d\"
mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
- i, c->format_name, os->codec_str, bandwidth_str,
s->streams[i]->codecpar->sample_rate);
+ i, os->format_name, os->codec_str, bandwidth_str,
s->streams[i]->codecpar->sample_rate);
avio_printf(out, "\t\t\t\t<AudioChannelConfiguration
schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\"
value=\"%d\" />\n",
s->streams[i]->codecpar->channels);
}
@@ -773,6 +768,120 @@ end:
return 0;
}
+static inline void set_all_segment_types(AVFormatContext *s,
int format_idx)
+{
+ DASHContext *c = s->priv_data;
+ for (int i = 0; i < s->nb_streams; ++i) {
+ OutputStream *os = &c->streams[i];
+ os->segment_type = formats[format_idx].segment_type;
+ os->format_name = formats[format_idx].str;
+ }
+}
+
+static int parse_segment_types(AVFormatContext *s)
+{
+ DASHContext *c = s->priv_data;
+ const char *p = c->segment_types_str;
+ enum { type_expected, type_parsed, parsing_streams } state;
+ int i, format_idx;
+
+ // Set the default container type in case if some streams are
not mentioned in the string
+ set_all_segment_types(s, 0);
+
+ if (!p)
+ return 0;
+
+ // Parse per-stream container types: mp4 webm:0,1,2 and so on
+ state = type_expected;
+ format_idx = 0;
+ while (*p) {
+ if (*p == ' ') {
+ if (state == type_parsed)
+ set_all_segment_types(s, format_idx);
+
+ state = type_expected;
+ ++p;
+ continue;
+ }
+
+ if (state == type_expected) {
+ for (i = 0; i < SEGMENT_TYPE_NB; i++) {
+ if (av_strstart(p, formats[i].str, &p)) {
+ state = type_parsed;
+ format_idx = i;
+ break;
+ }
+ }
+
+ if (state != type_parsed) {
+ av_log(s, AV_LOG_ERROR, "DASH segment type not
recognized at position %d of \"%s\"\n", (int)(p -
c->segment_types_str + 1), c->segment_types_str);
+ return AVERROR_MUXER_NOT_FOUND;
+ }
+
+ continue;
+ }
+
+ if (state == type_parsed) {
+ if (*p != ':') {
+ av_log(s, AV_LOG_ERROR, "Unexpected character at
position %d of \"%s\", a colon is expected\n", (int)(p -
c->segment_types_str + 1), c->segment_types_str);
+ return AVERROR(EINVAL);
+ }
+
+ state = parsing_streams;
+ ++p;
+ continue;
+ }
+
+ if (state == parsing_streams) {
+ while (*p && *p != ' ') {
+ if (*p == ',') {
+ ++p;
+ } else if (*p == 'a' || *p == 'v') {
+ // Select all streams of the given type
+ enum AVMediaType type = (*p == 'v') ?
AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO;
+
+ for (i = 0; i < s->nb_streams; ++i) {
+ if (s->streams[i]->codecpar->codec_type ==
type) {
+ OutputStream *os = &c->streams[i];
+ os->segment_type =
formats[format_idx].segment_type;
+ os->format_name =
formats[format_idx].str;
+ }
+ }
+
+ ++p;
+ } else {
+ // Select a stream by index
+ OutputStream *os;
+ const char* end_p = p;
+ i = strtol(p, (char**)&end_p, 10);
+ if (p == end_p) {
+ av_log(s, AV_LOG_ERROR, "Failed to parse
stream index at position %d of \"%s\"\n", (int)(p -
c->segment_types_str + 1), c->segment_types_str);
+ return AVERROR(EINVAL);
+ }
+ if (i < 0 || i >= s->nb_streams) {
+ av_log(s, AV_LOG_ERROR, "Selected stream
%d not found!\n", i);
+ return AVERROR(EINVAL);
+ }
+
+ os = &c->streams[i];
+ os->segment_type =
formats[format_idx].segment_type;
+ os->format_name = formats[format_idx].str;
+
+ p = end_p;
+ }
+ }
+
+ state = type_expected;
+ }
+ }
+
+ // Complete segment type setup if no streams are specified
after the container type
+ if (state == type_parsed)
+ set_all_segment_types(s, format_idx);
+
+ return 0;
+}
+
static int write_manifest(AVFormatContext *s, int final)
{
DASHContext *c = s->priv_data;
@@ -992,6 +1101,9 @@ static int dash_init(AVFormatContext *s)
if ((ret = parse_adaptation_sets(s)) < 0)
return ret;
+ if ((ret = parse_segment_types(s)) < 0)
+ return ret;
+
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
AdaptationSet *as = &c->as[os->as_idx - 1];
@@ -1017,13 +1129,10 @@ static int dash_init(AVFormatContext *s)
if (!ctx)
return AVERROR(ENOMEM);
- c->format_name = get_format_str(c->segment_type);
- if (!c->format_name)
- return AVERROR_MUXER_NOT_FOUND;
- if (c->segment_type == SEGMENT_TYPE_WEBM) {
- if ((!c->single_file &&
check_file_extension(c->init_seg_name, c->format_name) != 0) ||
- (!c->single_file &&
check_file_extension(c->media_seg_name, c->format_name) != 0) ||
- (c->single_file &&
check_file_extension(c->single_file_name, c->format_name) != 0)) {
+ if (os->segment_type == SEGMENT_TYPE_WEBM) {
+ if ((!c->single_file &&
check_file_extension(c->init_seg_name, os->format_name) != 0) ||
+ (!c->single_file &&
check_file_extension(c->media_seg_name, os->format_name) != 0) ||
+ (c->single_file &&
check_file_extension(c->single_file_name, os->format_name) != 0)) {
av_log(s, AV_LOG_WARNING,
"One or many segment file names doesn't
end with .webm. "
"Override -init_seg_name and/or
-media_seg_name and/or "
@@ -1031,7 +1140,7 @@ static int dash_init(AVFormatContext *s)
}
}
- ctx->oformat = av_guess_format(c->format_name, NULL,
NULL);
+ ctx->oformat = av_guess_format(os->format_name, NULL, NULL);
if (!ctx->oformat)
return AVERROR_MUXER_NOT_FOUND;
os->ctx = ctx;
@@ -1075,7 +1184,7 @@ static int dash_init(AVFormatContext *s)
return ret;
}
- if (c->segment_type == SEGMENT_TYPE_MP4) {
+ if (os->segment_type == SEGMENT_TYPE_MP4) {
if (c->streaming)
av_dict_set(&opts, "movflags",
"frag_every_frame+dash+delay_moov+global_sidx", 0);
else
@@ -1140,7 +1249,7 @@ static int dash_write_header(AVFormatContext *s)
// Flush init segment
// Only for WebM segment, since for mp4 delay_moov is
set and
// the init segment is thus flushed after the first
packets.
- if (c->segment_type == SEGMENT_TYPE_WEBM &&
+ if (os->segment_type == SEGMENT_TYPE_WEBM &&
(ret = flush_init_segment(s, os)) < 0)
return ret;
}
@@ -1311,7 +1420,7 @@ static int dash_flush(AVFormatContext *s, int
final, int stream)
}
if (!c->single_file) {
- if (c->segment_type == SEGMENT_TYPE_MP4 &&
!os->written_len)
+ if (os->segment_type == SEGMENT_TYPE_MP4 &&
!os->written_len)
write_styp(os->ctx->pb);
} else {
snprintf(os->full_path, sizeof(os->full_path),
"%s%s", c->dirname, os->initfile);
@@ -1501,7 +1610,7 @@ static int dash_write_packet(AVFormatContext
*s, AVPacket *pkt)
}
//write out the data immediately in streaming mode
- if (c->streaming && c->segment_type == SEGMENT_TYPE_MP4) {
+ if (c->streaming && os->segment_type == SEGMENT_TYPE_MP4) {
int len = 0;
uint8_t *buf = NULL;
if (!os->written_len)
@@ -1597,9 +1706,7 @@ static const AVOption options[] = {
{ "timeout", "set timeout for socket I/O operations",
OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX,
.flags = E },
{ "index_correction", "Enable/Disable segment index
correction logic", OFFSET(index_correction), AV_OPT_TYPE_BOOL, {
.i64 = 0 }, 0, 1, E },
{ "format_options","set list of options for the container
format (mp4/webm) used for dash", OFFSET(format_options_str),
AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E},
- { "dash_segment_type", "set dash segment files type",
OFFSET(segment_type), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_MP4 },
0, SEGMENT_TYPE_NB - 1, E, "segment_type"},
- { "mp4", "make segment file in ISOBMFF format", 0,
AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MP4 }, 0, UINT_MAX, E,
"segment_type"},
- { "webm", "make segment file in WebM format", 0,
AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_WEBM }, 0, UINT_MAX, E,
"segment_type"},
+ { "dash_segment_type", "DASH segment files type. Syntax:
\"<type> <type>:<ids>\" where <type> is one of mp4 or webm and
<ids> are comma-separated stream ids", OFFSET(segment_types_str),
AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, E },
{ NULL },
};