澎湃Logo
下载客户端

登录

  • +1

使用FFmpeg进行HLS打包——FFmpeg简单学

2022-05-09 12:08
来源:澎湃新闻·澎湃号·湃客
字号

▲扫描图中二维码了解音视频技术大会更多信息▲

Easy-Tech #023#

在本文中,我们将学习使用 FFmpeg 进行 HLS 打包。使用 FFmpeg 的好处在于:你可以在不离开命令行的情况下,执行提取视频、调整视频尺寸、转码、打包以及传输视频的所有操作。

我们首先看一下为 VOD(点播)创建 HLS 打包的所有步骤,然后再来了解 HLS 实时流的打包。

如果你想了解 HLS 播放列表的更多信息,可以访问我们的 m3u8 文件合集:https://ottverse.com/free-hls-m3u8-test-urls/ ,查看使用不同用例的不同厂商的示例。如果你刚刚接触 HLS,请阅读我们之前的文章:什么是 HLS(HTTP Live Streaming)? 和 理解 ABR 及其工作原理。

事不宜迟,让我们开始吧!

使用 FFmpeg 进行 HLS 打包的基础步骤

好,现在让我们看下使用 HLS 打包点播文件的基础步骤:

从磁盘读取输入视频

将视频缩放 / 调整(scale/resize)为所需的多种分辨率版本

将每个缩放后的视频转码到所需码率

将音频转码到所需码率

将视频与音频组合,然后打包每一个音、视频组合,再创建各 TS 视频切片和播放列表(playlist)

创建一个主播放列表(master playlist),用于指向每个变体(variant)

现在,让我们一步一步来解决。

FFmpeg 将视频调整为多种分辨率版本

第一步和第二步包括从磁盘中读取视频,然后将其调整为多种分辨率。上述操作仅需一个命令,如下所示:

ffmpeg -i brooklynsfinest_clip_1080p.mp4 \ -filter_complex \ "[0:v]split=3[v1][v2][v3];\ [v1]copy[v1out];\ [v2]scale=w=1280:h=720[v2out];\ [v3]scale=w=640:h=360[v3out]"

[0:v] 指输入文件的第一个视频流。在我们的例子中,只有一个视频流,它被分成 3 个输出 [v1]、[v2]、[v3]。它们每一个都作为 FFmpeg 缩放函数的输入,该缩放函数接受一个高度和宽度数值用于执行缩放。

这里,我们将输入视频调整为 1080p、720p 和 360p。

这里的 [v1out]、[v2out]、[v3out] 是包含缩放过程的输出变量。注意,这里我们假设缩放过程会保留长宽比(aspect ratio)。当然,你可以在必要时使用 letterboxing 来处理。

审校者注:letterboxing 是指将以宽银幕比例拍摄的电影转换到标准宽度的视频格式时,同时保留电影的原始宽高比,由此产生的视频图像上下都有黑条的这个过程;这些黑条是图像的一部分(即视频信号的每一帧)。LTBX 是其缩写,标识如此格式化的电影和图像。这个术语来自于信箱的形状,信箱是墙壁或门上的一个槽,邮件通过它来传递,它是长方形的,宽于它的高度。下图是一个 2.35:1 比例的 widescreen 图像经过 letterboxing 处理之后,放在 1.33:1 屏幕上的一个例子。如下:

除此之外,还有 pillarboxing 和 windowboxing 等常见转换模式。

将视频转码为多种码率用于 HLS 打包

接着,我们进入第三步和第四步:我们必须将视频转码为多种码率,正如 ABR 技术常做的那样。

记住,我们已经将视频调整为所需的分辨率并存储进 [v1out]、[v2out] 和 [v3out] 的输出。我们将直接使用它们作为转码步骤的输入。

-map [v1out] -c:v:0 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:0 5M -maxrate:v:0 5M -minrate:v:0 5M -bufsize:v:0 10M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map [v2out] -c:v:1 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:1 3M -maxrate:v:1 3M -minrate:v:1 3M -bufsize:v:1 3M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map [v3out] -c:v:2 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:2 1M -maxrate:v:2 1M -minrate:v:2 1M -bufsize:v:2 1M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map a:0 -c:a:0 aac -b:a:0 96k -ac 2 \ -map a:0 -c:a:1 aac -b:a:1 96k -ac 2 \ -map a:0 -c:a:2 aac -b:a:2 48k -ac 2 \

你能发现这里发生了什么吗?我们已将三个变量 [v1out]、 [v2out] 和 [v3out] 作为输入,并使用 libx264 的 slow 预设置转码每个输入到所需码率。

注意:你也可以选择你自己的编码参数,并根据自己的喜好和要求进行修改。在这个示例中,我使用一些简单的参数模拟 CBR 编码。使用 FFmpeg 有无数方法可以转码你的视频,你可以在预设置、crf 值和 CBR 设置等不同参数组合之间任意选择。

