diff mbox

[FFmpeg-devel,v3,1/2] avformat/hlsenc: Modularized playlist creation to allow reuse

Message ID 1511495757-20743-1-git-send-email-kjeyapal@akamai.com
State Superseded
Headers show

Commit Message

Jeyapal, Karthick Nov. 24, 2017, 3:55 a.m. UTC
---
 libavformat/hlsenc.c | 238 +++++++++++++++++++++++++++------------------------
 libavformat/hlsenc.h |  68 +++++++++++++++
 2 files changed, 194 insertions(+), 112 deletions(-)
 create mode 100644 libavformat/hlsenc.h

Comments

Liu Steven Nov. 24, 2017, 4:13 a.m. UTC | #1
> 在 2017年11月24日,11:55,Karthick J <kjeyapal@akamai.com> 写道:
> 
> ---
> libavformat/hlsenc.c | 238 +++++++++++++++++++++++++++------------------------
> libavformat/hlsenc.h |  68 +++++++++++++++
> 2 files changed, 194 insertions(+), 112 deletions(-)
> create mode 100644 libavformat/hlsenc.h
> 
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index 30ccf73..5c4f459 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -45,6 +45,7 @@
> 
> #include "avformat.h"
> #include "avio_internal.h"
> +#include "hlsenc.h"
> #include "internal.h"
> #include "os_support.h"
> 
> @@ -73,36 +74,11 @@ typedef struct HLSSegment {
>     struct HLSSegment *next;
> } HLSSegment;
> 
> -typedef enum HLSFlags {
> -    // Generate a single media file and use byte ranges in the playlist.
> -    HLS_SINGLE_FILE = (1 << 0),
> -    HLS_DELETE_SEGMENTS = (1 << 1),
> -    HLS_ROUND_DURATIONS = (1 << 2),
> -    HLS_DISCONT_START = (1 << 3),
> -    HLS_OMIT_ENDLIST = (1 << 4),
> -    HLS_SPLIT_BY_TIME = (1 << 5),
> -    HLS_APPEND_LIST = (1 << 6),
> -    HLS_PROGRAM_DATE_TIME = (1 << 7),
> -    HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime  e.g.: %%03d
> -    HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime  e.g.: %%09t
> -    HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime  e.g.: %%014s
> -    HLS_TEMP_FILE = (1 << 11),
> -    HLS_PERIODIC_REKEY = (1 << 12),
> -    HLS_INDEPENDENT_SEGMENTS = (1 << 13),
> -} HLSFlags;
> -
> typedef enum {
>     SEGMENT_TYPE_MPEGTS,
>     SEGMENT_TYPE_FMP4,
> } SegmentType;
> 
> -typedef enum {
> -    PLAYLIST_TYPE_NONE,
> -    PLAYLIST_TYPE_EVENT,
> -    PLAYLIST_TYPE_VOD,
> -    PLAYLIST_TYPE_NB,
> -} PlaylistType;
> -
> typedef struct VariantStream {
>     unsigned number;
>     int64_t sequence;
> @@ -207,6 +183,113 @@ typedef struct HLSContext {
>     unsigned int master_publish_rate;
> } HLSContext;
> 
> +void ff_hls_write_playlist_version(AVIOContext *out, int version) {
> +    if (!out)
> +        return;
> +    avio_printf(out, "#EXTM3U\n");
> +    avio_printf(out, "#EXT-X-VERSION:%d\n", version);
> +}
> +
> +void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
> +                              int bandwidth, char *filename) {
> +    if (!out || !filename)
> +        return;
> +
> +    if (!bandwidth) {
> +        av_log(NULL, AV_LOG_WARNING,
> +                "Bandwidth info not available, set audio and video bitrates\n");
> +        return;
> +    }
> +
> +    avio_printf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth);
> +    if (st && st->codecpar->width > 0 && st->codecpar->height > 0)
> +        avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width,
> +                st->codecpar->height);
> +    avio_printf(out, "\n%s\n\n", filename);
> +}
> +
> +void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
> +                                  int target_duration, int64_t sequence,
> +                                  uint32_t playlist_type) {
> +    if (!out)
> +        return;
> +    ff_hls_write_playlist_version(out, version);
> +    if (allowcache == 0 || allowcache == 1) {
> +        avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", allowcache == 0 ? "NO" : "YES");
> +    }
> +    avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
> +    avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
> +    av_log(NULL, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
> +
> +    if (playlist_type == PLAYLIST_TYPE_EVENT) {
> +        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
> +    } else if (playlist_type == PLAYLIST_TYPE_VOD) {
> +        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
> +    }
> +}
> +
> +void ff_hls_write_init_file(AVIOContext *out, char *filename,
> +                            int byterange_mode, int64_t size, int64_t pos) {
> +    avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", filename);
> +    if (byterange_mode) {
> +        avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", size, pos);
> +    }
> +    avio_printf(out, "\n");
> +}
> +
> +void ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
> +                            int byterange_mode, uint32_t flags, double 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) {
> +    if (!out || !filename)
> +        return;
> +
> +    if (insert_discont) {
> +        avio_printf(out, "#EXT-X-DISCONTINUITY\n");
> +    }
> +    if (flags & HLS_ROUND_DURATIONS)
> +        avio_printf(out, "#EXTINF:%ld,\n",  lrint(duration));
> +    else
> +        avio_printf(out, "#EXTINF:%f,\n", duration);
> +    if (byterange_mode)
> +        avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", size, pos);
> +
> +    if ((flags & HLS_PROGRAM_DATE_TIME) && prog_date_time) {
> +        time_t tt, wrongsecs;
> +        int milli;
> +        struct tm *tm, tmpbuf;
> +        char buf0[128], buf1[128];
> +        tt = (int64_t)prog_date_time;
> +        milli = av_clip(lrint(1000*(*prog_date_time - tt)), 0, 999);
> +        tm = localtime_r(&tt, &tmpbuf);
> +        strftime(buf0, sizeof(buf0), "%Y-%m-%dT%H:%M:%S", tm);
> +        if (!strftime(buf1, sizeof(buf1), "%z", tm) || buf1[1]<'0' ||buf1[1]>'2') {
> +            int tz_min, dst = tm->tm_isdst;
> +            tm = gmtime_r(&tt, &tmpbuf);
> +            tm->tm_isdst = dst;
> +            wrongsecs = mktime(tm);
> +            tz_min = (FFABS(wrongsecs - tt) + 30) / 60;
> +            snprintf(buf1, sizeof(buf1),
> +                     "%c%02d%02d",
> +                     wrongsecs <= tt ? '+' : '-',
> +                     tz_min / 60,
> +                     tz_min % 60);
> +        }
> +        avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1);
> +        *prog_date_time += duration;
> +    }
> +    if (baseurl)
> +        avio_printf(out, "%s", baseurl);
> +    avio_printf(out, "%s\n", filename);
> +}
> +
> +void ff_hls_write_end_list (AVIOContext *out) {
> +    if (!out)
> +        return;
> +    avio_printf(out, "#EXT-X-ENDLIST\n");
> +}
> +

