通过管道将 ffmpeg h.264 输出到 VLC 以提供 MJPEG 流

通过管道将 ffmpeg h.264 输出到 VLC 以提供 MJPEG 流

问题

我想将原始 RGB24 帧流式传输到 ffmpeg stdin,并将 h.264 从 ffmpeg 传输到 VLC,然后在某个任意端口上提供 HTTP MJPEG 流。我可以使用任一程序独立获得结果,但无法使管道正常工作。我怀疑这很重要,但这是 Raspberry Pi 4B rev 1.2,带有运行 Pi OS ARM32 的 v1 5MP 摄像头(无论哪个版本都是 2020 年 7 月左右的最新版本)。

细节

我今天的工作是一个使用 MMAL 和无头 Raspberry Pi 上的摄像头的 .NET Core 程序,它成功地将 640x480 24FPS h.264 输出到 VLC stdin,并且 VLC 能够通过 HTTP 将其作为 MJPEG 流中继(为了便于阅读,带有换行符):

cvlc stream:///dev/stdin 
--sout "#transcode{vcodec=mjpg,vb=2500,fps=24,acodec=none}
:standard{access=http{mime=multipart/x-mixed-replace;
boundary=7b3cc56e5f51db803f790dad720ed50a},mux=mpjpeg,
dst=:8554/}" :demux=h264

这最终可以通过我的 LAN 上的浏览​​器进行访问http://raspberrypi.local:8554/

但是,应用程序需要对每个视频帧进行后处理。由于 MMAL 处理管道的工作方式,没有合理的方法通过硬件 h.264 编码器运行处理后的帧。因此,我只能输出原始 RGB24 帧。(在有人指出我可以在端口和连接等地方拦截 MMAL 缓冲区之前,在更高的分辨率下,一个帧跨越多个缓冲区,而我需要执行的处理类型需要一个完整的帧。)

我也熟悉通过 发送到 ffmpeg stdin。我试图使用 ffmpeg 将原始帧动态转换为 h.264,然后将其导入 VLC,但我似乎找不到有效的组合。(根据 VLC 上的多路复用器矩阵流媒体功能页面,VLC 不支持原始输入到 MJPEG 输出。)

为了简化初始测试,我将原始 RGB24 帧输出到文件中。我可以让 ffmpeg 生成具有以下设置的有效 h.264 文件(包括一些选项,例如超快,这些选项是我想要的,但对于文件输出来说并不是必需的),所以我认为这个配置是正确的(再次带有换行符):

ffmpeg -f rawvideo -c:v rawvideo -pix_fmt rgb24 -s:v 640x480 -r 24 
-i /media/ramdisk/input.raw 
-f h264 -c:v libx264 -preset ultrafast -tune zerolatency -framerate 24 
/media/ramdisk/output.h264

我觉得我应该能够用管道替换最后一个输出文件参数,然后-将其传输到相同的 VLC 命令,但这样做不行。最终,这两个命令将出现在一个.sh脚本中,这样我就可以用一个 .NET 启动它Process

控制台输出

控制台输出(使用 ffmpeg-hide_banner抑制开关设置的输出)以一些 VLC 消息开始,这是正常的:

VLC media player 3.0.11 Vetinari (revision 3.0.11-0-gdc0c5ced72)
[00663e18] vlcpulse audio output error: PulseAudio server connection failure: Connection refused
[00693e58] main interface error: no suitable interface module
[005e8b58] main libvlc error: interface "globalhotkeys,none" initialization failed
[00693e58] dummy interface: using the dummy interface module...

接下来是一些 ffmpeg 输出:

[rawvideo @ 0x9672c0] Estimating duration from bitrate, this may be inaccurate
Input #0, rawvideo, from '/media/ramdisk/input.raw':
  Duration: 00:00:14.96, start: 0.000000, bitrate: 176947 kb/s
    Stream #0:0: Video: rawvideo (RGB[24] / 0x18424752), rgb24, 640x480, 176947 kb/s, 24 tbr, 24 tbn, 24 tbc
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (libx264))
Press [q] to stop, [?] for help
[libx264 @ 0x973520] using cpu capabilities: ARMv6 NEON
[libx264 @ 0x973520] profile High 4:4:4 Predictive, level 3.0, 4:4:4 8-bit
Output #0, h264, to 'pipe:':
  Metadata:
    encoder         : Lavf58.20.100
    Stream #0:0: Video: h264 (libx264), yuv444p, 640x480, q=-1--1, 24 fps, 24 tbn, 24 tbc
    Metadata:
      encoder         : Lavc58.35.100 libx264
    Side data:
      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1

然后是更多的 VLC 输出 - 前两行是此设置所特有的,它不是我将 h.264 直接输出到 VLC 时所看到的stdin

[b2705a68] main stream error: unknown query 0x30e in demux_vaControlHelper
[b0b00e18] mmal_codec generic: VCSM init succeeded: CMA
[b0b01110] avcodec encoder error: Unknown option "rc_buffer_aggressivity"

然后ffmpeg开始输出:

frame=  359 fps= 29 q=22.0 Lsize=     754kB time=00:00:14.95 bitrate= 413.0kbits/s speed= 1.2x
video:754kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%
[libx264 @ 0x973520] frame I:2     Avg QP:19.50  size: 18098
[libx264 @ 0x973520] frame P:357   Avg QP:22.00  size:  2062
[libx264 @ 0x973520] mb I  I16..4: 100.0%  0.0%  0.0%
[libx264 @ 0x973520] mb P  I16..4: 11.7%  0.0%  0.0%  P16..4: 22.5%  0.0%  0.0%  0.0%  0.0%    skip:65.8%
[libx264 @ 0x973520] coded y,u,v intra: 5.1% 0.3% 0.2% inter: 7.6% 1.1% 0.8%
[libx264 @ 0x973520] i16 v,h,dc,p: 33% 29% 22% 16%
[libx264 @ 0x973520] kb/s:413.00

没有发生其他事情。在此处理过程中,尝试通过 HTTP 连接从来都不起作用。我花了几个小时搜索并尝试了许多组合,这里列出的命令是唯一不会导致某种错误的命令。

注意我不是寻找涉及其他应用程序或协议的答案。我知道有很多方法可以实现流式输出,但我有理由只希望使用 ffmpeg、VLC 和 MJPEG。如果有更有效的方法来完成同样的事情,我不会在 ffmpeg 和 VLC 之间局限于 h.264。

答案1

如果我使用h264_omx硬件编码,这将有效,因此第二组格式开关变成:

-f h264 -c:v h264_omx -b:v 2500k

如果我将输入文件切换为stdin这样:-i -并将其放入名为的脚本中ffmpeg2clvc.sh

#!/bin/bash
ffmpeg -hide_banner -f rawvideo -c:v rawvideo -pix_fmt rgb24 -s:v 640x480 -r 24 -i - -f h264 -c:v h264_omx -b:v 2500k - | cvlc stream:///dev/stdin --sout "#transcode{vcodec=mjpg,vb=2500,fps=24,acodec=none}:standard{access=http{mime=multipart/x-mixed-replace;boundary=7b3cc56e5f51db803f790dad720ed50a},mux=mpjpeg,dst=:8554/}" :demux=h264

我可以使用此命令通过管道输入从文件流式传输:

cat /media/ramdisk/input.raw | ./ffmpeg2clvc.sh

这是从控制台进行的,我可以从另一台机器浏览该流。

(事实证明这只是一个部分解决方案。我的程序使用 Pi 相机,因此将硬件编码器绑定在一起,因此这实际上并不能解决我的问题——但在我意识到这一点之前,我已经将其作为答案发布了。)

相关内容