|
|
@@ -48,6 +48,12 @@ |
|
|
|
#include "vpcc.h" |
|
|
|
#include "dash.h" |
|
|
|
|
|
|
|
typedef enum { |
|
|
|
SEGMENT_TYPE_MP4 = 0, |
|
|
|
SEGMENT_TYPE_WEBM, |
|
|
|
SEGMENT_TYPE_NB |
|
|
|
} SegmentType; |
|
|
|
|
|
|
|
typedef struct Segment { |
|
|
|
char file[1024]; |
|
|
|
int64_t start_pos; |
|
|
@@ -69,7 +75,6 @@ typedef struct OutputStream { |
|
|
|
AVFormatContext *ctx; |
|
|
|
int ctx_inited, as_idx; |
|
|
|
AVIOContext *out; |
|
|
|
char format_name[8]; |
|
|
|
int packets_written; |
|
|
|
char initfile[1024]; |
|
|
|
int64_t init_start_pos, pos; |
|
|
@@ -126,6 +131,8 @@ typedef struct DASHContext { |
|
|
|
int64_t timeout; |
|
|
|
int index_correction; |
|
|
|
char *format_options_str; |
|
|
|
SegmentType segment_type; |
|
|
|
const char *format_name; |
|
|
|
} DASHContext; |
|
|
|
|
|
|
|
static struct codec_string { |
|
|
@@ -139,6 +146,15 @@ static struct codec_string { |
|
|
|
{ 0, NULL } |
|
|
|
}; |
|
|
|
|
|
|
|
static struct format_string { |
|
|
|
SegmentType segment_type; |
|
|
|
const char *str; |
|
|
|
} formats[] = { |
|
|
|
{ SEGMENT_TYPE_MP4, "mp4" }, |
|
|
|
{ SEGMENT_TYPE_WEBM, "webm" }, |
|
|
|
{ 0, NULL } |
|
|
|
}; |
|
|
|
|
|
|
|
static int dashenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename, |
|
|
|
AVDictionary **options) { |
|
|
|
DASHContext *c = s->priv_data; |
|
|
@@ -172,6 +188,14 @@ 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 void set_vp9_codec_str(AVFormatContext *s, AVCodecParameters *par, |
|
|
|
AVRational *frame_rate, char *str, int size) { |
|
|
|
VPCC vpcc; |
|
|
@@ -582,13 +606,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, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height); |
|
|
|
i, c->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, os->format_name, os->codec_str, bandwidth_str, s->streams[i]->codecpar->sample_rate); |
|
|
|
i, c->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); |
|
|
|
} |
|
|
@@ -960,27 +984,10 @@ static int dash_init(AVFormatContext *s) |
|
|
|
if (!ctx) |
|
|
|
return AVERROR(ENOMEM); |
|
|
|
|
|
|
|
// choose muxer based on codec: webm for VP8 and opus, mp4 otherwise |
|
|
|
// note: os->format_name is also used as part of the mimetype of the |
|
|
|
// representation, e.g. video/<format_name> |
|
|
|
if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VP8 || |
|
|
|
s->streams[i]->codecpar->codec_id == AV_CODEC_ID_OPUS || |
|
|
|
s->streams[i]->codecpar->codec_id == AV_CODEC_ID_VORBIS) { |
|
|
|
snprintf(os->format_name, sizeof(os->format_name), "webm"); |
|
|
|
|
|
|
|
if (s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { |
|
|
|
av_log(s, AV_LOG_ERROR, |
|
|
|
"WebM support in dashenc is experimental and has not " |
|
|
|
"been validated. For testing purposes, make sure " |
|
|
|
"to add -strict experimental and override " |
|
|
|
"-init_seg_name and -media_seg_name to end with " |
|
|
|
"the extension 'webm'.\n"); |
|
|
|
return AVERROR(EINVAL); |
|
|
|
} |
|
|
|
} else { |
|
|
|
snprintf(os->format_name, sizeof(os->format_name), "mp4"); |
|
|
|
} |
|
|
|
ctx->oformat = av_guess_format(os->format_name, NULL, NULL); |
|
|
|
c->format_name = get_format_str(c->segment_type); |
|
|
|
if (!c->format_name) |
|
|
|
return AVERROR_MUXER_NOT_FOUND; |
|
|
|
ctx->oformat = av_guess_format(c->format_name, NULL, NULL); |
|
|
|
if (!ctx->oformat) |
|
|
|
return AVERROR_MUXER_NOT_FOUND; |
|
|
|
os->ctx = ctx; |
|
|
@@ -1023,7 +1030,8 @@ static int dash_init(AVFormatContext *s) |
|
|
|
if (ret < 0) |
|
|
|
return ret; |
|
|
|
} |
|
|
|
if (!strcmp(os->format_name, "mp4")) { |
|
|
|
|
|
|
|
if (c->segment_type == SEGMENT_TYPE_MP4) { |
|
|
|
if (c->streaming) |
|
|
|
av_dict_set(&opts, "movflags", "frag_every_frame+dash+delay_moov", 0); |
|
|
|
else |
|
|
@@ -1088,7 +1096,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 (strcmp(os->format_name, "mp4") && |
|
|
|
if (c->segment_type == SEGMENT_TYPE_WEBM && |
|
|
|
(ret = flush_init_segment(s, os)) < 0) |
|
|
|
return ret; |
|
|
|
} |
|
|
@@ -1259,7 +1267,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) |
|
|
|
} |
|
|
|
|
|
|
|
if (!c->single_file) { |
|
|
|
if (!strcmp(os->format_name, "mp4") && !os->written_len) |
|
|
|
if (c->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); |
|
|
@@ -1449,7 +1457,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) |
|
|
|
} |
|
|
|
|
|
|
|
//write out the data immediately in streaming mode |
|
|
|
if (c->streaming && !strcmp(os->format_name, "mp4")) { |
|
|
|
if (c->streaming && c->segment_type == SEGMENT_TYPE_MP4) { |
|
|
|
int len = 0; |
|
|
|
uint8_t *buf = NULL; |
|
|
|
if (!os->written_len) |
|
|
@@ -1545,6 +1553,9 @@ 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"}, |
|
|
|
{ NULL }, |
|
|
|
}; |
|
|
|
|
|
|
|