]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/hlsenc.c
hlsenc: Fixing HLS_TEMP_FILE usage with HLS_SECOND_LEVEL_SEGMENT_...
[ffmpeg] / libavformat / hlsenc.c
index cdfbf458239200ddc632d231eed0972f3c0a278d..ff064732a1d336c79979ec0a3165d446290a80b6 100644 (file)
@@ -45,7 +45,9 @@
 
 #include "avformat.h"
 #include "avio_internal.h"
+#if CONFIG_HTTP_PROTOCOL
 #include "http.h"
+#endif
 #include "hlsplaylist.h"
 #include "internal.h"
 #include "os_support.h"
@@ -56,6 +58,11 @@ typedef enum {
   HLS_START_SEQUENCE_AS_FORMATTED_DATETIME = 2,  // YYYYMMDDhhmmss
 } StartSequenceSourceType;
 
+typedef enum {
+    CODEC_ATTRIBUTE_WRITTEN = 0,
+    CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN,
+} CodecAttributeStatus;
+
 #define KEYSIZE 16
 #define LINE_BUFFER_SIZE 1024
 #define HLS_MICROSECOND_UNIT   1000000
@@ -140,11 +147,21 @@ typedef struct VariantStream {
     int fmp4_init_mode;
 
     AVStream **streams;
+    char codec_attr[128];
+    CodecAttributeStatus attr_status;
     unsigned int nb_streams;
     int m3u8_created; /* status of media play-list creation */
+    char *agroup; /* audio group name */
+    char *ccgroup; /* closed caption group name */
     char *baseurl;
 } VariantStream;
 
+typedef struct ClosedCaptionsStream {
+    char *ccgroup; /* closed caption group name */
+    char *instreamid; /* closed captions INSTREAM-ID */
+    char *language; /* closed captions langauge */
+} ClosedCaptionsStream;
+
 typedef struct HLSContext {
     const AVClass *class;  // Class for private options.
     int64_t start_sequence;
@@ -193,14 +210,19 @@ typedef struct HLSContext {
 
     VariantStream *var_streams;
     unsigned int nb_varstreams;
+    ClosedCaptionsStream *cc_streams;
+    unsigned int nb_ccstreams;
 
     int master_m3u8_created; /* status of master play-list creation */
     char *master_m3u8_url; /* URL of the master m3u8 file */
     int version; /* HLS version */
     char *var_stream_map; /* user specified variant stream map string */
+    char *cc_stream_map; /* user specified closed caption streams map string */
     char *master_pl_name;
     unsigned int master_publish_rate;
     int http_persistent;
+    AVIOContext *m3u8_out;
+    AVIOContext *sub_m3u8_out;
 } HLSContext;
 
 static int mkdir_p(const char *path) {
@@ -236,38 +258,41 @@ static int mkdir_p(const char *path) {
     return ret;
 }
 
-static int is_http_proto(char *filename) {
-    const char *proto = avio_find_protocol_name(filename);
-    return proto ? (!av_strcasecmp(proto, "http") || !av_strcasecmp(proto, "https")) : 0;
-}
-
 static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename,
                           AVDictionary **options) {
     HLSContext *hls = s->priv_data;
-    int http_base_proto = is_http_proto(filename);
-    int err;
+    int http_base_proto = filename ? ff_is_http_proto(filename) : 0;
+    int err = AVERROR_MUXER_NOT_FOUND;
     if (!*pb || !http_base_proto || !hls->http_persistent) {
         err = s->io_open(s, pb, filename, AVIO_FLAG_WRITE, options);
+#if CONFIG_HTTP_PROTOCOL
     } else {
         URLContext *http_url_context = ffio_geturlcontext(*pb);
         av_assert0(http_url_context);
         err = ff_http_do_new_request(http_url_context, filename);
+#endif
     }
     return err;
 }
 
 static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) {
     HLSContext *hls = s->priv_data;
-    int http_base_proto = is_http_proto(filename);
-
+    int http_base_proto = filename ? ff_is_http_proto(filename) : 0;
     if (!http_base_proto || !hls->http_persistent || hls->key_info_file || hls->encrypt) {
         ff_format_io_close(s, pb);
+#if CONFIG_HTTP_PROTOCOL
+    } else {
+        URLContext *http_url_context = ffio_geturlcontext(*pb);
+        av_assert0(http_url_context);
+        avio_flush(*pb);
+        ffurl_shutdown(http_url_context, AVIO_FLAG_WRITE);
+#endif
     }
 }
 
 static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSContext *c)
 {
-    int http_base_proto = is_http_proto(s->filename);
+    int http_base_proto = ff_is_http_proto(s->url);
 
     if (c->method) {
         av_dict_set(options, "method", c->method, 0);
@@ -282,14 +307,62 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont
 
 }
 
-static int replace_int_data_in_filename(char *buf, int buf_size, const char *filename, char placeholder, int64_t number)
+static void write_codec_attr(AVStream *st, VariantStream *vs) {
+    int codec_strlen = strlen(vs->codec_attr);
+    char attr[32];
+
+    if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+        return;
+    if (vs->attr_status == CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN)
+        return;
+
+    if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
+        uint8_t *data = st->codecpar->extradata;
+        if (data && (data[0] | data[1] | data[2]) == 0 && data[3] == 1 && (data[4] & 0x1F) == 7) {
+            snprintf(attr, sizeof(attr),
+                     "avc1.%02x%02x%02x", data[5], data[6], data[7]);
+        } else {
+            goto fail;
+        }
+    } else if (st->codecpar->codec_id == AV_CODEC_ID_MP2) {
+        snprintf(attr, sizeof(attr), "mp4a.40.33");
+    } else if (st->codecpar->codec_id == AV_CODEC_ID_MP3) {
+        snprintf(attr, sizeof(attr), "mp4a.40.34");
+    } else if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
+        /* TODO : For HE-AAC, HE-AACv2, the last digit needs to be set to 5 and 29 respectively */
+        snprintf(attr, sizeof(attr), "mp4a.40.2");
+    } else if (st->codecpar->codec_id == AV_CODEC_ID_AC3) {
+        snprintf(attr, sizeof(attr), "ac-3");
+    } else if (st->codecpar->codec_id == AV_CODEC_ID_EAC3) {
+        snprintf(attr, sizeof(attr), "ec-3");
+    } else {
+        goto fail;
+    }
+    // Don't write the same attribute multiple times
+    if (!av_stristr(vs->codec_attr, attr)) {
+        snprintf(vs->codec_attr + codec_strlen,
+                 sizeof(vs->codec_attr) - codec_strlen,
+                 "%s%s", codec_strlen ? "," : "", attr);
+    }
+    return;
+
+fail:
+    vs->codec_attr[0] = '\0';
+    vs->attr_status = CODEC_ATTRIBUTE_WILL_NOT_BE_WRITTEN;
+    return;
+}
+
+static int replace_int_data_in_filename(char **s, const char *filename, char placeholder, int64_t number)
 {
     const char *p;
-    char *q, buf1[20], c;
-    int nd, len, addchar_count;
+    char *new_filename;
+    char c;
+    int nd, addchar_count;
     int found_count = 0;
+    AVBPrint buf;
+
+    av_bprint_init(&buf, 0, AV_BPRINT_SIZE_UNLIMITED);
 
-    q = buf;
     p = filename;
     for (;;) {
         c = *p;
@@ -306,13 +379,7 @@ static int replace_int_data_in_filename(char *buf, int buf_size, const char *fil
             }
 
             if (*(p + addchar_count) == placeholder) {
-                len = snprintf(buf1, sizeof(buf1), "%0*"PRId64, (number < 0) ? nd : nd++, number);
-                if (len < 1)  // returned error or empty buf1
-                    goto fail;
-                if ((q - buf + len) > buf_size - 1)
-                    goto fail;
-                memcpy(q, buf1, len);
-                q += len;
+                av_bprintf(&buf, "%0*"PRId64, (number < 0) ? nd : nd++, number);
                 p += (addchar_count + 1);
                 addchar_count = 0;
                 found_count++;
@@ -321,17 +388,17 @@ static int replace_int_data_in_filename(char *buf, int buf_size, const char *fil
         } else
             addchar_count = 1;
 
-        while (addchar_count--)
-            if ((q - buf) < buf_size - 1)
-                *q++ = *p++;
-            else
-                goto fail;
+        av_bprint_append_data(&buf, p, addchar_count);
+        p += addchar_count;
+    }
+    if (!av_bprint_is_complete(&buf)) {
+        av_bprint_finalize(&buf, NULL);
+        return -1;
     }
-    *q = '\0';
+    if (av_bprint_finalize(&buf, &new_filename) < 0 || !new_filename)
+        return -1;
+    *s = new_filename;
     return found_count;
-fail:
-    *q = '\0';
-    return -1;
 }
 
 static void write_styp(AVIOContext *pb)
@@ -344,6 +411,29 @@ static void write_styp(AVIOContext *pb)
     ffio_wfourcc(pb, "msix");
 }
 
