diff mbox

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

Message ID 1511945535-11215-1-git-send-email-kjeyapal@akamai.com
State Accepted
Commit da49cdf6401ea3caa616c226f24dfb407633acd0
Headers show

Commit Message

Jeyapal, Karthick Nov. 29, 2017, 8:52 a.m. UTC
---
 libavformat/Makefile      |   2 +-
 libavformat/hlsenc.c      | 115 +++++++-------------------------------
 libavformat/hlsplaylist.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++
 libavformat/hlsplaylist.h |  51 +++++++++++++++++
 4 files changed, 211 insertions(+), 95 deletions(-)
 create mode 100644 libavformat/hlsplaylist.c
 create mode 100644 libavformat/hlsplaylist.h

Comments

Liu Steven Nov. 29, 2017, 11:46 a.m. UTC | #1
Where can i find the v6 2/2 ?
> 在 2017年11月29日,16:52,Karthick J <kjeyapal@akamai.com> 写道:
> 
> ---
> libavformat/Makefile      |   2 +-
> libavformat/hlsenc.c      | 115 +++++++-------------------------------
> libavformat/hlsplaylist.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++
> libavformat/hlsplaylist.h |  51 +++++++++++++++++
> 4 files changed, 211 insertions(+), 95 deletions(-)
> create mode 100644 libavformat/hlsplaylist.c
> create mode 100644 libavformat/hlsplaylist.h
> 
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index b1e7b19..fd8b9f9 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -215,7 +215,7 @@ OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
> OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
> OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
> OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
> -OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o
> +OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o
> OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
> OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
> OBJS-$(CONFIG_ICO_MUXER)                 += icoenc.o
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index d5c732f..f63b08d 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -46,6 +46,7 @@
> #include "avformat.h"
> #include "avio_internal.h"
> #include "http.h"
> +#include "hlsplaylist.h"
> #include "internal.h"
> #include "os_support.h"
> 
> @@ -97,13 +98,6 @@ typedef enum {
>     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;
> @@ -1055,19 +1049,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);
> @@ -1133,8 +1114,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++) {
> @@ -1175,18 +1155,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);
>     }
> @@ -1215,6 +1184,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
>     char *iv_string = NULL;
>     AVDictionary *options = NULL;
>     double prog_date_time = vs->initial_prog_date_time;
> +    double *prog_date_time_p = (hls->flags & HLS_PROGRAM_DATE_TIME) ? &prog_date_time : NULL;
>     int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
> 
>     hls->version = 3;
> @@ -1245,12 +1215,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");
> @@ -1270,74 +1236,35 @@ 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,
> +                                en->duration, hls->flags & HLS_ROUND_DURATIONS,
> +                                en->size, en->pos, vs->baseurl,
> +                                en->filename, prog_date_time_p);
> +
>     }
> 
>     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,
> +                                    en->duration, 0, 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/hlsplaylist.c b/libavformat/hlsplaylist.c
> new file mode 100644
> index 0000000..235e519
> --- /dev/null
> +++ b/libavformat/hlsplaylist.c
> @@ -0,0 +1,138 @@
> +/*
> + * 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
> + */
> +
> +#include "config.h"
> +#include <stdint.h>
> +
> +#include "libavutil/time_internal.h"
> +
> +#include "avformat.h"
> +#include "hlsplaylist.h"
> +
> +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,
> +                             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) {
> +    if (!out || !filename)
> +        return;
> +
> +    if (insert_discont) {
> +        avio_printf(out, "#EXT-X-DISCONTINUITY\n");
> +    }
> +    if (round_duration)
> +        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 (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");
> +}
> +
> diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h
> new file mode 100644
> index 0000000..fd36c7e
> --- /dev/null
> +++ b/libavformat/hlsplaylist.h
> @@ -0,0 +1,51 @@
> +/*
> + * 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_HLSPLAYLIST_H_
> +#define AVFORMAT_HLSPLAYLIST_H_
> +
> +#include "libavutil/common.h"
> +
> +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,
> +                             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);
> +void ff_hls_write_end_list (AVIOContext *out);
> +
> +#endif /* AVFORMAT_HLSPLAYLIST_H_ */
> -- 
> 1.9.1
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Jeyapal, Karthick Nov. 29, 2017, 12:40 p.m. UTC | #2
On 11/29/17, 5:16 PM, "刘歧" <lq@chinaffmpeg.org> wrote:
>

>Where can i find the v6 2/2 ?

Patch 2/2 didn’t change since v4. 
So didn’t repost it. You have to use v4 2/2.

I am sorry. I was not sure what is the normal practice here. I didn’t repost it, so that the people don’t waste time in re-reviewing it.
Is it customary to repost the all patches(even if unchanged) in a patchset, when posting new versions of a particular patch?
Please advise. I will follow accordingly from next time onwards. 

Thanks and regards,
Karthick
Steven Liu Nov. 29, 2017, 2:09 p.m. UTC | #3
2017-11-29 20:40 GMT+08:00 Jeyapal, Karthick <kjeyapal@akamai.com>:
> On 11/29/17, 5:16 PM, "刘歧" <lq@chinaffmpeg.org> wrote:
>>
>>Where can i find the v6 2/2 ?
> Patch 2/2 didn’t change since v4.
> So didn’t repost it. You have to use v4 2/2.
>
> I am sorry. I was not sure what is the normal practice here. I didn’t repost it, so that the people don’t waste time in re-reviewing it.
> Is it customary to repost the all patches(even if unchanged) in a patchset, when posting new versions of a particular patch?
> Please advise. I will follow accordingly from next time onwards.

I think you should sent a patchset, i usually sent patchset to
maillist, because the version indent maybe can make the reviewers and
maintainers clearly what this version do, and merge quickly, they need
not find the old version patches.


Thanks
>
> Thanks and regards,
> Karthick
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
diff mbox

Patch

diff --git a/libavformat/Makefile b/libavformat/Makefile
index b1e7b19..fd8b9f9 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -215,7 +215,7 @@  OBJS-$(CONFIG_HDS_MUXER)                 += hdsenc.o
 OBJS-$(CONFIG_HEVC_DEMUXER)              += hevcdec.o rawdec.o
 OBJS-$(CONFIG_HEVC_MUXER)                += rawenc.o
 OBJS-$(CONFIG_HLS_DEMUXER)               += hls.o
-OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o
+OBJS-$(CONFIG_HLS_MUXER)                 += hlsenc.o hlsplaylist.o
 OBJS-$(CONFIG_HNM_DEMUXER)               += hnm.o
 OBJS-$(CONFIG_ICO_DEMUXER)               += icodec.o
 OBJS-$(CONFIG_ICO_MUXER)                 += icoenc.o
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index d5c732f..f63b08d 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -46,6 +46,7 @@ 
 #include "avformat.h"
 #include "avio_internal.h"
 #include "http.h"
+#include "hlsplaylist.h"
 #include "internal.h"
 #include "os_support.h"
 
@@ -97,13 +98,6 @@  typedef enum {
     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;
@@ -1055,19 +1049,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);
@@ -1133,8 +1114,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++) {
@@ -1175,18 +1155,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);
     }
