无实时约束处理大量 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鉴权出现跨域问题的解决方法

基于FFmpeg+rtsp读取摄像头实时图像

RTSP安防网络摄像头/海康大华硬盘录像机网页无插件直播流媒体服务器EasyNVR之鉴权接口的调用配置说明

WEB页面实时播放海康大华等摄像头RTSP视频流完全方案

Onvif/RTSP网络安防摄像机网页无插件直播方案EasyNVR如何判断前端设备的类型?

Python脚本在几秒钟后自动关闭,当它从IP摄像机对RTSP实时源进行对象检测时?