+static int flush_dynbuf(VariantStream *vs, int *range_length)
+{
+    AVFormatContext *ctx = vs->avf;
+    uint8_t *buffer;
+
+    if (!ctx->pb) {
+        return AVERROR(EINVAL);
+    }
+
+    // flush
+    av_write_frame(ctx, NULL);
+    avio_flush(ctx->pb);
+
+    // write out to file
+    *range_length = avio_close_dyn_buf(ctx->pb, &buffer);
+    ctx->pb = NULL;
+    avio_write(vs->out, buffer, *range_length);
+    av_free(buffer);
+
+    // re-open buffer
+    return avio_open_dyn_buf(&ctx->pb);
+}
+
 static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls,
                                    VariantStream *vs) {
 
@@ -377,7 +467,7 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls,
         if (hls->segment_filename) {
             dirname = av_strdup(hls->segment_filename);
         } else {
-            dirname = av_strdup(vs->avf->filename);
+            dirname = av_strdup(vs->avf->url);
         }
         if (!dirname) {
             ret = AVERROR(ENOMEM);
@@ -404,7 +494,7 @@ static int hls_delete_old_segments(AVFormatContext *s, HLSContext *hls,
             av_strlcat(path, segment->filename, path_size);
         }
 
-        proto = avio_find_protocol_name(s->filename);
+        proto = avio_find_protocol_name(s->url);
         if (hls->method || (proto && !av_strcasecmp(proto, "http"))) {
             av_dict_set(&options, "method", "DELETE", 0);
             if ((ret = vs->avf->io_open(vs->avf, &out, path, AVIO_FLAG_WRITE, &options)) < 0)
@@ -474,12 +564,12 @@ static int do_encrypt(AVFormatContext *s, VariantStream *vs)
     AVIOContext *pb;
     uint8_t key[KEYSIZE];
 
-    len = strlen(s->filename) + 4 + 1;
+    len = strlen(s->url) + 4 + 1;
     hls->key_basename = av_mallocz(len);
     if (!hls->key_basename)
         return AVERROR(ENOMEM);
 
-    av_strlcpy(hls->key_basename, s->filename, len);
+    av_strlcpy(hls->key_basename, s->url, len);
     av_strlcat(hls->key_basename, ".key", len);
 
     if (hls->key_url) {
@@ -609,7 +699,10 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs)
         return ret;
     oc = vs->avf;
 
-    oc->filename[0]        = '\0';
+    oc->url                = av_strdup("");
+    if (!oc->url)
+        return AVERROR(ENOMEM);
+
     oc->oformat            = vs->oformat;
     oc->interrupt_callback = s->interrupt_callback;
     oc->max_delay          = s->max_delay;
@@ -669,7 +762,9 @@ static int hls_mux_init(AVFormatContext *s, VariantStream *vs)
         if ((ret = avio_open_dyn_buf(&oc->pb)) < 0)
             return ret;
 
-        if ((ret = s->io_open(s, &vs->out, vs->base_output_dirname, AVIO_FLAG_WRITE, &options)) < 0) {
+        ret = hlsenc_io_open(s, &vs->out, vs->base_output_dirname, &options);
+        av_dict_free(&options);
+        if (ret < 0) {
             av_log(s, AV_LOG_ERROR, "Failed to open segment '%s'\n", vs->fmp4_init_filename);
             return ret;
         }
@@ -716,42 +811,38 @@ static int sls_flags_filename_process(struct AVFormatContext *s, HLSContext *hls
 {
     if ((hls->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) &&
         strlen(vs->current_segment_final_filename_fmt)) {
-        av_strlcpy(vs->avf->filename, vs->current_segment_final_filename_fmt, sizeof(vs->avf->filename));
+        char * new_url = av_strdup(vs->current_segment_final_filename_fmt);
+        if (!new_url) {
+            av_free(en);
+            return AVERROR(ENOMEM);
+        }
+        ff_format_set_url(vs->avf, new_url);
         if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
-            char * filename = av_strdup(vs->avf->filename);  // %%s will be %s after strftime
-            if (!filename) {
-                av_free(en);
-                return AVERROR(ENOMEM);
-            }
-            if (replace_int_data_in_filename(vs->avf->filename, sizeof(vs->avf->filename),
-                filename, 's', pos + size) < 1) {
+            char *filename = NULL;
+            if (replace_int_data_in_filename(&filename, vs->avf->url, 's', pos + size) < 1) {
                 av_log(hls, AV_LOG_ERROR,
                        "Invalid second level segment filename template '%s', "
                         "you can try to remove second_level_segment_size flag\n",
-                       filename);
+                       vs->avf->url);
                 av_free(filename);
                 av_free(en);
                 return AVERROR(EINVAL);
             }
-            av_free(filename);
+            ff_format_set_url(vs->avf, filename);
         }
         if (hls->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
-            char * filename = av_strdup(vs->avf->filename);  // %%t will be %t after strftime
-            if (!filename) {
-                av_free(en);
-                return AVERROR(ENOMEM);
-            }
-            if (replace_int_data_in_filename(vs->avf->filename, sizeof(vs->avf->filename),
-                filename, 't',  (int64_t)round(duration * HLS_MICROSECOND_UNIT)) < 1) {
+            char *filename = NULL;
+            if (replace_int_data_in_filename(&filename, vs->avf->url,
+                't',  (int64_t)round(duration * HLS_MICROSECOND_UNIT)) < 1) {
                 av_log(hls, AV_LOG_ERROR,
                        "Invalid second level segment filename template '%s', "
                         "you can try to remove second_level_segment_time flag\n",
-                       filename);
+                       vs->avf->url);
                 av_free(filename);
                 av_free(en);
                 return AVERROR(EINVAL);
             }
-            av_free(filename);
+            ff_format_set_url(vs->avf, filename);
         }
     }
     return 0;
@@ -803,58 +894,52 @@ static int sls_flag_check_duration_size(HLSContext *hls, VariantStream *vs)
 static void sls_flag_file_rename(HLSContext *hls, VariantStream *vs, char *old_filename) {
     if ((hls->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) &&
         strlen(vs->current_segment_final_filename_fmt)) {
-        ff_rename(old_filename, vs->avf->filename, hls);
+        ff_rename(old_filename, vs->avf->url, hls);
     }
 }
 
 static int sls_flag_use_localtime_filename(AVFormatContext *oc, HLSContext *c, VariantStream *vs)
 {
     if (c->flags & HLS_SECOND_LEVEL_SEGMENT_INDEX) {
-        char * filename = av_strdup(oc->filename);  // %%d will be %d after strftime
-        if (!filename)
-            return AVERROR(ENOMEM);
-        if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+        char *filename = NULL;
+        if (replace_int_data_in_filename(&filename,
 #if FF_API_HLS_WRAP
-            filename, 'd', c->wrap ? vs->sequence % c->wrap : vs->sequence) < 1) {
+            oc->url, 'd', c->wrap ? vs->sequence % c->wrap : vs->sequence) < 1) {
 #else
-            filename, 'd', vs->sequence) < 1) {
+            oc->url, 'd', vs->sequence) < 1) {
 #endif
             av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
                     "you can try to remove second_level_segment_index flag\n",
-                   filename);
+                   oc->url);
             av_free(filename);
             return AVERROR(EINVAL);
         }
-        av_free(filename);
+        ff_format_set_url(oc, filename);
     }
     if (c->flags & (HLS_SECOND_LEVEL_SEGMENT_SIZE | HLS_SECOND_LEVEL_SEGMENT_DURATION)) {
-        av_strlcpy(vs->current_segment_final_filename_fmt, oc->filename,
+        av_strlcpy(vs->current_segment_final_filename_fmt, oc->url,
                    sizeof(vs->current_segment_final_filename_fmt));
         if (c->flags & HLS_SECOND_LEVEL_SEGMENT_SIZE) {
-            char * filename = av_strdup(oc->filename);  // %%s will be %s after strftime
-            if (!filename)
-                return AVERROR(ENOMEM);
-            if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename), filename, 's', 0) < 1) {
+            char *filename = NULL;
+            if (replace_int_data_in_filename(&filename, oc->url, 's', 0) < 1) {
                 av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
                         "you can try to remove second_level_segment_size flag\n",
-                       filename);
+                       oc->url);
                 av_free(filename);
                 return AVERROR(EINVAL);
             }
-            av_free(filename);
+            ff_format_set_url(oc, filename);
         }
         if (c->flags & HLS_SECOND_LEVEL_SEGMENT_DURATION) {
-            char * filename = av_strdup(oc->filename);  // %%t will be %t after strftime
-            if (!filename)
-                return AVERROR(ENOMEM);
-            if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename), filename, 't', 0) < 1) {
+            char *filename = NULL;
+            if (replace_int_data_in_filename(&filename, oc->url, 't', 0) < 1) {
                 av_log(c, AV_LOG_ERROR, "Invalid second level segment filename template '%s', "
                         "you can try to remove second_level_segment_time flag\n",
-                       filename);
+                       oc->url);
                 av_free(filename);
                 return AVERROR(EINVAL);
             }
-            av_free(filename);
+            ff_format_set_url(oc, filename);
         }
     }
     return 0;
@@ -878,10 +963,10 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls,
         return ret;
     }
 
