|
|
|
@@ -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 }, |
|
|
|
|