The current implementation of the hls_time might produce segments with shorter duration in some cases which is not as expected. With the hls_min_time option the minimal segment duration will be guaranteed by splitting by the next keyframe after the desired duration of the segment has been reached.
Instead of changing the current behaviour of hls_time which might break existing workflows, the new option hls_min_time will override hls_time and guarantee the minimal segment duration. hls_time is supposed to define the minimal length of a segment, however this is not respected in all cases when a stream has variable GOP sizes. Imagine a stream starts with a key frame every 10 seconds for e.g. 30 seconds. After that, key frames will come every second. This will result in segments that are first 10 seconds (as expected), then 1 second for some time (not as expected) and later 2 seconds (as expected). How to reproduce: ffmpeg -t 30 -f lavfi -i testsrc2 -codec:v libx264 -g 250 part1.mp4 ffmpeg -t 30 -f lavfi -i testsrc2 -codec:v libx264 -g 25 part2.mp4 echo "file part1.mp4\nfile part2.mp4" > list.txt ffmpeg -f concat -i list.txt -codec copy \ -f hls -hls_time 2 -hls_list_size 0 parts.m3u8 cat parts.m3u8 v2 changes: Adjusting wording in commit message as suggested by Steven Liu. Signed-off-by: Ingo Oppermann <i...@datarhei.com> --- doc/muxers.texi | 7 +++++++ libavformat/hlsenc.c | 27 ++++++++++++++++++++------- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/doc/muxers.texi b/doc/muxers.texi index 30c95c3d34..082647bd9d 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1929,6 +1929,13 @@ Set the target segment length. Default value is 2. see @ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils}. Segment will be cut on the next key frame after this time has passed. +@item hls_min_time @var{duration} +Set the minimum target segment length. Default value is 0. + +@var{duration} must be a time duration specification, +see @ref{time duration syntax,,the Time duration section in the ffmpeg-utils(1) manual,ffmpeg-utils}. +Segment will be at least this long and will be cut at the following key frame. If set, it will override @option{hls_time}. + @item hls_list_size @var{size} Set the maximum number of playlist entries. If set to 0 the list file will contain all the segments. Default value is 5. diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index a93d35ab75..082229467f 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -204,6 +204,7 @@ typedef struct HLSContext { uint32_t start_sequence_source_type; // enum StartSequenceSourceType int64_t time; // Set by a private option. + int64_t min_time; // Set by a private option. int64_t init_time; // Set by a private option. int max_nb_segments; // Set by a private option. int hls_delete_threshold; // Set by a private option. @@ -2506,7 +2507,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) HLSContext *hls = s->priv_data; AVFormatContext *oc = NULL; AVStream *st = s->streams[pkt->stream_index]; - int64_t end_pts = 0; + int64_t end_pts = 0, current_pts; int is_ref_pkt = 1; int ret = 0, can_split = 1, i, j; int stream_index = 0; @@ -2547,11 +2548,16 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) end_pts = hls->recording_time * vs->number; if (vs->sequence - vs->nb_entries > hls->start_sequence && hls->init_time > 0) { - /* reset end_pts, hls->recording_time at end of the init hls list */ - int64_t init_list_dur = hls->init_time * vs->nb_entries; - int64_t after_init_list_dur = (vs->sequence - hls->start_sequence - vs->nb_entries) * hls->time; - hls->recording_time = hls->time; - end_pts = init_list_dur + after_init_list_dur ; + if (hls->min_time > 0) { + hls->recording_time = hls->min_time; + hls->init_time = 0; + } else { + /* reset end_pts, hls->recording_time at end of the init hls list */ + int64_t init_list_dur = hls->init_time * vs->nb_entries; + int64_t after_init_list_dur = (vs->sequence - hls->start_sequence - vs->nb_entries) * hls->time; + hls->recording_time = hls->time; + end_pts = init_list_dur + after_init_list_dur; + } } if (vs->start_pts == AV_NOPTS_VALUE) { @@ -2591,8 +2597,14 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } } + current_pts = pkt->pts - vs->start_pts; + if (hls->min_time > 0 && hls->init_time == 0) { + current_pts = pkt->pts - vs->end_pts; + end_pts = hls->min_time; + } + can_split = can_split && (pkt->pts - vs->end_pts > 0); - if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_pts, st->time_base, + if (vs->packets_written && can_split && av_compare_ts(current_pts, st->time_base, end_pts, AV_TIME_BASE_Q) >= 0) { int64_t new_start_pos; int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); @@ -3214,6 +3226,7 @@ static int hls_init(AVFormatContext *s) static const AVOption options[] = { {"start_number", "set first number in the sequence", OFFSET(start_sequence),AV_OPT_TYPE_INT64, {.i64 = 0}, 0, INT64_MAX, E}, {"hls_time", "set segment length", OFFSET(time), AV_OPT_TYPE_DURATION, {.i64 = 2000000}, 0, INT64_MAX, E}, + {"hls_min_time", "set minimum segment length", OFFSET(min_time), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, E}, {"hls_init_time", "set segment length at init list", OFFSET(init_time), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, INT64_MAX, E}, {"hls_list_size", "set maximum number of playlist entries", OFFSET(max_nb_segments), AV_OPT_TYPE_INT, {.i64 = 5}, 0, INT_MAX, E}, {"hls_delete_threshold", "set number of unreferenced segments to keep before deleting", OFFSET(hls_delete_threshold), AV_OPT_TYPE_INT, {.i64 = 1}, 1, INT_MAX, E}, base-commit: a4c1a5b08409cdfeb8e7f92c7fd443c8ff42e00d -- 2.39.5 (Apple Git-154) _______________________________________________ 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".