I suggest move the ff_ functions to a common file, because the hlsenc file modify frequently,
there have risk to be modify by other contributor, these functions maybe need design clearly,
it is used only by hlsenc and dashenc, i cannot sure it be used by other module future, 
so modify it is hard work for the modules after this commit.
Move them to segment_common.c or a good filename.c maybe better than in hlsenc.
What about your mind?

Thanks
> static int get_int_from_double(double val)
> {
>     return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val;
> @@ -1023,19 +1106,6 @@ static void hls_free_segments(HLSSegment *p)
>     }
> }
> 
> -static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version,
> -                                  int target_duration, int64_t sequence)
> -{
> -    avio_printf(out, "#EXTM3U\n");
> -    avio_printf(out, "#EXT-X-VERSION:%d\n", version);
> -    if (hls->allowcache == 0 || hls->allowcache == 1) {
> -        avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
> -    }
> -    avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
> -    avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
> -    av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
> -}
> -
> static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc)
> {
>     size_t len = strlen(oc->filename);
> @@ -1101,8 +1171,7 @@ static int create_master_playlist(AVFormatContext *s,
>         goto fail;
>     }
> 
> -    avio_printf(master_pb, "#EXTM3U\n");
> -    avio_printf(master_pb, "#EXT-X-VERSION:%d\n", hls->version);
> +    ff_hls_write_playlist_version(master_pb, hls->version);
> 
>     /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/
>     for (i = 0; i < hls->nb_varstreams; i++) {
> @@ -1143,18 +1212,7 @@ static int create_master_playlist(AVFormatContext *s,
>             bandwidth += aud_st->codecpar->bit_rate;
>         bandwidth += bandwidth / 10;
> 
> -        if (!bandwidth) {
> -            av_log(NULL, AV_LOG_WARNING,
> -                    "Bandwidth info not available, set audio and video bitrates\n");
> -            av_freep(&m3u8_rel_name);
> -            continue;
> -        }
> -
> -        avio_printf(master_pb, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth);
> -        if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0)
> -            avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width,
> -                    vid_st->codecpar->height);
> -        avio_printf(master_pb, "\n%s\n\n", m3u8_rel_name);
> +        ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name);
> 
>         av_freep(&m3u8_rel_name);
>     }
> @@ -1213,12 +1271,8 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
>     }
> 
>     vs->discontinuity_set = 0;
> -    write_m3u8_head_block(hls, out, hls->version, target_duration, sequence);
> -    if (hls->pl_type == PLAYLIST_TYPE_EVENT) {
> -        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
> -    } else if (hls->pl_type == PLAYLIST_TYPE_VOD) {
> -        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
> -    }
> +    ff_hls_write_playlist_header(out, hls->version, hls->allowcache,
> +                                 target_duration, sequence, hls->pl_type);
> 
>     if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){
>         avio_printf(out, "#EXT-X-DISCONTINUITY\n");
> @@ -1238,74 +1292,34 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
>             iv_string = en->iv_string;
>         }
> 
> -        if (en->discont) {
> -            avio_printf(out, "#EXT-X-DISCONTINUITY\n");
> -        }
> -
>         if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) {
> -            avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", vs->fmp4_init_filename);
> -            if (hls->flags & HLS_SINGLE_FILE) {
> -                avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", en->size, en->pos);
> -            }
> -            avio_printf(out, "\n");
> +            ff_hls_write_init_file(out, vs->fmp4_init_filename,
> +                                   hls->flags & HLS_SINGLE_FILE, en->size, en->pos);
>         }
> -        if (hls->flags & HLS_ROUND_DURATIONS)
> -            avio_printf(out, "#EXTINF:%ld,\n",  lrint(en->duration));
> -        else
> -            avio_printf(out, "#EXTINF:%f,\n", en->duration);
> -        if (byterange_mode)
> -            avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n",
> -                        en->size, en->pos);
> -
> -        if (hls->flags & HLS_PROGRAM_DATE_TIME) {
> -            time_t tt, wrongsecs;
> -            int milli;
> -            struct tm *tm, tmpbuf;
> -            char buf0[128], buf1[128];
> -            tt = (int64_t)prog_date_time;
> -            milli = av_clip(lrint(1000*(prog_date_time - tt)), 0, 999);
> -            tm = localtime_r(&tt, &tmpbuf);
> -            strftime(buf0, sizeof(buf0), "%Y-%m-%dT%H:%M:%S", tm);
> -            if (!strftime(buf1, sizeof(buf1), "%z", tm) || buf1[1]<'0' ||buf1[1]>'2') {
> -                int tz_min, dst = tm->tm_isdst;
> -                tm = gmtime_r(&tt, &tmpbuf);
> -                tm->tm_isdst = dst;
> -                wrongsecs = mktime(tm);
> -                tz_min = (FFABS(wrongsecs - tt) + 30) / 60;
> -                snprintf(buf1, sizeof(buf1),
> -                         "%c%02d%02d",
> -                         wrongsecs <= tt ? '+' : '-',
> -                         tz_min / 60,
> -                         tz_min % 60);
> -            }
> -            avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1);
> -            prog_date_time += en->duration;
> -        }
> -        if (vs->baseurl)
> -            avio_printf(out, "%s", vs->baseurl);
> -        avio_printf(out, "%s\n", en->filename);
> +
> +        ff_hls_write_file_entry(out, en->discont, byterange_mode, hls->flags,
> +                                en->duration, en->size, en->pos, vs->baseurl,
> +                                en->filename, &prog_date_time);
> +
>     }
> 
>     if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
> -        avio_printf(out, "#EXT-X-ENDLIST\n");
> +        ff_hls_write_end_list(out);
> 
>     if( vs->vtt_m3u8_name ) {
>         if ((ret = s->io_open(s, &sub_out, vs->vtt_m3u8_name, AVIO_FLAG_WRITE, &options)) < 0)
>             goto fail;
> -        write_m3u8_head_block(hls, sub_out, hls->version, target_duration, sequence);
> +        ff_hls_write_playlist_header(sub_out, hls->version, hls->allowcache,
> +                                     target_duration, sequence, PLAYLIST_TYPE_NONE);
> 
>         for (en = vs->segments; en; en = en->next) {
> -            avio_printf(sub_out, "#EXTINF:%f,\n", en->duration);
> -            if (byterange_mode)
> -                 avio_printf(sub_out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
> -                         en->size, en->pos);
> -            if (vs->baseurl)
> -                avio_printf(sub_out, "%s", vs->baseurl);
> -            avio_printf(sub_out, "%s\n", en->sub_filename);
> +            ff_hls_write_file_entry(sub_out, 0, byterange_mode,
> +                                    hls->flags, en->duration, en->size, en->pos,
> +                                    vs->baseurl, en->sub_filename, NULL);
>         }
> 
>         if (last)
> -            avio_printf(sub_out, "#EXT-X-ENDLIST\n");
> +            ff_hls_write_end_list(sub_out);
> 
>     }
> 
> diff --git a/libavformat/hlsenc.h b/libavformat/hlsenc.h
> new file mode 100644
> index 0000000..03b0cf3
> --- /dev/null
> +++ b/libavformat/hlsenc.h
> @@ -0,0 +1,68 @@
> +/*
> + * Apple HTTP Live Streaming segmenter
> + * Copyright (c) 2012, Luca Barbato
> + * Copyright (c) 2017 Akamai Technologies, Inc.
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#ifndef AVFORMAT_HLSENC_H_
> +#define AVFORMAT_HLSENC_H_
> +
> +#include "libavutil/common.h"
> +
> +typedef enum HLSFlags {
> +    // Generate a single media file and use byte ranges in the playlist.
> +    HLS_SINGLE_FILE = (1 << 0),
> +    HLS_DELETE_SEGMENTS = (1 << 1),
> +    HLS_ROUND_DURATIONS = (1 << 2),
> +    HLS_DISCONT_START = (1 << 3),
> +    HLS_OMIT_ENDLIST = (1 << 4),
> +    HLS_SPLIT_BY_TIME = (1 << 5),
> +    HLS_APPEND_LIST = (1 << 6),
> +    HLS_PROGRAM_DATE_TIME = (1 << 7),
> +    HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime  e.g.: %%03d
> +    HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime  e.g.: %%09t
> +    HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime  e.g.: %%014s
> +    HLS_TEMP_FILE = (1 << 11),
> +    HLS_PERIODIC_REKEY = (1 << 12),
> +    HLS_INDEPENDENT_SEGMENTS = (1 << 13),
> +} HLSFlags;
> +
> +typedef enum {
> +    PLAYLIST_TYPE_NONE,
> +    PLAYLIST_TYPE_EVENT,
> +    PLAYLIST_TYPE_VOD,
> +    PLAYLIST_TYPE_NB,
> +} PlaylistType;
> +
> +void ff_hls_write_playlist_version(AVIOContext *out, int version);
> +void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
> +                              int bandwidth, char *filename);
> +void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
> +                                  int target_duration, int64_t sequence,
> +                                  uint32_t playlist_type);
> +void ff_hls_write_init_file(AVIOContext *out, char *filename,
> +                            int byterange_mode, int64_t size, int64_t pos);
> +void ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
> +                            int byterange_mode, uint32_t flags, double 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);
> +void ff_hls_write_end_list (AVIOContext *out);
> +
> +#endif /* AVFORMAT_HLSENC_H_ */
> -- 
> 1.9.1
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Jeyapal, Karthick Nov. 24, 2017, 5:30 a.m. UTC | #2
>On 11/24/17, 9:44 AM, "刘歧" <lq@chinaffmpeg.org> wrote:

