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

Con*_*nic 18 python video opencv video-capture video-streaming

目标和问题

我想设置一个 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)
Run Code Online (Sandbox Code Playgroud)

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

cv2速度

.

实际/预期速度

问题:

  • 我在这里做错了什么?
  • 为什么这么慢?
  • 如何将其同步到实时速度?

nat*_*ncy 15

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

1.设置最大缓冲区大小

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

2.设置帧检索延迟

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

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


如果您尝试使用保存的.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
Run Code Online (Sandbox Code Playgroud)


Con*_*nic 8

尝试穿线

我已经从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  
Run Code Online (Sandbox Code Playgroud)
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) 
Run Code Online (Sandbox Code Playgroud)

紧张不安,但结果是实时的

流媒体工作。它保持实时性。然而,就好像所有的帧都汇集在一起​​,突然冲进视频中。我希望有人能解释一下。

改进空间

实时流可以在这里找到。

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

该网站是使用m3u8python 的streamlink流抓取工具进行抓取的。

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) 
Run Code Online (Sandbox Code Playgroud)

产生:

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>)

])


Run Code Online (Sandbox Code Playgroud)

流被读取错误的可能性。