-    filename = av_basename(vs->avf->filename);
+    filename = av_basename(vs->avf->url);
 
     if (hls->use_localtime_mkdir) {
-        filename = vs->avf->filename;
+        filename = vs->avf->url;
     }
     if ((find_segment_by_filename(vs->segments, filename) || find_segment_by_filename(vs->old_segments, filename))
         && !byterange_mode) {
@@ -890,7 +975,7 @@ static int hls_append_segment(struct AVFormatContext *s, HLSContext *hls,
     av_strlcpy(en->filename, filename, sizeof(en->filename));
 
     if(vs->has_subtitle)
-        av_strlcpy(en->sub_filename, av_basename(vs->vtt_avf->filename), sizeof(en->sub_filename));
+        av_strlcpy(en->sub_filename, av_basename(vs->vtt_avf->url), sizeof(en->sub_filename));
     else
         en->sub_filename[0] = '\0';
 
@@ -1016,10 +1101,15 @@ static int parse_playlist(AVFormatContext *s, const char *url, VariantStream *vs
             continue;
         } else if (line[0]) {
             if (is_segment) {
+                char *new_file = av_strdup(line);
+                if (!new_file) {
+                    ret = AVERROR(ENOMEM);
+                    goto fail;
+                }
+                ff_format_set_url(vs->avf, new_file);
                 is_segment = 0;
                 new_start_pos = avio_tell(vs->avf->pb);
                 vs->size = new_start_pos - vs->start_pos;
-                av_strlcpy(vs->avf->filename, line, sizeof(line));
                 ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
                 if (ret < 0)
                     goto fail;
@@ -1044,15 +1134,19 @@ static void hls_free_segments(HLSSegment *p)
     }
 }
 
-static void hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc)
+static int hls_rename_temp_file(AVFormatContext *s, AVFormatContext *oc)
 {
-    size_t len = strlen(oc->filename);
-    char final_filename[sizeof(oc->filename)];
+    size_t len = strlen(oc->url);
+    char *final_filename = av_strdup(oc->url);
+    int ret;
 
-    av_strlcpy(final_filename, oc->filename, len);
+    if (!final_filename)
+        return AVERROR(ENOMEM);
     final_filename[len-4] = '\0';
-    ff_rename(oc->filename, final_filename, s);
-    oc->filename[len-4] = '\0';
+    ret = ff_rename(oc->url, final_filename, s);
+    oc->url[len-4] = '\0';
+    av_freep(&final_filename);
+    return ret;
 }
 
 static int get_relative_url(const char *master_url, const char *media_url,
@@ -1077,13 +1171,13 @@ static int create_master_playlist(AVFormatContext *s,
                                   VariantStream * const input_vs)
 {
     HLSContext *hls = s->priv_data;
-    VariantStream *vs;
+    VariantStream *vs, *temp_vs;
     AVStream *vid_st, *aud_st;
-    AVIOContext *master_pb = 0;
     AVDictionary *options = NULL;
     unsigned int i, j;
     int m3u8_name_size, ret, bandwidth;
-    char *m3u8_rel_name;
+    char *m3u8_rel_name, *ccgroup;
+    ClosedCaptionsStream *ccs;
 
     input_vs->m3u8_created = 1;
     if (!hls->master_m3u8_created) {
@@ -1100,8 +1194,7 @@ static int create_master_playlist(AVFormatContext *s,
 
     set_http_options(s, &options, hls);
 
-    ret = s->io_open(s, &master_pb, hls->master_m3u8_url, AVIO_FLAG_WRITE,\
-                     &options);
+    ret = hlsenc_io_open(s, &hls->m3u8_out, hls->master_m3u8_url, &options);
     av_dict_free(&options);
     if (ret < 0) {
         av_log(NULL, AV_LOG_ERROR, "Failed to open master play list file '%s'\n",
@@ -1109,7 +1202,43 @@ static int create_master_playlist(AVFormatContext *s,
         goto fail;
     }
 
-    ff_hls_write_playlist_version(master_pb, hls->version);
+    ff_hls_write_playlist_version(hls->m3u8_out, hls->version);
+
+    for (i = 0; i < hls->nb_ccstreams; i++) {
+        ccs = &(hls->cc_streams[i]);
+        avio_printf(hls->m3u8_out, "#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS");
+        avio_printf(hls->m3u8_out, ",GROUP-ID=\"%s\"", ccs->ccgroup);
+        avio_printf(hls->m3u8_out, ",NAME=\"%s\"", ccs->instreamid);
+        if (ccs->language)
+            avio_printf(hls->m3u8_out, ",LANGUAGE=\"%s\"", ccs->language);
+        avio_printf(hls->m3u8_out, ",INSTREAM-ID=\"%s\"\n", ccs->instreamid);
+    }
+
+    /* For audio only variant streams add #EXT-X-MEDIA tag with attributes*/
+    for (i = 0; i < hls->nb_varstreams; i++) {
+        vs = &(hls->var_streams[i]);
+
+        if (vs->has_video || vs->has_subtitle || !vs->agroup)
+            continue;
+
+        m3u8_name_size = strlen(vs->m3u8_name) + 1;
+        m3u8_rel_name = av_malloc(m3u8_name_size);
+        if (!m3u8_rel_name) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        av_strlcpy(m3u8_rel_name, vs->m3u8_name, m3u8_name_size);
+        ret = get_relative_url(hls->master_m3u8_url, vs->m3u8_name,
+                               m3u8_rel_name, m3u8_name_size);
+        if (ret < 0) {
+            av_log(s, AV_LOG_ERROR, "Unable to find relative URL\n");
+            goto fail;
+        }
+
+        ff_hls_write_audio_rendition(hls->m3u8_out, vs->agroup, m3u8_rel_name, 0, 1);
+
+        av_freep(&m3u8_rel_name);
+    }
 
     /* For variant streams with video add #EXT-X-STREAM-INF tag with attributes*/
     for (i = 0; i < hls->nb_varstreams; i++) {
@@ -1143,6 +1272,25 @@ static int create_master_playlist(AVFormatContext *s,
             continue;
         }
 
+        /**
+         * Traverse through the list of audio only rendition streams and find
+         * the rendition which has highest bitrate in the same audio group
+         */
+        if (vs->agroup) {
+            for (j = 0; j < hls->nb_varstreams; j++) {
+                temp_vs = &(hls->var_streams[j]);
+                if (!temp_vs->has_video && !temp_vs->has_subtitle &&
+                    temp_vs->agroup &&
+                    !av_strcasecmp(temp_vs->agroup, vs->agroup)) {
+                    if (!aud_st)
+                        aud_st = temp_vs->streams[0];
+                    if (temp_vs->streams[0]->codecpar->bit_rate >
+                            aud_st->codecpar->bit_rate)
+                        aud_st = temp_vs->streams[0];
+                }
+            }
+        }
+
         bandwidth = 0;
         if (vid_st)
             bandwidth += vid_st->codecpar->bit_rate;
@@ -1150,7 +1298,23 @@ static int create_master_playlist(AVFormatContext *s,
             bandwidth += aud_st->codecpar->bit_rate;
         bandwidth += bandwidth / 10;
 
-        ff_hls_write_stream_info(vid_st, master_pb, bandwidth, m3u8_rel_name);
+        ccgroup = NULL;
+        if (vid_st && vs->ccgroup) {
+            /* check if this group name is available in the cc map string */
+            for (j = 0; j < hls->nb_ccstreams; j++) {
+                ccs = &(hls->cc_streams[j]);
+                if (!av_strcasecmp(ccs->ccgroup, vs->ccgroup)) {
+                    ccgroup = vs->ccgroup;
+                    break;
+                }
+            }
+            if (j == hls->nb_ccstreams)
+                av_log(NULL, AV_LOG_WARNING, "mapping ccgroup %s not found\n",
+                        vs->ccgroup);
+        }
+
+        ff_hls_write_stream_info(vid_st, hls->m3u8_out, bandwidth, m3u8_rel_name,
+                aud_st ? vs->agroup : NULL, vs->codec_attr, ccgroup);
 
         av_freep(&m3u8_rel_name);
     }
@@ -1158,7 +1322,7 @@ fail:
     if(ret >=0)
         hls->master_m3u8_created = 1;
     av_freep(&m3u8_rel_name);
-    ff_format_io_close(s, &master_pb);
+    hlsenc_io_close(s, &hls->m3u8_out, hls->master_m3u8_url);
     return ret;
 }
 
@@ -1168,11 +1332,9 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
     HLSSegment *en;
     int target_duration = 0;
     int ret = 0;
-    AVIOContext *out = NULL;
-    AVIOContext *sub_out = NULL;
     char temp_filename[1024];
     int64_t sequence = FFMAX(hls->start_sequence, vs->sequence - vs->nb_entries);
-    const char *proto = avio_find_protocol_name(s->filename);
+    const char *proto = avio_find_protocol_name(s->url);
     int use_rename = proto && !strcmp(proto, "file");
     static unsigned warned_non_file;
     char *key_uri = NULL;
@@ -1201,72 +1363,76 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
 
     set_http_options(s, &options, hls);
     snprintf(temp_filename, sizeof(temp_filename), use_rename ? "%s.tmp" : "%s", vs->m3u8_name);
-    if ((ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, &options)) < 0)
+    if ((ret = hlsenc_io_open(s, &hls->m3u8_out, temp_filename, &options)) < 0)
         goto fail;
 
     for (en = vs->segments; en; en = en->next) {
         if (target_duration <= en->duration)
-            target_duration = hls_get_int_from_double(en->duration);
+            target_duration = lrint(en->duration);
     }
 
     vs->discontinuity_set = 0;
-    ff_hls_write_playlist_header(out, hls->version, hls->allowcache,
+    ff_hls_write_playlist_header(hls->m3u8_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");
+        avio_printf(hls->m3u8_out, "#EXT-X-DISCONTINUITY\n");
         vs->discontinuity_set = 1;
     }
     if (vs->has_video && (hls->flags & HLS_INDEPENDENT_SEGMENTS)) {
-        avio_printf(out, "#EXT-X-INDEPENDENT-SEGMENTS\n");
+        avio_printf(hls->m3u8_out, "#EXT-X-INDEPENDENT-SEGMENTS\n");
     }
     for (en = vs->segments; en; en = en->next) {
         if ((hls->encrypt || hls->key_info_file) && (!key_uri || strcmp(en->key_uri, key_uri) ||
                                     av_strcasecmp(en->iv_string, iv_string))) {
-            avio_printf(out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri);
+            avio_printf(hls->m3u8_out, "#EXT-X-KEY:METHOD=AES-128,URI=\"%s\"", en->key_uri);
             if (*en->iv_string)
-                avio_printf(out, ",IV=0x%s", en->iv_string);
-            avio_printf(out, "\n");
+                avio_printf(hls->m3u8_out, ",IV=0x%s", en->iv_string);
+            avio_printf(hls->m3u8_out, "\n");
             key_uri = en->key_uri;
             iv_string = en->iv_string;
         }
 
         if ((hls->segment_type == SEGMENT_TYPE_FMP4) && (en == vs->segments)) {
-            ff_hls_write_init_file(out, vs->fmp4_init_filename,
+            ff_hls_write_init_file(hls->m3u8_out, vs->fmp4_init_filename,
                                    hls->flags & HLS_SINGLE_FILE, en->size, en->pos);
         }
 
-        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);
-
+        ret = ff_hls_write_file_entry(hls->m3u8_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 (ret < 0) {
+            av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n");
+        }
     }
 
     if (last && (hls->flags & HLS_OMIT_ENDLIST)==0)
-        ff_hls_write_end_list(out);
+        ff_hls_write_end_list(hls->m3u8_out);
 
     if( vs->vtt_m3u8_name ) {
-        if ((ret = s->io_open(s, &sub_out, vs->vtt_m3u8_name, AVIO_FLAG_WRITE, &options)) < 0)
+        if ((ret = hlsenc_io_open(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name, &options)) < 0)
             goto fail;
-        ff_hls_write_playlist_header(sub_out, hls->version, hls->allowcache,
+        ff_hls_write_playlist_header(hls->sub_m3u8_out, hls->version, hls->allowcache,
                                      target_duration, sequence, PLAYLIST_TYPE_NONE);
-
         for (en = vs->segments; en; en = en->next) {
-            ff_hls_write_file_entry(sub_out, 0, byterange_mode,
-                                    en->duration, 0, en->size, en->pos,
-                                    vs->baseurl, en->sub_filename, NULL);
+            ret = ff_hls_write_file_entry(hls->sub_m3u8_out, 0, byterange_mode,
+                                          en->duration, 0, en->size, en->pos,
+                                          vs->baseurl, en->sub_filename, NULL);
+            if (ret < 0) {
+                av_log(s, AV_LOG_WARNING, "ff_hls_write_file_entry get error\n");
+            }
         }
 
         if (last)
-            ff_hls_write_end_list(sub_out);
+            ff_hls_write_end_list(hls->sub_m3u8_out);
 
     }
 
 fail:
     av_dict_free(&options);
-    ff_format_io_close(s, &out);
-    ff_format_io_close(s, &sub_out);
+    hlsenc_io_close(s, &hls->m3u8_out, temp_filename);
+    hlsenc_io_close(s, &hls->sub_m3u8_out, vs->vtt_m3u8_name);
     if (ret >= 0 && use_rename)
         ff_rename(temp_filename, vs->m3u8_name, s);
 
@@ -1287,28 +1453,41 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
     int err = 0;
 
     if (c->flags & HLS_SINGLE_FILE) {
-        av_strlcpy(oc->filename, vs->basename,
-                   sizeof(oc->filename));
-        if (vs->vtt_basename)
-            av_strlcpy(vtt_oc->filename, vs->vtt_basename,
-                  sizeof(vtt_oc->filename));
+        char *new_name = av_strdup(vs->basename);
+        if (!new_name)
+            return AVERROR(ENOMEM);
+        ff_format_set_url(oc, new_name);
+        if (vs->vtt_basename) {
+            new_name = av_strdup(vs->vtt_basename);
+            if (!new_name)
+                return AVERROR(ENOMEM);
+            ff_format_set_url(vtt_oc, new_name);
+        }
     } else if (c->max_seg_size > 0) {
-        if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+        char *filename = NULL;
+        if (replace_int_data_in_filename(&filename,
 #if FF_API_HLS_WRAP
             vs->basename, 'd', c->wrap ? vs->sequence % c->wrap : vs->sequence) < 1) {
 #else
             vs->basename, 'd', vs->sequence) < 1) {
 #endif
+                av_free(filename);
                 av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s', you can try to use -use_localtime 1 with it\n", vs->basename);
                 return AVERROR(EINVAL);
         }
+        ff_format_set_url(oc, filename);
     } else {
         if (c->use_localtime) {
             time_t now0;
             struct tm *tm, tmpbuf;
+            int bufsize = strlen(vs->basename) + 1024;
+            char *buf = av_mallocz(bufsize);
+            if (!buf)
+                return AVERROR(ENOMEM);
             time(&now0);
             tm = localtime_r(&now0, &tmpbuf);
-            if (!strftime(oc->filename, sizeof(oc->filename), vs->basename, tm)) {
+            ff_format_set_url(oc, buf);
+            if (!strftime(oc->url, bufsize, vs->basename, tm)) {
                 av_log(oc, AV_LOG_ERROR, "Could not get segment filename with use_localtime\n");
                 return AVERROR(EINVAL);
             }
@@ -1320,7 +1499,7 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
 
             if (c->use_localtime_mkdir) {
                 const char *dir;
-                char *fn_copy = av_strdup(oc->filename);
+                char *fn_copy = av_strdup(oc->url);
                 if (!fn_copy) {
                     return AVERROR(ENOMEM);
                 }
@@ -1332,25 +1511,33 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
                 }
                 av_free(fn_copy);
             }
-        } else if (replace_int_data_in_filename(oc->filename, sizeof(oc->filename),
+        } else {
+            char *filename = NULL;
+            if (replace_int_data_in_filename(&filename,
 #if FF_API_HLS_WRAP
                    vs->basename, 'd', c->wrap ? vs->sequence % c->wrap : vs->sequence) < 1) {
 #else
                    vs->basename, 'd', vs->sequence) < 1) {
 #endif
-            av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -use_localtime 1 with it\n", vs->basename);
-            return AVERROR(EINVAL);
+                av_free(filename);
+                av_log(oc, AV_LOG_ERROR, "Invalid segment filename template '%s' you can try to use -use_localtime 1 with it\n", vs->basename);
+                return AVERROR(EINVAL);
+            }
+            ff_format_set_url(oc, filename);
         }
         if( vs->vtt_basename) {
-            if (replace_int_data_in_filename(vtt_oc->filename, sizeof(vtt_oc->filename),
+            char *filename = NULL;
+            if (replace_int_data_in_filename(&filename,
 #if FF_API_HLS_WRAP
                 vs->vtt_basename, 'd', c->wrap ? vs->sequence % c->wrap : vs->sequence) < 1) {
 #else
                 vs->vtt_basename, 'd', vs->sequence) < 1) {
 #endif
+                av_free(filename);
                 av_log(vtt_oc, AV_LOG_ERROR, "Invalid segment filename template '%s'\n", vs->vtt_basename);
                 return AVERROR(EINVAL);
             }
+            ff_format_set_url(vtt_oc, filename);
        }
     }
     vs->number++;
@@ -1358,7 +1545,10 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
     set_http_options(s, &options, c);
 
     if (c->flags & HLS_TEMP_FILE) {
-        av_strlcat(oc->filename, ".tmp", sizeof(oc->filename));
+        char *new_name = av_asprintf("%s.tmp", oc->url);
+        if (!new_name)
+            return AVERROR(ENOMEM);
+        ff_format_set_url(oc, new_name);
     }
 
     if (c->key_info_file || c->encrypt) {
@@ -1386,7 +1576,7 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
         if ((err = av_dict_set(&options, "encryption_iv", iv_string, 0)) < 0)
            goto fail;
 
-        filename = av_asprintf("crypto:%s", oc->filename);
+        filename = av_asprintf("crypto:%s", oc->url);
         if (!filename) {
             err = AVERROR(ENOMEM);
             goto fail;
@@ -1396,19 +1586,18 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
         av_dict_free(&options);
         if (err < 0)
             return err;
-    } else
-        if ((err = hlsenc_io_open(s, &oc->pb, oc->filename, &options)) < 0)
+    } else if (c->segment_type != SEGMENT_TYPE_FMP4) {
+        if ((err = hlsenc_io_open(s, &oc->pb, oc->url, &options)) < 0)
             goto fail;
+    }
     if (vs->vtt_basename) {
         set_http_options(s, &options, c);
-        if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->filename, &options)) < 0)
+        if ((err = hlsenc_io_open(s, &vtt_oc->pb, vtt_oc->url, &options)) < 0)
             goto fail;
     }
     av_dict_free(&options);
 
-    if (c->segment_type == SEGMENT_TYPE_FMP4 && !(c->flags & HLS_SINGLE_FILE)) {
-            write_styp(oc->pb);
-    } else {
+    if (c->segment_type != SEGMENT_TYPE_FMP4) {
         /* We only require one PAT/PMT per segment. */
         if (oc->oformat->priv_class && oc->priv_data) {
             char period[21];
@@ -1450,7 +1639,7 @@ static const char * get_default_pattern_localtime_fmt(AVFormatContext *s)
     return (HAVE_LIBC_MSVCRT || !strftime(b, sizeof(b), "%s", p) || !strcmp(b, "%s")) ? "-%Y%m%d%H%M%S.ts" : "-%s.ts";
 }
 
-static int format_name(char *name, int name_buf_len, int i)
+static int append_postfix(char *name, int name_buf_len, int i)
 {
     char *p;
     char extension[10] = {'\0'};
@@ -1469,6 +1658,91 @@ static int format_name(char *name, int name_buf_len, int i)
     return 0;
 }
 
+static int validate_name(int nb_vs, const char *fn)
+{
+    const char *filename, *subdir_name;
+    char *fn_dup = NULL;
+    int ret = 0;
+
+    if (!fn) {
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    fn_dup = av_strdup(fn);
+    if (!fn_dup) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    filename = av_basename(fn);
+    subdir_name = av_dirname(fn_dup);
+
+    if (nb_vs > 1 && !av_stristr(filename, "%v") && !av_stristr(subdir_name, "%v")) {
+        av_log(NULL, AV_LOG_ERROR, "More than 1 variant streams are present, %%v is expected in the filename %s\n",
+                fn);
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    if (av_stristr(filename, "%v") && av_stristr(subdir_name, "%v")) {
+        av_log(NULL, AV_LOG_ERROR, "%%v is expected either in filename or in the sub-directory name of file %s\n",
+                fn);
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+fail:
+    av_freep(&fn_dup);
+    return ret;
+}
+
+static int format_name(char *buf, int buf_len, int index)
+{
+    const char *proto, *dir;
+    char *orig_buf_dup = NULL, *mod_buf = NULL, *mod_buf_dup = NULL;
+    int ret = 0;
+
+    if (!av_stristr(buf, "%v"))
+        return ret;
+
+    orig_buf_dup = av_strdup(buf);
+    if (!orig_buf_dup) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    if (replace_int_data_in_filename(&mod_buf, orig_buf_dup, 'v', index) < 1) {
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    av_strlcpy(buf, mod_buf, buf_len);
+
+    proto = avio_find_protocol_name(orig_buf_dup);
+    dir = av_dirname(orig_buf_dup);
+
+    /* if %v is present in the file's directory, create sub-directory */
+    if (av_stristr(dir, "%v") && proto && !strcmp(proto, "file")) {
+        mod_buf_dup = av_strdup(buf);
+        if (!mod_buf_dup) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        dir = av_dirname(mod_buf_dup);
+        if (mkdir_p(dir) == -1 && errno != EEXIST) {
+            ret = AVERROR(errno);
+            goto fail;
+        }
+    }
+
+fail:
+    av_freep(&orig_buf_dup);
+    av_freep(&mod_buf_dup);
+    av_freep(&mod_buf);
+    return ret;
+}
+
 static int get_nth_codec_stream_index(AVFormatContext *s,
                                       enum AVMediaType codec_type,
                                       int stream_id)
@@ -1500,6 +1774,7 @@ static int parse_variant_stream_mapstring(AVFormatContext *s)
     /**
      * Expected format for var_stream_map string is as below:
      * "a:0,v:0 a:1,v:1"
+     * "a:0,agroup:a0 a:1,agroup:a1 v:0,agroup:a0  v:1,agroup:a1"
      * This string specifies how to group the audio, video and subtitle streams
      * into different variant streams. The variant stream groups are separated
      * by space.
@@ -1508,6 +1783,7 @@ static int parse_variant_stream_mapstring(AVFormatContext *s)
      * respectively. Allowed values are 0 to 9 digits (limited just based on
      * practical usage)
      *
+     * agroup: is key to specify audio group. A string can be given as value.
      */
     p = av_strdup(hls->var_stream_map);
     q = p;
@@ -1546,7 +1822,17 @@ static int parse_variant_stream_mapstring(AVFormatContext *s)
         while (keyval = av_strtok(varstr, ",", &saveptr2)) {
             varstr = NULL;
 
-            if (av_strstart(keyval, "v:", &val)) {
+            if (av_strstart(keyval, "agroup:", &val)) {
+                vs->agroup = av_strdup(val);
+                if (!vs->agroup)
+                    return AVERROR(ENOMEM);
+                continue;
+            } else if (av_strstart(keyval, "ccgroup:", &val)) {
+                vs->ccgroup = av_strdup(val);
+                if (!vs->ccgroup)
+                    return AVERROR(ENOMEM);
+                continue;
+            } else if (av_strstart(keyval, "v:", &val)) {
                 codec_type = AVMEDIA_TYPE_VIDEO;
             } else if (av_strstart(keyval, "a:", &val)) {
                 codec_type = AVMEDIA_TYPE_AUDIO;
@@ -1576,9 +1862,94 @@ static int parse_variant_stream_mapstring(AVFormatContext *s)
     return 0;
 }
 
+static int parse_cc_stream_mapstring(AVFormatContext *s)
+{
+    HLSContext *hls = s->priv_data;
+    int nb_ccstreams;
+    char *p, *q, *saveptr1, *saveptr2, *ccstr, *keyval;
+    const char *val;
+    ClosedCaptionsStream *ccs;
+
+    p = av_strdup(hls->cc_stream_map);
+    q = p;
+    while(av_strtok(q, " \t", &saveptr1)) {
+        q = NULL;
+        hls->nb_ccstreams++;
+    }
+    av_freep(&p);
+
+    hls->cc_streams = av_mallocz(sizeof(*hls->cc_streams) * hls->nb_ccstreams);
+    if (!hls->cc_streams)
+        return AVERROR(ENOMEM);
+
+    p = hls->cc_stream_map;
+    nb_ccstreams = 0;
+    while (ccstr = av_strtok(p, " \t", &saveptr1)) {
+        p = NULL;
+
+        if (nb_ccstreams < hls->nb_ccstreams)
+            ccs = &(hls->cc_streams[nb_ccstreams++]);
+        else
+            return AVERROR(EINVAL);
+
+        while (keyval = av_strtok(ccstr, ",", &saveptr2)) {
+            ccstr = NULL;
+
+            if (av_strstart(keyval, "ccgroup:", &val)) {
+                ccs->ccgroup = av_strdup(val);
+                if (!ccs->ccgroup)
+                    return AVERROR(ENOMEM);
+            } else if (av_strstart(keyval, "instreamid:", &val)) {
+                ccs->instreamid = av_strdup(val);
+                if (!ccs->instreamid)
+                    return AVERROR(ENOMEM);
+            } else if (av_strstart(keyval, "language:", &val)) {
+                ccs->language = av_strdup(val);
+                if (!ccs->language)
+                    return AVERROR(ENOMEM);
+            } else {
+                av_log(s, AV_LOG_ERROR, "Invalid keyval %s\n", keyval);
+                return AVERROR(EINVAL);
+            }
+        }
+
+        if (!ccs->ccgroup || !ccs->instreamid) {
+            av_log(s, AV_LOG_ERROR, "Insufficient parameters in cc stream map string\n");
+            return AVERROR(EINVAL);
+        }
+
+        if (av_strstart(ccs->instreamid, "CC", &val)) {
+            if(atoi(val) < 1 || atoi(val) > 4) {
+                av_log(s, AV_LOG_ERROR, "Invalid instream ID CC index %d in %s, range 1-4\n",
+                       atoi(val), ccs->instreamid);
+                return AVERROR(EINVAL);
+            }
+        } else if (av_strstart(ccs->instreamid, "SERVICE", &val)) {
+            if(atoi(val) < 1 || atoi(val) > 63) {
+                av_log(s, AV_LOG_ERROR, "Invalid instream ID SERVICE index %d in %s, range 1-63 \n",
+                       atoi(val), ccs->instreamid);
+                return AVERROR(EINVAL);
+            }
+        } else {
+            av_log(s, AV_LOG_ERROR, "Invalid instream ID %s, supported are CCn or SERIVICEn\n",
+                   ccs->instreamid);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return 0;
+}
+
 static int update_variant_stream_info(AVFormatContext *s) {
     HLSContext *hls = s->priv_data;
     unsigned int i;
+    int ret = 0;
+
+    if (hls->cc_stream_map) {
+        ret = parse_cc_stream_mapstring(s);
+        if (ret < 0)
+            return ret;
+    }
 
     if (hls->var_stream_map) {
         return parse_variant_stream_mapstring(s);
@@ -1596,6 +1967,13 @@ static int update_variant_stream_info(AVFormatContext *s) {
         if (!hls->var_streams[0].streams)
             return AVERROR(ENOMEM);
 
+        //by default, the first available ccgroup is mapped to the variant stream
+        if (hls->nb_ccstreams) {
+            hls->var_streams[0].ccgroup = av_strdup(hls->cc_streams[0].ccgroup);
+            if (!hls->var_streams[0].ccgroup)
+                return AVERROR(ENOMEM);
+        }
+
         for (i = 0; i < s->nb_streams; i++)
             hls->var_streams[0].streams[i] = s->streams[i];
     }
@@ -1604,416 +1982,108 @@ static int update_variant_stream_info(AVFormatContext *s) {
 
 static int update_master_pl_info(AVFormatContext *s) {
     HLSContext *hls = s->priv_data;
-    int m3u8_name_size, ret;
-    char *p;
+    const char *dir;
+    char *fn1= NULL, *fn2 = NULL;
+    int ret = 0;
 
-    m3u8_name_size = strlen(s->filename) + strlen(hls->master_pl_name) + 1;
-    hls->master_m3u8_url = av_malloc(m3u8_name_size);
-    if (!hls->master_m3u8_url) {
+    fn1 = av_strdup(s->url);
+    if (!fn1) {
         ret = AVERROR(ENOMEM);
-        return ret;
+        goto fail;
     }
 
-    av_strlcpy(hls->master_m3u8_url, s->filename, m3u8_name_size);
-    p = strrchr(hls->master_m3u8_url, '/') ?
-            strrchr(hls->master_m3u8_url, '/') :
-            strrchr(hls->master_m3u8_url, '\\');
-    if (p) {
-        *(p + 1) = '\0';
-        av_strlcat(hls->master_m3u8_url, hls->master_pl_name, m3u8_name_size);
-    } else {
-        av_strlcpy(hls->master_m3u8_url, hls->master_pl_name, m3u8_name_size);
+    dir = av_dirname(fn1);
+
+    /**
+     * if output file's directory has %v, variants are created in sub-directories
+     * then master is created at the sub-directories level
+     */
+    if (dir && av_stristr(av_basename(dir), "%v")) {
+        fn2 = av_strdup(dir);
+        if (!fn2) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        dir = av_dirname(fn2);
     }
 
-    return 0;
+    if (dir && strcmp(dir, "."))
+        hls->master_m3u8_url = av_append_path_component(dir, hls->master_pl_name);
+    else
+        hls->master_m3u8_url = av_strdup(hls->master_pl_name);
+
+    if (!hls->master_m3u8_url) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+fail:
+    av_freep(&fn1);
+    av_freep(&fn2);
+
+    return ret;
 }
 
 static int hls_write_header(AVFormatContext *s)
 {
     HLSContext *hls = s->priv_data;
     int ret, i, j;
-    char *p = NULL;
-    const char *pattern = "%d.ts";
-    const char *pattern_localtime_fmt = get_default_pattern_localtime_fmt(s);
-    const char *vtt_pattern = "%d.vtt";
     AVDictionary *options = NULL;
-    int basename_size = 0;
-    int vtt_basename_size = 0, m3u8_name_size = 0;
     VariantStream *vs = NULL;
-    int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1;
-
-    ret = update_variant_stream_info(s);
-    if (ret < 0) {
-        av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n",
-                ret);
-        goto fail;
-    }
-
-    //TODO: Updates needed to encryption functionality with periodic re-key when more than one variant streams are present
-    if (hls->nb_varstreams > 1 && hls->flags & HLS_PERIODIC_REKEY) {
-        ret = AVERROR(EINVAL);
-        av_log(s, AV_LOG_ERROR, "Periodic re-key not supported when more than one variant streams are present\n");
-        goto fail;
-    }
-
-    if (hls->master_pl_name) {
-        ret = update_master_pl_info(s);
-        if (ret < 0) {
-            av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n",
-                    ret);
-            goto fail;
-        }
-    }
-
-    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
-        pattern = "%d.m4s";
-    }
-    if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) ||
-        (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME)) {
-        time_t t = time(NULL); // we will need it in either case
-        if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) {
-            hls->start_sequence = (int64_t)t;
-        } else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME) {
-            char b[15];
-            struct tm *p, tmbuf;
-            if (!(p = localtime_r(&t, &tmbuf)))
-                return AVERROR(ENOMEM);
-            if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
-                return AVERROR(ENOMEM);
-            hls->start_sequence = strtoll(b, NULL, 10);
-        }
-        av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
-    }
 
     for (i = 0; i < hls->nb_varstreams; i++) {
         vs = &hls->var_streams[i];
 
-    vs->sequence       = hls->start_sequence;
-    hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
-    vs->start_pts      = AV_NOPTS_VALUE;
-    vs->end_pts      = AV_NOPTS_VALUE;
-    vs->current_segment_final_filename_fmt[0] = '\0';
-
-    if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) {
-        // Independent segments cannot be guaranteed when splitting by time
-        hls->flags &= ~HLS_INDEPENDENT_SEGMENTS;
-        av_log(s, AV_LOG_WARNING,
-               "'split_by_time' and 'independent_segments' cannot be enabled together. "
-               "Disabling 'independent_segments' flag\n");
-    }
-
-    if (hls->flags & HLS_PROGRAM_DATE_TIME) {
-        time_t now0;
-        time(&now0);
-        vs->initial_prog_date_time = now0;
-    }
-
-    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);
-            goto fail;
-        }
-    }
-
-    for (j = 0; j < vs->nb_streams; j++) {
-        vs->has_video +=
-            vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
-        vs->has_subtitle +=
-            vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
-    }
-
-    if (vs->has_video > 1)
-        av_log(s, AV_LOG_WARNING,
-               "More than a single video stream present, "
-               "expect issues decoding it.\n");
-
-    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
-        vs->oformat = av_guess_format("mp4", NULL, NULL);
-    } else {
-        vs->oformat = av_guess_format("mpegts", NULL, NULL);
-    }
-
-    if (!vs->oformat) {
-        ret = AVERROR_MUXER_NOT_FOUND;
-        goto fail;
-    }
-
-    if(vs->has_subtitle) {
-        vs->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
-        if (!vs->oformat) {
-            ret = AVERROR_MUXER_NOT_FOUND;
-            goto fail;
-        }
-    }
-
-    if (hls->segment_filename) {
-        basename_size = strlen(hls->segment_filename) + 1;
-        if (hls->nb_varstreams > 1) {
-            basename_size += strlen(POSTFIX_PATTERN);
-        }
-        vs->basename = av_malloc(basename_size);
-        if (!vs->basename) {
-            ret = AVERROR(ENOMEM);
-            goto fail;
-        }
-
-        av_strlcpy(vs->basename, hls->segment_filename, basename_size);
-    } else {
-        if (hls->flags & HLS_SINGLE_FILE) {
-            if (hls->segment_type == SEGMENT_TYPE_FMP4) {
-                pattern = ".m4s";
-            } else {
-                pattern = ".ts";
-            }
-        }
-
-        if (hls->use_localtime) {
-            basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1;
-        } else {
-            basename_size = strlen(s->filename) + strlen(pattern) + 1;
-        }
-
-        if (hls->nb_varstreams > 1) {
-            basename_size += strlen(POSTFIX_PATTERN);
-        }
-
-        vs->basename = av_malloc(basename_size);
-        if (!vs->basename) {
-            ret = AVERROR(ENOMEM);
-            goto fail;
-        }
-
-        av_strlcpy(vs->basename, s->filename, basename_size);
-
-        p = strrchr(vs->basename, '.');
-        if (p)
-            *p = '\0';
-        if (hls->use_localtime) {
-            av_strlcat(vs->basename, pattern_localtime_fmt, basename_size);
-        } else {
-            av_strlcat(vs->basename, pattern, basename_size);
-        }
-    }
-
-    m3u8_name_size = strlen(s->filename) + 1;
-    if (hls->nb_varstreams > 1) {
-        m3u8_name_size += strlen(POSTFIX_PATTERN);
-    }
-
-    vs->m3u8_name = av_malloc(m3u8_name_size);
-    if (!vs->m3u8_name ) {
-        ret = AVERROR(ENOMEM);
-        goto fail;
-    }
-
-    av_strlcpy(vs->m3u8_name, s->filename, m3u8_name_size);
-
-    if (hls->nb_varstreams > 1) {
-        ret = format_name(vs->basename, basename_size, i);
-        if (ret < 0)
-             goto fail;
-        ret = format_name(vs->m3u8_name, m3u8_name_size, i);
-        if (ret < 0)
-             goto fail;
-    }
-
-    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
-        if (hls->nb_varstreams > 1)
-            fmp4_init_filename_len += strlen(POSTFIX_PATTERN);
-        vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len);
-        if (!vs->fmp4_init_filename ) {
-            ret = AVERROR(ENOMEM);
+        av_dict_copy(&options, hls->format_options, 0);
+        ret = avformat_write_header(vs->avf, &options);
+        if (av_dict_count(options)) {
+            av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str);
+            ret = AVERROR(EINVAL);
+            av_dict_free(&options);
             goto fail;
         }
-        av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename,
-                fmp4_init_filename_len);
-        if (hls->nb_varstreams > 1) {
-            ret = format_name(vs->fmp4_init_filename, fmp4_init_filename_len, i);
-            if (ret < 0)
-                 goto fail;
-        }
-
-        if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
-            fmp4_init_filename_len = strlen(vs->fmp4_init_filename) + 1;
-            vs->base_output_dirname = av_malloc(fmp4_init_filename_len);
-            if (!vs->base_output_dirname) {
-                ret = AVERROR(ENOMEM);
-                goto fail;
-            }
-            av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
-                    fmp4_init_filename_len);
-        } else {
-            fmp4_init_filename_len = strlen(vs->m3u8_name) +
-                                         strlen(vs->fmp4_init_filename) + 1;
-
-            vs->base_output_dirname = av_malloc(fmp4_init_filename_len);
-            if (!vs->base_output_dirname) {
-                ret = AVERROR(ENOMEM);
-                goto fail;
+        av_dict_free(&options);
+        //av_assert0(s->nb_streams == hls->avf->nb_streams);
+        for (j = 0; j < vs->nb_streams; j++) {
+            AVStream *inner_st;
+            AVStream *outer_st = vs->streams[j];
+
+            if (hls->max_seg_size > 0) {
+                if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
+                    (outer_st->codecpar->bit_rate > hls->max_seg_size)) {
+                    av_log(s, AV_LOG_WARNING, "Your video bitrate is bigger than hls_segment_size, "
+                           "(%"PRId64 " > %"PRId64 "), the result maybe not be what you want.",
+                           outer_st->codecpar->bit_rate, hls->max_seg_size);
+                }
             }
 
-            av_strlcpy(vs->base_output_dirname, vs->m3u8_name,
-                    fmp4_init_filename_len);
-            p = strrchr(vs->base_output_dirname, '/');
-            if (p) {
-                *(p + 1) = '\0';
-                av_strlcat(vs->base_output_dirname, vs->fmp4_init_filename,
-                        fmp4_init_filename_len);
-            } else {
-                av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
-                        fmp4_init_filename_len);
+            if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
+                inner_st = vs->avf->streams[j];
+            else if (vs->vtt_avf)
+                inner_st = vs->vtt_avf->streams[0];
+            else {
+                /* We have a subtitle stream, when the user does not want one */
+                inner_st = NULL;
+                continue;
             }
-        }
-    }
-
-    if (!hls->use_localtime) {
-        ret = sls_flag_check_duration_size_index(hls);
-        if (ret < 0) {
-             goto fail;
-        }
-    } else {
-        ret = sls_flag_check_duration_size(hls, vs);
-        if (ret < 0) {
-             goto fail;
-        }
-    }
-    if(vs->has_subtitle) {
-
-        if (hls->flags & HLS_SINGLE_FILE)
-            vtt_pattern = ".vtt";
-        vtt_basename_size = strlen(s->filename) + strlen(vtt_pattern) + 1;
-        if (hls->nb_varstreams > 1) {
-            vtt_basename_size += strlen(POSTFIX_PATTERN);
-        }
-
-        vs->vtt_basename = av_malloc(vtt_basename_size);
-        if (!vs->vtt_basename) {
-            ret = AVERROR(ENOMEM);
-            goto fail;
-        }
-        vs->vtt_m3u8_name = av_malloc(vtt_basename_size);
-        if (!vs->vtt_m3u8_name ) {
-            ret = AVERROR(ENOMEM);
-            goto fail;
-        }
-        av_strlcpy(vs->vtt_basename, s->filename, vtt_basename_size);
-        p = strrchr(vs->vtt_basename, '.');
-        if (p)
-            *p = '\0';
-
-        if( hls->subtitle_filename ) {
-            strcpy(vs->vtt_m3u8_name, hls->subtitle_filename);
-        } else {
-            strcpy(vs->vtt_m3u8_name, vs->vtt_basename);
-            av_strlcat(vs->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size);
-        }
-        av_strlcat(vs->vtt_basename, vtt_pattern, vtt_basename_size);
-
-        if (hls->nb_varstreams > 1) {
-            ret= format_name(vs->vtt_basename, vtt_basename_size, i);
-            if (ret < 0)
-                 goto fail;
-            ret = format_name(vs->vtt_m3u8_name, vtt_basename_size, i);
-            if (ret < 0)
-                 goto fail;
-        }
-    }
-
-    if (hls->baseurl) {
-        int baseurl_len;
-        baseurl_len = strlen(hls->baseurl);
-        vs->baseurl = av_malloc(baseurl_len);
-        if (!vs->baseurl) {
-            ret = AVERROR(ENOMEM);
-            goto fail;
-        }
-        av_strlcpy(vs->baseurl, hls->baseurl, baseurl_len);
-    }
-
-    if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) {
-        vs->fmp4_init_filename  = av_strdup(vs->basename);
-        if (!vs->fmp4_init_filename) {
-            ret = AVERROR(ENOMEM);
-            goto fail;
-        }
-    }
-
-    if ((ret = hls_mux_init(s, vs)) < 0)
-        goto fail;
-
-    if (hls->flags & HLS_APPEND_LIST) {
-        parse_playlist(s, vs->m3u8_name, vs);
-        vs->discontinuity = 1;
-        if (hls->init_time > 0) {
-            av_log(s, AV_LOG_WARNING, "append_list mode does not support hls_init_time,"
-                   " hls_init_time value will have no effect\n");
-            hls->init_time = 0;
-            hls->recording_time = hls->time * AV_TIME_BASE;
-        }
-    }
-
-    if (hls->segment_type != SEGMENT_TYPE_FMP4 || hls->flags & HLS_SINGLE_FILE) {
-        if ((ret = hls_start(s, vs)) < 0)
-            goto fail;
-    }
-
-    av_dict_copy(&options, hls->format_options, 0);
-    ret = avformat_write_header(vs->avf, &options);
-    if (av_dict_count(options)) {
-        av_log(s, AV_LOG_ERROR, "Some of provided format options in '%s' are not recognized\n", hls->format_options_str);
-        ret = AVERROR(EINVAL);
-        av_dict_free(&options);
-        goto fail;
-    }
-    av_dict_free(&options);
-    //av_assert0(s->nb_streams == hls->avf->nb_streams);
-    for (j = 0; j < vs->nb_streams; j++) {
-        AVStream *inner_st;
-        AVStream *outer_st = vs->streams[j];
-
-        if (hls->max_seg_size > 0) {
-            if ((outer_st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
-                (outer_st->codecpar->bit_rate > hls->max_seg_size)) {
-                av_log(s, AV_LOG_WARNING, "Your video bitrate is bigger than hls_segment_size, "
-                       "(%"PRId64 " > %"PRId64 "), the result maybe not be what you want.",
-                       outer_st->codecpar->bit_rate, hls->max_seg_size);
+            avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
+            write_codec_attr(outer_st, vs);
+
+        }
+        /* Update the Codec Attr string for the mapped audio groups */
+        if (vs->has_video && vs->agroup) {
+            for (j = 0; j < hls->nb_varstreams; j++) {
+                VariantStream *vs_agroup = &(hls->var_streams[j]);
+                if (!vs_agroup->has_video && !vs_agroup->has_subtitle &&
+                    vs_agroup->agroup &&
+                    !av_strcasecmp(vs_agroup->agroup, vs->agroup)) {
+                    write_codec_attr(vs_agroup->streams[0], vs);
+                }
             }
         }
-
-        if (outer_st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE)
-            inner_st = vs->avf->streams[j];
-        else if (vs->vtt_avf)
-            inner_st = vs->vtt_avf->streams[0];
-        else {
-            /* We have a subtitle stream, when the user does not want one */
-            inner_st = NULL;
-            continue;
-        }
-        avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den);
-    }
     }
 fail:
 
-    if (ret < 0) {
-        av_freep(&hls->key_basename);
-        for (i = 0; i < hls->nb_varstreams && hls->var_streams; i++) {
-            vs = &hls->var_streams[i];
-            av_freep(&vs->basename);
-            av_freep(&vs->vtt_basename);
-            av_freep(&vs->fmp4_init_filename);
-            av_freep(&vs->m3u8_name);
-            av_freep(&vs->vtt_m3u8_name);
-            av_freep(&vs->streams);
-            av_freep(&vs->baseurl);
-            if (vs->avf)
-                avformat_free_context(vs->avf);
-            if (vs->vtt_avf)
-                avformat_free_context(vs->vtt_avf);
-        }
-        av_freep(&hls->var_streams);
-        av_freep(&hls->master_m3u8_url);
-    }
     return ret;
 }
 
@@ -2098,36 +2168,34 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
     if (vs->packets_written && can_split && av_compare_ts(pkt->pts - vs->start_pts, st->time_base,
                                    end_pts, AV_TIME_BASE_Q) >= 0) {
         int64_t new_start_pos;
-        char *old_filename = av_strdup(vs->avf->filename);
+        char *old_filename = NULL;
         int byterange_mode = (hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size > 0);
 
-        if (!old_filename) {
-            return AVERROR(ENOMEM);
-        }
-
         av_write_frame(vs->avf, NULL); /* Flush any buffered data */
 
         new_start_pos = avio_tell(vs->avf->pb);
         vs->size = new_start_pos - vs->start_pos;
 
         if (!byterange_mode) {
-            if (hls->segment_type == SEGMENT_TYPE_FMP4 && !vs->init_range_length) {
-                avio_flush(oc->pb);
-                range_length = avio_close_dyn_buf(oc->pb, &buffer);
-                avio_write(vs->out, buffer, range_length);
-                vs->init_range_length = range_length;
-                avio_open_dyn_buf(&oc->pb);
-                vs->packets_written = 0;
-                ff_format_io_close(s, &vs->out);
-                hlsenc_io_close(s, &vs->out, vs->base_output_dirname);
+            if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+                if (!vs->init_range_length) {
+                    avio_flush(oc->pb);
+                    range_length = avio_close_dyn_buf(oc->pb, &buffer);
+                    avio_write(vs->out, buffer, range_length);
+                    vs->init_range_length = range_length;
+                    avio_open_dyn_buf(&oc->pb);
+                    vs->packets_written = 0;
+                    ff_format_io_close(s, &vs->out);
+                    hlsenc_io_close(s, &vs->out, vs->base_output_dirname);
+                }
             } else {
-                hlsenc_io_close(s, &oc->pb, oc->filename);
+                hlsenc_io_close(s, &oc->pb, oc->url);
             }
             if (vs->vtt_avf) {
-                hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->filename);
+                hlsenc_io_close(s, &vs->vtt_avf->pb, vs->vtt_avf->url);
             }
         }
-        if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
+        if ((hls->flags & HLS_TEMP_FILE) && oc->url[0]) {
             if (!(hls->flags & HLS_SINGLE_FILE) || (hls->max_seg_size <= 0))
                 if ((vs->avf->oformat->priv_class && vs->avf->priv_data) && hls->segment_type != SEGMENT_TYPE_FMP4)
                     av_opt_set(vs->avf->priv_data, "mpegts_flags", "resend_headers", 0);
@@ -2138,8 +2206,27 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
             vs->number--;
         }
 
-        if (!vs->fmp4_init_mode || byterange_mode)
-            ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
+        if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+            ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL);
+            if (ret < 0) {
+                av_log(NULL, AV_LOG_ERROR, "Failed to open file '%s'\n",
+                    vs->avf->url);
+                return ret;
+            }
+            write_styp(vs->out);
+            ret = flush_dynbuf(vs, &range_length);
+            if (ret < 0) {
+                return ret;
+            }
+            ff_format_io_close(s, &vs->out);
+        }
+
+        old_filename = av_strdup(vs->avf->url);
+        if (!old_filename) {
+            return AVERROR(ENOMEM);
+        }
+
+        ret = hls_append_segment(s, hls, vs, vs->duration, vs->start_pos, vs->size);
         vs->start_pos = new_start_pos;
         if (ret < 0) {
             av_free(old_filename);
@@ -2192,6 +2279,7 @@ static int hls_write_trailer(struct AVFormatContext *s)
     AVFormatContext *vtt_oc = NULL;
     char *old_filename = NULL;
     int i;
+    int ret = 0;
     VariantStream *vs = NULL;
 
     for (i = 0; i < hls->nb_varstreams; i++) {
@@ -2199,20 +2287,41 @@ static int hls_write_trailer(struct AVFormatContext *s)
 
     oc = vs->avf;
     vtt_oc = vs->vtt_avf;
-    old_filename = av_strdup(vs->avf->filename);
+    old_filename = av_strdup(vs->avf->url);
 
     if (!old_filename) {
         return AVERROR(ENOMEM);
     }
+    if ( hls->segment_type == SEGMENT_TYPE_FMP4) {
+        int range_length = 0;
+        ret = hlsenc_io_open(s, &vs->out, vs->avf->url, NULL);
+        if (ret < 0) {
+            av_log(NULL, AV_LOG_ERROR, "Failed to open file '%s'\n", vs->avf->url);
+            goto failed;
+        }
+        write_styp(vs->out);
+        ret = flush_dynbuf(vs, &range_length);
+        if (ret < 0) {
+            goto failed;
+        }
+        ff_format_io_close(s, &vs->out);
+    }
 
-
+failed:
     av_write_trailer(oc);
     if (oc->pb) {
         vs->size = avio_tell(vs->avf->pb) - vs->start_pos;
-        ff_format_io_close(s, &oc->pb);
+        if (hls->segment_type != SEGMENT_TYPE_FMP4)
+            ff_format_io_close(s, &oc->pb);
 
-        if ((hls->flags & HLS_TEMP_FILE) && oc->filename[0]) {
+        if ((hls->flags & HLS_TEMP_FILE) && oc->url[0]) {
             hls_rename_temp_file(s, oc);
+            av_free(old_filename);
+            old_filename = av_strdup(vs->avf->url);
+
+            if (!old_filename) {
+                return AVERROR(ENOMEM);
+            }
         }
 
         /* after av_write_trailer, then duration + 1 duration per packet */
@@ -2246,15 +2355,380 @@ static int hls_write_trailer(struct AVFormatContext *s)
     av_free(old_filename);
     av_freep(&vs->m3u8_name);
     av_freep(&vs->streams);
+    av_freep(&vs->agroup);
+    av_freep(&vs->ccgroup);
     av_freep(&vs->baseurl);
     }
 
+    for (i = 0; i < hls->nb_ccstreams; i++) {
+        ClosedCaptionsStream *ccs = &hls->cc_streams[i];
+        av_freep(&ccs->ccgroup);
+        av_freep(&ccs->instreamid);
+        av_freep(&ccs->language);
+    }
+
+    ff_format_io_close(s, &hls->m3u8_out);
+    ff_format_io_close(s, &hls->sub_m3u8_out);
     av_freep(&hls->key_basename);
     av_freep(&hls->var_streams);
+    av_freep(&hls->cc_streams);
     av_freep(&hls->master_m3u8_url);
     return 0;
 }
 
+
+static int hls_init(AVFormatContext *s)
+{
+    int ret = 0;
+    int i = 0;
+    int j = 0;
+    HLSContext *hls = s->priv_data;
+    const char *pattern = "%d.ts";
+    VariantStream *vs = NULL;
+    int basename_size = 0;
+    const char *pattern_localtime_fmt = get_default_pattern_localtime_fmt(s);
+    const char *vtt_pattern = "%d.vtt";
+    char *p = NULL;
+    int vtt_basename_size = 0;
+    int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1;
+
+    ret = update_variant_stream_info(s);
+    if (ret < 0) {
+        av_log(s, AV_LOG_ERROR, "Variant stream info update failed with status %x\n",
+                ret);
+        goto fail;
+    }
+    //TODO: Updates needed to encryption functionality with periodic re-key when more than one variant streams are present
+    if (hls->nb_varstreams > 1 && hls->flags & HLS_PERIODIC_REKEY) {
+        ret = AVERROR(EINVAL);
+        av_log(s, AV_LOG_ERROR, "Periodic re-key not supported when more than one variant streams are present\n");
+        goto fail;
+    }
+
+    ret = validate_name(hls->nb_varstreams, s->url);
+    if (ret < 0)
+        goto fail;
+
+    if (hls->segment_filename) {
+        ret = validate_name(hls->nb_varstreams, hls->segment_filename);
+        if (ret < 0)
+            goto fail;
+    }
+
+    if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
+        ret = validate_name(hls->nb_varstreams, hls->fmp4_init_filename);
+        if (ret < 0)
+            goto fail;
+    }
+
+    if (hls->subtitle_filename) {
+        ret = validate_name(hls->nb_varstreams, hls->subtitle_filename);
+        if (ret < 0)
+            goto fail;
+    }
+
+    if (hls->master_pl_name) {
+        ret = update_master_pl_info(s);
+        if (ret < 0) {
+            av_log(s, AV_LOG_ERROR, "Master stream info update failed with status %x\n",
+                    ret);
+            goto fail;
+        }
+    }
+
+    if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+        pattern = "%d.m4s";
+    }
+    if ((hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) ||
+        (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME)) {
+        time_t t = time(NULL); // we will need it in either case
+        if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_SECONDS_SINCE_EPOCH) {
+            hls->start_sequence = (int64_t)t;
+        } else if (hls->start_sequence_source_type == HLS_START_SEQUENCE_AS_FORMATTED_DATETIME) {
+            char b[15];
+            struct tm *p, tmbuf;
+            if (!(p = localtime_r(&t, &tmbuf)))
+                return AVERROR(ENOMEM);
+            if (!strftime(b, sizeof(b), "%Y%m%d%H%M%S", p))
+                return AVERROR(ENOMEM);
+            hls->start_sequence = strtoll(b, NULL, 10);
+        }
+        av_log(hls, AV_LOG_DEBUG, "start_number evaluated to %"PRId64"\n", hls->start_sequence);
+    }
+
+    hls->recording_time = (hls->init_time ? hls->init_time : hls->time) * AV_TIME_BASE;
+    for (i = 0; i < hls->nb_varstreams; i++) {
+        vs = &hls->var_streams[i];
+
+        vs->m3u8_name = av_strdup(s->url);
+        if (!vs->m3u8_name ) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+        ret = format_name(vs->m3u8_name, strlen(s->url) + 1, i);
+        if (ret < 0)
+            goto fail;
+
+        vs->sequence       = hls->start_sequence;
+        vs->start_pts      = AV_NOPTS_VALUE;
+        vs->end_pts      = AV_NOPTS_VALUE;
+        vs->current_segment_final_filename_fmt[0] = '\0';
+
+        if (hls->flags & HLS_SPLIT_BY_TIME && hls->flags & HLS_INDEPENDENT_SEGMENTS) {
+            // Independent segments cannot be guaranteed when splitting by time
+            hls->flags &= ~HLS_INDEPENDENT_SEGMENTS;
+            av_log(s, AV_LOG_WARNING,
+                   "'split_by_time' and 'independent_segments' cannot be enabled together. "
+                   "Disabling 'independent_segments' flag\n");
+        }
+
+        if (hls->flags & HLS_PROGRAM_DATE_TIME) {
+            time_t now0;
+            time(&now0);
+            vs->initial_prog_date_time = now0;
+        }
+        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);
+                goto fail;
+            }
+        }
+
+        for (j = 0; j < vs->nb_streams; j++) {
+            vs->has_video += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
+            vs->has_subtitle += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
+        }
+
+        if (vs->has_video > 1)
+            av_log(s, AV_LOG_WARNING, "More than a single video stream present, expect issues decoding it.\n");
+        if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+            vs->oformat = av_guess_format("mp4", NULL, NULL);
+        } else {
+            vs->oformat = av_guess_format("mpegts", NULL, NULL);
+        }
+
+        if (!vs->oformat) {
+            ret = AVERROR_MUXER_NOT_FOUND;
+            goto fail;
+        }
+
+        if (vs->has_subtitle) {
+            vs->vtt_oformat = av_guess_format("webvtt", NULL, NULL);
+            if (!vs->oformat) {
+                ret = AVERROR_MUXER_NOT_FOUND;
+                goto fail;
+            }
+        }
+        if (hls->segment_filename) {
+            basename_size = strlen(hls->segment_filename) + 1;
+            vs->basename = av_malloc(basename_size);
+            if (!vs->basename) {
+                ret = AVERROR(ENOMEM);
+                goto fail;
+            }
+
+            av_strlcpy(vs->basename, hls->segment_filename, basename_size);
+            ret = format_name(vs->basename, basename_size, i);
+            if (ret < 0)
+                goto fail;
+        } else {
+            if (hls->flags & HLS_SINGLE_FILE) {
+                if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+                    pattern = ".m4s";
+                } else {
+                    pattern = ".ts";
+                }
+            }
+
+            if (hls->use_localtime) {
+                basename_size = strlen(vs->m3u8_name) + strlen(pattern_localtime_fmt) + 1;
+            } else {
+                basename_size = strlen(vs->m3u8_name) + strlen(pattern) + 1;
+            }
+
+            vs->basename = av_malloc(basename_size);
+            if (!vs->basename) {
+                ret = AVERROR(ENOMEM);
+                goto fail;
+            }
+
+            av_strlcpy(vs->basename, vs->m3u8_name, basename_size);
+
+            p = strrchr(vs->basename, '.');
+            if (p)
+                *p = '\0';
+            if (hls->use_localtime) {
+                av_strlcat(vs->basename, pattern_localtime_fmt, basename_size);
+            } else {
+                av_strlcat(vs->basename, pattern, basename_size);
+            }
+        }
+
+        if (hls->segment_type == SEGMENT_TYPE_FMP4) {
+            if (hls->nb_varstreams > 1)
+                fmp4_init_filename_len += strlen(POSTFIX_PATTERN);
+            vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len);
+            if (!vs->fmp4_init_filename ) {
+                ret = AVERROR(ENOMEM);
+                goto fail;
+            }
+            av_strlcpy(vs->fmp4_init_filename, hls->fmp4_init_filename,
+                       fmp4_init_filename_len);
+
+            if (av_strcasecmp(hls->fmp4_init_filename, "init.mp4")) {
+                ret = format_name(vs->fmp4_init_filename, fmp4_init_filename_len, i);
+                if (ret < 0)
+                    goto fail;
+
+                fmp4_init_filename_len = strlen(vs->fmp4_init_filename) + 1;
+                vs->base_output_dirname = av_malloc(fmp4_init_filename_len);
+                if (!vs->base_output_dirname) {
+                    ret = AVERROR(ENOMEM);
+                    goto fail;
+                }
+                av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
+                           fmp4_init_filename_len);
+            } else {
+                if (hls->nb_varstreams > 1) {
+                    ret = append_postfix(vs->fmp4_init_filename, fmp4_init_filename_len, i);
+                    if (ret < 0)
+                         goto fail;
+                }
+
+                fmp4_init_filename_len = strlen(vs->m3u8_name) +
+                    strlen(vs->fmp4_init_filename) + 1;
+
+                vs->base_output_dirname = av_malloc(fmp4_init_filename_len);
+                if (!vs->base_output_dirname) {
+                    ret = AVERROR(ENOMEM);
+                    goto fail;
+                }
+
+                av_strlcpy(vs->base_output_dirname, vs->m3u8_name,
+                           fmp4_init_filename_len);
+                p = strrchr(vs->base_output_dirname, '/');
+                if (p) {
+                    *(p + 1) = '\0';
+                    av_strlcat(vs->base_output_dirname, vs->fmp4_init_filename,
+                               fmp4_init_filename_len);
+                } else {
+                    av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
+                               fmp4_init_filename_len);
+                }
+            }
+        }
+
+        if (!hls->use_localtime) {
+            ret = sls_flag_check_duration_size_index(hls);
+            if (ret < 0) {
+                goto fail;
+            }
+        } else {
+            ret = sls_flag_check_duration_size(hls, vs);
+            if (ret < 0) {
+                goto fail;
+            }
+        }
+        if (vs->has_subtitle) {
+
+            if (hls->flags & HLS_SINGLE_FILE)
+                vtt_pattern = ".vtt";
+            vtt_basename_size = strlen(vs->m3u8_name) + strlen(vtt_pattern) + 1;
+
+            vs->vtt_basename = av_malloc(vtt_basename_size);
+            if (!vs->vtt_basename) {
+                ret = AVERROR(ENOMEM);
+                goto fail;
+            }
+            vs->vtt_m3u8_name = av_malloc(vtt_basename_size);
+            if (!vs->vtt_m3u8_name ) {
+                ret = AVERROR(ENOMEM);
+                goto fail;
+            }
+            av_strlcpy(vs->vtt_basename, vs->m3u8_name, vtt_basename_size);
+            p = strrchr(vs->vtt_basename, '.');
+            if (p)
+                *p = '\0';
+
+            if ( hls->subtitle_filename ) {
+                strcpy(vs->vtt_m3u8_name, hls->subtitle_filename);
+                ret = format_name(vs->vtt_m3u8_name, vtt_basename_size, i);
+                if (ret < 0)
+                    goto fail;
+            } else {
+                strcpy(vs->vtt_m3u8_name, vs->vtt_basename);
+                av_strlcat(vs->vtt_m3u8_name, "_vtt.m3u8", vtt_basename_size);
+            }
+            av_strlcat(vs->vtt_basename, vtt_pattern, vtt_basename_size);
+        }
+
+        if (hls->baseurl) {
+            vs->baseurl = av_strdup(hls->baseurl);
+            if (!vs->baseurl) {
+                ret = AVERROR(ENOMEM);
+                goto fail;
+            }
+        }
+
+        if ((hls->flags & HLS_SINGLE_FILE) && (hls->segment_type == SEGMENT_TYPE_FMP4)) {
+            vs->fmp4_init_filename  = av_strdup(vs->basename);
+            if (!vs->fmp4_init_filename) {
+                ret = AVERROR(ENOMEM);
+                goto fail;
+            }
+        }
+        if ((ret = hls_mux_init(s, vs)) < 0)
+            goto fail;
+
+        if (hls->flags & HLS_APPEND_LIST) {
+            parse_playlist(s, vs->m3u8_name, vs);
+            vs->discontinuity = 1;
+            if (hls->init_time > 0) {
+                av_log(s, AV_LOG_WARNING, "append_list mode does not support hls_init_time,"
+                       " hls_init_time value will have no effect\n");
+                hls->init_time = 0;
+                hls->recording_time = hls->time * AV_TIME_BASE;
+            }
+        }
+
+        if ((ret = hls_start(s, vs)) < 0)
+            goto fail;
+    }
+
+fail:
+    if (ret < 0) {
+        av_freep(&hls->key_basename);
+        for (i = 0; i < hls->nb_varstreams && hls->var_streams; i++) {
+            vs = &hls->var_streams[i];
+            av_freep(&vs->basename);
+            av_freep(&vs->vtt_basename);
+            av_freep(&vs->fmp4_init_filename);
+            av_freep(&vs->m3u8_name);
+            av_freep(&vs->vtt_m3u8_name);
+            av_freep(&vs->streams);
+            av_freep(&vs->agroup);
+            av_freep(&vs->ccgroup);
+            av_freep(&vs->baseurl);
+            if (vs->avf)
+                avformat_free_context(vs->avf);
+            if (vs->vtt_avf)
+                avformat_free_context(vs->vtt_avf);
+        }
+        for (i = 0; i < hls->nb_ccstreams; i++) {
+            ClosedCaptionsStream *ccs = &hls->cc_streams[i];
+            av_freep(&ccs->ccgroup);
+            av_freep(&ccs->instreamid);
+            av_freep(&ccs->language);
+        }
+        av_freep(&hls->var_streams);
+        av_freep(&hls->cc_streams);
+        av_freep(&hls->master_m3u8_url);
+    }
+
+    return ret;
+}
+
 #define OFFSET(x) offsetof(HLSContext, x)
 #define E AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
