You've already forked FFmpeg
mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-08-10 06:10:52 +02:00
avformat/dashenc: add Trick Mode support for AdaptationSets
Signed-off-by: James Almer <jamrial@gmail.com>
This commit is contained in:
@@ -286,6 +286,8 @@ For example, -adaptation_sets "id=0,descriptor=<SupplementalProperty schemeIdUri
|
|||||||
Please note that descriptor string should be a self-closing xml tag.
|
Please note that descriptor string should be a self-closing xml tag.
|
||||||
seg_duration, frag_duration and frag_type override the global option values for each adaptation set.
|
seg_duration, frag_duration and frag_type override the global option values for each adaptation set.
|
||||||
For example, -adaptation_sets "id=0,seg_duration=2,frag_duration=1,frag_type=duration,streams=v id=1,seg_duration=2,frag_type=none,streams=a"
|
For example, -adaptation_sets "id=0,seg_duration=2,frag_duration=1,frag_type=duration,streams=v id=1,seg_duration=2,frag_type=none,streams=a"
|
||||||
|
type_id marks an adaptation set as containing streams meant to be used for Trick Mode for the referenced adaptation set.
|
||||||
|
For example, -adaptation_sets "id=0,seg_duration=2,frag_type=none,streams=0 id=1,seg_duration=10,frag_type=none,trick_id=0,streams=1"
|
||||||
@item timeout @var{timeout}
|
@item timeout @var{timeout}
|
||||||
Set timeout for socket I/O operations. Applicable only for HTTP output.
|
Set timeout for socket I/O operations. Applicable only for HTTP output.
|
||||||
@item index_correction @var{index_correction}
|
@item index_correction @var{index_correction}
|
||||||
|
@@ -93,6 +93,7 @@ typedef struct AdaptationSet {
|
|||||||
int max_width, max_height;
|
int max_width, max_height;
|
||||||
int nb_streams;
|
int nb_streams;
|
||||||
AVRational par;
|
AVRational par;
|
||||||
|
int trick_idx;
|
||||||
} AdaptationSet;
|
} AdaptationSet;
|
||||||
|
|
||||||
typedef struct OutputStream {
|
typedef struct OutputStream {
|
||||||
@@ -135,6 +136,7 @@ typedef struct OutputStream {
|
|||||||
int frag_type;
|
int frag_type;
|
||||||
int64_t gop_size;
|
int64_t gop_size;
|
||||||
AVRational sar;
|
AVRational sar;
|
||||||
|
int coding_dependency;
|
||||||
} OutputStream;
|
} OutputStream;
|
||||||
|
|
||||||
typedef struct DASHContext {
|
typedef struct DASHContext {
|
||||||
@@ -819,6 +821,8 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
|
|||||||
|
|
||||||
if (!final && c->ldash && as->max_frag_duration)
|
if (!final && c->ldash && as->max_frag_duration)
|
||||||
avio_printf(out, "\t\t\t<Resync dT=\"%"PRId64"\" type=\"0\"/>\n", as->max_frag_duration);
|
avio_printf(out, "\t\t\t<Resync dT=\"%"PRId64"\" type=\"0\"/>\n", as->max_frag_duration);
|
||||||
|
if (as->trick_idx >= 0)
|
||||||
|
avio_printf(out, "\t\t\t<EssentialProperty id=\"%d\" schemeIdUri=\"http://dashif.org/guidelines/trickmode\" value=\"%d\"/>\n", as->id, as->trick_idx);
|
||||||
role = av_dict_get(as->metadata, "role", NULL, 0);
|
role = av_dict_get(as->metadata, "role", NULL, 0);
|
||||||
if (role)
|
if (role)
|
||||||
avio_printf(out, "\t\t\t<Role schemeIdUri=\"urn:mpeg:dash:role:2011\" value=\"%s\"/>\n", role->value);
|
avio_printf(out, "\t\t\t<Role schemeIdUri=\"urn:mpeg:dash:role:2011\" value=\"%s\"/>\n", role->value);
|
||||||
@@ -846,6 +850,13 @@ static int write_adaptation_set(AVFormatContext *s, AVIOContext *out, int as_ind
|
|||||||
avio_printf(out, " sar=\"%d:%d\"", os->sar.num, os->sar.den);
|
avio_printf(out, " sar=\"%d:%d\"", os->sar.num, os->sar.den);
|
||||||
if (st->avg_frame_rate.num && av_cmp_q(as->min_frame_rate, as->max_frame_rate) < 0)
|
if (st->avg_frame_rate.num && av_cmp_q(as->min_frame_rate, as->max_frame_rate) < 0)
|
||||||
avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
|
avio_printf(out, " frameRate=\"%d/%d\"", st->avg_frame_rate.num, st->avg_frame_rate.den);
|
||||||
|
if (as->trick_idx >= 0) {
|
||||||
|
AdaptationSet *tas = &c->as[as->trick_idx];
|
||||||
|
if (!as->ambiguous_frame_rate && !tas->ambiguous_frame_rate)
|
||||||
|
avio_printf(out, " maxPlayoutRate=\"%d\"", FFMAX((int)av_q2d(av_div_q(tas->min_frame_rate, as->min_frame_rate)), 1));
|
||||||
|
}
|
||||||
|
if (!os->coding_dependency)
|
||||||
|
avio_printf(out, " codingDependency=\"false\"");
|
||||||
avio_printf(out, ">\n");
|
avio_printf(out, ">\n");
|
||||||
} else {
|
} else {
|
||||||
avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
|
avio_printf(out, "\t\t\t<Representation id=\"%d\" mimeType=\"audio/%s\" codecs=\"%s\"%s audioSamplingRate=\"%d\">\n",
|
||||||
@@ -889,6 +900,7 @@ static int add_adaptation_set(AVFormatContext *s, AdaptationSet **as, enum AVMed
|
|||||||
memset(*as, 0, sizeof(**as));
|
memset(*as, 0, sizeof(**as));
|
||||||
(*as)->media_type = type;
|
(*as)->media_type = type;
|
||||||
(*as)->frag_type = -1;
|
(*as)->frag_type = -1;
|
||||||
|
(*as)->trick_idx = -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -939,7 +951,8 @@ static int parse_adaptation_sets(AVFormatContext *s)
|
|||||||
|
|
||||||
// syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
|
// syntax id=0,streams=0,1,2 id=1,streams=3,4 and so on
|
||||||
// option id=0,descriptor=descriptor_str,streams=0,1,2 and so on
|
// option id=0,descriptor=descriptor_str,streams=0,1,2 and so on
|
||||||
// option id=0,seg_duration=2.5,frag_duration=0.5,streams=0,1,2 and so on
|
// option id=0,seg_duration=2.5,frag_duration=0.5,streams=0,1,2
|
||||||
|
// id=1,trick_id=0,seg_duration=10,frag_type=none,streams=3 and so on
|
||||||
// descriptor is useful to the scheme defined by ISO/IEC 23009-1:2014/Amd.2:2015
|
// descriptor is useful to the scheme defined by ISO/IEC 23009-1:2014/Amd.2:2015
|
||||||
// descriptor_str should be a self-closing xml tag.
|
// descriptor_str should be a self-closing xml tag.
|
||||||
// seg_duration and frag_duration have the same syntax as the global options of
|
// seg_duration and frag_duration have the same syntax as the global options of
|
||||||
@@ -1025,6 +1038,20 @@ static int parse_adaptation_sets(AVFormatContext *s)
|
|||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
p += n;
|
p += n;
|
||||||
|
if (*p)
|
||||||
|
p++;
|
||||||
|
state = parse_default;
|
||||||
|
} else if ((state != new_set) && av_strstart(p, "trick_id=", &p)) {
|
||||||
|
char trick_id_str[10], *end_str;
|
||||||
|
|
||||||
|
n = strcspn(p, ",");
|
||||||
|
snprintf(trick_id_str, sizeof(trick_id_str), "%.*s", n, p);
|
||||||
|
p += n;
|
||||||
|
|
||||||
|
as->trick_idx = strtol(trick_id_str, &end_str, 10);
|
||||||
|
if (trick_id_str == end_str || as->trick_idx < 0)
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
if (*p)
|
if (*p)
|
||||||
p++;
|
p++;
|
||||||
state = parse_default;
|
state = parse_default;
|
||||||
@@ -1086,6 +1113,22 @@ end:
|
|||||||
return AVERROR(EINVAL);
|
return AVERROR(EINVAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check references for trick mode AdaptationSet
|
||||||
|
for (i = 0; i < c->nb_as; i++) {
|
||||||
|
as = &c->as[i];
|
||||||
|
if (as->trick_idx < 0)
|
||||||
|
continue;
|
||||||
|
for (n = 0; n < c->nb_as; n++) {
|
||||||
|
if (c->as[n].id == as->trick_idx)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (n >= c->nb_as) {
|
||||||
|
av_log(s, AV_LOG_ERROR, "reference AdaptationSet id \"%d\" not found for trick mode AdaptationSet id \"%d\"\n", as->trick_idx, as->id);
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1476,7 +1519,8 @@ static int dash_init(AVFormatContext *s)
|
|||||||
return ret;
|
return ret;
|
||||||
// We only want to parse frame headers
|
// We only want to parse frame headers
|
||||||
os->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
|
os->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
|
||||||
}
|
} else
|
||||||
|
os->coding_dependency = 1;
|
||||||
|
|
||||||
if (c->single_file) {
|
if (c->single_file) {
|
||||||
if (os->single_file_name)
|
if (os->single_file_name)
|
||||||
@@ -2014,6 +2058,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
if (!os->availability_time_offset &&
|
if (!os->availability_time_offset &&
|
||||||
((os->frag_type == FRAG_TYPE_DURATION && os->seg_duration != os->frag_duration) ||
|
((os->frag_type == FRAG_TYPE_DURATION && os->seg_duration != os->frag_duration) ||
|
||||||
(os->frag_type == FRAG_TYPE_EVERY_FRAME && pkt->duration))) {
|
(os->frag_type == FRAG_TYPE_EVERY_FRAME && pkt->duration))) {
|
||||||
|
AdaptationSet *as = &c->as[os->as_idx - 1];
|
||||||
int64_t frame_duration = 0;
|
int64_t frame_duration = 0;
|
||||||
|
|
||||||
switch (os->frag_type) {
|
switch (os->frag_type) {
|
||||||
@@ -2038,6 +2083,19 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
seg_end_duration = os->seg_duration;
|
seg_end_duration = os->seg_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (os->parser &&
|
||||||
|
(os->frag_type == FRAG_TYPE_PFRAMES ||
|
||||||
|
as->trick_idx >= 0)) {
|
||||||
|
// Parse the packets only in scenarios where it's needed
|
||||||
|
uint8_t *data;
|
||||||
|
int size;
|
||||||
|
av_parser_parse2(os->parser, os->parser_avctx,
|
||||||
|
&data, &size, pkt->data, pkt->size,
|
||||||
|
pkt->pts, pkt->dts, pkt->pos);
|
||||||
|
|
||||||
|
os->coding_dependency |= os->parser->pict_type != AV_PICTURE_TYPE_I;
|
||||||
|
}
|
||||||
|
|
||||||
if (pkt->flags & AV_PKT_FLAG_KEY && os->packets_written &&
|
if (pkt->flags & AV_PKT_FLAG_KEY && os->packets_written &&
|
||||||
av_compare_ts(elapsed_duration, st->time_base,
|
av_compare_ts(elapsed_duration, st->time_base,
|
||||||
seg_end_duration, AV_TIME_BASE_Q) >= 0) {
|
seg_end_duration, AV_TIME_BASE_Q) >= 0) {
|
||||||
@@ -2085,14 +2143,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
|
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
|
||||||
os->frag_type == FRAG_TYPE_PFRAMES &&
|
os->frag_type == FRAG_TYPE_PFRAMES &&
|
||||||
os->packets_written) {
|
os->packets_written) {
|
||||||
uint8_t *data;
|
|
||||||
int size;
|
|
||||||
|
|
||||||
av_assert0(os->parser);
|
av_assert0(os->parser);
|
||||||
av_parser_parse2(os->parser, os->parser_avctx,
|
|
||||||
&data, &size, pkt->data, pkt->size,
|
|
||||||
pkt->pts, pkt->dts, pkt->pos);
|
|
||||||
|
|
||||||
if ((os->parser->pict_type == AV_PICTURE_TYPE_P &&
|
if ((os->parser->pict_type == AV_PICTURE_TYPE_P &&
|
||||||
st->codecpar->video_delay &&
|
st->codecpar->video_delay &&
|
||||||
!(os->last_flags & AV_PKT_FLAG_KEY)) ||
|
!(os->last_flags & AV_PKT_FLAG_KEY)) ||
|
||||||
@@ -2111,7 +2162,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pkt->flags & AV_PKT_FLAG_KEY && (os->packets_written || os->nb_segments) && !os->gop_size) {
|
if (pkt->flags & AV_PKT_FLAG_KEY && (os->packets_written || os->nb_segments) && !os->gop_size && as->trick_idx < 0) {
|
||||||
os->gop_size = os->last_duration + av_rescale_q(os->total_pkt_duration, st->time_base, AV_TIME_BASE_Q);
|
os->gop_size = os->last_duration + av_rescale_q(os->total_pkt_duration, st->time_base, AV_TIME_BASE_Q);
|
||||||
c->max_gop_size = FFMAX(c->max_gop_size, os->gop_size);
|
c->max_gop_size = FFMAX(c->max_gop_size, os->gop_size);
|
||||||
}
|
}
|
||||||
|
@@ -33,7 +33,7 @@
|
|||||||
// Also please add any ticket numbers that you believe might be affected here
|
// Also please add any ticket numbers that you believe might be affected here
|
||||||
#define LIBAVFORMAT_VERSION_MAJOR 58
|
#define LIBAVFORMAT_VERSION_MAJOR 58
|
||||||
#define LIBAVFORMAT_VERSION_MINOR 38
|
#define LIBAVFORMAT_VERSION_MINOR 38
|
||||||
#define LIBAVFORMAT_VERSION_MICRO 100
|
#define LIBAVFORMAT_VERSION_MICRO 101
|
||||||
|
|
||||||
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
|
#define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
|
||||||
LIBAVFORMAT_VERSION_MINOR, \
|
LIBAVFORMAT_VERSION_MINOR, \
|
||||||
|
Reference in New Issue
Block a user