>

>

>> 在 2017年11月24日,11:55,Karthick J <kjeyapal@akamai.com> 写道:

>> 

>> ---

>> libavformat/hlsenc.c | 238 +++++++++++++++++++++++++++------------------------

>> libavformat/hlsenc.h |  68 +++++++++++++++

>> 2 files changed, 194 insertions(+), 112 deletions(-)

>> create mode 100644 libavformat/hlsenc.h

>[…]

>I suggest move the ff_ functions to a common file, because the hlsenc file modify frequently,

>there have risk to be modify by other contributor, these functions maybe need design clearly,

>it is used only by hlsenc and dashenc, i cannot sure it be used by other module future, 

>so modify it is hard work for the modules after this commit.

>Move them to segment_common.c or a good filename.c maybe better than in hlsenc.

>What about your mind?

Thanks for the reply. 
I have segregated ff_ functions into a separate .c file as you suggested, in patchset v4.

>

>Thanks


regards,
Karthick
diff mbox

Patch

diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index 30ccf73..5c4f459 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -45,6 +45,7 @@ 
 
 #include "avformat.h"
 #include "avio_internal.h"
+#include "hlsenc.h"
 #include "internal.h"
 #include "os_support.h"
 
@@ -73,36 +74,11 @@  typedef struct HLSSegment {
     struct HLSSegment *next;
 } HLSSegment;
 
