一个 7×24 小时不间断的直播流,视频之间不重连:通过管道喂给一个 ffmpeg

视频

全天候播放成品视频的直播频道有两种简单的设计,但两者都有缺陷。每个视频重启一次 ffmpeg,会在每次切换时断开 RTMP 连接。concat 播放列表在启动时读取一次,因此新视频必须等到下次重启才能播放。

设计方案

每个视频都按直播流的分辨率和帧率转换为一次 MPEG-TS;竖屏视频叠加在自身的模糊副本上,而不是黑边。一个 ffmpeg 进程从标准输入读取 MPEG-TS,以恒定码率编码,每两秒一个关键帧,符合直播推流的要求。一个小型馈送器按实时节奏将准备好的文件依次写入该管道,并在每次切换时选择下一个文件:优先播放从未播放过的视频,其次是最近最少播放的。ffmpeg 的 MPEG-TS 读取器发现每个文件的时间戳都重新开始,并对其进行偏移,因此其输出可以不间断地运行。

细节一:从第一个字节开始计时

我们的第一个版本在 ffmpeg 启动时开始计时。准备第一个文件花了九秒,因此馈送器认为它落后了九秒,并一次性全部发送。改为在写入第一个字节时开始计时解决了这个问题:53 秒的运行产生了 53 秒的流。

细节二:接缝处的音频

在切换时,下一个文件的音频比上一个文件的音频结束提前了 17 毫秒开始。编码器警告其输入“时间倒退”,复用器修补了时间戳。使用 aresample=async=1:min_hard_comp=0.01:first_pts=0 进行重采样,可在编码器看到之前修剪或填充此类重叠。在一次略超一分钟、包含五次切换的运行中,接收到的流中每个音频和视频时间戳都在上升,且流解码时没有任何错误。

细节三:某些警告是正常的

每个文件都会重置其 MPEG-TS 计数器和时钟,因此 ffmpeg 会在每次切换时报告“Packet corrupt”和“timestamp discontinuity”。这些行是预期内的,我们将其从日志中排除,以便真正的错误更加突出。

看门狗

如果 ffmpeg 退出或其进度停滞一分钟,流会以逐渐延长的间隔重启,并在多次失败后发送消息。在我们的测试中,我们手动终止了编码器;七秒后流便恢复了,运行中途添加的视频无需重连即加入了轮播。重启是一次短暂的重连,因此请将 YouTube 直播设置为自动开始且不自动停止。每条日志行(包括 ffmpeg 自身的日志)中的流密钥均已掩码处理。Studio 将此直播频道作为 systemd 服务提供。

已更新: