使用ffmpeg进行动态转码和HLS流传输



我正在构建一个涉及提供各种视频内容的web应用程序。网络友好的音频和视频编解码器处理起来没有任何问题,但我在设计与HTML5视频播放器(如mkv容器或H265)不兼容的视频文件时遇到了问题。

到目前为止,我所做的是在服务器上使用ffmpeg对视频文件进行转码,并制作HLS master和VOD播放列表,并在前端使用HLS.js。然而,问题是,ffmpeg将播放列表视为直播播放列表,直到整个文件的转码完成,然后它将播放列表更改为VOD。因此,在代码转换结束之前,用户无法进行搜索,并且如果用户决定在中途搜索视频文件,我的服务器已经对整个文件进行了不必要的代码转换。我正在使用以下ffmpeg命令行参数

ffmpeg -i sample.mkv 
-c:v libx264 
-crf 18 
-preset ultrafast 
-maxrate 4000k 
-bufsize 8000k 
-vf "scale=1280:-1,format=yuv420p" 
-c:a copy -start_number 0 
-hls_time 10 
-hls_list_size 0 
-f hls 
file.m3u8

现在为了改进这个系统,我尝试通过我的应用程序而不是ffmpeg生成VOD播放列表,因为格式不言自明。网络应用程序将使用视频属性(如持续时间、分辨率和比特率(服务器已知))预先生成HLS主播放列表和VOD播放列表,并将主播放列表提供给客户端。然后,客户端开始请求各个视频片段,此时服务器将对每个片段进行单独的转码和生成,并为它们提供服务。搜索是可能的,因为客户端已经有完整的VOD播放列表,它可以请求用户想要的特定片段。在我看来,好处是,如果用户决定向前搜索并在中途播放视频,我的服务器将不必对整个文件进行转码。

现在,我尝试使用以下命令从sample.mkv手动创建段(每个段10个)

ffmpeg -ss 90 
-t 10 
-i sample.mkv 
-g 52 
-strict experimental 
-movflags +frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov 
-c:v libx264 
-crf 18 
-preset ultrafast 
-maxrate 4000k 
-bufsize 8000k 
-vf "scale=1280:-1,format=yuv420p" 
-c:a copy 
fileSequence0.mp4

等等,以及作为的VOD播放列表

#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
fileSequence0.mp4
#EXTINF:10.0,
fileSequence1.mp4
...
... and so on 
...
#EXT-X-ENDLIST

其播放第一片段很好,但不播放后续片段。

现在我的问题,

  1. 为什么后面的片段不播放?我做错了什么?

  2. 我的技术可行吗?由于只有在关键帧之后才能进行分段,因此预先设置分段持续时间会有任何问题吗?ffmpeg是否可以绕过这一点?

我对视频处理和生成的了解充其量也算不上什么。我非常感谢你的指点。

这是可能的,但非常困难。我甚至认为ffmpeg可能不可能做到这一点。传输流具有时间戳和连续性计数器,这些值应跨段边界保留。-copyts标志可能对此有所帮助。在这种情况下,B帧非常难以处理,因为它们最终会在段外带有时间戳。音频也很难。当编码器初始化时,音频有启动样本,这意味着你可能在音频弹出时每个片段都有额外的样本。

TLDR是可能的,但您需要了解容器和底层编解码器的结构,并使用它们。

我们有类似的设置,用于在服务器上提供实时转码。所以读到这篇文章,我发现了很多相似之处。我希望我能帮上一点忙。

首先,我注意到一些事情:

  • 您复制音轨。在.mmv文件中,可以是任何格式,包括HLS不支持的格式,我建议也对音频进行编码
  • 您使用超快预设。这是一个非常有损的预设,在该级别进行编码将导致文件的大小是非常快的两倍。我知道你想尽快完成编码,但当人们需要流式传输两倍多的数据时,你会产生不同的瓶颈

我不太确定你为什么要按需编码内容,或者每次创建一个唯一的文件,或者节省磁盘空间或时间,或者其他原因,所以我的一些想法可能不适用于这里,但也许其中一些会有所帮助:

  • 预先对视频进行编码,然后提供该文件。这需要一些编码时间,但你真的可以让你的服务器开始处理数字,使文件尽可能小,这为你节省了硬盘和带宽成本,而且你只需要编码一次。

  • 如果你确实想按需对文件进行编码,我仍然会将文件编码为一种ffmpeg读取速度非常快的文件格式。你已经可以正确地将音频编码为最终格式,所以以后不必再这样做了。因此,当需要进行按需编码时,只需要编码一个读起来非常快的视频文件,然后将音频复制到其中。然后我还会考虑将中间文件从4k60/转换为4k30,甚至转换为1080p30。这使得编码速度大大加快。

  • 如果你可以访问服务器,我真的建议你在CPU上添加一个GPU,比如Intel QuickSync,或者添加一个nVidia Quadro p400卡,它价格低廉,但速度更快,提供更好的质量和更小的文件大小。(FFMPEG可以做到这一点,不需要其他软件)。一张Quadro卡在1080p分辨率下可以达到2 x 300 fps。因此,它可以在一分钟内准备好一段20分钟的视频。

根据视频的持续时间和大小,它可以非常快地完成。如果你想要更快,你可以与其他编码服务器/gpu并行进行。请记住,您的存储位置需要能够跟上。我们的瓶颈是千兆网络连接。

如果你有足够的电源,视频可以在30秒内完全编码。在视频开始之前,你可以显示一个广告或倒计时钟,甚至只是一个沙漏,然后他就有了一个完全可以查找的视频。这完全忽略了问题。

至少我们是这样处理这个问题的。它对我们有效,但有点像暴力解决方案。

您可以在第一个段之后为每个段添加一个#EXT-X-DISCONTINUITY标记。

我将#EXT-X-DISCONTINUITY标签与MPEGTS和SCTE-35一起用于拼接具有完全不同的时间戳和连续性计数器的广告。

这样的东西:

#EXTM3U
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:4
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
fileSequence0.mp4
#EXT-X-DISCONTINUITY
#EXTINF:10.0,
fileSequence1.mp4
#EXT-X-DISCONTINUITY
#EXTINF:10.0,
fileSequence2.mp4    ...
... and so on 
...
#EXT-X-ENDLIST

引用rfc:

在播放应用了EXT-X-DISCNTINUITY标签的媒体段之前,客户端必须准备重置其解析器和解码器
;否则,可能会出现播放错误。

最新更新