-typedef enum HLSFlags {
-    // Generate a single media file and use byte ranges in the playlist.
-    HLS_SINGLE_FILE = (1 << 0),
-    HLS_DELETE_SEGMENTS = (1 << 1),
-    HLS_ROUND_DURATIONS = (1 << 2),
-    HLS_DISCONT_START = (1 << 3),
-    HLS_OMIT_ENDLIST = (1 << 4),
-    HLS_SPLIT_BY_TIME = (1 << 5),
-    HLS_APPEND_LIST = (1 << 6),
-    HLS_PROGRAM_DATE_TIME = (1 << 7),
-    HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime  e.g.: %%03d
-    HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime  e.g.: %%09t
-    HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime  e.g.: %%014s
-    HLS_TEMP_FILE = (1 << 11),
-    HLS_PERIODIC_REKEY = (1 << 12),
-    HLS_INDEPENDENT_SEGMENTS = (1 << 13),
-} HLSFlags;
-
 typedef enum {
     SEGMENT_TYPE_MPEGTS,
     SEGMENT_TYPE_FMP4,
 } SegmentType;
 
-typedef enum {
-    PLAYLIST_TYPE_NONE,
-    PLAYLIST_TYPE_EVENT,
-    PLAYLIST_TYPE_VOD,
-    PLAYLIST_TYPE_NB,
-} PlaylistType;
-
 typedef struct VariantStream {
     unsigned number;
     int64_t sequence;
@@ -207,6 +183,113 @@  typedef struct HLSContext {
     unsigned int master_publish_rate;
 } HLSContext;
 
+void ff_hls_write_playlist_version(AVIOContext *out, int version) {
+    if (!out)
+        return;
+    avio_printf(out, "#EXTM3U\n");
+    avio_printf(out, "#EXT-X-VERSION:%d\n", version);
+}
+
+void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
+                              int bandwidth, char *filename) {
+    if (!out || !filename)
+        return;
+
+    if (!bandwidth) {
+        av_log(NULL, AV_LOG_WARNING,
+                "Bandwidth info not available, set audio and video bitrates\n");
+        return;
+    }
+
+    avio_printf(out, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth);
+    if (st && st->codecpar->width > 0 && st->codecpar->height > 0)
+        avio_printf(out, ",RESOLUTION=%dx%d", st->codecpar->width,
+                st->codecpar->height);
+    avio_printf(out, "\n%s\n\n", filename);
+}
+
+void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
+                                  int target_duration, int64_t sequence,
+                                  uint32_t playlist_type) {
+    if (!out)
+        return;
+    ff_hls_write_playlist_version(out, version);
+    if (allowcache == 0 || allowcache == 1) {
+        avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", allowcache == 0 ? "NO" : "YES");
+    }
+    avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
+    avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
+    av_log(NULL, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
+
+    if (playlist_type == PLAYLIST_TYPE_EVENT) {
+        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
+    } else if (playlist_type == PLAYLIST_TYPE_VOD) {
+        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
+    }
+}
+
+void ff_hls_write_init_file(AVIOContext *out, char *filename,
+                            int byterange_mode, int64_t size, int64_t pos) {
+    avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", filename);
+    if (byterange_mode) {
+        avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", size, pos);
+    }
+    avio_printf(out, "\n");
+}
+
+void ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
+                            int byterange_mode, uint32_t flags, double 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) {
+    if (!out || !filename)
+        return;
+
+    if (insert_discont) {
+        avio_printf(out, "#EXT-X-DISCONTINUITY\n");
+    }
+    if (flags & HLS_ROUND_DURATIONS)
+        avio_printf(out, "#EXTINF:%ld,\n",  lrint(duration));
+    else
+        avio_printf(out, "#EXTINF:%f,\n", duration);
+    if (byterange_mode)
+        avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n", size, pos);
+
+    if ((flags & HLS_PROGRAM_DATE_TIME) && prog_date_time) {
+        time_t tt, wrongsecs;
+        int milli;
+        struct tm *tm, tmpbuf;
+        char buf0[128], buf1[128];
+        tt = (int64_t)prog_date_time;
+        milli = av_clip(lrint(1000*(*prog_date_time - tt)), 0, 999);
+        tm = localtime_r(&tt, &tmpbuf);
+        strftime(buf0, sizeof(buf0), "%Y-%m-%dT%H:%M:%S", tm);
+        if (!strftime(buf1, sizeof(buf1), "%z", tm) || buf1[1]<'0' ||buf1[1]>'2') {
+            int tz_min, dst = tm->tm_isdst;
+            tm = gmtime_r(&tt, &tmpbuf);
+            tm->tm_isdst = dst;
+            wrongsecs = mktime(tm);
+            tz_min = (FFABS(wrongsecs - tt) + 30) / 60;
+            snprintf(buf1, sizeof(buf1),
+                     "%c%02d%02d",
+                     wrongsecs <= tt ? '+' : '-',
+                     tz_min / 60,
+                     tz_min % 60);
+        }
+        avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1);
+        *prog_date_time += duration;
+    }
+    if (baseurl)
+        avio_printf(out, "%s", baseurl);
+    avio_printf(out, "%s\n", filename);
+}
+
+void ff_hls_write_end_list (AVIOContext *out) {
+    if (!out)
+        return;
+    avio_printf(out, "#EXT-X-ENDLIST\n");
+}
+
 static int get_int_from_double(double val)
 {
     return (int)((val - (int)val) >= 0.001) ? (int)(val + 1) : (int)val;
@@ -1023,19 +1106,6 @@  static void hls_free_segments(HLSSegment *p)
     }
 }
 
