Implemented as as a seg_duration key=value entry in the adaptation_sets muxer option. It has the same syntax as the global seg_duration option, and has precedence over it if set.
Signed-off-by: James Almer <jamr...@gmail.com> --- libavformat/dashenc.c | 49 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 8c28fb6b6e..419043ee0b 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -31,6 +31,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/mathematics.h" #include "libavutil/opt.h" +#include "libavutil/parseutils.h" #include "libavutil/rational.h" #include "libavutil/time.h" #include "libavutil/time_internal.h" @@ -70,6 +71,7 @@ typedef struct Segment { typedef struct AdaptationSet { char id[10]; char *descriptor; + int64_t seg_duration; enum AVMediaType media_type; AVDictionary *metadata; AVRational min_frame_rate, max_frame_rate; @@ -85,6 +87,7 @@ typedef struct OutputStream { int64_t init_start_pos, pos; int init_range_length; int nb_segments, segments_size, segment_index; + int64_t seg_duration; Segment **segments; int64_t first_pts, start_pts, max_pts; int64_t last_dts, last_pts; @@ -613,7 +616,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont int timescale = c->use_timeline ? os->ctx->streams[0]->time_base.den : AV_TIME_BASE; avio_printf(out, "\t\t\t\t<SegmentTemplate timescale=\"%d\" ", timescale); if (!c->use_timeline) { - avio_printf(out, "duration=\"%"PRId64"\" ", c->seg_duration); + avio_printf(out, "duration=\"%"PRId64"\" ", os->seg_duration); if (c->streaming && os->availability_time_offset) avio_printf(out, "availabilityTimeOffset=\"%.3f\" ", os->availability_time_offset); @@ -839,7 +842,7 @@ static int parse_adaptation_sets(AVFormatContext *s) { DASHContext *c = s->priv_data; const char *p = c->adaptation_sets; - enum { new_set, parse_id, parsing_streams, parse_descriptor } state; + enum { new_set, parse_id, parsing_streams, parse_descriptor, parse_seg_duration } state; AdaptationSet *as; int i, n, ret; @@ -857,8 +860,11 @@ static int parse_adaptation_sets(AVFormatContext *s) // 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,seg_duration=2.5,streams=0,1,2 and so on // 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. + // seg_duration has the same syntax as the global seg_duration option, and has + // precedence over it if set. state = new_set; while (*p) { if (*p == ' ') { @@ -876,7 +882,25 @@ static int parse_adaptation_sets(AVFormatContext *s) if (*p) p++; state = parse_id; - } else if (state == parse_id && av_strstart(p, "descriptor=", &p)) { + } else if (state != new_set && av_strstart(p, "seg_duration=", &p)) { + char str[32]; + int64_t usecs = 0; + + n = strcspn(p, ","); + snprintf(str, sizeof(str), "%.*s", n, p); + p += n; + if (*p) + p++; + + ret = av_parse_time(&usecs, str, 1); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Unable to parse option value \"%s\" as duration\n", str); + return ret; + } + + as->seg_duration = usecs; + state = parse_seg_duration; + } else if (state != new_set && av_strstart(p, "descriptor=", &p)) { n = strcspn(p, ">") + 1; //followed by one comma, so plus 1 if (n < strlen(p)) { as->descriptor = av_strndup(p, n); @@ -888,7 +912,7 @@ static int parse_adaptation_sets(AVFormatContext *s) if (*p) p++; state = parse_descriptor; - } else if ((state == parse_id || state == parse_descriptor) && av_strstart(p, "streams=", &p)) { //descriptor is optional + } else if ((state != new_set) && av_strstart(p, "streams=", &p)) { //descriptor and duration are optional state = parsing_streams; } else if (state == parsing_streams) { AdaptationSet *as = &c->as[c->nb_as - 1]; @@ -1360,6 +1384,7 @@ static int dash_init(AVFormatContext *s) os->first_pts = AV_NOPTS_VALUE; os->max_pts = AV_NOPTS_VALUE; os->last_dts = AV_NOPTS_VALUE; + os->seg_duration = as->seg_duration ? as->seg_duration : c->seg_duration; os->segment_index = 1; if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) @@ -1581,7 +1606,7 @@ static int dash_flush(AVFormatContext *s, int final, int stream) c->streams[stream].first_pts, s->streams[stream]->time_base, AV_TIME_BASE_Q); - next_exp_index = (pts_diff / c->seg_duration) + 1; + next_exp_index = (pts_diff / c->streams[stream].seg_duration) + 1; } } @@ -1597,6 +1622,9 @@ static int dash_flush(AVFormatContext *s, int final, int stream) // Flush all audio streams as well, in sync with video keyframes, // but not the other video streams. if (stream >= 0 && i != stream) { + if (s->streams[stream]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO && + s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) + continue; if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO) continue; // Make sure we don't flush audio streams multiple times, when @@ -1739,22 +1767,22 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) if (!os->availability_time_offset && pkt->duration) { int64_t frame_duration = av_rescale_q(pkt->duration, st->time_base, AV_TIME_BASE_Q); - os->availability_time_offset = ((double) c->seg_duration - + os->availability_time_offset = ((double) os->seg_duration - frame_duration) / AV_TIME_BASE; } if (c->use_template && !c->use_timeline) { elapsed_duration = pkt->pts - os->first_pts; - seg_end_duration = (int64_t) os->segment_index * c->seg_duration; + seg_end_duration = (int64_t) os->segment_index * os->seg_duration; } else { elapsed_duration = pkt->pts - os->start_pts; - seg_end_duration = c->seg_duration; + seg_end_duration = os->seg_duration; } - if ((!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && - 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, seg_end_duration, AV_TIME_BASE_Q) >= 0) { + if (!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { int64_t prev_duration = c->last_duration; c->last_duration = av_rescale_q(pkt->pts - os->start_pts, @@ -1772,6 +1800,7 @@ static int dash_write_packet(AVFormatContext *s, AVPacket *pkt) "and use_template, or keep a stricter keyframe interval\n"); } } + } if ((ret = dash_flush(s, 0, pkt->stream_index)) < 0) return ret; -- 2.24.1 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".