> On Aug 6, 2018, at 7:19 AM, Liu Steven <l...@chinaffmpeg.org> wrote: > > > >>> 在 2018年8月6日,下午7:12,Ronak Patel <ronak2121-at-yahoo....@ffmpeg.org> 写道: >>> >>> >>> On Aug 5, 2018, at 10:54 PM, Liu Steven <l...@chinaffmpeg.org> wrote: >>> >>> >>> >>>> 在 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 >> >> Sorry I will submit this separately. Will remove. >> >>> 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 >> Ok will remove >> >>> 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 >> Ok will remove >>> >>> final_filename[len-4] = '\0'; >>> + >>> >>> ——you add a empty line >> Ok will remove >>> ret = ff_rename(oc->url, final_filename, s); >>> - oc->url[len-4] = '\0’; >>> ——Why do you give the len - 4 = 0? >> This code was already there. All I’m doing is removing this part that was >> causing a problem when you use the HLS_TEMP option. >> >>> 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? >> >> I’ll test it. >> >>> 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? >> >> I can keep it for http put and HLS_TEMP if that’s what you want. >> >>> >>> 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? >> >> Ok will remove. >> >>> if ((ret = hlsenc_io_open(s, has->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? >> >> Yes I put the code properly under braces. But if you don’t like it, I can >> remove. >> >>> } >>> >>> 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). >> >> You do not need to do this for VOD. This is the main reason why ffmpeg takes >> over 7 days to fragment a 153 hour VOD audio file. Other tools can do it in >> less than 5 minutes. Why would you write the same VOD playlist over 155000 >> times on disk when the input is never changing. >> >> VOD does not ever change once it is written so it makes sense to wait until >> the very end to write out the entire manifest. There’s no refreshing >> required. Only live or event playlists need any sort of refresh. Ffmpeg is >> written predominantly for the event or live use case I’ve noticed, which has >> forced you to make poor decisions for VOD. >> >> If I could I would rewrite all of this logic to clearly separate VOD from >> event and live playlist generation logic to make the code clearer. This is >> hard to understand code in general. > > Maybe you want record the fragments to a VOD Service, or time shift for the > history stream. Do i guess right or wrong? >>
If that’s what you want, you would wait until the very end anyway. Why would you upload a partial manifest file? For VOD, you don’t do anything until you get the whole manifest. If you wanted a partial one, that’s an event, and you can pass that option to get the refreshing behavior. Even with event, this algorithm of writing out the entire manifest just to add a new segment is very wasteful, slow and unnecessary. This is an O(N!) algorithm for something you can solve in O(N) time. You can just append and adjust the EXT-X-TARGETDURATION for your video content. >>> >>> 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 >> >> _______________________________________________ >> ffmpeg-devel mailing list >> ffmpeg-devel@ffmpeg.org >> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel > > > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org http://ffmpeg.org/mailman/listinfo/ffmpeg-devel