-static void write_m3u8_head_block(HLSContext *hls, AVIOContext *out, int version,
-                                  int target_duration, int64_t sequence)
-{
-    avio_printf(out, "#EXTM3U\n");
-    avio_printf(out, "#EXT-X-VERSION:%d\n", version);
-    if (hls->allowcache == 0 || hls->allowcache == 1) {
-        avio_printf(out, "#EXT-X-ALLOW-CACHE:%s\n", hls->allowcache == 0 ? "NO" : "YES");
-    }
-    avio_printf(out, "#EXT-X-TARGETDURATION:%d\n", target_duration);
-    avio_printf(out, "#EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
-    av_log(hls, AV_LOG_VERBOSE, "EXT-X-MEDIA-SEQUENCE:%"PRId64"\n", sequence);
-}
-
 static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc)
 {
     size_t len = strlen(oc->filename);
@@ -1101,8 +1171,7 @@  static int create_master_playlist(AVFormatContext *s,
         goto fail;
     }
 
-    avio_printf(master_pb, "#EXTM3U\n");
-    avio_printf(master_pb, "#EXT-X-VERSION:%d\n", hls->version);
+    ff_hls_write_playlist_version(master_pb, hls->version);
 
     /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/
     for (i = 0; i < hls->nb_varstreams; i++) {
@@ -1143,18 +1212,7 @@  static int create_master_playlist(AVFormatContext *s,
             bandwidth += aud_st->codecpar->bit_rate;
         bandwidth += bandwidth / 10;
 
-        if (!bandwidth) {
-            av_log(NULL, AV_LOG_WARNING,
-                    "Bandwidth info not available, set audio and video bitrates\n");
-            av_freep(&m3u8_rel_name);
-            continue;
-        }
-
-        avio_printf(master_pb, "#EXT-X-STREAM-INF:BANDWIDTH=%d", bandwidth);
-        if (vid_st && vid_st->codecpar->width > 0 && vid_st->codecpar->height > 0)
-            avio_printf(master_pb, ",RESOLUTION=%dx%d", vid_st->codecpar->width,
-                    vid_st->codecpar->height);
-        avio_printf(master_pb, "\n%s\n\n", m3u8_rel_name);
+        ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name);
 
         av_freep(&m3u8_rel_name);
     }
@@ -1213,12 +1271,8 @@  static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
     }
 
     vs->discontinuity_set = 0;
