我一直在思考这个问题。我不是ffmpeg 的专家,可能有更简单的方法,所以如果有人让我知道更好的方法,我可能会删除它。
目前,我的想法是,如果ffmpeg 将 JPEG 或 PNG 作为输出写入,您需要一个能够解析连接流并搜索 START-OF-IMAGE/END-OF-IMAGE 或 ( JPEG SOI/EOI 或 PNG IHDR/IEND)标记并理解块和部分。所以,我认为强制ffmpeg 写入固定长度的图像可能更简单,然后你可以只读取N 字节,知道它对应于一个帧并且你不需要一堆 "glue" 编写和维护的代码 - 只是标准工具。
我没有你的 UDP 流,所以我只使用了 .mov 文件。然后我强制RGB24 编码和一个固定大小、调整大小的 640x480 帧。然后我将其拆分为 640x480x3 RGB 字节的块,并要求 ImageMagick 从原始视频创建一个 PNG 并将其作为 PNG 输入 Redis:
#!/bin/bash
# Set height and width of frame, and size in bytes
h=480
w=640
bytes=$((h*w*3))
ffmpeg -i video.mov -r 1 -s ${w}x${h} -update 1 -pix_fmt rgb24 -f rawvideo - 2> /dev/null | \
gsplit -b $bytes --filter="magick -depth 8 -size ${w}x${h} rgb:- png:- | redis-cli -x SET sb4up"
然后我使用 OpenCV 每 0.5 秒从 Redis 抓取图像并显示结果。
#!/usr/bin/env python3
import redis
import cv2
import numpy as np
host='localhost'
port=6379
r = redis.Redis(host,port)
while True:
png=r.get('sb4up')
frame = cv2.imdecode(np.frombuffer(png,np.uint8), cv2.IMREAD_COLOR)
cv2.imshow("Viewer", frame)
cv2.waitKey(500)
注意:我使用 gsplit 表示 GNU split,因为我在 Mac 上。在 Linux 上,只需使用 split。
注意:显然,您可以使用不同大小的框架,只要您在代码的两个部分都更新它 - 即在 gsplit 和 ImageMagick 中。
注意:您不一定需要安装 ImageMagick 并对每个图像进行 PNG 编码,因为在 Redis 所以空间不会成为问题,因为没有数千帧,所以您可以省略 ImageMagick 并将原始 RGB24 数据写入 Redis 而无需ImageMagick。然后从 Redis 中获取原始数据并使用np.reshape() 将其制成正确形状的图像。
我仍在思考,可能会想出更好的东西... Perl 可能适合在模式上拆分二进制流...
另一个想法是让ffmpeg 用frame-%03d.png 之类的序列号写入PNG 文件,然后运行fswatch/inotify 来抓取各个帧并将它们填充到Redis中。