@@ -2308,6 +2782,7 @@ static const AVOption options[] = {
     {"datetime", "current datetime as YYYYMMDDhhmmss", 0, AV_OPT_TYPE_CONST, {.i64 = HLS_START_SEQUENCE_AS_FORMATTED_DATETIME }, INT_MIN, INT_MAX, E, "start_sequence_source_type" },
     {"http_user_agent", "override User-Agent field in HTTP header", OFFSET(user_agent), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
     {"var_stream_map", "Variant stream map string", OFFSET(var_stream_map), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
+    {"cc_stream_map", "Closed captions stream map string", OFFSET(cc_stream_map), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
     {"master_pl_name", "Create HLS master playlist with this name", OFFSET(master_pl_name), AV_OPT_TYPE_STRING, {.str = NULL},  0, 0,    E},
     {"master_pl_publish_rate", "Publish master play list every after this many segment intervals", OFFSET(master_publish_rate), AV_OPT_TYPE_INT, {.i64 = 0}, 0, UINT_MAX, E},
     {"http_persistent", "Use persistent HTTP connections", OFFSET(http_persistent), AV_OPT_TYPE_BOOL, {.i64 = 0 }, 0, 1, E },
@@ -2331,6 +2806,7 @@ AVOutputFormat ff_hls_muxer = {
     .video_codec    = AV_CODEC_ID_H264,
     .subtitle_codec = AV_CODEC_ID_WEBVTT,
     .flags          = AVFMT_NOFILE | AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH,
+    .init           = hls_init,
     .write_header   = hls_write_header,
     .write_packet   = hls_write_packet,
     .write_trailer  = hls_write_trailer,