Message ID | 20170701120544.798-1-lq@chinaffmpeg.org |
---|---|
State | Accepted |
Commit | 274bd1670b9c85e24f37d08f9043dd82e0a28c67 |
Headers | show |
2017-07-01 20:05 GMT+08:00 Steven Liu <lq@chinaffmpeg.org>: > add the fmp4 format into hlsenc > because the fmp4 format add into hls from version 7. > the spec link is: > https://tools.ietf.org/html/draft-pantos-http-live-streaming-20 > and the describe on WWDC > https://developer.apple.com/videos/play/wwdc2017/515/ > > Signed-off-by: Steven Liu <lq@chinaffmpeg.org> > --- > doc/muxers.texi | 17 ++++++++ > libavformat/hlsenc.c | 107 +++++++++++++++++++++++++++++++++++++++++++-------- > 2 files changed, 108 insertions(+), 16 deletions(-) > > diff --git a/doc/muxers.texi b/doc/muxers.texi > index 0866142..94472ce 100644 > --- a/doc/muxers.texi > +++ b/doc/muxers.texi > @@ -614,6 +614,23 @@ in the playlist. > Hex-coded 16byte initialization vector for every segment instead > of the autogenerated ones. > > +@item hls_segment_type @var{flags} > +Possible values: > + > +@table @samp > +@item mpegts > +If this flag is set, the hls segment files will format to mpegts. > +the mpegts files is used in all hls versions. > + > +@item fmp4 > +If this flag is set, the hls segment files will format to fragment mp4 looks like dash. > +the fmp4 files is used in hls after version 7. > + > +@end table > + > +@item hls_fmp4_init_filename @var{filename} > +set filename to the fragment files header file, default filename is @file{init.mp4}. > + > @item hls_flags @var{flags} > Possible values: > > diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c > index 40143c8..a49b594 100644 > --- a/libavformat/hlsenc.c > +++ b/libavformat/hlsenc.c > @@ -88,6 +88,11 @@ typedef enum HLSFlags { > } HLSFlags; > > typedef enum { > + SEGMENT_TYPE_MPEGTS, > + SEGMENT_TYPE_FMP4, > +} SegmentType; > + > +typedef enum { > PLAYLIST_TYPE_NONE, > PLAYLIST_TYPE_EVENT, > PLAYLIST_TYPE_VOD, > @@ -115,6 +120,9 @@ typedef struct HLSContext { > uint32_t flags; // enum HLSFlags > uint32_t pl_type; // enum PlaylistType > char *segment_filename; > + char *fmp4_init_filename; > + int segment_type; > + int fmp4_init_mode; > > int use_localtime; ///< flag to expand filename with localtime > int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename > @@ -256,6 +264,16 @@ fail: > return -1; > } > > +static void write_styp(AVIOContext *pb) > +{ > + avio_wb32(pb, 24); > + ffio_wfourcc(pb, "styp"); > + ffio_wfourcc(pb, "msdh"); > + avio_wb32(pb, 0); /* minor */ > + ffio_wfourcc(pb, "msdh"); > + ffio_wfourcc(pb, "msix"); > +} > + > static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls) { > > HLSSegment *segment, *previous_segment = NULL; > @@ -508,6 +526,7 @@ static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) > > static int hls_mux_init(AVFormatContext *s) > { > + AVDictionary *options = NULL; > HLSContext *hls = s->priv_data; > AVFormatContext *oc; > AVFormatContext *vtt_oc = NULL; > @@ -553,7 +572,35 @@ static int hls_mux_init(AVFormatContext *s) > } > hls->start_pos = 0; > hls->new_start = 1; > + hls->fmp4_init_mode = 0; > > + if (hls->segment_type == SEGMENT_TYPE_FMP4) { > + hls->fmp4_init_mode = 1; > + if ((ret = s->io_open(s, &oc->pb, hls->fmp4_init_filename, AVIO_FLAG_WRITE, NULL)) < 0) { > + av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", hls->fmp4_init_filename); > + return ret; > + } > + > + if (hls->format_options_str) { > + ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); > + if (ret < 0) { > + av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", > + hls->format_options_str); > + return ret; > + } > + } > + > + av_dict_copy(&options, hls->format_options, 0); > + av_dict_set(&options, "fflags", "-autobsf", 0); > + av_dict_set(&options, "movflags", "frag_custom+dash+delay_moov", 0); > + ret = avformat_init_output(oc, &options); > + if (av_dict_count(options)) { > + av_log(s, AV_LOG_ERROR, "Some of the provided format options in '%s' are not recognized\n", hls->format_options_str); > + av_dict_free(&options); > + return AVERROR(EINVAL); > + } > + av_dict_free(&options); > + } > return 0; > } > > @@ -922,6 +969,9 @@ static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version > } > avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); > avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); > + if (hls->segment_type == SEGMENT_TYPE_FMP4) { > + avio_printf(out, "#EXT-X-MAP:URI=\"%s\"\n", hls->fmp4_init_filename); > + } > av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); > } > > @@ -961,6 +1011,10 @@ static int hls_window(AVFormatContext *s, int last) > sequence = 0; > } > > + if (hls->segment_type == SEGMENT_TYPE_FMP4) { > + 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"); > > @@ -1194,15 +1248,19 @@ static int hls_start(AVFormatContext *s) > } > av_dict_free(&options); > > - /* We only require one PAT/PMT per segment. */ > - if (oc->oformat->priv_class && oc->priv_data) { > - char period[21]; > + if (c->segment_type == SEGMENT_TYPE_FMP4) { > + write_styp(oc->pb); > + } else { > + /* We only require one PAT/PMT per segment. */ > + if (oc->oformat->priv_class && oc->priv_data) { > + char period[21]; > > - snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1); > + snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1); > > - av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); > - av_opt_set(oc->priv_data, "sdt_period", period, 0); > - av_opt_set(oc->priv_data, "pat_period", period, 0); > + av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); > + av_opt_set(oc->priv_data, "sdt_period", period, 0); > + av_opt_set(oc->priv_data, "pat_period", period, 0); > + } > } > > if (c->vtt_basename) { > @@ -1289,7 +1347,11 @@ static int hls_write_header(AVFormatContext *s) > "More than a single video stream present, " > "expect issues decoding it.\n"); > > - hls->oformat = av_guess_format("mpegts", NULL, NULL); > + if (hls->segment_type == SEGMENT_TYPE_FMP4) { > + hls->oformat = av_guess_format("mp4", NULL, NULL); > + } else { > + hls->oformat = av_guess_format("mpegts", NULL, NULL); > + } > > if (!hls->oformat) { > ret = AVERROR_MUXER_NOT_FOUND; > @@ -1390,8 +1452,10 @@ static int hls_write_header(AVFormatContext *s) > } > } > > - if ((ret = hls_start(s)) < 0) > - goto fail; > + if (hls->segment_type != SEGMENT_TYPE_FMP4) { > + if ((ret = hls_start(s)) < 0) > + goto fail; > + } > > av_dict_copy(&options, hls->format_options, 0); > ret = avformat_write_header(hls->avf, &options); > @@ -1448,7 +1512,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > AVStream *st = s->streams[pkt->stream_index]; > int64_t end_pts = hls->recording_time * hls->number; > int is_ref_pkt = 1; > - int ret, can_split = 1; > + int ret = 0, can_split = 1; > int stream_index = 0; > > if (hls->sequence - hls->nb_entries > hls->start_sequence && hls->init_time > 0) { > @@ -1495,7 +1559,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > } > > } > - if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base, > + if (hls->fmp4_init_mode || can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base, > end_pts, AV_TIME_BASE_Q) >= 0) { > int64_t new_start_pos; > char *old_filename = av_strdup(hls->avf->filename); > @@ -1505,7 +1569,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > return AVERROR(ENOMEM); > } > > - av_write_frame(oc, NULL); /* Flush any buffered data */ > + av_write_frame(hls->avf, NULL); /* Flush any buffered data */ > > new_start_pos = avio_tell(hls->avf->pb); > hls->size = new_start_pos - hls->start_pos; > @@ -1518,12 +1582,18 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > } > if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) { > if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) > - if (hls->avf->oformat->priv_class && hls->avf->priv_data) > + if ((hls->avf->oformat->priv_class && hls->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4) > av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0); > hls_rename_temp_file(s, oc); > } > > - ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size); > + if (hls->fmp4_init_mode) { > + hls->number--; > + } > + > + if (!hls->fmp4_init_mode) > + ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size); > + > hls->start_pos = new_start_pos; > if (ret < 0) { > av_free(old_filename); > @@ -1533,6 +1603,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) > hls->end_pts = pkt->pts; > hls->duration = 0; > > + hls->fmp4_init_mode = 0; > if (hls->flags & HLS_SINGLE_FILE) { > hls->number++; > } else if (hls->max_seg_size > 0) { > @@ -1640,6 +1711,10 @@ static const AVOption options[] = { > {"hls_enc_key_url", "url to access the key to decrypt the segments", OFFSET(key_url), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, > {"hls_enc_iv", "hex-coded 16 byte initialization vector", OFFSET(iv), AV_OPT_TYPE_STRING, .flags = E}, > {"hls_subtitle_path", "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, > + {"hls_segment_type", "set hls segment files type", OFFSET(segment_type), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "segment_type"}, > + {"fmp4", "make segment file to fragment mp4 files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX, E, "segment_type"}, > + {"mpegts", "make segment file to mpegts files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, UINT_MAX, E, "segment_type"}, > + {"hls_fmp4_init_filename", "set fragment mp4 file init filename", OFFSET(fmp4_init_filename), AV_OPT_TYPE_STRING, {.str = "init.mp4"}, 0, 0, E}, > {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"}, > {"single_file", "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, "flags"}, > {"temp_file", "write segment to temporary file and rename when complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_FILE }, 0, UINT_MAX, E, "flags"}, > @@ -1682,7 +1757,7 @@ AVOutputFormat ff_hls_muxer = { > .audio_codec = AV_CODEC_ID_AAC, > .video_codec = AV_CODEC_ID_H264, > .subtitle_codec = AV_CODEC_ID_WEBVTT, > - .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, > + .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, > .write_header = hls_write_header, > .write_packet = hls_write_packet, > .write_trailer = hls_write_trailer, > -- > 2.10.1.382.ga23ca1b.dirty > > > > _______________________________________________ > ffmpeg-devel mailing list > ffmpeg-devel@ffmpeg.org > http://ffmpeg.org/mailman/listinfo/ffmpeg-devel Pushed!
On 7/3/2017 1:22 PM, Steven Liu wrote:
> Pushed!
Seems this patch has unexpected behavior when 'hls_segment_type=fmp4'
and 'hls_flags=single_file'.
It should probably create a self indexed file (sidx boxes at the start)
and a byte range m3u8, no?
At the very least it should fail or warn the user.
Cheers,
- Derek
2017-07-06 8:02 GMT+08:00 Steven Liu <lingjiujianke@gmail.com>: > 2017-07-05 23:28 GMT+08:00 Derek Buitenhuis <derek.buitenhuis@gmail.com>: >> On 7/3/2017 1:22 PM, Steven Liu wrote: >>> Pushed! >> >> Seems this patch has unexpected behavior when 'hls_segment_type=fmp4' >> and 'hls_flags=single_file'. >> >> It should probably create a self indexed file (sidx boxes at the start) >> and a byte range m3u8, no? >> >> At the very least it should fail or warn the user. > agreed, i will add warn later, and try to add single file support ASAP. resend to maillist >> >> Cheers, >> - Derek >> > > > Thanks
diff --git a/doc/muxers.texi b/doc/muxers.texi index 0866142..94472ce 100644 --- a/doc/muxers.texi +++ b/doc/muxers.texi @@ -614,6 +614,23 @@ in the playlist. Hex-coded 16byte initialization vector for every segment instead of the autogenerated ones. +@item hls_segment_type @var{flags} +Possible values: + +@table @samp +@item mpegts +If this flag is set, the hls segment files will format to mpegts. +the mpegts files is used in all hls versions. + +@item fmp4 +If this flag is set, the hls segment files will format to fragment mp4 looks like dash. +the fmp4 files is used in hls after version 7. + +@end table + +@item hls_fmp4_init_filename @var{filename} +set filename to the fragment files header file, default filename is @file{init.mp4}. + @item hls_flags @var{flags} Possible values: diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c index 40143c8..a49b594 100644 --- a/libavformat/hlsenc.c +++ b/libavformat/hlsenc.c @@ -88,6 +88,11 @@ typedef enum HLSFlags { } HLSFlags; typedef enum { + SEGMENT_TYPE_MPEGTS, + SEGMENT_TYPE_FMP4, +} SegmentType; + +typedef enum { PLAYLIST_TYPE_NONE, PLAYLIST_TYPE_EVENT, PLAYLIST_TYPE_VOD, @@ -115,6 +120,9 @@ typedef struct HLSContext { uint32_t flags; // enum HLSFlags uint32_t pl_type; // enum PlaylistType char *segment_filename; + char *fmp4_init_filename; + int segment_type; + int fmp4_init_mode; int use_localtime; ///< flag to expand filename with localtime int use_localtime_mkdir;///< flag to mkdir dirname in timebased filename @@ -256,6 +264,16 @@ fail: return -1; } +static void write_styp(AVIOContext *pb) +{ + avio_wb32(pb, 24); + ffio_wfourcc(pb, "styp"); + ffio_wfourcc(pb, "msdh"); + avio_wb32(pb, 0); /* minor */ + ffio_wfourcc(pb, "msdh"); + ffio_wfourcc(pb, "msix"); +} + static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls) { HLSSegment *segment, *previous_segment = NULL; @@ -508,6 +526,7 @@ static int read_chomp_line(AVIOContext *s, char *buf, int maxlen) static int hls_mux_init(AVFormatContext *s) { + AVDictionary *options = NULL; HLSContext *hls = s->priv_data; AVFormatContext *oc; AVFormatContext *vtt_oc = NULL; @@ -553,7 +572,35 @@ static int hls_mux_init(AVFormatContext *s) } hls->start_pos = 0; hls->new_start = 1; + hls->fmp4_init_mode = 0; + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + hls->fmp4_init_mode = 1; + if ((ret = s->io_open(s, &oc->pb, hls->fmp4_init_filename, AVIO_FLAG_WRITE, NULL)) < 0) { + av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", hls->fmp4_init_filename); + return ret; + } + + if (hls->format_options_str) { + ret = av_dict_parse_string(&hls->format_options, hls->format_options_str, "=", ":", 0); + if (ret < 0) { + av_log(s, AV_LOG_ERROR, "Could not parse format options list '%s'\n", + hls->format_options_str); + return ret; + } + } + + av_dict_copy(&options, hls->format_options, 0); + av_dict_set(&options, "fflags", "-autobsf", 0); + av_dict_set(&options, "movflags", "frag_custom+dash+delay_moov", 0); + ret = avformat_init_output(oc, &options); + if (av_dict_count(options)) { + av_log(s, AV_LOG_ERROR, "Some of the provided format options in '%s' are not recognized\n", hls->format_options_str); + av_dict_free(&options); + return AVERROR(EINVAL); + } + av_dict_free(&options); + } return 0; } @@ -922,6 +969,9 @@ static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version } avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration); avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + avio_printf(out, "#EXT-X-MAP:URI=\"%s\"\n", hls->fmp4_init_filename); + } av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence); } @@ -961,6 +1011,10 @@ static int hls_window(AVFormatContext *s, int last) sequence = 0; } + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + 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"); @@ -1194,15 +1248,19 @@ static int hls_start(AVFormatContext *s) } av_dict_free(&options); - /* We only require one PAT/PMT per segment. */ - if (oc->oformat->priv_class && oc->priv_data) { - char period[21]; + if (c->segment_type == SEGMENT_TYPE_FMP4) { + write_styp(oc->pb); + } else { + /* We only require one PAT/PMT per segment. */ + if (oc->oformat->priv_class && oc->priv_data) { + char period[21]; - snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1); + snprintf(period, sizeof(period), "%d", (INT_MAX / 2) - 1); - av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); - av_opt_set(oc->priv_data, "sdt_period", period, 0); - av_opt_set(oc->priv_data, "pat_period", period, 0); + av_opt_set(oc->priv_data, "mpegts_flags", "resend_headers", 0); + av_opt_set(oc->priv_data, "sdt_period", period, 0); + av_opt_set(oc->priv_data, "pat_period", period, 0); + } } if (c->vtt_basename) { @@ -1289,7 +1347,11 @@ static int hls_write_header(AVFormatContext *s) "More than a single video stream present, " "expect issues decoding it.\n"); - hls->oformat = av_guess_format("mpegts", NULL, NULL); + if (hls->segment_type == SEGMENT_TYPE_FMP4) { + hls->oformat = av_guess_format("mp4", NULL, NULL); + } else { + hls->oformat = av_guess_format("mpegts", NULL, NULL); + } if (!hls->oformat) { ret = AVERROR_MUXER_NOT_FOUND; @@ -1390,8 +1452,10 @@ static int hls_write_header(AVFormatContext *s) } } - if ((ret = hls_start(s)) < 0) - goto fail; + if (hls->segment_type != SEGMENT_TYPE_FMP4) { + if ((ret = hls_start(s)) < 0) + goto fail; + } av_dict_copy(&options, hls->format_options, 0); ret = avformat_write_header(hls->avf, &options); @@ -1448,7 +1512,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) AVStream *st = s->streams[pkt->stream_index]; int64_t end_pts = hls->recording_time * hls->number; int is_ref_pkt = 1; - int ret, can_split = 1; + int ret = 0, can_split = 1; int stream_index = 0; if (hls->sequence - hls->nb_entries > hls->start_sequence && hls->init_time > 0) { @@ -1495,7 +1559,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } } - if (can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base, + if (hls->fmp4_init_mode || can_split && av_compare_ts(pkt->pts - hls->start_pts, st->time_base, end_pts, AV_TIME_BASE_Q) >= 0) { int64_t new_start_pos; char *old_filename = av_strdup(hls->avf->filename); @@ -1505,7 +1569,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) return AVERROR(ENOMEM); } - av_write_frame(oc, NULL); /* Flush any buffered data */ + av_write_frame(hls->avf, NULL); /* Flush any buffered data */ new_start_pos = avio_tell(hls->avf->pb); hls->size = new_start_pos - hls->start_pos; @@ -1518,12 +1582,18 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) } if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) { if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0)) - if (hls->avf->oformat->priv_class && hls->avf->priv_data) + if ((hls->avf->oformat->priv_class && hls->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4) av_opt_set(hls->avf->priv_data, "mpegts_flags", "resend_headers", 0); hls_rename_temp_file(s, oc); } - ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size); + if (hls->fmp4_init_mode) { + hls->number--; + } + + if (!hls->fmp4_init_mode) + ret = hls_append_segment(s, hls, hls->duration, hls->start_pos, hls->size); + hls->start_pos = new_start_pos; if (ret < 0) { av_free(old_filename); @@ -1533,6 +1603,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt) hls->end_pts = pkt->pts; hls->duration = 0; + hls->fmp4_init_mode = 0; if (hls->flags & HLS_SINGLE_FILE) { hls->number++; } else if (hls->max_seg_size > 0) { @@ -1640,6 +1711,10 @@ static const AVOption options[] = { {"hls_enc_key_url", "url to access the key to decrypt the segments", OFFSET(key_url), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, {"hls_enc_iv", "hex-coded 16 byte initialization vector", OFFSET(iv), AV_OPT_TYPE_STRING, .flags = E}, {"hls_subtitle_path", "set path of hls subtitles", OFFSET(subtitle_filename), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E}, + {"hls_segment_type", "set hls segment files type", OFFSET(segment_type), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "segment_type"}, + {"fmp4", "make segment file to fragment mp4 files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_FMP4 }, 0, UINT_MAX, E, "segment_type"}, + {"mpegts", "make segment file to mpegts files in m3u8", 0, AV_OPT_TYPE_CONST, {.i64 = SEGMENT_TYPE_MPEGTS }, 0, UINT_MAX, E, "segment_type"}, + {"hls_fmp4_init_filename", "set fragment mp4 file init filename", OFFSET(fmp4_init_filename), AV_OPT_TYPE_STRING, {.str = "init.mp4"}, 0, 0, E}, {"hls_flags", "set flags affecting HLS playlist and media file generation", OFFSET(flags), AV_OPT_TYPE_FLAGS, {.i64 = 0 }, 0, UINT_MAX, E, "flags"}, {"single_file", "generate a single media file indexed with byte ranges", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_SINGLE_FILE }, 0, UINT_MAX, E, "flags"}, {"temp_file", "write segment to temporary file and rename when complete", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_TEMP_FILE }, 0, UINT_MAX, E, "flags"}, @@ -1682,7 +1757,7 @@ AVOutputFormat ff_hls_muxer = { .audio_codec = AV_CODEC_ID_AAC, .video_codec = AV_CODEC_ID_H264, .subtitle_codec = AV_CODEC_ID_WEBVTT, - .flags = AVFMT_NOFILE | AVFMT_ALLOW_FLUSH, + .flags = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH, .write_header = hls_write_header, .write_packet = hls_write_packet, .write_trailer = hls_write_trailer,
add the fmp4 format into hlsenc because the fmp4 format add into hls from version 7. the spec link is: https://tools.ietf.org/html/draft-pantos-http-live-streaming-20 and the describe on WWDC https://developer.apple.com/videos/play/wwdc2017/515/ Signed-off-by: Steven Liu <lq@chinaffmpeg.org> --- doc/muxers.texi | 17 ++++++++ libavformat/hlsenc.c | 107 +++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 108 insertions(+), 16 deletions(-)