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

刘歧 lq at chinaffmpeg.org
Wed Dec 27 06:14:15 EET 2017



> On 27 Dec 2017, at 11:43, Vishwanath Dixit <vdixit at akamai.com> wrote:
> 
> 
> 
> On 12/26/17 4:29 PM, 刘歧 wrote:
>> 
>>> On 26 Dec 2017, at 18:38, Vishwanath Dixit <vdixit at akamai.com> wrote:
>>> 
>>> 
>>> 
>>> On 12/26/17 3:37 PM, 刘歧 wrote:
>>>>> On 26 Dec 2017, at 17:53, vdixit at akamai.com wrote:
>>>>> 
>>>>> From: Vishwanath Dixit <vdixit at 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
>>>>> + at var{filename} pattern must contain the string "%v", this string specifies
>>>>> +the position of variant stream index in the generated segment file names.
>>>>> + at 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
>>>>> + at end example
>>>>> +This example will produce the playlists segment file sets:
>>>>> + at file{file_0_000.ts}, @file{file_0_001.ts}, @file{file_0_002.ts}, etc. and
>>>>> + at 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
>>>>> + at 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 at ffmpeg.org
>>>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>>>> 
>>> _______________________________________________
>>> ffmpeg-devel mailing list
>>> ffmpeg-devel at ffmpeg.org
>>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>> 
>> 
>> _______________________________________________
>> ffmpeg-devel mailing list
>> ffmpeg-devel at ffmpeg.org
>> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel





More information about the ffmpeg-devel mailing list