-    write_m3u8_head_block(hls, out, hls->version, target_duration, sequence);
-    if (hls->pl_type == PLAYLIST_TYPE_EVENT) {
-        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:EVENT\n");
-    } else if (hls->pl_type == PLAYLIST_TYPE_VOD) {
-        avio_printf(out, "#EXT-X-PLAYLIST-TYPE:VOD\n");
-    }
+    ff_hls_write_playlist_header(out, hls->version, hls->allowcache,
+                                 target_duration, sequence, hls->pl_type);
 
     if((hls->flags & HLS_DISCONT_START) && sequence==hls->start_sequence && vs->discontinuity_set==0 ){
         avio_printf(out, "#EXT-X-DISCONTINUITY\n");
@@ -1238,74 +1292,34 @@  static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
             iv_string = en->iv_string;
         }
 
-        if (en->discont) {
-            avio_printf(out, "#EXT-X-DISCONTINUITY\n");
-        }
-
         if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) {
-            avio_printf(out, "#EXT-X-MAP:URI=\"%s\"", vs->fmp4_init_filename);
-            if (hls->flags & HLS_SINGLE_FILE) {
-                avio_printf(out, ",BYTERANGE=\"%"PRId64"@%"PRId64"\"", en->size, en->pos);
-            }
-            avio_printf(out, "\n");
+            ff_hls_write_init_file(out, vs->fmp4_init_filename,
+                                   hls->flags & HLS_SINGLE_FILE, en->size, en->pos);
         }