重要的是,我们已经将 - keyint_min 设置为 48,它会强制设置关键帧出现的周期,这在 ABR 转码技术中非常重要。

现在,我们进行到下一个阶段:为每一个码率版本 / 变体(rendition/variant)创建 HLS m3u8 播放清单。

使用 FFmpeg 创建 HLS 播放清单(m3u8)

现在我们已经有了将视频转码为不同码率变体的命令,让我们来使用 FFmpeg 创建 HLS 点播播放列表。

下面是进行 HLS 打包所需的重要设置:

hls_playlist_type=vod: 通过设置该值,FFmpeg 创建了一个点播播放列表,将 #EXT-X-PLAYLIST-TYPE:VOD 插入到 m3u8 头部中,并强制 hls_list_size 为 0。

hls_time seconds:我们需要使用它设置目标切片长度(以秒为单位)。

默认值为 2 秒,当 2 秒过去,切片将在下一个关键帧处被切片。

之所以要求确保每个比特流变体在每 N 秒结束的时候都有一个关键帧(这点非常重要),因为 ABR 要求切片时候的关键帧要对齐,这样才能无缝切换。

hls_segment_type:这里有两个值:mpegts 或 fmp4,用于指定创建 TS 片段或 fmp4(CMAF)片段,这对创建 HLS 和 DASH 的单一数据流很有用。

-hls_flags independent_segments:当确保播放列表中所有切片都以一个关键帧开始时,将 #EXT-X-INDEPENDENT-SEGMENTS 添加到播放列表中。

hls_segment_filename filename: 用于在打包过程中为所创建的视频切片命名。

下面是为单一视频文件创建播放列表的示例:

-f hls \ -hls_time 2 \ -hls_playlist_type vod \ -hls_flags independent_segments \ -hls_segment_type mpegts \ -hls_segment_filename stream_%v/data%02d.ts \ -var_stream_map “v:0,a:0 v:1,a:1 v:2,a:2” stream_%v/stream.m3u8

如果你看到最后一行,你会注意到一个名为 var_stream_map 的功能选项。它是做什么的?

var_stream_map 是一个 FFmpeg 功能选项,它帮助我们将各种视频和音频转码组合起来,以创建不同的 HLS 播放列表。如果你有两个使用相同视频但不同音频的码率版本,那么你可以选择不同的视频和音频版本并将它们连接起来,而不是为了创建不同的播放列表而创建多个编码。

比如,-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" 是指由 a:0 表示的音频流被用于三种视频码率版本(rendition)。

FFmpeg 获取这些音频和视频的组合后,创建出名为 stream_% v.m3u8 的各变体的.m3u8 文件,其中 % v 是一个迭代器,它从被打包的视频流编号获取对应的值。

使用 FFmpeg 创建主播放列表(m3u8)

如果你已经理解了如何使用 FFmpeg 创建 HLS 播放列表,那么使用 FFmpeg 创建主播放列表对你而言就很简单了。如果你不知道什么是主播放列表的话,我可以告诉你:主播放列表就是一个文件,它列出了已由 HLS 打包的各个变体的播放列表。

为了使用 FFmpeg 创建主播放列表,只需将关键词 master_pl_name 添加到你的 FFmpeg 命令中,并提供你为主播放列表准备的名称。比如,如果你想称 “主播放列表” 为 “master.m3u8”,只需写下如下命令:

-master_pl_name master.m3u8

这就可以了。在 FFmpeg 执行该命令行之后,你将拥有一个 HLS 主播放列表,其中列出了其他播放列表的名称。

使用 FFmpeg-VOD 进行 HLS 打包的最终脚本

ffmpeg -i brooklynsfinest_clip_1080p.mp4 \ -filter_complex \ "[0:v]split=3[v1][v2][v3]; \ [v1]copy[v1out]; [v2]scale=w=1280:h=720[v2out]; [v3]scale=w=640:h=360[v3out]" \ -map [v1out] -c:v:0 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:0 5M -maxrate:v:0 5M -minrate:v:0 5M -bufsize:v:0 10M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map [v2out] -c:v:1 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:1 3M -maxrate:v:1 3M -minrate:v:1 3M -bufsize:v:1 3M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map [v3out] -c:v:2 libx264 -x264-params "nal-hrd=cbr:force-cfr=1" -b:v:2 1M -maxrate:v:2 1M -minrate:v:2 1M -bufsize:v:2 1M -preset slow -g 48 -sc_threshold 0 -keyint_min 48 \ -map a:0 -c:a:0 aac -b:a:0 96k -ac 2 \ -map a:0 -c:a:1 aac -b:a:1 96k -ac 2 \ -map a:0 -c:a:2 aac -b:a:2 48k -ac 2 \ -f hls \ -hls_time 2 \ -hls_playlist_type vod \ -hls_flags independent_segments \ -hls_segment_type mpegts \ -hls_segment_filename stream_%v/data%02d.ts \ -master_pl_name master.m3u8 \ -var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" stream_%v.m3u8

