> 在 2018年8月4日,上午2:17,Ronak <ronak2...@yahoo.com> 写道: > >>>>>>> I have read this patch some problem for this patch. >>>>>>> >>>>>>> 1. maybe there will have a problem when duration is not same when every >>>>>>> fragment, for example: >>>>>>> liuqideMacBook-Pro:xxx liuqi$ ./ffmpeg -v quiet -i >>>>>>> ~/Movies/Test/bbb_sunflower_1080p_30fps_normal.mp4 -c copy -f hls >>>>>>> -hls_list_size 0 output_test.m3u8 >>>>>>> liuqideMacBook-Pro:xxx liuqi$ head -n 10 output_test.m3u8 >>>>>>> #EXTM3U >>>>>>> #EXT-X-VERSION:3 >>>>>>> #EXT-X-TARGETDURATION:8 >>>>>>> #EXT-X-MEDIA-SEQUENCE:0 >>>>>>> #EXTINF:3.866667, >>>>>>> output_test0.ts >>>>>>> #EXTINF:7.300000, >>>>>>> output_test1.ts >>>>>>> #EXTINF:8.333333, >>>>>>> output_test2.ts >>>>>>> >>>>>>> the output_test0.ts’s duration is short than output_test1.ts, the >>>>>>> #EXT-X-TARGETDURATION need update to the longest duration. >>>>>>> this operation (check the longest duration) will happen at every >>>>>>> fragment write complete. >>>>>>> it will not update when move the update option to the hls_write_header, >>>>>>> >>>>>> >>>>>> This is a problem in the code that splits the mpegts files. I've filed a >>>>>> separate issue for this here: https://trac.ffmpeg.org/ticket/7341. >>>>>> Mpegts segmentation should be following the hls_time parameter (or the >>>>>> default length). >>>>> This is whatever hls_time, is decide by keyframe position, this is happen >>>>> when GOP size is not a permanent t position. >>>>> >> >>>>>> This is happening now with fMP4 assets, but not with mpegts. >>>>> Whatever fmp4 or mpegts, all of them need fix the problem of duration >>>>> refresh. >>>>> >>>>> for example: >>>>> >>>>> liuqideMacBook-Pro:xxx liuqi$ ./ffmpeg -ss -v quiet -i >>>>> ~/Movies/Test/bbb_sunflower_1080p_30fps_normal.mp4 -c copy -f hls >>>>> -hls_list_size 0 -hls_segment_type fmp4 -hls_time 3 output_test.m3u8 >>>>> liuqideMacBook-Pro:xxx liuqi$ head -n 10 output_test.m3u8 >>>>> #EXTM3U >>>>> #EXT-X-VERSION:7 >>>>> #EXT-X-TARGETDURATION:8 >>>>> #EXT-X-MEDIA-SEQUENCE:0 >>>>> #EXT-X-MAP:URI="init.mp4" >>>>> #EXTINF:3.866667, >>>>> output_test0.m4s >>>>> #EXTINF:7.300000, >>>>> output_test1.m4s >>>>> #EXTINF:8.333333, >>>>> liuqideMacBook-Pro:xxx liuqi$ >>>> >>>> This is after your patch: >>>> liuqideMacBook-Pro:xxx liuqi$ ./ffmpeg -ss 17 -v quiet -i >>>> ~/Movies/Test/bbb_sunflower_1080p_30fps_normal.mp4 -c copy -f hls >>>> -hls_list_size 0 -hls_segment_type fmp4 -hls_time 3 output_test.m3u8 >>>> liuqideMacBook-Pro:xxx liuqi$ head -n 10 output_test.m3u8 >>>> #EXTM3U >>>> #EXT-X-VERSION:7 >>>> #EXT-X-TARGETDURATION:3 >>>> #EXT-X-MEDIA-SEQUENCE:0 >>>> #EXT-X-MAP:URI="init.mp4" >>>> #EXTINF:3.866667, >>>> output_test0.m4s >>>> #EXTINF:7.300000, >>>> output_test1.m4s >>>> #EXTINF:8.333333, >>>> >>>> The RFC https://www.rfc-editor.org/rfc/rfc8216.txt describe: >>>> >>>> 4.3.3.1. EXT-X-TARGETDURATION >>>> >>>> The EXT-X-TARGETDURATION tag specifies the maximum Media Segment >>>> duration. The EXTINF duration of each Media Segment in the Playlist >>>> file, when rounded to the nearest integer, MUST be less than or equal >>>> to the target duration; longer segments can trigger playback stalls >>>> or other errors. It applies to the entire Playlist file. Its format >>>> is: >>>> >>>> #EXT-X-TARGETDURATION:<s> >>>> >>>> where s is a decimal-integer indicating the target duration in >>>> seconds. The EXT-X-TARGETDURATION tag is REQUIRED. >>>> >>>> your patch make the EXT-X-TARGETDURATION less than EXTINF:7.300000 >>>> EXTINF:8.333333 >>> >>> >>>>>>> 2. the version maybe will update when use hls_segment_type or >>>>>>> append_list etc. when the operation is support from different version >>>>>>> m3u8. >>>>>> >>>>>> I don't follow what you mean here. The version number is known up front, >>>>>> based on the options that were passed in. It should be illegal to switch >>>>>> between versions when trying to update an existing manifest. When can >>>>>> this legitimately happen? >>>>> there maybe have some player cannot support high version of m3u8, for >>>>> example old parser or player just support the VERSION 3, >>>>> this must think about all of the player or parser, because ffmpeg is not >>>>> used only by myself. >>>>> >>>>> Or what about get the #EXT-X-VERSION position, to update it? looks like >>>>> flvenc.c or movenc.c date shift >>>>> >>>>>> >>>>>>> 3. need update segments vs->segments when hls_list_size option is set. >>>>>>> >>>>>> >>>>>> What do you mean by this and where should I do it? >>>>> for example, hls_list_size is 4, the m3u8 list should refresh every time >>>>> when make a new fragment. >>>>> >>>>> first time: >>>>> 1.m4s >>>>> 2.m4s >>>>> 3.m4s >>>>> 4.m4s >>>>> >>>>> sencond time: >>>>> 2.m4s >>>>> 3.m4s >>>>> 4.m4s >>>>> 5.m4s >>>> >>>> after your patch: >>>> >>>> liuqideMacBook-Pro:xxx liuqi$ ./ffmpeg -v quiet -i >>>> ~/Movies/Test/bbb_sunflower_1080p_30fps_normal.mp4 -c copy -f hls >>>> -hls_list_size 4 -hls_segment_type fmp4 -hls_time 3 -t 50 output_test.m3u8 >>>> liuqideMacBook-Pro:xxx liuqi$ cat output_test.m3u8 >>>> #EXTM3U >>>> #EXT-X-VERSION:7 >>>> #EXT-X-TARGETDURATION:3 >>>> #EXT-X-MEDIA-SEQUENCE:0 >>>> #EXT-X-MAP:URI="init.mp4" >>>> #EXTINF:3.866667, >>>> output_test0.m4s >>>> #EXTINF:7.300000, >>>> output_test1.m4s >>>> #EXTINF:8.333333, >>>> output_test2.m4s >>>> #EXTINF:3.966667, >>>> output_test3.m4s >>>> #EXTINF:8.333333, >>>> output_test4.m4s >>>> #EXTINF:4.033333, >>>> output_test5.m4s >>>> #EXTINF:8.333333, >>>> output_test6.m4s >>>> #EXTINF:4.633333, >>>> output_test7.m4s >>>> liuqideMacBook-Pro:xxx liuqi$ >>>> liuqideMacBook-Pro:xxx liuqi$ >>>> >>>> the m3u8 list is incorrect, because users want control the m3u8 list >>>> length, because their disk do not have enough space to save the fragments. >>>> >>> >>> Ok I will fix this. > > > I'm attaching a new patch that resolves all of these issues, while still > resolving this bug for VOD playlists. > > Can you please review? > > > <0001-libavformat-hlsenc-Fix-HLS-Manifest-Generation-from-.patch>
From bbc4870c0d685f5c07e82042c3f2ef153d83f3d1 Mon Sep 17 00:00:00 2001 From: "Ronak Patel (Audible)" <ron...@audible.com> Date: Thu, 2 Aug 2018 09:25:12 -0400 Subject: [PATCH] libavformat/hlsenc: Fix HLS Manifest Generation from an N^2 algorithm to N. This fixes the creation of the hls manifest in hlsenc.c by writing the entire manifest at the end for VOD playlists. Live & Event Playlists are unaffected. This also fixes the behavior with HLS_TEMP_FILE to work correctly when -hlsflags temp_file is specified, instead of always relying on use_rename, which caused these problems. Files that would previously take over a week to fragment now take 1 minute on the same hardware. This was a 153 hour audio file (2.2GB of audio). Signed-off-by: Ronak Patel <ronak2...@yahoo.com> --- libavformat/dashenc.c | 2 +- libavformat/hlsenc.c | 54 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/libavformat/dashenc.c b/libavformat/dashenc.c -------you have modify dash index ae57fd5..ae22c08 100644 --- a/libavformat/dashenc.c +++ b/libavformat/dashenc.c @@ -483,7 +483,7 @@ static void output_segment_list(OutputStream *os, AVIOContext *out, AVFormatCont target_duration = lrint(duration); } - ff_hls_write_playlist_header(c->m3u8_out, 6, -1, target_duration, + ff_hls_write_playlist_header(c->m3u8_out, 7, -1, target_duration, start_number, PLAYLIST_TYPE_NONE); ff_hls_write_init_file(c->m3u8_out, os->initfile, c->single_file, diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index b5644f0..0eb0801 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -942,6 +942,7 @@ static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c, V if (c->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) { av_strlcpy(vs->current_segment_final_filename_fmt, oc->url, sizeof(vs->current_segment_final_filename_fmt)); + ——you add a empty line if (c->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) { char *filename = NULL; if (replace_int_data_in_filename(&filename, oc->url, 's', 0) < 1) { @@ -1166,9 +1167,10 @@ static int hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc) if (!final_filename) return AVERROR(ENOMEM); + ——you add a empty line final_filename[len-4] = '\0'; + ——you add a empty line ret = ff_rename(oc->url, final_filename, s); - oc->url[len-4] = '\0’; ——Why do you give the len - 4 = 0? av_freep(&final_filename); return ret; } @@ -1373,9 +1375,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) int ret = 0; char temp_filename[1024]; int64_t sequence = FFMAX(hls->start_sequence, vs->sequence - vs->nb_entries); - const char *proto = avio_find_protocol_name(s->url); - int use_rename = proto && !strcmp(proto, "file"); - static unsigned warned_non_file; + int use_temp_file = (s->flags & HLS_TEMP_FILE); ——What will have if use http put method? char *key_uri = NULL; char *iv_string = NULL; AVDictionary *options = NULL; @@ -1397,11 +1397,9 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs) hls->version = 7; } - if (!use_rename && !warned_non_file++) - av_log(s, AV_LOG_ERROR, "Cannot use rename on non file protocol, this may lead to races and temporary partial files\n"); - ——I have see this message long time, i have not remove this message because this is used to http method. Why do you remove it? set_http_options(s, &options, hls); - snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", vs->m3u8_name); + snprintf(temp_filename, sizeof(temp_filename), use_temp_file ? "%s.tmp" : "%s", vs->m3u8_name); + //av_log(s, AV_LOG_INFO, "We're going to write out to %s", temp_filename); ------this info message is unused? if ((ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options)) < 0) goto fail; @@ -1472,8 +1470,9 @@ fail: av_dict_free(&options); hlsenc_io_close(s, &hls->m3u8_out, temp_filename); hlsenc_io_close(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name); - if (ret >= 0 && use_rename) - ff_rename(temp_filename, vs->m3u8_name, s); + if (use_temp_file) { + ff_rename(temp_filename, vs->m3u8_name, s); + } if (ret >= 0 && hls->master_pl_name) if (create_master_playlist(s, vs) < 0) @@ -2253,11 +2252,14 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->url); } } + + // look to rename the asset name if ((hls->flags & HLS_TEMP_FILE) && oc->url[0]) { - if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) - if ((vs->avf->oformat->priv_class && vs->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4) - av_opt_set(vs->avf->priv_data, "mpegts_flags", "resend_headers", 0); - hls_rename_temp_file(s, oc); + if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) { + if ((vs->avf->oformat->priv_class && vs->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4) { + av_opt_set(vs->avf->priv_data, "mpegts_flags", "resend_headers", 0); + } + } ——Just reindent? } if (vs->fmp4_init_mode) { @@ -2286,6 +2288,17 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) return ret; } ff_format_io_close(s, &vs->out); + + // rename that segment from .tmp to the real one + if ((hls->flags & HLS_TEMP_FILE) && oc->url[0]) { + hls_rename_temp_file(s, oc); + av_free(old_filename); + old_filename = av_strdup(vs->avf->url); + + if (!old_filename) { + return AVERROR(ENOMEM); + } + } } } @@ -2334,14 +2347,16 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) return ret; } - if (!vs->fmp4_init_mode || byterange_mode) + // 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) { ------Whatever event VOD or Live Streaming, the EXT-X-TARGETDURATION need refresh when lrint(current fragment duration) is large than lrint(the before duration). if ((ret = hls_window(s, 0, vs)) < 0) { return ret; } + } } - vs->packets_written++; ret = ff_write_chained(oc, stream_index, pkt, s, 0); + vs->packets_written++; return ret; } @@ -2394,15 +2409,16 @@ failed: if (hls->segment_type != SEGMENT_TYPE_FMP4) ff_format_io_close(s, &oc->pb); - if ((hls->flags & HLS_TEMP_FILE) && oc->url[0]) { - hls_rename_temp_file(s, oc); + // rename that segment from .tmp to the real one + if ((hls->flags & HLS_TEMP_FILE) && oc->url[0] && !(hls->flags & HLS_SINGLE_FILE)) { + hls_rename_temp_file(s, oc); av_free(old_filename); old_filename = av_strdup(vs->avf->url); if (!old_filename) { return AVERROR(ENOMEM); } - } + } /* after av_write_trailer, then duration + 1 duration per packet */ hls_append_segment(s, hls, vs, vs->duration + vs->dpp, vs->start_pos, vs->size); -- 2.6.3 > > Thanks, > > Ronak > > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel