From patchwork Tue Dec 26 09:53:40 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dixit, Vishwanath" X-Patchwork-Id: 6981 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.2.79.195 with SMTP id r64csp6651955jad; Tue, 26 Dec 2017 01:53:58 -0800 (PST) X-Google-Smtp-Source: ACJfBouTAD5Yaj3LQZo/SDStAii4+rai2ETTtfHsAUILgaFXK7DCwAJDJWF2qE99Eiu+S8V0TXFY X-Received: by 10.28.92.146 with SMTP id q140mr22702987wmb.41.1514282038780; Tue, 26 Dec 2017 01:53:58 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1514282038; cv=none; d=google.com; s=arc-20160816; b=ivXKJx2zPLkgvpjYmkabigqK0v9PQcb/GwIlU/H16nHKXNg2t012f3k1W9DDc7iO1g IW8wJRgcsVGF2vvp8kkqau3fcuHo+3YX3yH4jb0C1fffSxBQrZ4RvVkJ5qStTrpXBcWO ztu6Dc+hDWrlrUacG9I4x+lDHR5BvwTUgBkJY+U+urtOP+6RWzN3ONx2Dppvj3RGBJOP PV2AwAQY/pefNEfly4tcSZCprN5LmP52/mfoDLx+VIdfjtkuw/8jTAD9n9g1Xs8zrhpU usmVJtAEmjlq6V9PUqKbl9noqnH7vtTeSIwnhnDVtIqrzcNihD0CC4K9vfwWn+iAV6Zv LYFQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:feedback-id:references:in-reply-to :message-id:date:to:from:dkim-signature:delivered-to :arc-authentication-results; bh=1r9iOS4gqrN2MohbhLuhrxCmSOqYtN2zPWldx0ILyZo=; b=OWKkHgi0CQykToEhre9tdxH8rzKhV9rYh7JPE3EMqta/OQB+4MJDjHSTzIMbweA0w0 qlgi9oArEprtZ3khlBmBIFDU9DQTBd2egivc9YeOvwf1AgTovpSSHhaZXXIGwgYJPxr2 UTAFFLS5bM/kMitmV/B/UVO9HUtqYOPNGcEzQyH2qyyYf/duFdv2m2ZuQBp6AkuyFCUs 0QiRmGpbvUoEJ5tOX0K6tdtCTFlwkHudCjUD6luLtUZVqIIHq3/+H+hmVJt0RqMk7iLz OXR1mdN1bKdTYgp6c+J1hyqA5eKPD2TkQhSbNvDMjd4EwNzfe+XQ1PyEX7KjWuluZS4s RfVQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@smtpservice.net header.s=m78bu0.a1-4.dyn header.b=wZKQ1q0Q; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=akamai.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id c6si11555446wrg.330.2017.12.26.01.53.58; Tue, 26 Dec 2017 01:53:58 -0800 (PST) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@smtpservice.net header.s=m78bu0.a1-4.dyn header.b=wZKQ1q0Q; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=QUARANTINE sp=NONE dis=NONE) header.from=akamai.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id A87626882D3; Tue, 26 Dec 2017 11:53:40 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from a2i831.smtp2go.com (a2i831.smtp2go.com [103.47.207.63]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 7C7536806CB for ; Tue, 26 Dec 2017 11:53:33 +0200 (EET) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=smtpservice.net; s=m78bu0.a1-4.dyn; x=1514282932; h=Feedback-ID: X-Smtpcorp-Track:Message-Id:Date:Subject:To:From:Reply-To:Sender: List-Unsubscribe; bh=VHaYd9Uu9Z9sY3uFWOyEu/r5RQ9A9j0Icb2GdMrNqt4=; b=wZKQ1q0Q abhW0dwxtFspFLZIvoBvW1+c96Xfw1uWzQ8HzxS+zmdhlV5gZfoFLOGe2QCNn1P4IwKg6Fus4kUTD 0D+gc43vo5t6240GftxdpX65q5l6eGZ9JUo6WRwzArid60vkCAtMSMzSaezNJrMpd320JRRVx4WVw 9QWbSHnb1mMiF16AdwjYVj6F8qXui40JARhReNGOjnV9xwgzMHBemNGXw3E+HGvL/aQA51A5Em8R5 NFWWu8k/QLrQo1sGTwslR6/9entMEy4KjGk9wiant9YpqEAp83Ox91cS86utSINDb9jYzVMtbFV7l 34vMOn87ECcPj5UPCi/smLlk9g==; From: vdixit@akamai.com To: ffmpeg-devel@ffmpeg.org Date: Tue, 26 Dec 2017 15:23:40 +0530 Message-Id: <1514282020-7389-1-git-send-email-vdixit@akamai.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1512724625-7671-2-git-send-email-vdixit@akamai.com> References: <1512724625-7671-2-git-send-email-vdixit@akamai.com> X-Smtpcorp-Track: 1-T_vP4ba5GjQ_.Q_wXlFyhA Feedback-ID: 337386m:337386asVRLGB:337386sQy6WoqnsB:SMTPCORP X-Report-Abuse: Please forward a copy of this message, including all headers, to Subject: [FFmpeg-devel] [PATCH v2 2/3] avformat/hlsenc: configurable variant stream index position in filenames X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Vishwanath Dixit MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" From: Vishwanath Dixit --- 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]; + + 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) {