diff mbox

[FFmpeg-devel,v2,2/3] avformat/hlsenc: configurable variant stream index position in filenames

Message ID 1514282020-7389-1-git-send-email-vdixit@akamai.com
State Superseded
Headers show

Commit Message

Dixit, Vishwanath Dec. 26, 2017, 9:53 a.m. UTC
From: Vishwanath Dixit <vdixit@akamai.com>

---
 doc/muxers.texi      |  31 +++++++++--
 libavformat/hlsenc.c | 153 ++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 127 insertions(+), 57 deletions(-)

Comments

Steven Liu Dec. 26, 2017, 10:07 a.m. UTC | #1
> On 26 Dec 2017, at 17:53, vdixit@akamai.com wrote:
> 
> From: Vishwanath Dixit <vdixit@akamai.com>
> 
> ---
> doc/muxers.texi      |  31 +++++++++--
> libavformat/hlsenc.c | 153 ++++++++++++++++++++++++++++++++++-----------------
> 2 files changed, 127 insertions(+), 57 deletions(-)
> 
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index 93db549..6af970d 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -575,6 +575,17 @@ Should a relative path be specified, the path of the created segment
> files will be relative to the current working directory.
> When use_localtime_mkdir is set, the whole expanded value of @var{filename} will be written into the m3u8 segment list.
> 
> +When @code{var_stream_map} is set with two or more variant streams, the
> +@var{filename} pattern must contain the string "%v", this string specifies
> +the position of variant stream index in the generated segment file names.
> +@example
> +ffmpeg -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
> +  -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
> +  -hls_segment_filename 'file_%v_%03d.ts' out_%v.m3u8
> +@end example
> +This example will produce the playlists segment file sets:
> +@file{file_0_000.ts}, @file{file_0_001.ts}, @file{file_0_002.ts}, etc. and
> +@file{file_1_000.ts}, @file{file_1_001.ts}, @file{file_1_002.ts}, etc.
> 
> @item use_localtime
> Use strftime() on @var{filename} to expand the segment filename with localtime.
> @@ -701,6 +712,10 @@ the fmp4 files is used in hls after version 7.
> @item hls_fmp4_init_filename @var{filename}
> set filename to the fragment files header file, default filename is @file{init.mp4}.
> 
> +When @code{var_stream_map} is set with two or more variant streams, the
> +@var{filename} pattern must contain the string "%v", this string specifies
> +the position of variant stream index in the generated init file names.
> +
> @item hls_flags @var{flags}
> Possible values:
> 
> @@ -814,32 +829,36 @@ Expected string format is like this "a:0,v:0 a:1,v:1 ....". Here a:, v:, s: are
> the keys to specify audio, video and subtitle streams respectively.
> Allowed values are 0 to 9 (limited just based on practical usage).
> 
> +When there are two or more variant streams, the output filename pattern must
> +contain the string "%v", this string specifies the position of variant stream
> +index in the output media playlist filenames.
> +
> @example
> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>   -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
> -  http://example.com/live/out.m3u8
> +  http://example.com/live/out_%v.m3u8
> @end example
> This example creates two hls variant streams. The first variant stream will
> contain video stream of bitrate 1000k and audio stream of bitrate 64k and the
> second variant stream will contain video stream of bitrate 256k and audio
> -stream of bitrate 32k. Here, two media playlist with file names out_1.m3u8 and
> -out_2.m3u8 will be created.
> +stream of bitrate 32k. Here, two media playlist with file names out_0.m3u8 and
> +out_1.m3u8 will be created.
> @example
> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k \
>   -map 0:v -map 0:a -map 0:v -f hls -var_stream_map "v:0 a:0 v:1" \
> -  http://example.com/live/out.m3u8
> +  http://example.com/live/out_%v.m3u8
> @end example
> This example creates three hls variant streams. The first variant stream will
> be a video only stream with video bitrate 1000k, the second variant stream will
> be an audio only stream with bitrate 64k and the third variant stream will be a
> video only stream with bitrate 256k. Here, three media playlist with file names
> -out_1.m3u8, out_2.m3u8 and out_3.m3u8 will be created.
> +out_0.m3u8, out_1.m3u8 and out_2.m3u8 will be created.
> @example
> ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k -b:v:1 3000k  \
>   -map 0:a -map 0:a -map 0:v -map 0:v -f hls \
>   -var_stream_map "a:0,agroup:aud_low a:1,agroup:aud_high v:0,agroup:aud_low v:1,agroup:aud_high" \
>   -master_pl_name master.m3u8 \
> -  http://example.com/live/out.m3u8
> +  http://example.com/live/out_%v.m3u8
> @end example
> This example creates two audio only and two video only variant streams. In
> addition to the #EXT-X-STREAM-INF tag for each variant stream in the master
> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
> index bd43336..76a4110 100644
> --- a/libavformat/hlsenc.c
> +++ b/libavformat/hlsenc.c
> @@ -1536,7 +1536,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'};
> @@ -1555,6 +1555,53 @@ 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;
> +    int ret = 0;
> +
> +    if (!fn) {
> +        ret = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +    filename = av_basename(fn);
> +
> +    if (nb_vs > 1 && !av_stristr(filename, "%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;
> +    }
> +
> +fail:
> +    return ret;
> +}
> +
> +static int format_name(char *buf, int buf_len, int index)
> +{
> +    char *orig_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(buf, buf_len, orig_buf_dup, 'v', index) < 1) {
> +        ret = AVERROR(EINVAL);
> +        goto fail;
> +    }
> +
> +fail:
> +    av_freep(&orig_buf_dup);
> +    return ret;
> +}
> +
> static int get_nth_codec_stream_index(AVFormatContext *s,
>                                       enum AVMediaType codec_type,
>                                       int stream_id)
> @@ -1698,7 +1745,7 @@ static int update_variant_stream_info(AVFormatContext *s) {
> static int update_master_pl_info(AVFormatContext *s) {
>     HLSContext *hls = s->priv_data;
>     const char *dir;
> -    char *fn;
> +    char *fn = NULL;
>     int ret = 0;
> 
>     fn = av_strdup(s->filename);
> @@ -2076,6 +2123,28 @@ static int hls_init(AVFormatContext *s)
>         goto fail;
>     }
> 
> +    ret = validate_name(hls->nb_varstreams, s->filename);
> +    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) {
> @@ -2108,6 +2177,18 @@ 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];
> +
from here
> +        m3u8_name_size = strlen(s->filename) + 1;
> +        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);
to here.  is this mean strdup?

