]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/hlsenc.c
avformat/hlsenc: fix memleak problem about fmp4_init_filename
[ffmpeg] / libavformat / hlsenc.c
index 0eebcb44621f5a439a08558bbddfd57f8c17038f..d64755436ed2a51c2ba0b08b3edab8daecb15c76 100644 (file)
@@ -58,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
@@ -124,6 +129,7 @@ typedef struct VariantStream {
     int nb_entries;
     int discontinuity_set;
     int discontinuity;
+    int reference_stream_index;
 
     HLSSegment *segments;
     HLSSegment *last_segment;
@@ -142,11 +148,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;
@@ -195,16 +211,20 @@ 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;
+    int64_t timeout;
 } HLSContext;
 
 static int mkdir_p(const char *path) {
@@ -240,15 +260,10 @@ 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 = filename ? is_http_proto(filename) : 0;
+    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);
@@ -264,18 +279,22 @@ static int hlsenc_io_open(AVFormatContext *s, AVIOContext **pb, char *filename,
 
 static void hlsenc_io_close(AVFormatContext *s, AVIOContext **pb, char *filename) {
     HLSContext *hls = s->priv_data;
-    int http_base_proto = filename ? is_http_proto(filename) : 0;
-
+    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);
@@ -287,17 +306,66 @@ static void set_http_options(AVFormatContext *s, AVDictionary **options, HLSCont
         av_dict_set(options, "user_agent", c->user_agent, 0);
     if (c->http_persistent)
         av_dict_set_int(options, "multiple_requests", 1, 0);
+    if (c->timeout >= 0)
+        av_dict_set_int(options, "timeout", c->timeout, 0);
+}
 
+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 *buf, int buf_size, const char *filename, char placeholder, int64_t number)
+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;
@@ -314,13 +382,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++;
@@ -329,17 +391,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)
@@ -408,7 +470,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);
@@ -435,7 +497,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)
@@ -505,12 +567,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) {
@@ -640,7 +702,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;
@@ -749,42 +814,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;
@@ -836,58 +897,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;
@@ -911,10 +966,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) {
@@ -923,7 +978,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';
 
@@ -1049,10 +1104,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;
@@ -1077,15 +1137,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,
@@ -1110,12 +1174,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;
     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) {
@@ -1142,6 +1207,42 @@ static int create_master_playlist(AVFormatContext *s,
 
     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++) {
         vs = &(hls->var_streams[i]);
@@ -1174,6 +1275,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;
@@ -1181,7 +1301,23 @@ static int create_master_playlist(AVFormatContext *s,
             bandwidth += aud_st->codecpar->bit_rate;
         bandwidth += bandwidth / 10;
 
-        ff_hls_write_stream_info(vid_st, hls->m3u8_out, 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);
     }
@@ -1201,7 +1337,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
     int ret = 0;
     char temp_filename[1024];
     int64_t sequence = FFMAX(hls->start_sequence, vs->sequence - vs->nb_entries);
-    const char *proto = avio_find_protocol_name(s->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;
@@ -1235,7 +1371,7 @@ static int hls_window(AVFormatContext *s, int last, VariantStream *vs)
 
     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;
@@ -1320,28 +1456,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);
             }
@@ -1353,7 +1502,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);
                 }
@@ -1365,25 +1514,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++;
@@ -1391,7 +1548,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) {
@@ -1419,7 +1579,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;
@@ -1430,12 +1590,12 @@ static int hls_start(AVFormatContext *s, VariantStream *vs)
         if (err < 0)
             return err;
     } else if (c->segment_type != SEGMENT_TYPE_FMP4) {
-        if ((err = hlsenc_io_open(s, &oc->pb, oc->filename, &options)) < 0)
+        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);
@@ -1482,7 +1642,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'};
@@ -1501,6 +1661,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)
@@ -1532,6 +1777,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.
@@ -1540,6 +1786,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;
@@ -1578,7 +1825,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;
@@ -1608,9 +1865,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);
@@ -1628,6 +1970,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];
     }
@@ -1636,28 +1985,46 @@ 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)
@@ -1703,6 +2070,19 @@ static int hls_write_header(AVFormatContext *s)
                 continue;
             }
             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);
+                }
+            }
         }
     }
 fail:
@@ -1764,7 +2144,7 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
     if (vs->has_video) {
         can_split = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                     ((pkt->flags & AV_PKT_FLAG_KEY) || (hls->flags & HLS_SPLIT_BY_TIME));
-        is_ref_pkt = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
+        is_ref_pkt = (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) && (pkt->stream_index == vs->reference_stream_index);
     }
     if (pkt->pts == AV_NOPTS_VALUE)
         is_ref_pkt = can_split = 0;
@@ -1791,13 +2171,9 @@ 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);
@@ -1816,13 +2192,13 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
                     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);