让我们看下该脚本的输出。

它首先生成一个主播放列表,三个文件夹包含独立的切片,以及三个变体的播放列表。

下面是 master.m3u8 文件:

#EXTM3U #EXT-X-VERSION:6 #EXT-X-STREAM-INF:BANDWIDTH=5605600,RESOLUTION=1920x1080,CODECS="avc1.640032,mp4a.40.2" stream_0.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=3405600,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2" stream_1.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1205600,RESOLUTION=640x360,CODECS="avc1.64001e,mp4a.40.2" stream_2.m3u8

你可以看到,主播放列表引用了分别用于 1080p、720p 和 360p 的 HLS 变体播放列表。

现在,我们来看看 1080p HLS 变体。它很明确地表明它是一个 VOD 播放列表,视频切片都是独立的,每个切片长度是 2 秒(按照我们的设置)。

#EXTM3U #EXT-X-VERSION:6 #EXT-X-TARGETDURATION:2 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-PLAYLIST-TYPE:VOD #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:2.002000, data00.ts #EXTINF:2.002000, data01.ts #EXTINF:2.002011, data02.ts #EXTINF:2.002000, data03.ts #EXTINF:2.002000, data04.ts #EXTINF:2.002000, data05.ts #EXTINF:2.002000, data06.ts #EXTINF:2.002000, data07.ts #EXTINF:2.002011, data08.ts #EXTINF:2.002000, data09.ts #EXTINF:0.041711, data10.ts #EXT-X-ENDLIST

使用 FFmpeg 进行 HLS 直播打包

如果你想通过 FFmpeg 创建一个直播 HLS 播放列表,这个过程和我们刚刚讲过的 VOD 步骤区别不是很大。下面是你需要做出的更改:

删除 - hls_playlist_type vod

添加 - hls_list_size ,并将其设置为一个数字,该数字表示各个变体播放列表中的切片数(你希望设置的数字)。

比如,如果我们将 - hls_list_size 设置为 2,那么整个播放列表将只包含两个切片,FFmpeg 将通过添加新的切片及删除旧切片来重写这个播放列表。

下面是一个示例:

#EXTM3U #EXT-X-VERSION:6 #EXT-X-TARGETDURATION:2 #EXT-X-MEDIA-SEQUENCE:1 #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:2.002000, data01.ts #EXTINF:2.002011, data02.ts

几秒之后,切片 data01.ts 被删除,并被切片 data03.ts 所取代。

#EXTM3U #EXT-X-VERSION:6 #EXT-X-TARGETDURATION:2 #EXT-X-MEDIA-SEQUENCE:2 #EXT-X-INDEPENDENT-SEGMENTS #EXTINF:2.002011, data02.ts #EXTINF:2.002000, data03.ts

FFmepg 中其他有用的 HLS 打包选项

最后,让我们来快速浏览一些 FFmpeg 为点播和直播 HLS 打包所提供的一些其他有趣选项:

hls_base_url baseurl : 可用于将 baseurl 表示的值追加到播放列表的每个条目。

hls_fmp4_init_filename filename :  设置文件名为分片文件头文件,默认文件名为 init.mp4。当你把片段类型设置为 fmp4 而非 mpegts 时,就会用到这个文件。

hls_fmp4_init_resend:在 m3u8 文件每次刷新时,重新发送 init 文件,默认为 0。

审校者注:当__var_stream_map__设置为两个或多个变体流时,文件名模式必须包含字符串 "% v",这个字符串指定变体流索引在生成的 init 文件名中的位置。这时候可以结合上面的 hls_fmp4_init_resend 重新发送 init 文件。

iframes_only : 将 #EXT-X-I-FRAMES-ONLY 添加到包含视频切片并只能在 #EXT-X-BYTERANGE 模式下播放 I 帧的播放列表中。

结语

现在,我希望你已经很好地理解了如何通过 FFmpeg 来执行 HLS 流媒体协议转码和打包。有关使用 FFmpeg 进行 HLS 打包的完整选项列表,请查看 FFmpeg 文档:

https://ffmpeg.org/ffmpeg-formats.html#hls-2。

保重,我们下次见!

致谢:

本文已获得作者 Krishna Rao Vijayanagar 授权翻译和发布,特此感谢。

原文链接:

https://ottverse.com/hls-packaging-using-ffmpeg-live-vod/

 

    本文为澎湃号作者或机构在澎湃新闻上传并发布,仅代表该作者或机构观点,不代表澎湃新闻的观点或立场,澎湃新闻仅提供信息发布平台。申请澎湃号请用电脑访问http://renzheng.thepaper.cn。

    +1
    收藏
    我要举报

            扫码下载澎湃新闻客户端

            沪ICP备14003370号

            沪公网安备31010602000299号

            互联网新闻信息服务许可证:31120170006

            增值电信业务经营许可证:沪B2-2017116

            © 2014-2024 上海东方报业有限公司

            反馈