> +        ret = format_name(vs->m3u8_name, m3u8_name_size, i);
> +        if (ret < 0)
> +            goto fail;
> +
>         vs->sequence       = hls->start_sequence;
>         vs->start_pts      = AV_NOPTS_VALUE;
>         vs->end_pts      = AV_NOPTS_VALUE;
> @@ -2161,9 +2242,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);
> @@ -2171,6 +2249,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) {
> @@ -2181,13 +2262,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);
> @@ -2196,7 +2273,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)
> @@ -2208,28 +2285,6 @@ 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);
> @@ -2240,13 +2295,12 @@ 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) {
> +
> +            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;
> -            }
> 
> -            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) {
> @@ -2256,6 +2310,12 @@ static int hls_init(AVFormatContext *s)
>                 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;
> 
> @@ -2294,10 +2354,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) {
> @@ -2309,27 +2366,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) {
> -- 
> 1.9.1
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Dixit, Vishwanath Dec. 26, 2017, 10:38 a.m. UTC | #2
On 12/26/17 3:37 PM, 刘歧 wrote:
>> On 26 Dec 2017, at 17:53, vdixit@akamai.com wrote:
>>
>> From: Vishwanath Dixit <vdixit@akamai.com>
>>
>> ---
>> doc/muxers.texi      |  31 +++++++++--
>> libavformat/hlsenc.c | 153 ++++++++++++++++++++++++++++++++++-----------------
>> 2 files changed, 127 insertions(+), 57 deletions(-)
>>
>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>> index 93db549..6af970d 100644
>> --- a/doc/muxers.texi
>> +++ b/doc/muxers.texi
>> @@ -575,6 +575,17 @@ Should a relative path be specified, the path of the created segment
>> files will be relative to the current working directory.
>> When use_localtime_mkdir is set, the whole expanded value of @var{filename} will be written into the m3u8 segment list.
>>
>> +When @code{var_stream_map} is set with two or more variant streams, the
>> +@var{filename} pattern must contain the string "%v", this string specifies
>> +the position of variant stream index in the generated segment file names.
>> +@example
>> +ffmpeg -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>> +  -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>> +  -hls_segment_filename 'file_%v_%03d.ts' out_%v.m3u8
>> +@end example
>> +This example will produce the playlists segment file sets:
>> +@file{file_0_000.ts}, @file{file_0_001.ts}, @file{file_0_002.ts}, etc. and
>> +@file{file_1_000.ts}, @file{file_1_001.ts}, @file{file_1_002.ts}, etc.
>>
>> @item use_localtime
>> Use strftime() on @var{filename} to expand the segment filename with localtime.
>> @@ -701,6 +712,10 @@ the fmp4 files is used in hls after version 7.
>> @item hls_fmp4_init_filename @var{filename}
>> set filename to the fragment files header file, default filename is @file{init.mp4}.
>>
>> +When @code{var_stream_map} is set with two or more variant streams, the
>> +@var{filename} pattern must contain the string "%v", this string specifies
>> +the position of variant stream index in the generated init file names.
>> +
>> @item hls_flags @var{flags}
>> Possible values:
>>
>> @@ -814,32 +829,36 @@ Expected string format is like this "a:0,v:0 a:1,v:1 ....". Here a:, v:, s: are
>> the keys to specify audio, video and subtitle streams respectively.
>> Allowed values are 0 to 9 (limited just based on practical usage).
>>
>> +When there are two or more variant streams, the output filename pattern must
>> +contain the string "%v", this string specifies the position of variant stream
>> +index in the output media playlist filenames.
>> +
>> @example
>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>>    -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>> -  http://example.com/live/out.m3u8
>> +  http://example.com/live/out_%v.m3u8
>> @end example
>> This example creates two hls variant streams. The first variant stream will
>> contain video stream of bitrate 1000k and audio stream of bitrate 64k and the
>> second variant stream will contain video stream of bitrate 256k and audio
>> -stream of bitrate 32k. Here, two media playlist with file names out_1.m3u8 and
>> -out_2.m3u8 will be created.
>> +stream of bitrate 32k. Here, two media playlist with file names out_0.m3u8 and
>> +out_1.m3u8 will be created.
>> @example
>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k \
>>    -map 0:v -map 0:a -map 0:v -f hls -var_stream_map "v:0 a:0 v:1" \
>> -  http://example.com/live/out.m3u8
>> +  http://example.com/live/out_%v.m3u8
>> @end example
>> This example creates three hls variant streams. The first variant stream will
>> be a video only stream with video bitrate 1000k, the second variant stream will
>> be an audio only stream with bitrate 64k and the third variant stream will be a
>> video only stream with bitrate 256k. Here, three media playlist with file names
>> -out_1.m3u8, out_2.m3u8 and out_3.m3u8 will be created.
>> +out_0.m3u8, out_1.m3u8 and out_2.m3u8 will be created.
>> @example
>> ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k -b:v:1 3000k  \
>>    -map 0:a -map 0:a -map 0:v -map 0:v -f hls \
>>    -var_stream_map "a:0,agroup:aud_low a:1,agroup:aud_high v:0,agroup:aud_low v:1,agroup:aud_high" \
>>    -master_pl_name master.m3u8 \
>> -  http://example.com/live/out.m3u8
>> +  http://example.com/live/out_%v.m3u8
>> @end example
>> This example creates two audio only and two video only variant streams. In
>> addition to the #EXT-X-STREAM-INF tag for each variant stream in the master
>> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
>> index bd43336..76a4110 100644
>> --- a/libavformat/hlsenc.c
>> +++ b/libavformat/hlsenc.c
>> @@ -1536,7 +1536,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'};
>> @@ -1555,6 +1555,53 @@ 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;
>> +    int ret = 0;
>> +
>> +    if (!fn) {
>> +        ret = AVERROR(EINVAL);
>> +        goto fail;
>> +    }
>> +
>> +    filename = av_basename(fn);
>> +
>> +    if (nb_vs > 1 && !av_stristr(filename, "%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;
>> +    }
>> +
>> +fail:
>> +    return ret;
>> +}
>> +
>> +static int format_name(char *buf, int buf_len, int index)
>> +{
>> +    char *orig_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(buf, buf_len, orig_buf_dup, 'v', index) < 1) {
>> +        ret = AVERROR(EINVAL);
>> +        goto fail;
>> +    }
>> +
>> +fail:
>> +    av_freep(&orig_buf_dup);
>> +    return ret;
>> +}
>> +
>> static int get_nth_codec_stream_index(AVFormatContext *s,
>>                                        enum AVMediaType codec_type,
>>                                        int stream_id)
>> @@ -1698,7 +1745,7 @@ static int update_variant_stream_info(AVFormatContext *s) {
>> static int update_master_pl_info(AVFormatContext *s) {
>>      HLSContext *hls = s->priv_data;
>>      const char *dir;
>> -    char *fn;
>> +    char *fn = NULL;
>>      int ret = 0;
>>
>>      fn = av_strdup(s->filename);
>> @@ -2076,6 +2123,28 @@ static int hls_init(AVFormatContext *s)
>>          goto fail;
>>      }
>>
>> +    ret = validate_name(hls->nb_varstreams, s->filename);
>> +    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) {
>> @@ -2108,6 +2177,18 @@ 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];
>> +
> from here
>> +        m3u8_name_size = strlen(s->filename) + 1;
>> +        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);
> to here.  is this mean strdup?
Yes, it is doing the same operation as that of strdup. Since, the size 
of the allocated memory was needed to be used in the below line 
(m3u8_name_size), av_malloc was used instead of strdup.
>> +        ret = format_name(vs->m3u8_name, m3u8_name_size, i);
>> +        if (ret < 0)
>> +            goto fail;
>> +
>>          vs->sequence       = hls->start_sequence;
>>          vs->start_pts      = AV_NOPTS_VALUE;
>>          vs->end_pts      = AV_NOPTS_VALUE;
>> @@ -2161,9 +2242,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);
>> @@ -2171,6 +2249,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) {
>> @@ -2181,13 +2262,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);
>> @@ -2196,7 +2273,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)
>> @@ -2208,28 +2285,6 @@ 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);
>> @@ -2240,13 +2295,12 @@ 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) {
>> +
>> +            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;
>> -            }
>>
>> -            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) {
>> @@ -2256,6 +2310,12 @@ static int hls_init(AVFormatContext *s)
>>                  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;
>>
>> @@ -2294,10 +2354,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) {
>> @@ -2309,27 +2366,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) {
>> -- 
>> 1.9.1
>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
Steven Liu Dec. 26, 2017, 10:59 a.m. UTC | #3
> On 26 Dec 2017, at 18:38, Vishwanath Dixit <vdixit@akamai.com> wrote:
> 
> 
> 
> On 12/26/17 3:37 PM, 刘歧 wrote:
>>> On 26 Dec 2017, at 17:53, vdixit@akamai.com wrote:
>>> 
>>> From: Vishwanath Dixit <vdixit@akamai.com>
>>> 
>>> ---
>>> doc/muxers.texi      |  31 +++++++++--
>>> libavformat/hlsenc.c | 153 ++++++++++++++++++++++++++++++++++-----------------
>>> 2 files changed, 127 insertions(+), 57 deletions(-)
>>> 
>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>> index 93db549..6af970d 100644
>>> --- a/doc/muxers.texi
>>> +++ b/doc/muxers.texi
>>> @@ -575,6 +575,17 @@ Should a relative path be specified, the path of the created segment
>>> files will be relative to the current working directory.
>>> When use_localtime_mkdir is set, the whole expanded value of @var{filename} will be written into the m3u8 segment list.
>>> 
>>> +When @code{var_stream_map} is set with two or more variant streams, the
>>> +@var{filename} pattern must contain the string "%v", this string specifies
>>> +the position of variant stream index in the generated segment file names.
>>> +@example
>>> +ffmpeg -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>>> +  -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>>> +  -hls_segment_filename 'file_%v_%03d.ts' out_%v.m3u8
>>> +@end example
>>> +This example will produce the playlists segment file sets:
>>> +@file{file_0_000.ts}, @file{file_0_001.ts}, @file{file_0_002.ts}, etc. and
>>> +@file{file_1_000.ts}, @file{file_1_001.ts}, @file{file_1_002.ts}, etc.
>>> 
>>> @item use_localtime
>>> Use strftime() on @var{filename} to expand the segment filename with localtime.
>>> @@ -701,6 +712,10 @@ the fmp4 files is used in hls after version 7.
>>> @item hls_fmp4_init_filename @var{filename}
>>> set filename to the fragment files header file, default filename is @file{init.mp4}.
>>> 
>>> +When @code{var_stream_map} is set with two or more variant streams, the
>>> +@var{filename} pattern must contain the string "%v", this string specifies
>>> +the position of variant stream index in the generated init file names.
>>> +
>>> @item hls_flags @var{flags}
>>> Possible values:
>>> 
>>> @@ -814,32 +829,36 @@ Expected string format is like this "a:0,v:0 a:1,v:1 ....". Here a:, v:, s: are
>>> the keys to specify audio, video and subtitle streams respectively.
>>> Allowed values are 0 to 9 (limited just based on practical usage).
>>> 
>>> +When there are two or more variant streams, the output filename pattern must
>>> +contain the string "%v", this string specifies the position of variant stream
>>> +index in the output media playlist filenames.
>>> +
>>> @example
>>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>>>   -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>>> -  http://example.com/live/out.m3u8
>>> +  http://example.com/live/out_%v.m3u8
>>> @end example
>>> This example creates two hls variant streams. The first variant stream will
>>> contain video stream of bitrate 1000k and audio stream of bitrate 64k and the
>>> second variant stream will contain video stream of bitrate 256k and audio
>>> -stream of bitrate 32k. Here, two media playlist with file names out_1.m3u8 and
>>> -out_2.m3u8 will be created.
>>> +stream of bitrate 32k. Here, two media playlist with file names out_0.m3u8 and
>>> +out_1.m3u8 will be created.
>>> @example
>>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k \
>>>   -map 0:v -map 0:a -map 0:v -f hls -var_stream_map "v:0 a:0 v:1" \
>>> -  http://example.com/live/out.m3u8
>>> +  http://example.com/live/out_%v.m3u8
>>> @end example
>>> This example creates three hls variant streams. The first variant stream will
>>> be a video only stream with video bitrate 1000k, the second variant stream will
>>> be an audio only stream with bitrate 64k and the third variant stream will be a
>>> video only stream with bitrate 256k. Here, three media playlist with file names
>>> -out_1.m3u8, out_2.m3u8 and out_3.m3u8 will be created.
>>> +out_0.m3u8, out_1.m3u8 and out_2.m3u8 will be created.
>>> @example
>>> ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k -b:v:1 3000k  \
>>>   -map 0:a -map 0:a -map 0:v -map 0:v -f hls \
>>>   -var_stream_map "a:0,agroup:aud_low a:1,agroup:aud_high v:0,agroup:aud_low v:1,agroup:aud_high" \
>>>   -master_pl_name master.m3u8 \
>>> -  http://example.com/live/out.m3u8
>>> +  http://example.com/live/out_%v.m3u8
>>> @end example
>>> This example creates two audio only and two video only variant streams. In
>>> addition to the #EXT-X-STREAM-INF tag for each variant stream in the master
>>> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
>>> index bd43336..76a4110 100644
>>> --- a/libavformat/hlsenc.c
>>> +++ b/libavformat/hlsenc.c
>>> @@ -1536,7 +1536,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'};
>>> @@ -1555,6 +1555,53 @@ 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;
>>> +    int ret = 0;
>>> +
>>> +    if (!fn) {
>>> +        ret = AVERROR(EINVAL);
>>> +        goto fail;
>>> +    }
>>> +
>>> +    filename = av_basename(fn);
>>> +
>>> +    if (nb_vs > 1 && !av_stristr(filename, "%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;
>>> +    }
>>> +
>>> +fail:
>>> +    return ret;
>>> +}
>>> +
>>> +static int format_name(char *buf, int buf_len, int index)
>>> +{
>>> +    char *orig_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(buf, buf_len, orig_buf_dup, 'v', index) < 1) {
>>> +        ret = AVERROR(EINVAL);
>>> +        goto fail;
>>> +    }
>>> +
>>> +fail:
>>> +    av_freep(&orig_buf_dup);
>>> +    return ret;
>>> +}
>>> +
>>> static int get_nth_codec_stream_index(AVFormatContext *s,
>>>                                       enum AVMediaType codec_type,
>>>                                       int stream_id)
>>> @@ -1698,7 +1745,7 @@ static int update_variant_stream_info(AVFormatContext *s) {
>>> static int update_master_pl_info(AVFormatContext *s) {
>>>     HLSContext *hls = s->priv_data;
>>>     const char *dir;
>>> -    char *fn;
>>> +    char *fn = NULL;
>>>     int ret = 0;
>>> 
>>>     fn = av_strdup(s->filename);
>>> @@ -2076,6 +2123,28 @@ static int hls_init(AVFormatContext *s)
>>>         goto fail;
>>>     }
>>> 
>>> +    ret = validate_name(hls->nb_varstreams, s->filename);
>>> +    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) {
>>> @@ -2108,6 +2177,18 @@ 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];
>>> +
>> from here
>>> +        m3u8_name_size = strlen(s->filename) + 1;
>>> +        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);
>> to here.  is this mean strdup?
> Yes, it is doing the same operation as that of strdup. Since, the size of the allocated memory was needed to be used in the below line (m3u8_name_size), av_malloc was used instead of strdup.
What about modify to :

