lavf/dashenc: Add DASH segment type auto and make it the default

This commit restores the ability to create DASH streams with codecs
that require different containers that was lost after commit
2efdbf7367. It adds a new "auto" value for
the dash_segment_type option and makes it the default. When in this mode,
the segment format will be chosen based on the codec used in the stream:
webm for Vorbis, Opus, VP8 or VP9, mp4 otherwise.
This commit is contained in:
Andrey Semashev 2018-11-17 20:40:50 +03:00 committed by Karthick J
parent 1ee4b4006b
commit 322e53f8ae
2 changed files with 59 additions and 20 deletions

View File

@ -289,10 +289,13 @@ 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}
@item -dash_segment_type @var{dash_segment_type}
Possible values:
@item auto
If this flag is set, the dash segment files format will be selected based on the stream codec. This is the default mode.
@item mp4
If this flag is set, the dash segment files will be in in ISOBMFF format. This is the default format.
If this flag is set, the dash segment files will be in in ISOBMFF format.
@item webm
If this flag is set, the dash segment files will be in in WebM format.

View File

@ -49,7 +49,8 @@
#include "dash.h"
typedef enum {
SEGMENT_TYPE_MP4 = 0,
SEGMENT_TYPE_AUTO = 0,
SEGMENT_TYPE_MP4,
SEGMENT_TYPE_WEBM,
SEGMENT_TYPE_NB
} SegmentType;
@ -84,6 +85,8 @@ typedef struct OutputStream {
int64_t first_pts, start_pts, max_pts;
int64_t last_dts, last_pts;
int bit_rate;
SegmentType segment_type; /* segment type selected for this particular stream */
const char *format_name;
char codec_str[100];
int written_len;
@ -131,8 +134,7 @@ typedef struct DASHContext {
int64_t timeout;
int index_correction;
char *format_options_str;
SegmentType segment_type;
const char *format_name;
SegmentType segment_type_option; /* segment type as specified in options */
} DASHContext;
static struct codec_string {
@ -151,6 +153,7 @@ static struct format_string {
SegmentType segment_type;
const char *str;
} formats[] = {
{ SEGMENT_TYPE_AUTO, "auto" },
{ SEGMENT_TYPE_MP4, "mp4" },
{ SEGMENT_TYPE_WEBM, "webm" },
{ 0, NULL }
@ -197,6 +200,38 @@ static const char *get_format_str(SegmentType segment_type) {
return NULL;
}
static inline SegmentType select_segment_type(SegmentType segment_type, AVCodecID codec_id)
{
if (segment_type == SEGMENT_TYPE_AUTO) {
if (codec_id == AV_CODEC_ID_OPUS || codec_id == AV_CODEC_ID_VORBIS ||
codec_id == AV_CODEC_ID_VP8 || codec_id == AV_CODEC_ID_VP9) {
segment_type = SEGMENT_TYPE_WEBM;
} else {
segment_type = SEGMENT_TYPE_MP4;
}
}
return segment_type;
}
static int init_segment_types(AVFormatContext *s)
{
DASHContext *c = s->priv_data;
for (int i = 0; i < s->nb_streams; ++i) {
OutputStream *os = &c->streams[i];
SegmentType segment_type = select_segment_type(
c->segment_type_option, s->streams[i]->codecpar->codec_id);
os->segment_type = segment_type;
os->format_name = get_format_str(segment_type);
if (!os->format_name) {
av_log(s, AV_LOG_ERROR, "Could not select DASH segment type for stream %d\n", i);
return AVERROR_MUXER_NOT_FOUND;
}
}
return 0;
}
static int check_file_extension(const char *filename, const char *extension) {
char *dot;
if (!filename || !extension)
@ -622,13 +657,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);
}
@ -993,6 +1028,9 @@ static int dash_init(AVFormatContext *s)
if ((ret = parse_adaptation_sets(s)) < 0)
return ret;
if ((ret = init_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];
@ -1018,13 +1056,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 "
@ -1032,7 +1067,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;
@ -1076,7 +1111,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
@ -1141,7 +1176,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;
}
@ -1312,7 +1347,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);
@ -1502,7 +1537,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)
@ -1598,7 +1633,8 @@ 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"},
{ "dash_segment_type", "set dash segment files type", OFFSET(segment_type_option), AV_OPT_TYPE_INT, {.i64 = SEGMENT_TYPE_AUTO }, 0, SEGMENT_TYPE_NB - 1, E, "segment_type"},
{ "auto", "select segment file format based on codec", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_AUTO }, 0, UINT_MAX, 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"},
{ NULL },