@@ -1215,6 +1184,7 @@  static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
     char *iv_string = NULL;
     AVDictionary *options = NULL;
     double prog_date_time = vs->initial_prog_date_time;
+    double *prog_date_time_p = (hls->flags & HLS_PROGRAM_DATE_TIME) ? &prog_date_time : NULL;
     int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
 
     hls->version = 3;
@@ -1245,12 +1215,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");
@@ -1270,74 +1236,35 @@  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,
+                                en->duration, hls->flags & HLS_ROUND_DURATIONS,
+                                en->size, en->pos, vs->baseurl,
+                                en->filename, prog_date_time_p);
+
     }
 
     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,
+                                    en->duration, 0, 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/hlsplaylist.c b/libavformat/hlsplaylist.c
new file mode 100644
index 0000000..235e519
--- /dev/null
+++ b/libavformat/hlsplaylist.c
@@ -0,0 +1,138 @@ 
+/*
+ * 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
+ */
+
+#include "config.h"
+#include <stdint.h>
+
+#include "libavutil/time_internal.h"
+
+#include "avformat.h"
+#include "hlsplaylist.h"
+
+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,
+                             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) {
+    if (!out || !filename)
+        return;
+
+    if (insert_discont) {
+        avio_printf(out, "#EXT-X-DISCONTINUITY\n");
+    }
+    if (round_duration)
+        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 (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");
+}
+
diff --git a/libavformat/hlsplaylist.h b/libavformat/hlsplaylist.h
new file mode 100644
index 0000000..fd36c7e
--- /dev/null
+++ b/libavformat/hlsplaylist.h
@@ -0,0 +1,51 @@ 
+/*
+ * 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_HLSPLAYLIST_H_
+#define AVFORMAT_HLSPLAYLIST_H_
+
+#include "libavutil/common.h"
+
+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,
+                             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);
+void ff_hls_write_end_list (AVIOContext *out);
+
+#endif /* AVFORMAT_HLSPLAYLIST_H_ */