-        if (hls->flags & HLS_ROUND_DURATIONS)
-            avio_printf(out, "#EXTINF:%ld,\n",  lrint(en->duration));
-        else
-            avio_printf(out, "#EXTINF:%f,\n", en->duration);
-        if (byterange_mode)
-            avio_printf(out, "#EXT-X-BYTERANGE:%"PRId64"@%"PRId64"\n",
-                        en->size, en->pos);
-
-        if (hls->flags & HLS_PROGRAM_DATE_TIME) {
-            time_t tt, wrongsecs;
-            int milli;
-            struct tm *tm, tmpbuf;
-            char buf0[128], buf1[128];
-            tt = (int64_t)prog_date_time;
-            milli = av_clip(lrint(1000*(prog_date_time - tt)), 0, 999);
-            tm = localtime_r(&tt, &tmpbuf);
-            strftime(buf0, sizeof(buf0), "%Y-%m-%dT%H:%M:%S", tm);
-            if (!strftime(buf1, sizeof(buf1), "%z", tm) || buf1[1]<'0' ||buf1[1]>'2') {
-                int tz_min, dst = tm->tm_isdst;
-                tm = gmtime_r(&tt, &tmpbuf);
-                tm->tm_isdst = dst;
-                wrongsecs = mktime(tm);
-                tz_min = (FFABS(wrongsecs - tt) + 30) / 60;
-                snprintf(buf1, sizeof(buf1),
-                         "%c%02d%02d",
-                         wrongsecs <= tt ? '+' : '-',
-                         tz_min / 60,
-                         tz_min % 60);
-            }
-            avio_printf(out, "#EXT-X-PROGRAM-DATE-TIME:%s.%03d%s\n", buf0, milli, buf1);
-            prog_date_time += en->duration;
-        }
-        if (vs->baseurl)
-            avio_printf(out, "%s", vs->baseurl);
-        avio_printf(out, "%s\n", en->filename);
+
+        ff_hls_write_file_entry(out, en->discont, byterange_mode, hls->flags,
+                                en->duration, en->size, en->pos, vs->baseurl,
+                                en->filename, &prog_date_time);
+
     }
 
     if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
