OpenCV 实时流视频捕获速度很慢。如何丢帧或与实时同步?

OpenCV real time streaming video capture is slow. How to drop frames or get synced with real time?

目标与问题

我想设置一个 opencv 系统来处理 HLS 流或 RMTP 流,但是,我 运行 遇到了一个关于帧速率降低和累积滞后的奇怪问题。就好像视频从它应该在流中的位置越来越落后。

我正在寻找一种与实时源保持同步的方法,即使这意味着丢帧。

当前方法

import cv2

cap = cv2.VideoCapture()
cap.open('https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w1421640637.m3u8')

while (True):
    _, frame = cap.read()
    cv2.imshow("camCapture", frame)
    cv2.waitKey(1)

我已经验证了 VLC 上流的质量,它似乎在那里工作正常。

cv2 速度

realistic/expected速度

问题:

尝试穿线

我已经尝试 this solution from nathancy 并取得了小成功。

涉及:

  • 为从源捕获图像创建一个单独的线程
  • 使用主线程专门显示。

代码:

import cv2
from threading import Thread

class ThreadedCamera(object):
    def __init__(self, source = 0):

        self.capture = cv2.VideoCapture(source)

        self.thread = Thread(target = self.update, args = ())
        self.thread.daemon = True
        self.thread.start()

        self.status = False
        self.frame  = None

    def update(self):
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()

    def grab_frame(self):
        if self.status:
            return self.frame
        return None  
if __name__ == '__main__':
    stream_link = "https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w1421640637.m3u8"
    streamer = ThreadedCamera(stream_link)

    while True:
        frame = streamer.grab_frame()
        if frame is not None:
            cv2.imshow("Context", frame)
        cv2.waitKey(1) 

紧张,但实时结果

.

流式传输有效。它保持实时。然而,就好像所有的帧都聚集在一起,突然冲进了视频。我希望有人解释一下。

改进空间

实时流可以在这里找到。

https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet

此站点是为 m3u8 使用 python 的 streamlink 流抓取工具抓取的。


import streamlink

streams = streamlink.streams("https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet")
print(streams)

产量:

OrderedDict([

('720p',<HLSStream('https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w202109066.m3u8')>),

('live', <RTMPStream({'rtmp': 'rtmp://videos3.earthcam.com/fecnetwork/', 'playpath': '9974.flv', 'pageUrl': 'https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet','swfUrl': 'http://static.earthcam.com/swf/streaming/stream_viewer_v3.swf', 'live': 'true'}, redirect=False>),

('worst', <HLSStream('https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w202109066.m3u8')>),

('best', <RTMPStream({'rtmp': 'rtmp://videos3.earthcam.com/fecnetwork/', 'playpath': '9974.flv', 'pageUrl': 'https://www.earthcam.com/usa/newyork/timessquare/?cam=tsstreet', 'swfUrl': 'http://static.earthcam.com/swf/streaming/stream_viewer_v3.swf', 'live': 'true'}, redirect=False>)

])


流读取错误的可能性。

我的假设是,抖动很可能是由于网络限制造成的,并且在帧数据包丢失时发生。当一个帧被丢弃时,这会导致程序显示最后一个“好”帧,从而导致显示冻结。这可能是硬件或带宽问题,但我们可以通过软件缓解部分问题。以下是一些可能的更改:

1.设置最大缓冲区大小

我们使用 cv2.CAP_PROP_BUFFERSIZE 参数将 cv2.videoCapture() 对象设置为具有有限的缓冲区大小。这个想法是通过限制缓冲区,我们将始终拥有最新的帧。这也有助于缓解帧随机向前跳转的问题。

2。设置帧检索延迟

目前,我认为 read() 读取速度太快,即使它在自己的专用线程中也是如此。这可能是为什么所有帧看起来都集中起来并在下一帧突然爆发的原因之一。例如,假设在一秒的时间间隔内,它可能会产生 15 个新帧,但在下一个一秒的时间间隔内,只返回 3 帧。这可能是由于网络数据包丢帧,所以为了确保我们获得恒定的帧率,我们简单地在帧检索线程中添加一个延迟。获得大致 ~30 FPS 的延迟可以很好地“规范化”帧速率并平滑帧之间的过渡,以防丢包。

注意:我们应该尝试匹配流的帧率,但我不确定网络摄像头的 FPS 是多少所以我只是猜测 30每秒帧数。此外,通常有一个“直接”流 link 而不是通过可以大大提高性能的中间网络服务器。


如果您尝试使用保存的 .mp4 视频文件,您会发现没有抖动。这证实了我的怀疑,即问题很可能是由于网络延迟造成的。

from threading import Thread
import cv2, time

class ThreadedCamera(object):
    def __init__(self, src=0):
        self.capture = cv2.VideoCapture(src)
        self.capture.set(cv2.CAP_PROP_BUFFERSIZE, 2)
       
        # FPS = 1/X
        # X = desired FPS
        self.FPS = 1/30
        self.FPS_MS = int(self.FPS * 1000)
        
        # Start frame retrieval thread
        self.thread = Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()
        
    def update(self):
        while True:
            if self.capture.isOpened():
                (self.status, self.frame) = self.capture.read()
            time.sleep(self.FPS)
            
    def show_frame(self):
        cv2.imshow('frame', self.frame)
        cv2.waitKey(self.FPS_MS)

if __name__ == '__main__':
    src = 'https://videos3.earthcam.com/fecnetwork/9974.flv/chunklist_w1421640637.m3u8'
    threaded_camera = ThreadedCamera(src)
    while True:
        try:
            threaded_camera.show_frame()
        except AttributeError:
            pass

相关 camera/IP/RTSP/streaming、FPS、视频、线程和多处理帖子

  1. Video Streaming from IP Camera in Python Using OpenCV cv2.VideoCapture