使用FFmpeg进行即时转码和HLS流媒体传输

10

我正在开发一个涉及提供各种视频内容的Web应用程序。与HTML5视频播放器不兼容的视频文件如mkv容器或H265等无法处理。尽管Web友好的音频和视频编解码器没有问题,但我在设计这些不能被HTML5视频播放器处理的视频文件的传递方案时遇到了麻烦。

到目前为止,我使用了ffmpeg在服务器上对视频文件进行转码,并创建了HLS主播放列表和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

现在为了改进这个系统,我尝试通过我的应用程序生成VOD播放列表而不是使用ffmpeg,因为格式是自说明的。Web应用程序会预先使用视频属性(如持续时间、分辨率和比特率)(这些属性已知于服务器端)生成 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是否可以解决这个问题?

我的视频处理和生成知识非常有限。非常感谢您提供一些指导。

3个回答

3

这是可能的,但非常困难。我甚至认为使用ffmpeg可能不太可能。传输流具有时间戳和连续计数器,这些值应在片段边界保留。-copyts标志可能对此有所帮助。在这种情况下,B帧的处理非常困难,因为它们的时间戳将落在片段之外。音频也很困难。音频在编码器初始化时具有引导样本,这意味着您可能会在每个片段中获得额外的样本,从而产生音频爆裂声。

简而言之,这是可能的,但需要理解容器和底层编解码器的结构,并与它们一起工作。


2
我们在服务器上设置了类似的实时转码方案。所以,阅读此内容,我看到了很多相似之处。希望我能稍微帮上忙。
首先,我注意到以下几点:
- 您复制了音轨。在 .mkv 文件中,这可以是任何格式,包括不支持 HLS 的格式。我建议您也对音频进行编码。 - 您使用了 ultrafast 预设。这是一个非常损失的预设,使用该级别进行编码将导致文件大小是 veryfast 的两倍,但质量相同。我理解您想尽可能快地完成编码,但当人们需要流式传输两倍的数据时,您将创建不同的瓶颈。 我不太确定您为什么要按需编码内容,是为了每次创建唯一的文件,还是为了节省磁盘空间或时间,或其他原因,因此我的一些想法可能不适用于此处,但也许其中一些可以帮助到您:
  • 提前对视频进行编码,并提供该文件。这需要一些编码时间,但你可以让服务器开始计算,使文件尽可能小,从而节省硬盘和带宽成本,而且你只需要进行一次编码。

  • 如果你确实想按需编码文件,我仍然会将文件编码为ffmpeg快速读取的文件格式。你已经可以将音频正确编码为最终格式,因此无需再进行编码。所以,当需要按需编码时,只需要编码一个非常快速读取的视频文件,然后将音频复制到其中。

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

根据视频的时长和大小,它可以非常快速地完成。如果您想更快,可以与其他编码服务器/ GPU 并行处理。请注意,您的存储位置需要跟得上。我们的瓶颈最终是千兆网络连接。

如果您有足够的计算能力,视频可能完全在30秒左右编码完成。在视频开始之前,您可以显示广告或倒计时时钟,甚至只是一个沙漏,然后他就有了一个完全可搜索的视频。这完全避免了问题。

这至少是我们解决问题的方法。对我们有效,但它是一种比较暴力的解决方案。


在我的情况下,可能也包括 OP 的情况,我们无法立即或完全获取输入源。我们也不能必要地存储整个转码,或者想要等待。有一个非常庞大、不同的数据集,预处理是不可行的。 - Matt Joiner

1
你可以在第一个片段之后添加一个#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-DISCONTINUITY标签的媒体片段之前重置其解析器和解码器,否则可能会出现播放错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接