@@ -1834,10 +2210,10 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
         }
 
         if (hls->segment_type == SEGMENT_TYPE_FMP4) {
-            ret = hlsenc_io_open(s, &vs->out, vs->avf->filename, NULL);
+            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->filename);
+                    vs->avf->url);
                 return ret;
             }
             write_styp(vs->out);
@@ -1847,6 +2223,12 @@ static int hls_write_packet(AVFormatContext *s, AVPacket *pkt)
             }
             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) {
@@ -1908,34 +2290,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->filename, NULL);
+        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->filename);
-            return AVERROR(ENOENT);
+            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) {
-            return ret;
+            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;
         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 */
@@ -1969,11 +2358,23 @@ 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;
 }
@@ -1991,7 +2392,7 @@ static int hls_init(AVFormatContext *s)
     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, m3u8_name_size = 0;
+    int vtt_basename_size = 0;
     int fmp4_init_filename_len = strlen(hls->fmp4_init_filename) + 1;
 
     ret = update_variant_stream_info(s);
@@ -2007,6 +2408,28 @@ static int hls_init(AVFormatContext *s)
         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) {
@@ -2039,6 +2462,16 @@ static int hls_init(AVFormatContext *s)
     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;
@@ -2067,6 +2500,11 @@ static int hls_init(AVFormatContext *s)
 
         for (j = 0; j < vs->nb_streams; j++) {
             vs->has_video += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO;
+            /* Get one video stream to reference for split segments
+             * so use the first video stream index. */
+            if ((vs->has_video == 1) && (vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)) {
+                vs->reference_stream_index = vs->streams[j]->index;
+            }
             vs->has_subtitle += vs->streams[j]->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE;
         }
 
@@ -2092,9 +2530,6 @@ static int hls_init(AVFormatContext *s)
         }
         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);
@@ -2102,6 +2537,9 @@ static int hls_init(AVFormatContext *s)
             }
 
             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) {
@@ -2112,13 +2550,9 @@ static int hls_init(AVFormatContext *s)
             }
 
             if (hls->use_localtime) {
-                basename_size = strlen(s->filename) + strlen(pattern_localtime_fmt) + 1;
+                basename_size = strlen(vs->m3u8_name) + strlen(pattern_localtime_fmt) + 1;
             } else {
-                basename_size = strlen(s->filename) + strlen(pattern) + 1;
-            }
-
-            if (hls->nb_varstreams > 1) {
-                basename_size += strlen(POSTFIX_PATTERN);
+                basename_size = strlen(vs->m3u8_name) + strlen(pattern) + 1;
             }
 
             vs->basename = av_malloc(basename_size);
@@ -2127,7 +2561,7 @@ static int hls_init(AVFormatContext *s)
                 goto fail;
             }
 
-            av_strlcpy(vs->basename, s->filename, basename_size);
+            av_strlcpy(vs->basename, vs->m3u8_name, basename_size);
 
             p = strrchr(vs->basename, '.');
             if (p)
@@ -2139,31 +2573,16 @@ static int hls_init(AVFormatContext *s)
             }
         }
 
-        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);
+            if (hls->flags & HLS_SINGLE_FILE) {
+                vs->fmp4_init_filename  = av_strdup(vs->basename);
+                if (!vs->fmp4_init_filename) {
+                    ret = AVERROR(ENOMEM);
+                    goto fail;
+                }
+            } else {
             vs->fmp4_init_filename = av_malloc(fmp4_init_filename_len);
             if (!vs->fmp4_init_filename ) {
                 ret = AVERROR(ENOMEM);
@@ -2172,41 +2591,31 @@ static int hls_init(AVFormatContext *s)
             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);
+                ret = append_postfix(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;
+            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;
-                }
+            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,
+            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);
-                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);
-                }
+            } else {
+                av_strlcpy(vs->base_output_dirname, vs->fmp4_init_filename,
+                           fmp4_init_filename_len);
+            }
             }
         }
 
@@ -2225,10 +2634,7 @@ static int hls_init(AVFormatContext *s)
 
             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);
-            }
+            vtt_basename_size = strlen(vs->m3u8_name) + strlen(vtt_pattern) + 1;
 
             vs->vtt_basename = av_malloc(vtt_basename_size);
             if (!vs->vtt_basename) {
@@ -2240,27 +2646,21 @@ static int hls_init(AVFormatContext *s)
                 ret = AVERROR(ENOMEM);
                 goto fail;
             }
-            av_strlcpy(vs->vtt_basename, s->filename, vtt_basename_size);
+            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->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) {
@@ -2271,13 +2671,6 @@ static int hls_init(AVFormatContext *s)
             }
         }
 
-        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;
 
@@ -2307,13 +2700,22 @@ fail:
             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);
     }
 
@@ -2373,9 +2775,11 @@ 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 },
+    {"timeout", "set timeout for socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_DURATION, { .i64 = -1 }, -1, INT_MAX, .flags = E },
     { NULL },
 };