+static int format_name(char *name, int name_buf_len, int i)
+{
+ char *p;
+ char extension[10] = {'\0'};
+
+ p = strrchr(name, '.');
+ if (p) {
+ av_strlcpy(extension, p, sizeof(extension));
+ *p = '\0';
+ }
+
+ snprintf(name + strlen(name), name_buf_len - strlen(name), POSTFIX_PATTERN, i);
+
+ if (strlen(extension))
+ av_strlcat(name, extension, name_buf_len);
+
+ return 0;
+}
+
+static int get_nth_codec_stream_index(AVFormatContext *s,
+ enum AVMediaType codec_type,
+ int stream_id)
+{
+ unsigned int stream_index, cnt;
+ if (stream_id < 0 || stream_id > s->nb_streams - 1)
+ return -1;
+ cnt = 0;
+ for (stream_index = 0; stream_index < s->nb_streams; stream_index++) {
+ if (s->streams[stream_index]->codecpar->codec_type != codec_type)
+ continue;
+ if (cnt == stream_id)
+ return stream_index;
+ cnt++;
+ }
+ return -1;
+}
+
+static int parse_variant_stream_mapstring(AVFormatContext *s)
+{
+ HLSContext *hls = s->priv_data;
+ VariantStream *vs;
+ int stream_index;
+ enum AVMediaType codec_type;
+ int nb_varstreams, nb_streams;
+ char *p, *q, *saveptr1, *saveptr2, *varstr, *keyval;
+ const char *val;
+
+ /**
+ * Expected format for var_stream_map string is as below:
+ * "a:0,v:0 a:1,v:1"
+ * This string specifies how to group the audio, video and subtitle streams
+ * into different variant streams. The variant stream groups are separated
+ * by space.
+ *
+ * a:, v:, s: are keys to specify audio, video and subtitle streams
+ * respectively. Allowed values are 0 to 9 digits (limited just based on
+ * practical usage)
+ *
+ */
+ p = av_strdup(hls->var_stream_map);
+ q = p;
+ while(av_strtok(q, " \t", &saveptr1)) {
+ q = NULL;
+ hls->nb_varstreams++;
+ }
+ av_freep(&p);
+
+ hls->var_streams = av_mallocz(sizeof(*hls->var_streams) * hls->nb_varstreams);
+ if (!hls->var_streams)
+ return AVERROR(ENOMEM);
+
+ p = hls->var_stream_map;
+ nb_varstreams = 0;
+ while (varstr = av_strtok(p, " \t", &saveptr1)) {
+ p = NULL;
+
+ if (nb_varstreams < hls->nb_varstreams)
+ vs = &(hls->var_streams[nb_varstreams++]);
+ else
+ return AVERROR(EINVAL);
+
+ q = varstr;
+ while (q < varstr + strlen(varstr)) {
+ if (!av_strncasecmp(q, "a:", 2) || !av_strncasecmp(q, "v:", 2) ||
+ !av_strncasecmp(q, "s:", 2))
+ vs->nb_streams++;
+ q++;
+ }
+ vs->streams = av_mallocz(sizeof(AVStream *) * vs->nb_streams);
+ if (!vs->streams)
+ return AVERROR(ENOMEM);
+
+ nb_streams = 0;
+ while (keyval = av_strtok(varstr, ",", &saveptr2)) {
+ varstr = NULL;
+
+ if (av_strstart(keyval, "v:", &val)) {
+ codec_type = AVMEDIA_TYPE_VIDEO;
+ } else if (av_strstart(keyval, "a:", &val)) {
+ codec_type = AVMEDIA_TYPE_AUDIO;
+ } else if (av_strstart(keyval, "s:", &val)) {
+ codec_type = AVMEDIA_TYPE_SUBTITLE;
+ } else {
+ av_log(s, AV_LOG_ERROR, "Invalid keyval %s\n", keyval);
+ return AVERROR(EINVAL);
+ }
+
+ stream_index = -1;
+ if (av_isdigit(*val))
+ stream_index = get_nth_codec_stream_index (s, codec_type,
+ atoi(val));
+
+ if (stream_index >= 0 && nb_streams < vs->nb_streams) {
+ vs->streams[nb_streams++] = s->streams[stream_index];
+ } else {
+ av_log(s, AV_LOG_ERROR, "Unable to map stream at %s\n", keyval);
+ return AVERROR(EINVAL);
+ }
+ }
+ }
+ av_log(s, AV_LOG_DEBUG, "Number of variant streams %d\n",
+ hls->nb_varstreams);
+
+ return 0;
+}
+
+static int update_variant_stream_info(AVFormatContext *s) {
+ HLSContext *hls = s->priv_data;
+ unsigned int i;
+
+ if (hls->var_stream_map) {
+ return parse_variant_stream_mapstring(s);
+ } else {
+ //By default, a single variant stream with all the codec streams is created
+ hls->nb_varstreams = 1;
+ hls->var_streams = av_mallocz(sizeof(*hls->var_streams) *
+ hls->nb_varstreams);
+ if (!hls->var_streams)
+ return AVERROR(ENOMEM);
+
+ hls->var_streams[0].nb_streams = s->nb_streams;
+ hls->var_streams[0].streams = av_mallocz(sizeof(AVStream *) *
+ hls->var_streams[0].nb_streams);
+ if (!hls->var_streams[0].streams)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < s->nb_streams; i++)
+ hls->var_streams[0].streams[i] = s->streams[i];
+ }
+ return 0;
+}
+
+static int update_master_pl_info(AVFormatContext *s) {
+ HLSContext *hls = s->priv_data;
+ int m3u8_name_size, ret;
+ char *p;
+
+ 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) {
+ ret = AVERROR(ENOMEM);
+ return ret;
+ }
+
+ 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);
+ }
+
+ return 0;
+}
+