在 Opencv 中处理相机流,使用 FFMPEG 将其推送到 RTMP(NGINX RTMP 模块)
Processing Camera stream in Opencv, pushing it over RTMP (NGINX RTMP Module) using FFMPEG
输出视频:
https://youtu.be/VxfoBQjoY6E
解释:
我想:在 Opencv 中处理摄像头流并将其推送到 RTMP 服务器。我已经设置了 NGINX(RTMP 模块),并且测试了使用 RTMP(Flash Player)和 HLS 的流媒体视频。
我正在循环读取帧并使用 python 中的 'subprocess' 来执行 ffmpeg 命令。这是我正在使用的命令:
command = [ffmpeg,
'-y',
'-f', 'rawvideo',
'-vcodec','rawvideo',
'-pix_fmt', 'bgr24',
'-s', dimension,
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrafast',
'-f', 'flv',
'rtmp://10.10.10.80/live/mystream']
import subprocess as sp
...
proc = sp.Popen(command, stdin=sp.PIPE,shell=False)
...
proc.stdin.write(frame.tostring()) #frame is read using opencv
问题:
我可以很好地看到流,但它经常冻结和恢复。这是 FFMPEG 终端日志的输出:
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (libx264))
frame= 117 fps= 16 q=22.0 size= 344kB time=00:00:04.04 bitrate= 697.8kbits/s speed=0.543x
最后提到了速度。我相信它应该接近 1 倍。我不确定如何实现。
我和服务器在同一个网络上,如果需要,我可以 post 我的 python 代码。需要一些 ffmpeg 大师给我一些建议。
编辑
我的输入 fps 实际上是 ~3。
使用 '-use_wallclock_as_timestamps', '1'
我可以在日志中看到速度接近 1x。
但是 HLS 没有直播,有大约 2 分钟的延迟,它停止并且 .克里斯的建议部分奏效了。我不确定到底是哪里出了问题,我开始相信它与nginx-rtmp模块有关。
这是最终输出,左边是flash,右边是hls。我在最后展示了 ffmpeg 选项。
https://youtu.be/jsm6XNFOUE4
我以前在流式传输原始视频(就像您一样)或来自 MJPEG 源时遇到过类似问题。有两个输入选项可以尝试使用 ffmpeg 使其保持在 1x 速度:
选项 1
ffmpeg -re -i <rest of input options>
-re
告诉 ffmpeg 以本机输入速率读取
选项 2
ffmpeg -use_wallclock_as_timestamps 1 -i <rest of input options>
-use_wallclock_as_timestamps
告诉 ffmepg 只获取每一帧,获取系统时间,并将其作为时间戳。我发现这个选项在速度减慢时最有效。
不顾一切
确保您以恒定的帧速率进行编码。当帧率变化时 ffmpeg 可能会有点挑剔,因此在输出选项上,使用 -r 25
(将 25 替换为您想要的输出帧率)强制 ffmpeg 使用静态帧率 out
我认为最简单的方法是使用 MoviePy 模块(顺便说一句,我发现它很棒)。看看 MoviePy Video Writer。它应该写入本地文件,但您也可以通过在命令末尾添加 -f flv
来指定输出格式,将其与 rtmp 流一起使用。要将帧写入流,只需使用方法 write_frame(your_frame)
。它对我来说效果很好
我遇到了同样的问题,发现 ffmpeg 不能自动填充空白,所以当你以 3 fps 的速度输入 ffmpeg 时,客户端仍会以 25 fps 播放,因此客户端需要先缓存很多帧播放然后快速播放,然后停止缓存帧
所以当你用这样的代码喂 ffmpeg 时,很容易填补空白
timeStart = get_time_seconds()
count = 0
some loop:
frame = getimage()
targetCount = (get_time_seconds()-timeStart)*25
repeatCount = targetCount - count
loop for repeatCount times:
proc.stdin.write(frame.tostring())
count = targetCount
输出视频: https://youtu.be/VxfoBQjoY6E
解释:
我想:在 Opencv 中处理摄像头流并将其推送到 RTMP 服务器。我已经设置了 NGINX(RTMP 模块),并且测试了使用 RTMP(Flash Player)和 HLS 的流媒体视频。
我正在循环读取帧并使用 python 中的 'subprocess' 来执行 ffmpeg 命令。这是我正在使用的命令:
command = [ffmpeg,
'-y',
'-f', 'rawvideo',
'-vcodec','rawvideo',
'-pix_fmt', 'bgr24',
'-s', dimension,
'-i', '-',
'-c:v', 'libx264',
'-pix_fmt', 'yuv420p',
'-preset', 'ultrafast',
'-f', 'flv',
'rtmp://10.10.10.80/live/mystream']
import subprocess as sp
...
proc = sp.Popen(command, stdin=sp.PIPE,shell=False)
...
proc.stdin.write(frame.tostring()) #frame is read using opencv
问题:
我可以很好地看到流,但它经常冻结和恢复。这是 FFMPEG 终端日志的输出:
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> h264 (libx264))
frame= 117 fps= 16 q=22.0 size= 344kB time=00:00:04.04 bitrate= 697.8kbits/s speed=0.543x
最后提到了速度。我相信它应该接近 1 倍。我不确定如何实现。
我和服务器在同一个网络上,如果需要,我可以 post 我的 python 代码。需要一些 ffmpeg 大师给我一些建议。
编辑
我的输入 fps 实际上是 ~3。
使用 '-use_wallclock_as_timestamps', '1'
我可以在日志中看到速度接近 1x。
但是 HLS 没有直播,有大约 2 分钟的延迟,它停止并且 .克里斯的建议部分奏效了。我不确定到底是哪里出了问题,我开始相信它与nginx-rtmp模块有关。
这是最终输出,左边是flash,右边是hls。我在最后展示了 ffmpeg 选项。 https://youtu.be/jsm6XNFOUE4
我以前在流式传输原始视频(就像您一样)或来自 MJPEG 源时遇到过类似问题。有两个输入选项可以尝试使用 ffmpeg 使其保持在 1x 速度:
选项 1
ffmpeg -re -i <rest of input options>
-re
告诉 ffmpeg 以本机输入速率读取
选项 2
ffmpeg -use_wallclock_as_timestamps 1 -i <rest of input options>
-use_wallclock_as_timestamps
告诉 ffmepg 只获取每一帧,获取系统时间,并将其作为时间戳。我发现这个选项在速度减慢时最有效。
不顾一切
确保您以恒定的帧速率进行编码。当帧率变化时 ffmpeg 可能会有点挑剔,因此在输出选项上,使用 -r 25
(将 25 替换为您想要的输出帧率)强制 ffmpeg 使用静态帧率 out
我认为最简单的方法是使用 MoviePy 模块(顺便说一句,我发现它很棒)。看看 MoviePy Video Writer。它应该写入本地文件,但您也可以通过在命令末尾添加 -f flv
来指定输出格式,将其与 rtmp 流一起使用。要将帧写入流,只需使用方法 write_frame(your_frame)
。它对我来说效果很好
我遇到了同样的问题,发现 ffmpeg 不能自动填充空白,所以当你以 3 fps 的速度输入 ffmpeg 时,客户端仍会以 25 fps 播放,因此客户端需要先缓存很多帧播放然后快速播放,然后停止缓存帧
所以当你用这样的代码喂 ffmpeg 时,很容易填补空白
timeStart = get_time_seconds()
count = 0
some loop:
frame = getimage()
targetCount = (get_time_seconds()-timeStart)*25
repeatCount = targetCount - count
loop for repeatCount times:
proc.stdin.write(frame.tostring())
count = targetCount