m3u8_name_size = strlen(s->filename) + 1;
vs_>m3u8_name = av_strdup(s->filename);
if (!vs->m3u8_name) {
    ret = AVERROR(ENOMEM);
    goto fail;
}
ret = format_name(vs->m3u8_name, m3u8_name_size, i);

?

>>> +        ret = format_name(vs->m3u8_name, m3u8_name_size, i);
>>> +        if (ret < 0)
>>> +            goto fail;
>>> +
>>>         vs->sequence       = hls->start_sequence;
>>>         vs->start_pts      = AV_NOPTS_VALUE;
>>>         vs->end_pts      = AV_NOPTS_VALUE;
>>> @@ -2161,9 +2242,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);
>>> @@ -2171,6 +2249,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) {
>>> @@ -2181,13 +2262,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);
>>> @@ -2196,7 +2273,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)
>>> @@ -2208,28 +2285,6 @@ 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);
>>> @@ -2240,13 +2295,12 @@ 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) {
>>> +
>>> +            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;
>>> -            }
>>> 
>>> -            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) {
>>> @@ -2256,6 +2310,12 @@ static int hls_init(AVFormatContext *s)
>>>                 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;
>>> 
>>> @@ -2294,10 +2354,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) {
>>> @@ -2309,27 +2366,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) {
>>> -- 
>>> 1.9.1
>>> 
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org
>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Dixit, Vishwanath Dec. 27, 2017, 3:43 a.m. UTC | #4
On 12/26/17 4:29 PM, 刘歧 wrote:
>
>> On 26 Dec 2017, at 18:38, Vishwanath Dixit <vdixit@akamai.com> wrote:
>>
>>
>>
>> On 12/26/17 3:37 PM, 刘歧 wrote:
>>>> On 26 Dec 2017, at 17:53, vdixit@akamai.com wrote:
>>>>
>>>> From: Vishwanath Dixit <vdixit@akamai.com>
>>>>
>>>> ---
>>>> doc/muxers.texi      |  31 +++++++++--
>>>> libavformat/hlsenc.c | 153 ++++++++++++++++++++++++++++++++++-----------------
>>>> 2 files changed, 127 insertions(+), 57 deletions(-)
>>>>
>>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>>> index 93db549..6af970d 100644
>>>> --- a/doc/muxers.texi
>>>> +++ b/doc/muxers.texi
>>>> @@ -575,6 +575,17 @@ Should a relative path be specified, the path of the created segment
>>>> files will be relative to the current working directory.
>>>> When use_localtime_mkdir is set, the whole expanded value of @var{filename} will be written into the m3u8 segment list.
>>>>
>>>> +When @code{var_stream_map} is set with two or more variant streams, the
>>>> +@var{filename} pattern must contain the string "%v", this string specifies
>>>> +the position of variant stream index in the generated segment file names.
>>>> +@example
>>>> +ffmpeg -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>>>> +  -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>>>> +  -hls_segment_filename 'file_%v_%03d.ts' out_%v.m3u8
>>>> +@end example
>>>> +This example will produce the playlists segment file sets:
>>>> +@file{file_0_000.ts}, @file{file_0_001.ts}, @file{file_0_002.ts}, etc. and
>>>> +@file{file_1_000.ts}, @file{file_1_001.ts}, @file{file_1_002.ts}, etc.
>>>>
>>>> @item use_localtime
>>>> Use strftime() on @var{filename} to expand the segment filename with localtime.
>>>> @@ -701,6 +712,10 @@ the fmp4 files is used in hls after version 7.
>>>> @item hls_fmp4_init_filename @var{filename}
>>>> set filename to the fragment files header file, default filename is @file{init.mp4}.
>>>>
>>>> +When @code{var_stream_map} is set with two or more variant streams, the
>>>> +@var{filename} pattern must contain the string "%v", this string specifies
>>>> +the position of variant stream index in the generated init file names.
>>>> +
>>>> @item hls_flags @var{flags}
>>>> Possible values:
>>>>
>>>> @@ -814,32 +829,36 @@ Expected string format is like this "a:0,v:0 a:1,v:1 ....". Here a:, v:, s: are
>>>> the keys to specify audio, video and subtitle streams respectively.
>>>> Allowed values are 0 to 9 (limited just based on practical usage).
>>>>
>>>> +When there are two or more variant streams, the output filename pattern must
>>>> +contain the string "%v", this string specifies the position of variant stream
>>>> +index in the output media playlist filenames.
>>>> +
>>>> @example
>>>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>>>>    -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>>>> -  http://example.com/live/out.m3u8
>>>> +  http://example.com/live/out_%v.m3u8
>>>> @end example
>>>> This example creates two hls variant streams. The first variant stream will
>>>> contain video stream of bitrate 1000k and audio stream of bitrate 64k and the
>>>> second variant stream will contain video stream of bitrate 256k and audio
>>>> -stream of bitrate 32k. Here, two media playlist with file names out_1.m3u8 and
>>>> -out_2.m3u8 will be created.
>>>> +stream of bitrate 32k. Here, two media playlist with file names out_0.m3u8 and
>>>> +out_1.m3u8 will be created.
>>>> @example
>>>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k \
>>>>    -map 0:v -map 0:a -map 0:v -f hls -var_stream_map "v:0 a:0 v:1" \
>>>> -  http://example.com/live/out.m3u8
>>>> +  http://example.com/live/out_%v.m3u8
>>>> @end example
>>>> This example creates three hls variant streams. The first variant stream will
>>>> be a video only stream with video bitrate 1000k, the second variant stream will
>>>> be an audio only stream with bitrate 64k and the third variant stream will be a
>>>> video only stream with bitrate 256k. Here, three media playlist with file names
>>>> -out_1.m3u8, out_2.m3u8 and out_3.m3u8 will be created.
>>>> +out_0.m3u8, out_1.m3u8 and out_2.m3u8 will be created.
>>>> @example
>>>> ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k -b:v:1 3000k  \
>>>>    -map 0:a -map 0:a -map 0:v -map 0:v -f hls \
>>>>    -var_stream_map "a:0,agroup:aud_low a:1,agroup:aud_high v:0,agroup:aud_low v:1,agroup:aud_high" \
>>>>    -master_pl_name master.m3u8 \
>>>> -  http://example.com/live/out.m3u8
>>>> +  http://example.com/live/out_%v.m3u8
>>>> @end example
>>>> This example creates two audio only and two video only variant streams. In
>>>> addition to the #EXT-X-STREAM-INF tag for each variant stream in the master
>>>> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
>>>> index bd43336..76a4110 100644
>>>> --- a/libavformat/hlsenc.c
>>>> +++ b/libavformat/hlsenc.c
>>>> @@ -1536,7 +1536,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'};
>>>> @@ -1555,6 +1555,53 @@ 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;
>>>> +    int ret = 0;
>>>> +
>>>> +    if (!fn) {
>>>> +        ret = AVERROR(EINVAL);
>>>> +        goto fail;
>>>> +    }
>>>> +
>>>> +    filename = av_basename(fn);
>>>> +
>>>> +    if (nb_vs > 1 && !av_stristr(filename, "%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;
>>>> +    }
>>>> +
>>>> +fail:
>>>> +    return ret;
>>>> +}
>>>> +
>>>> +static int format_name(char *buf, int buf_len, int index)
>>>> +{
>>>> +    char *orig_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(buf, buf_len, orig_buf_dup, 'v', index) < 1) {
>>>> +        ret = AVERROR(EINVAL);
>>>> +        goto fail;
>>>> +    }
>>>> +
>>>> +fail:
>>>> +    av_freep(&orig_buf_dup);
>>>> +    return ret;
>>>> +}
>>>> +
>>>> static int get_nth_codec_stream_index(AVFormatContext *s,
>>>>                                        enum AVMediaType codec_type,
>>>>                                        int stream_id)
>>>> @@ -1698,7 +1745,7 @@ static int update_variant_stream_info(AVFormatContext *s) {
>>>> static int update_master_pl_info(AVFormatContext *s) {
>>>>      HLSContext *hls = s->priv_data;
>>>>      const char *dir;
>>>> -    char *fn;
>>>> +    char *fn = NULL;
>>>>      int ret = 0;
>>>>
>>>>      fn = av_strdup(s->filename);
>>>> @@ -2076,6 +2123,28 @@ static int hls_init(AVFormatContext *s)
>>>>          goto fail;
>>>>      }
>>>>
>>>> +    ret = validate_name(hls->nb_varstreams, s->filename);
>>>> +    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) {
>>>> @@ -2108,6 +2177,18 @@ 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];
>>>> +
>>> from here
>>>> +        m3u8_name_size = strlen(s->filename) + 1;
>>>> +        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);
>>> to here.  is this mean strdup?
>> Yes, it is doing the same operation as that of strdup. Since, the size of the allocated memory was needed to be used in the below line (m3u8_name_size), av_malloc was used instead of strdup.
> What about modify to :
>
> m3u8_name_size = strlen(s->filename) + 1;
> vs_>m3u8_name = av_strdup(s->filename);
> if (!vs->m3u8_name) {
>      ret = AVERROR(ENOMEM);
>      goto fail;
> }
> ret = format_name(vs->m3u8_name, m3u8_name_size, i);
>
> ?
Thanks for the suggestion. I have made the change in the similar lines 
and have submitted the updated patch set with version v3. 
(https://patchwork.ffmpeg.org/patch/6985/)
>>>> +        ret = format_name(vs->m3u8_name, m3u8_name_size, i);
>>>> +        if (ret < 0)
>>>> +            goto fail;
>>>> +
>>>>          vs->sequence       = hls->start_sequence;
>>>>          vs->start_pts      = AV_NOPTS_VALUE;
>>>>          vs->end_pts      = AV_NOPTS_VALUE;
>>>> @@ -2161,9 +2242,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);
>>>> @@ -2171,6 +2249,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) {
>>>> @@ -2181,13 +2262,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);
>>>> @@ -2196,7 +2273,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)
>>>> @@ -2208,28 +2285,6 @@ 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);
>>>> @@ -2240,13 +2295,12 @@ 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) {
>>>> +
>>>> +            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;
>>>> -            }
>>>>
>>>> -            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) {
>>>> @@ -2256,6 +2310,12 @@ static int hls_init(AVFormatContext *s)
>>>>                  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;
>>>>
>>>> @@ -2294,10 +2354,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) {
>>>> @@ -2309,27 +2366,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) {
>>>> -- 
>>>> 1.9.1
>>>>
>>>> _______________________________________________
>>>> ffmpeg-devel mailing list
>>>> ffmpeg-devel@ffmpeg.org
>>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>>
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Steven Liu Dec. 27, 2017, 4:14 a.m. UTC | #5
> On 27 Dec 2017, at 11:43, Vishwanath Dixit <vdixit@akamai.com> wrote:
> 
> 
> 
> On 12/26/17 4:29 PM, 刘歧 wrote:
>> 
>>> On 26 Dec 2017, at 18:38, Vishwanath Dixit <vdixit@akamai.com> wrote:
>>> 
>>> 
>>> 
>>> On 12/26/17 3:37 PM, 刘歧 wrote:
>>>>> On 26 Dec 2017, at 17:53, vdixit@akamai.com wrote:
>>>>> 
>>>>> From: Vishwanath Dixit <vdixit@akamai.com>
>>>>> 
>>>>> ---
>>>>> doc/muxers.texi      |  31 +++++++++--
>>>>> libavformat/hlsenc.c | 153 ++++++++++++++++++++++++++++++++++-----------------
>>>>> 2 files changed, 127 insertions(+), 57 deletions(-)
>>>>> 
>>>>> diff --git a/doc/muxers.texi b/doc/muxers.texi
>>>>> index 93db549..6af970d 100644
>>>>> --- a/doc/muxers.texi
>>>>> +++ b/doc/muxers.texi
>>>>> @@ -575,6 +575,17 @@ Should a relative path be specified, the path of the created segment
>>>>> files will be relative to the current working directory.
>>>>> When use_localtime_mkdir is set, the whole expanded value of @var{filename} will be written into the m3u8 segment list.
>>>>> 
>>>>> +When @code{var_stream_map} is set with two or more variant streams, the
>>>>> +@var{filename} pattern must contain the string "%v", this string specifies
>>>>> +the position of variant stream index in the generated segment file names.
>>>>> +@example
>>>>> +ffmpeg -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>>>>> +  -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>>>>> +  -hls_segment_filename 'file_%v_%03d.ts' out_%v.m3u8
>>>>> +@end example
>>>>> +This example will produce the playlists segment file sets:
>>>>> +@file{file_0_000.ts}, @file{file_0_001.ts}, @file{file_0_002.ts}, etc. and
>>>>> +@file{file_1_000.ts}, @file{file_1_001.ts}, @file{file_1_002.ts}, etc.
>>>>> 
>>>>> @item use_localtime
>>>>> Use strftime() on @var{filename} to expand the segment filename with localtime.
>>>>> @@ -701,6 +712,10 @@ the fmp4 files is used in hls after version 7.
>>>>> @item hls_fmp4_init_filename @var{filename}
>>>>> set filename to the fragment files header file, default filename is @file{init.mp4}.
>>>>> 
>>>>> +When @code{var_stream_map} is set with two or more variant streams, the
>>>>> +@var{filename} pattern must contain the string "%v", this string specifies
>>>>> +the position of variant stream index in the generated init file names.
>>>>> +
>>>>> @item hls_flags @var{flags}
>>>>> Possible values:
>>>>> 
>>>>> @@ -814,32 +829,36 @@ Expected string format is like this "a:0,v:0 a:1,v:1 ....". Here a:, v:, s: are
>>>>> the keys to specify audio, video and subtitle streams respectively.
>>>>> Allowed values are 0 to 9 (limited just based on practical usage).
>>>>> 
>>>>> +When there are two or more variant streams, the output filename pattern must
>>>>> +contain the string "%v", this string specifies the position of variant stream
>>>>> +index in the output media playlist filenames.
>>>>> +
>>>>> @example
>>>>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
>>>>>   -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
>>>>> -  http://example.com/live/out.m3u8
>>>>> +  http://example.com/live/out_%v.m3u8
>>>>> @end example
>>>>> This example creates two hls variant streams. The first variant stream will
>>>>> contain video stream of bitrate 1000k and audio stream of bitrate 64k and the
>>>>> second variant stream will contain video stream of bitrate 256k and audio
>>>>> -stream of bitrate 32k. Here, two media playlist with file names out_1.m3u8 and
>>>>> -out_2.m3u8 will be created.
>>>>> +stream of bitrate 32k. Here, two media playlist with file names out_0.m3u8 and
>>>>> +out_1.m3u8 will be created.
>>>>> @example
>>>>> ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k \
>>>>>   -map 0:v -map 0:a -map 0:v -f hls -var_stream_map "v:0 a:0 v:1" \
>>>>> -  http://example.com/live/out.m3u8
>>>>> +  http://example.com/live/out_%v.m3u8
>>>>> @end example
>>>>> This example creates three hls variant streams. The first variant stream will
>>>>> be a video only stream with video bitrate 1000k, the second variant stream will
>>>>> be an audio only stream with bitrate 64k and the third variant stream will be a
>>>>> video only stream with bitrate 256k. Here, three media playlist with file names
>>>>> -out_1.m3u8, out_2.m3u8 and out_3.m3u8 will be created.
>>>>> +out_0.m3u8, out_1.m3u8 and out_2.m3u8 will be created.
>>>>> @example
>>>>> ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k -b:v:1 3000k  \
>>>>>   -map 0:a -map 0:a -map 0:v -map 0:v -f hls \
>>>>>   -var_stream_map "a:0,agroup:aud_low a:1,agroup:aud_high v:0,agroup:aud_low v:1,agroup:aud_high" \
>>>>>   -master_pl_name master.m3u8 \
>>>>> -  http://example.com/live/out.m3u8
>>>>> +  http://example.com/live/out_%v.m3u8
>>>>> @end example
>>>>> This example creates two audio only and two video only variant streams. In
>>>>> addition to the #EXT-X-STREAM-INF tag for each variant stream in the master
>>>>> diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
>>>>> index bd43336..76a4110 100644
>>>>> --- a/libavformat/hlsenc.c
>>>>> +++ b/libavformat/hlsenc.c
>>>>> @@ -1536,7 +1536,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'};
>>>>> @@ -1555,6 +1555,53 @@ 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;
>>>>> +    int ret = 0;
>>>>> +
>>>>> +    if (!fn) {
>>>>> +        ret = AVERROR(EINVAL);
>>>>> +        goto fail;
>>>>> +    }
>>>>> +
>>>>> +    filename = av_basename(fn);
>>>>> +
>>>>> +    if (nb_vs > 1 && !av_stristr(filename, "%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;
>>>>> +    }
>>>>> +
>>>>> +fail:
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> +static int format_name(char *buf, int buf_len, int index)
>>>>> +{
>>>>> +    char *orig_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(buf, buf_len, orig_buf_dup, 'v', index) < 1) {
>>>>> +        ret = AVERROR(EINVAL);
>>>>> +        goto fail;
>>>>> +    }
>>>>> +
>>>>> +fail:
>>>>> +    av_freep(&orig_buf_dup);
>>>>> +    return ret;
>>>>> +}
>>>>> +
>>>>> static int get_nth_codec_stream_index(AVFormatContext *s,
>>>>>                                       enum AVMediaType codec_type,
>>>>>                                       int stream_id)
>>>>> @@ -1698,7 +1745,7 @@ static int update_variant_stream_info(AVFormatContext *s) {
>>>>> static int update_master_pl_info(AVFormatContext *s) {
>>>>>     HLSContext *hls = s->priv_data;
>>>>>     const char *dir;
>>>>> -    char *fn;
>>>>> +    char *fn = NULL;
>>>>>     int ret = 0;
>>>>> 
>>>>>     fn = av_strdup(s->filename);
>>>>> @@ -2076,6 +2123,28 @@ static int hls_init(AVFormatContext *s)
>>>>>         goto fail;
>>>>>     }
>>>>> 
>>>>> +    ret = validate_name(hls->nb_varstreams, s->filename);
>>>>> +    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) {
>>>>> @@ -2108,6 +2177,18 @@ 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];
>>>>> +
>>>> from here
>>>>> +        m3u8_name_size = strlen(s->filename) + 1;
>>>>> +        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);
>>>> to here.  is this mean strdup?
>>> Yes, it is doing the same operation as that of strdup. Since, the size of the allocated memory was needed to be used in the below line (m3u8_name_size), av_malloc was used instead of strdup.
>> What about modify to :
>> 
>> m3u8_name_size = strlen(s->filename) + 1;
>> vs_>m3u8_name = av_strdup(s->filename);
>> if (!vs->m3u8_name) {
>>     ret = AVERROR(ENOMEM);
>>     goto fail;
>> }
>> ret = format_name(vs->m3u8_name, m3u8_name_size, i);
>> 
>> ?
> Thanks for the suggestion. I have made the change in the similar lines and have submitted the updated patch set with version v3. (https://patchwork.ffmpeg.org/patch/6985/)
I have seen them, just wait developers full review, if no objections, i will merge these patches.


Thanks.
>>>>> +        ret = format_name(vs->m3u8_name, m3u8_name_size, i);
>>>>> +        if (ret < 0)
>>>>> +            goto fail;
>>>>> +
>>>>>         vs->sequence       = hls->start_sequence;
>>>>>         vs->start_pts      = AV_NOPTS_VALUE;
>>>>>         vs->end_pts      = AV_NOPTS_VALUE;
>>>>> @@ -2161,9 +2242,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);
>>>>> @@ -2171,6 +2249,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) {
>>>>> @@ -2181,13 +2262,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);
>>>>> @@ -2196,7 +2273,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)
>>>>> @@ -2208,28 +2285,6 @@ 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);
>>>>> @@ -2240,13 +2295,12 @@ 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) {
>>>>> +
>>>>> +            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;
>>>>> -            }
>>>>> 
>>>>> -            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) {
>>>>> @@ -2256,6 +2310,12 @@ static int hls_init(AVFormatContext *s)
>>>>>                 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;
>>>>> 
>>>>> @@ -2294,10 +2354,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) {
>>>>> @@ -2309,27 +2366,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) {
>>>>> -- 
>>>>> 1.9.1
>>>>> 
>>>>> _______________________________________________
>>>>> ffmpeg-devel mailing list
>>>>> ffmpeg-devel@ffmpeg.org
>>>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>>> 
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel@ffmpeg.org
>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel@ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
diff mbox

Patch

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 93db549..6af970d 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -575,6 +575,17 @@  Should a relative path be specified, the path of the created segment
 files will be relative to the current working directory.
 When use_localtime_mkdir is set, the whole expanded value of @var{filename} will be written into the m3u8 segment list.
 
+When @code{var_stream_map} is set with two or more variant streams, the
+@var{filename} pattern must contain the string "%v", this string specifies
+the position of variant stream index in the generated segment file names.
+@example
+ffmpeg -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
+  -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
+  -hls_segment_filename 'file_%v_%03d.ts' out_%v.m3u8
+@end example
+This example will produce the playlists segment file sets:
+@file{file_0_000.ts}, @file{file_0_001.ts}, @file{file_0_002.ts}, etc. and
+@file{file_1_000.ts}, @file{file_1_001.ts}, @file{file_1_002.ts}, etc.
 
 @item use_localtime
 Use strftime() on @var{filename} to expand the segment filename with localtime.
@@ -701,6 +712,10 @@  the fmp4 files is used in hls after version 7.
 @item hls_fmp4_init_filename @var{filename}
 set filename to the fragment files header file, default filename is @file{init.mp4}.
 
+When @code{var_stream_map} is set with two or more variant streams, the
+@var{filename} pattern must contain the string "%v", this string specifies
+the position of variant stream index in the generated init file names.
+
 @item hls_flags @var{flags}
 Possible values:
 
@@ -814,32 +829,36 @@  Expected string format is like this "a:0,v:0 a:1,v:1 ....". Here a:, v:, s: are
 the keys to specify audio, video and subtitle streams respectively.
 Allowed values are 0 to 9 (limited just based on practical usage).
 
+When there are two or more variant streams, the output filename pattern must
+contain the string "%v", this string specifies the position of variant stream
+index in the output media playlist filenames.
+
 @example
 ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k -b:a:1 32k \
   -map 0:v -map 0:a -map 0:v -map 0:a -f hls -var_stream_map "v:0,a:0 v:1,a:1" \
-  http://example.com/live/out.m3u8
+  http://example.com/live/out_%v.m3u8
 @end example
 This example creates two hls variant streams. The first variant stream will
 contain video stream of bitrate 1000k and audio stream of bitrate 64k and the
 second variant stream will contain video stream of bitrate 256k and audio
-stream of bitrate 32k. Here, two media playlist with file names out_1.m3u8 and
-out_2.m3u8 will be created.
+stream of bitrate 32k. Here, two media playlist with file names out_0.m3u8 and
+out_1.m3u8 will be created.
 @example
 ffmpeg -re -i in.ts -b:v:0 1000k -b:v:1 256k -b:a:0 64k \
   -map 0:v -map 0:a -map 0:v -f hls -var_stream_map "v:0 a:0 v:1" \
-  http://example.com/live/out.m3u8
+  http://example.com/live/out_%v.m3u8
 @end example
 This example creates three hls variant streams. The first variant stream will
 be a video only stream with video bitrate 1000k, the second variant stream will
 be an audio only stream with bitrate 64k and the third variant stream will be a
 video only stream with bitrate 256k. Here, three media playlist with file names
-out_1.m3u8, out_2.m3u8 and out_3.m3u8 will be created.
+out_0.m3u8, out_1.m3u8 and out_2.m3u8 will be created.
 @example
 ffmpeg -re -i in.ts -b:a:0 32k -b:a:1 64k -b:v:0 1000k -b:v:1 3000k  \
   -map 0:a -map 0:a -map 0:v -map 0:v -f hls \
   -var_stream_map "a:0,agroup:aud_low a:1,agroup:aud_high v:0,agroup:aud_low v:1,agroup:aud_high" \
   -master_pl_name master.m3u8 \
-  http://example.com/live/out.m3u8
+  http://example.com/live/out_%v.m3u8
 @end example
 This example creates two audio only and two video only variant streams. In
 addition to the #EXT-X-STREAM-INF tag for each variant stream in the master
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index bd43336..76a4110 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -1536,7 +1536,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'};
@@ -1555,6 +1555,53 @@  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;
+    int ret = 0;
+
+    if (!fn) {
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    filename = av_basename(fn);
+
+    if (nb_vs > 1 && !av_stristr(filename, "%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;
+    }
+
+fail:
+    return ret;
+}
+
+static int format_name(char *buf, int buf_len, int index)
+{
+    char *orig_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(buf, buf_len, orig_buf_dup, 'v', index) < 1) {
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+fail:
+    av_freep(&orig_buf_dup);
+    return ret;
+}
+
 static int get_nth_codec_stream_index(AVFormatContext *s,
                                       enum AVMediaType codec_type,
                                       int stream_id)
@@ -1698,7 +1745,7 @@  static int update_variant_stream_info(AVFormatContext *s) {
 static int update_master_pl_info(AVFormatContext *s) {
     HLSContext *hls = s->priv_data;
     const char *dir;
-    char *fn;
+    char *fn = NULL;
     int ret = 0;
 
     fn = av_strdup(s->filename);
@@ -2076,6 +2123,28 @@  static int hls_init(AVFormatContext *s)
         goto fail;
     }
 
+    ret = validate_name(hls->nb_varstreams, s->filename);
+    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) {
@@ -2108,6 +2177,18 @@  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];
+
+        m3u8_name_size = strlen(s->filename) + 1;
+        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);
+        ret = format_name(vs->m3u8_name, m3u8_name_size, i);
+        if (ret < 0)
+            goto fail;
+
         vs->sequence       = hls->start_sequence;
         vs->start_pts      = AV_NOPTS_VALUE;
         vs->end_pts      = AV_NOPTS_VALUE;
@@ -2161,9 +2242,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);
@@ -2171,6 +2249,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) {
@@ -2181,13 +2262,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);
@@ -2196,7 +2273,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)
@@ -2208,28 +2285,6 @@  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);
@@ -2240,13 +2295,12 @@  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) {
+
+            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;
-            }
 
-            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) {
@@ -2256,6 +2310,12 @@  static int hls_init(AVFormatContext *s)
                 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;
 
@@ -2294,10 +2354,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) {
@@ -2309,27 +2366,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) {