Message ID | 20190328070800.8C3ADD8001@mail.noc.grnet.gr |
---|---|
State | New |
Headers | show |
On 3/27/19 2:38 PM, Zenon Mousmoulas wrote: > Add experimental support for LHLS (low-latency HLS), following what > was introduced to lavf/dashenc in f22fcd4483f. > --- > doc/muxers.texi | 6 +++++ > libavformat/dashenc.c | 2 +- > libavformat/hlsenc.c | 63 ++++++++++++++++++++++++++++++++++++++--------- > libavformat/hlsplaylist.c | 28 +++++++++++++-------- > libavformat/hlsplaylist.h | 3 ++- > 5 files changed, 79 insertions(+), 23 deletions(-) > > diff --git a/doc/muxers.texi b/doc/muxers.texi > index aac7d94edf..4ebaf559c5 100644 > --- a/doc/muxers.texi > +++ b/doc/muxers.texi > @@ -1067,6 +1067,12 @@ Set timeout for socket I/O operations. Applicable only for HTTP output. > @item -ignore_io_errors > Ignore IO errors during open, write and delete. Useful for long-duration runs with network output. > > +@item -lhls @var{lhls} > +Enable low-latency HLS (LHLS). Adds #EXT-X-PREFETCH tag with current segment's URI. > +Apple does not have an official spec for LHLS. Meanwhile hls.js player folks are > +trying to standardize an open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md > +This is an experimental feature. > + > @end table > > @anchor{ico} > diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c > index 1b74bce060..04950222d3 100644 > --- a/libavformat/dashenc.c > +++ b/libavformat/dashenc.c > @@ -483,7 +483,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, > (double) seg->duration / timescale, 0, > seg->range_length, seg->start_pos, NULL, > c->single_file ? os->initfile : seg->file, > - &prog_date_time); > + &prog_date_time, 0); > if (ret < 0) { > av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error > "); > } > diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c > index 5f9a200c6e..4a427fc514 100644 > --- a/libavformat/hlsenc.c > +++ b/libavformat/hlsenc.c > @@ -158,6 +158,7 @@ typedef struct VariantStream { > char *agroup; /* audio group name */ > char *ccgroup; /* closed caption group name */ > char *baseurl; > + int m3u8_got_prefetch; > } VariantStream; > > typedef struct ClosedCaptionsStream { > @@ -230,6 +231,7 @@ typedef struct HLSContext { > AVIOContext *sub_m3u8_out; > int64_t timeout; > int ignore_io_errors; > + int lhls; > int has_default_key; /* has DEFAULT field of var_stream_map */ > int has_video_m3u8; /* has video stream m3u8 list */ > } HLSContext; > @@ -1360,7 +1362,7 @@ fail: > return ret; > } > > -static int hls_window(AVFormatContext *s, int last, VariantStream *vs) > +static int hls_window(AVFormatContext *s, int last, VariantStream *vs, char *prefetch_url) > { > HLSContext *hls = s->priv_data; > HLSSegment *en; > @@ -1439,12 +1441,23 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) > ret = ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode, > en->duration, hls->flags & HLS_ROUND_DURATIONS, > en->size, en->pos, vs->baseurl, > - en->filename, prog_date_time_p); > + en->filename, prog_date_time_p, 0); > if (ret < 0) { > av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error > "); > } > } > > + if (prefetch_url) > + ret = en ? > + ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode, > + en->duration, hls->flags & HLS_ROUND_DURATIONS, > + en->size, en->pos, vs->baseurl, > + prefetch_url, prog_date_time_p, 1) : > + ff_hls_write_file_entry(hls->m3u8_out, 0, byterange_mode, > + 0, hls->flags & HLS_ROUND_DURATIONS, > + 0, 0, vs->baseurl, > + prefetch_url, prog_date_time_p, 1); > + > if (last && (hls->flags & HLS_OMIT_ENDLIST)==0) > ff_hls_write_end_list(hls->m3u8_out); > > @@ -1459,7 +1472,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) > for (en = vs->segments; en; en = en->next) { > ret = ff_hls_write_file_entry(hls->sub_m3u8_out, 0, byterange_mode, > en->duration, 0, en->size, en->pos, > - vs->baseurl, en->sub_filename, NULL); > + vs->baseurl, en->sub_filename, NULL, 0); > if (ret < 0) { > av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error > "); > } > @@ -2158,6 +2171,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > int64_t end_pts = 0; > int is_ref_pkt = 1; > int ret = 0, can_split = 1, i, j; > + int byterange_mode; > int stream_index = 0; > int range_length = 0; > const char *proto = NULL; > @@ -2232,10 +2246,16 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > > } > > + byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); > + > + if (oc->url[0]) { > + proto = avio_find_protocol_name(oc->url); > + use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE); > + } > + > if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_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); > > av_write_frame(vs->avf, NULL); /* Flush any buffered data */ > > @@ -2272,11 +2292,6 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > } > } > > - if (oc->url[0]) { > - proto = avio_find_protocol_name(oc->url); > - use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE); > - } > - > // look to rename the asset name > if (use_temp_file) { > if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) > @@ -2358,9 +2373,18 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > > // if we're building a VOD playlist, skip writing the manifest multiple times, and just wait until the end > if (hls->pl_type != PLAYLIST_TYPE_VOD) { > - if ((ret = hls_window(s, 0, vs)) < 0) { > + if ((ret = hls_window(s, 0, vs, NULL)) < 0) { > + return ret; > + } > + vs->m3u8_got_prefetch = 0; > + } > + } else if (hls->lhls && !use_temp_file && !byterange_mode && > + !vs->m3u8_got_prefetch) { > + if (hls->pl_type != PLAYLIST_TYPE_VOD) { > + if ((ret = hls_window(s, 0, vs, (char *)av_basename(oc->url))) < 0) { > return ret; > } > + vs->m3u8_got_prefetch = 1; > } > } > > @@ -2504,7 +2528,7 @@ failed: > avformat_free_context(oc); > > vs->avf = NULL; > - hls_window(s, 1, vs); > + hls_window(s, 1, vs, NULL); > av_free(old_filename); > } > > @@ -2588,6 +2612,22 @@ static int hls_init(AVFormatContext *s) > } > } > > + if (hls->lhls && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { > + av_log(s, AV_LOG_ERROR, > + "LHLS is experimental, please set -strict experimental in order to enable it. > "); > + return AVERROR_EXPERIMENTAL; > + } > + if (hls->lhls && hls->flags & HLS_TEMP_FILE) { > + av_log(s, AV_LOG_WARNING, > + "'lhls' will be disabled because 'temp_file' flag is enabled' > "); > + hls->lhls = 0; > + } > + if (hls->lhls && hls->flags & HLS_SINGLE_FILE) { > + av_log(s, AV_LOG_WARNING, > + "'lhls' will be disabled because 'single_file' flag is enabled' > "); > + hls->lhls = 0; > + } > + > if (hls->segment_type == SEGMENT_TYPE_FMP4) { > pattern = "%d.m4s"; > } > @@ -2942,6 +2982,7 @@ static const AVOption options[] = { > {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, > {"timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E }, > {"ignore_io_errors", "Ignore IO errors for stable long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, > + { "lhls", "Enable low-latency HLS (experimental). Adds #EXT-X-PREFETCH tag with current segment's URI", OFFSET(lhls), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, > { NULL }, > }; > > diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c > index 0537049a97..8db0c93c12 100644 > --- a/libavformat/hlsplaylist.c > +++ b/libavformat/hlsplaylist.c > @@ -108,21 +108,27 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, > double duration, int round_duration, > int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set > char *baseurl, //Ignored if NULL > - char *filename, double *prog_date_time) { > + char *filename, double *prog_date_time, > + int prefetch) { Can we create a separate function like below for writing prefetch segments? int ff_hls_write_prefetch(AVIOContext *out, int insert_discont, char *baseurl, //Ignored if NULL char *filename) Because the current function(ff_hls_write_file_entry) is already pretty complex with multiple arguments, most of which are not applicable for PREFETCH. Anyways, the condition if(prefetch) is anyways there on the calling side(hlsenc.c and dashenc.c). A separate function like above called by both hlsenc.c and dashenc.c will be much cleaner. > if (!out || !filename) > return AVERROR(EINVAL); > > if (insert_discont) { > - avio_printf(out, "#EXT-X-DISCONTINUITY > "); > + if (prefetch) > + avio_printf(out, "#EXT-X-PREFETCH-DISCONTINUITY > "); > + else > + avio_printf(out, "#EXT-X-DISCONTINUITY > "); > } > - if (round_duration) > - avio_printf(out, "#EXTINF:%ld, > ", lrint(duration)); > - else > - avio_printf(out, "#EXTINF:%f, > ", duration); > - if (byterange_mode) > - avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64" > ", size, pos); > - > - if (prog_date_time) { > + if (!prefetch) { > + if (round_duration) > + avio_printf(out, "#EXTINF:%ld, > ", lrint(duration)); > + else > + avio_printf(out, "#EXTINF:%f, > ", duration); > + if (byterange_mode) > + avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64" > ", size, pos); > + } > + > + if (!prefetch && prog_date_time) { > time_t tt, wrongsecs; > int milli; > struct tm *tm, tmpbuf; > @@ -149,6 +155,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, > avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s > ", buf0, milli, buf1); > *prog_date_time += duration; > } > + if (prefetch) > + avio_printf(out, "#EXT-X-PREFETCH:"); > if (baseurl) > avio_printf(out, "%s", baseurl); > avio_printf(out, "%s > ", filename); > diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h > index 54c93a3963..af35162e08 100644 > --- a/libavformat/hlsplaylist.h > +++ b/libavformat/hlsplaylist.h > @@ -52,7 +52,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, > double duration, int round_duration, > int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set > char *baseurl, //Ignored if NULL > - char *filename, double *prog_date_time); > + char *filename, double *prog_date_time, > + int prefetch); > void ff_hls_write_end_list (AVIOContext *out); > > #endif /* AVFORMAT_HLSPLAYLIST_H_ */
On Wed, Mar 27, 2019 at 11:08:02AM +0200, Zenon Mousmoulas wrote: > Add experimental support for LHLS (low-latency HLS), following what > was introduced to lavf/dashenc in f22fcd4483f. > --- > doc/muxers.texi | 6 +++++ > libavformat/dashenc.c | 2 +- > libavformat/hlsenc.c | 63 ++++++++++++++++++++++++++++++++++++++--------- > libavformat/hlsplaylist.c | 28 +++++++++++++-------- > libavformat/hlsplaylist.h | 3 ++- > 5 files changed, 79 insertions(+), 23 deletions(-) > > diff --git a/doc/muxers.texi b/doc/muxers.texi > index aac7d94edf..4ebaf559c5 100644 > --- a/doc/muxers.texi > +++ b/doc/muxers.texi > @@ -1067,6 +1067,12 @@ Set timeout for socket I/O operations. Applicable only for HTTP output. > @item -ignore_io_errors > Ignore IO errors during open, write and delete. Useful for long-duration runs with network output. > > +@item -lhls @var{lhls} > +Enable low-latency HLS (LHLS). Adds #EXT-X-PREFETCH tag with current segment's URI. > +Apple does not have an official spec for LHLS. Meanwhile hls.js player folks are > +trying to standardize an open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md > +This is an experimental feature. > + > @end table > > @anchor{ico} > diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c > index 1b74bce060..04950222d3 100644 > --- a/libavformat/dashenc.c > +++ b/libavformat/dashenc.c > @@ -483,7 +483,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, > (double) seg->duration / timescale, 0, > seg->range_length, seg->start_pos, NULL, > c->single_file ? os->initfile : seg->file, > - &prog_date_time); > + &prog_date_time, 0); > if (ret < 0) { > av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error > "); patch corrupted by newlines [...]
diff --git a/doc/muxers.texi b/doc/muxers.texi index aac7d94edf..4ebaf559c5 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -1067,6 +1067,12 @@ Set timeout for socket I/O operations. Applicable only for HTTP output. @item -ignore_io_errors Ignore IO errors during open, write and delete. Useful for long-duration runs with network output. +@item -lhls @var{lhls} +Enable low-latency HLS (LHLS). Adds #EXT-X-PREFETCH tag with current segment's URI. +Apple does not have an official spec for LHLS. Meanwhile hls.js player folks are +trying to standardize an open LHLS spec. The draft spec is available in https://github.com/video-dev/hlsjs-rfcs/blob/lhls-spec/proposals/0001-lhls.md +This is an experimental feature. + @end table @anchor{ico} diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c index 1b74bce060..04950222d3 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -483,7 +483,7 @@ static void write_hls_media_playlist(OutputStream *os, AVFormatContext *s, (double) seg->duration / timescale, 0, seg->range_length, seg->start_pos, NULL, c->single_file ? os->initfile : seg->file, - &prog_date_time); + &prog_date_time, 0); if (ret < 0) { av_log(os->ctx, AV_LOG_WARNING, "ff_hls_write_file_entry get error "); } diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 5f9a200c6e..4a427fc514 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -158,6 +158,7 @@ typedef struct VariantStream { char *agroup; /* audio group name */ char *ccgroup; /* closed caption group name */ char *baseurl; + int m3u8_got_prefetch; } VariantStream; typedef struct ClosedCaptionsStream { @@ -230,6 +231,7 @@ typedef struct HLSContext { AVIOContext *sub_m3u8_out; int64_t timeout; int ignore_io_errors; + int lhls; int has_default_key; /* has DEFAULT field of var_stream_map */ int has_video_m3u8; /* has video stream m3u8 list */ } HLSContext; @@ -1360,7 +1362,7 @@ fail: return ret; } -static int hls_window(AVFormatContext *s, int last, VariantStream *vs) +static int hls_window(AVFormatContext *s, int last, VariantStream *vs, char *prefetch_url) { HLSContext *hls = s->priv_data; HLSSegment *en; @@ -1439,12 +1441,23 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) ret = ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode, en->duration, hls->flags & HLS_ROUND_DURATIONS, en->size, en->pos, vs->baseurl, - en->filename, prog_date_time_p); + en->filename, prog_date_time_p, 0); if (ret < 0) { av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error "); } } + if (prefetch_url) + ret = en ? + ff_hls_write_file_entry(hls->m3u8_out, en->discont, byterange_mode, + en->duration, hls->flags & HLS_ROUND_DURATIONS, + en->size, en->pos, vs->baseurl, + prefetch_url, prog_date_time_p, 1) : + ff_hls_write_file_entry(hls->m3u8_out, 0, byterange_mode, + 0, hls->flags & HLS_ROUND_DURATIONS, + 0, 0, vs->baseurl, + prefetch_url, prog_date_time_p, 1); + if (last && (hls->flags & HLS_OMIT_ENDLIST)==0) ff_hls_write_end_list(hls->m3u8_out); @@ -1459,7 +1472,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) for (en = vs->segments; en; en = en->next) { ret = ff_hls_write_file_entry(hls->sub_m3u8_out, 0, byterange_mode, en->duration, 0, en->size, en->pos, - vs->baseurl, en->sub_filename, NULL); + vs->baseurl, en->sub_filename, NULL, 0); if (ret < 0) { av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error "); } @@ -2158,6 +2171,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) int64_t end_pts = 0; int is_ref_pkt = 1; int ret = 0, can_split = 1, i, j; + int byterange_mode; int stream_index = 0; int range_length = 0; const char *proto = NULL; @@ -2232,10 +2246,16 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } + byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0); + + if (oc->url[0]) { + proto = avio_find_protocol_name(oc->url); + use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE); + } + if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_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); av_write_frame(vs->avf, NULL); /* Flush any buffered data */ @@ -2272,11 +2292,6 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } } - if (oc->url[0]) { - proto = avio_find_protocol_name(oc->url); - use_temp_file = proto && !strcmp(proto, "file") && (hls->flags & HLS_TEMP_FILE); - } - // look to rename the asset name if (use_temp_file) { if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) @@ -2358,9 +2373,18 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) // if we're building a VOD playlist, skip writing the manifest multiple times, and just wait until the end if (hls->pl_type != PLAYLIST_TYPE_VOD) { - if ((ret = hls_window(s, 0, vs)) < 0) { + if ((ret = hls_window(s, 0, vs, NULL)) < 0) { + return ret; + } + vs->m3u8_got_prefetch = 0; + } + } else if (hls->lhls && !use_temp_file && !byterange_mode && + !vs->m3u8_got_prefetch) { + if (hls->pl_type != PLAYLIST_TYPE_VOD) { + if ((ret = hls_window(s, 0, vs, (char *)av_basename(oc->url))) < 0) { return ret; } + vs->m3u8_got_prefetch = 1; } } @@ -2504,7 +2528,7 @@ failed: avformat_free_context(oc); vs->avf = NULL; - hls_window(s, 1, vs); + hls_window(s, 1, vs, NULL); av_free(old_filename); } @@ -2588,6 +2612,22 @@ static int hls_init(AVFormatContext *s) } } + if (hls->lhls && s->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL) { + av_log(s, AV_LOG_ERROR, + "LHLS is experimental, please set -strict experimental in order to enable it. "); + return AVERROR_EXPERIMENTAL; + } + if (hls->lhls && hls->flags & HLS_TEMP_FILE) { + av_log(s, AV_LOG_WARNING, + "'lhls' will be disabled because 'temp_file' flag is enabled' "); + hls->lhls = 0; + } + if (hls->lhls && hls->flags & HLS_SINGLE_FILE) { + av_log(s, AV_LOG_WARNING, + "'lhls' will be disabled because 'single_file' flag is enabled' "); + hls->lhls = 0; + } + if (hls->segment_type == SEGMENT_TYPE_FMP4) { pattern = "%d.m4s"; } @@ -2942,6 +2982,7 @@ static const AVOption options[] = { {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E }, {"timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E }, {"ignore_io_errors", "Ignore IO errors for stable long-duration runs with network output", OFFSET(ignore_io_errors), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, + { "lhls", "Enable low-latency HLS (experimental). Adds #EXT-X-PREFETCH tag with current segment's URI", OFFSET(lhls), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E }, { NULL }, }; diff --git a/libavformat/hlsplaylist.c b/libavformat/hlsplaylist.c index 0537049a97..8db0c93c12 100644 --- a/libavformat/hlsplaylist.c +++ b/libavformat/hlsplaylist.c @@ -108,21 +108,27 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, double duration, int round_duration, int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set char *baseurl, //Ignored if NULL - char *filename, double *prog_date_time) { + char *filename, double *prog_date_time, + int prefetch) { if (!out || !filename) return AVERROR(EINVAL); if (insert_discont) { - avio_printf(out, "#EXT-X-DISCONTINUITY "); + if (prefetch) + avio_printf(out, "#EXT-X-PREFETCH-DISCONTINUITY "); + else + avio_printf(out, "#EXT-X-DISCONTINUITY "); } - if (round_duration) - avio_printf(out, "#EXTINF:%ld, ", lrint(duration)); - else - avio_printf(out, "#EXTINF:%f, ", duration); - if (byterange_mode) - avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64" ", size, pos); - - if (prog_date_time) { + if (!prefetch) { + if (round_duration) + avio_printf(out, "#EXTINF:%ld, ", lrint(duration)); + else + avio_printf(out, "#EXTINF:%f, ", duration); + if (byterange_mode) + avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64" ", size, pos); + } + + if (!prefetch && prog_date_time) { time_t tt, wrongsecs; int milli; struct tm *tm, tmpbuf; @@ -149,6 +155,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s ", buf0, milli, buf1); *prog_date_time += duration; } + if (prefetch) + avio_printf(out, "#EXT-X-PREFETCH:"); if (baseurl) avio_printf(out, "%s", baseurl); avio_printf(out, "%s ", filename); diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h index 54c93a3963..af35162e08 100644 --- a/libavformat/hlsplaylist.h +++ b/libavformat/hlsplaylist.h @@ -52,7 +52,8 @@ int ff_hls_write_file_entry(AVIOContext *out, int insert_discont, double duration, int round_duration, int64_t size, int64_t pos, //Used only if HLS_SINGLE_FILE flag is set char *baseurl, //Ignored if NULL - char *filename, double *prog_date_time); + char *filename, double *prog_date_time, + int prefetch); void ff_hls_write_end_list (AVIOContext *out); #endif /* AVFORMAT_HLSPLAYLIST_H_ */