无实时约束处理大量 rtsp 摄像头
Posted
技术标签:
【中文标题】无实时约束处理大量 rtsp 摄像头【英文标题】:Handle large number of rtsp cameras without real-time constraint 【发布时间】:2022-01-09 07:12:36 【问题描述】:问题定义
假设我有大量的rtsp相机(> 100),我想对其进行一些操作,例如图像特征提取。
重要:我对实时性能不感兴趣,我可以在一分钟内进行 4 次特征提取。显然,越多越好!
现在,瓶颈是图像采集。帧是用cv2
获取的
请阅读以下部分了解我的尝试。
伪代码(当前解决方案)
while True:
for every rstp_cameras:
open_connection
read_current_frame(no batch - the real time frame)
process_frame
close
我尝试过的
在 *** 上,您可以找到很多关于实时读取 rtsp 摄像头的答案,但都受到摄像头数量的限制或存在一些缺点。 我试过(用python):
-
每个相机的线程 [cv2 with ffmpeg]
为线程中的每个摄像头打开一个连接,然后获取每个摄像头可用的最后一帧。
此解决方案有效,但仅适用于少量摄像机。如果我们增加数字,高端 cpu 的使用率将达到 100%(因为线程在后台总是读取最后一帧,如果我不问最后一帧,则丢弃它)
[当前解决方案,无线程,ffmpeg with cv2] 在每次迭代时打开一个连接,读取帧并关闭连接。此解决方案使我可以使用最后一帧,但主要缺点是打开期间丢失了时间(打开所有帧丢失了大约 70 秒)
带有 gstreamer 的 Cv2,无线程
基于此answer。 如果您的摄像头数量很少,这是我发现的最佳解决方案。使用 20 台或更多相机时,线程解决方案也有同样的问题。
问题与回顾
现在,我很清楚,在一个工作站中处理所有这些摄像机是很困难的,因为我找到的所有解决方案都是为了返回可用的最后一帧(实时)连续读取背景。
目前,我还没有找到一个解决方案,可以让我打开一次连接,读取低 cpu 使用率的实时帧,这样我就可以在大量摄像头上使用它。
阅读的并行化是解决问题的唯一方法吗?我的意思是,将相机分成批次,在不同的工作站分配批次,然后以某种方式组合图像?
谢谢。
【问题讨论】:
是时候深入挖掘 ffmpeg 的 API 并直接使用它了。 OpenCV 具有用于视频 I/O 的方便接口。它们不是用来承受负载的。 【参考方案1】:您可以尝试使用带有-discard nokey
参数的FFmpeg,如here 所述。
我不确定它是否会起作用......
据我了解,-discard nokey
仅解码关键帧,并跳过所有其他视频帧。
假设只解码关键帧可以大大减少 CPU 使用率(取决于关键帧的频率)。
这是一个代码示例(针对一个 RTSP 流):
import numpy as np
import subprocess as sp
# Use public RTSP Streaming for testing:
in_stream = "rtsp://wowzaec2demo.streamlock.net/vod/mp4:BigBuckBunny_115k.mov"
# Use OpenCV for getting the video resolution.
cap = cv2.VideoCapture(in_stream)
# Get resolution of input video
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# Release VideoCapture - it was used just for getting video resolution
cap.release()
# http://zulko.github.io/blog/2013/09/27/read-and-write-video-frames-in-python-using-ffmpeg/
FFMPEG_BIN = "ffmpeg" # on Linux ans Mac OS (also works on Windows when ffmpeg.exe is in the path)
ffmpeg_cmd = [FFMPEG_BIN,
'-discard', 'nokey',
'-rtsp_transport', 'tcp',
'-max_delay', '30000000', # 30 seconds
'-i', in_stream,
'-f', 'rawvideo',
'-pix_fmt', 'bgr24',
'-vcodec', 'rawvideo', '-an', 'pipe:']
# Open sub-process that gets in_stream as input and uses stdout as an output PIPE.
process = sp.Popen(ffmpeg_cmd, stdout=sp.PIPE)
while True:
raw_frame = process.stdout.read(width*height*3)
if len(raw_frame) != (width*height*3):
print('Error reading frame!') # Break the loop in case of an error (too few bytes were read).
break
# Transform the byte read into a numpy array, and reshape it to video frame dimensions
frame = np.frombuffer(raw_frame, np.uint8).reshape((height, width, 3))
# Show frame for testing
cv2.imshow('frame', frame)
cv2.waitKey(1)
process.stdout.close()
process.wait()
cv2.destroyAllWindows()
如上尝试打开100个FFmpeg子进程...
【讨论】:
感谢您的回复!是的,它似乎对cpu的影响很小。但是,我做了这个实验,它不起作用。我创建了 100 个进程,遍历它们(在while True
内)并读取框架。在最后一次迭代中(即当我到达第 100 个相机时,我插入了一个time.sleep(8)
。然后,如果我观看我保存的图像,我可以看到它们没有反映现实,它们积累了很高的延迟,肯定大于8秒,好像被缓存了一样。这是个问题,我希望我看帧的时候能反映真实情况。
为避免堆积,为每个摄像头创建一个读取线程(排空管道)。仅在非常特殊的情况下,简单地迭代摄像机才会起作用,即所有视频流的 GOP 大小相同,并且您可以足够快地读取帧以避免累积。
我认为插入thread
来排空管道可能会使cpu 饱和,就像其他涉及线程的解决方案一样。
我认为线程不会使 CPU 饱和,因为执行 raw_frame = process.stdout.read(width*height*3)
不是 CPU 密集型的 - 在数据准备好之前,线程应该处于挂起状态。即使等待(内部 Python 实现)不是最有效的(并且存在上下文切换开销),它仍然是相对较轻的 CPU 任务。
好吧,我不明白你的意思。我会试试看。谢谢!以上是关于无实时约束处理大量 rtsp 摄像头的主要内容,如果未能解决你的问题,请参考以下文章
海康大华等网络摄像头RTSP_Onvif网页无插件直播流媒体服务器EasyNVR鉴权出现跨域问题的解决方法
RTSP安防网络摄像头/海康大华硬盘录像机网页无插件直播流媒体服务器EasyNVR之鉴权接口的调用配置说明