-        avio_printf(out, "#EXT-X-ENDLIST\n");
+        ff_hls_write_end_list(out);
 
     if( vs->vtt_m3u8_name ) {
         if ((ret = s->io_open(s, &sub_out, vs->vtt_m3u8_name, AVIO_FLAG_WRITE, &options)) < 0)
             goto fail;
-        write_m3u8_head_block(hls, sub_out, hls->version, target_duration, sequence);
+        ff_hls_write_playlist_header(sub_out, hls->version, hls->allowcache,
+                                     target_duration, sequence, PLAYLIST_TYPE_NONE);
 
         for (en = vs->segments; en; en = en->next) {
-            avio_printf(sub_out, "#EXTINF:%f,\n", en->duration);
-            if (byterange_mode)
-                 avio_printf(sub_out, "#EXT-X-BYTERANGE:%"PRIi64"@%"PRIi64"\n",
-                         en->size, en->pos);
-            if (vs->baseurl)
-                avio_printf(sub_out, "%s", vs->baseurl);
-            avio_printf(sub_out, "%s\n", en->sub_filename);
+            ff_hls_write_file_entry(sub_out, 0, byterange_mode,
+                                    hls->flags, en->duration, en->size, en->pos,
+                                    vs->baseurl, en->sub_filename, NULL);
         }
 
         if (last)
-            avio_printf(sub_out, "#EXT-X-ENDLIST\n");
+            ff_hls_write_end_list(sub_out);
 
     }
 
diff --git a/libavformat/hlsenc.h b/libavformat/hlsenc.h
new file mode 100644
index 0000000..03b0cf3
--- /dev/null
+++ b/libavformat/hlsenc.h
@@ -0,0 +1,68 @@ 
+/*
+ * Apple HTTP Live Streaming segmenter
+ * Copyright (c) 2012, Luca Barbato
+ * Copyright (c) 2017 Akamai Technologies, Inc.
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVFORMAT_HLSENC_H_
+#define AVFORMAT_HLSENC_H_
+
+#include "libavutil/common.h"
+
+typedef enum HLSFlags {
+    // Generate a single media file and use byte ranges in the playlist.
+    HLS_SINGLE_FILE = (1 << 0),
+    HLS_DELETE_SEGMENTS = (1 << 1),
+    HLS_ROUND_DURATIONS = (1 << 2),
+    HLS_DISCONT_START = (1 << 3),
+    HLS_OMIT_ENDLIST = (1 << 4),
+    HLS_SPLIT_BY_TIME = (1 << 5),
+    HLS_APPEND_LIST = (1 << 6),
+    HLS_PROGRAM_DATE_TIME = (1 << 7),
+    HLS_SECOND_LEVEL_SEGMENT_INDEX = (1 << 8), // include segment index in segment filenames when use_localtime  e.g.: %%03d
+    HLS_SECOND_LEVEL_SEGMENT_DURATION = (1 << 9), // include segment duration (microsec) in segment filenames when use_localtime  e.g.: %%09t
+    HLS_SECOND_LEVEL_SEGMENT_SIZE = (1 << 10), // include segment size (bytes) in segment filenames when use_localtime  e.g.: %%014s
+    HLS_TEMP_FILE = (1 << 11),
+    HLS_PERIODIC_REKEY = (1 << 12),
+    HLS_INDEPENDENT_SEGMENTS = (1 << 13),
+} HLSFlags;
+
+typedef enum {
+    PLAYLIST_TYPE_NONE,
+    PLAYLIST_TYPE_EVENT,
+    PLAYLIST_TYPE_VOD,
+    PLAYLIST_TYPE_NB,
+} PlaylistType;
+
+void ff_hls_write_playlist_version(AVIOContext *out, int version);
+void ff_hls_write_stream_info(AVStream *st, AVIOContext *out,
+                              int bandwidth, char *filename);
+void ff_hls_write_playlist_header(AVIOContext *out, int version, int allowcache,
+                                  int target_duration, int64_t sequence,
+                                  uint32_t playlist_type);
+void ff_hls_write_init_file(AVIOContext *out, char *filename,
+                            int byterange_mode, int64_t size, int64_t pos);
+void ff_hls_write_file_entry(AVIOContext *out, int insert_discont,
+                            int byterange_mode, uint32_t flags, double 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);
+void ff_hls_write_end_list (AVIOContext *out);
+
+#endif /* AVFORMAT_HLSENC_H_ */