152 lines
5.6 KiB

import math
import queue
import cv2
import threading
import time
from myutils.ConfigManager import myCongif
from myutils.MyDeque import MyDeque
import subprocess as sp
class VideoCaptureWithFPS:
'''视频捕获的封装类,是一个通道一个
打开摄像头 0--USB摄像头1-RTSP,2-海康SDK
'''
def __init__(self, source,type=1):
self.source = source
self.width = None
self.height = None
self.bok = False
# GStreamer --- 内存占用太高,且工作环境的部署也不简单
# self.pipeline = (
# "rtspsrc location=rtsp://192.168.3.102/live1 protocols=udp latency=100 ! "
# "rtph264depay !"
# " h264parse !"
# " avdec_h264 !"
# " videoconvert !"
# " appsink"
# )
#self.cap = cv2.VideoCapture(self.pipeline, cv2.CAP_GSTREAMER)
#FFmpeg --更加定制化的使用--但要明确宽高。。。
# self.ffmpeg_cmd = [
# 'ffmpeg',
# '-rtsp_transport', 'udp',
# '-i', 'rtsp://192.168.3.102/live1',
# '-f', 'image2pipe',
# '-pix_fmt', 'bgr24',
# '-vcodec', 'rawvideo', '-'
# ]
# self.pipe = sp.Popen(self.ffmpeg_cmd, stdout=sp.PIPE, bufsize=10 ** 8)
# opencv -- 后端默认使用的就是FFmpeg -- 不支持UDP
self.running = True
#self.frame_queue = queue.Queue(maxsize=1)
self.frame_queue = MyDeque(5)
#self.frame = None
#self.read_lock = threading.Lock()
self.thread = threading.Thread(target=self.update)
self.thread.start()
def openViedo_opencv(self,source):
self.cap = cv2.VideoCapture(source)
if self.cap.isOpened(): # 若没有打开成功,在读取画面的时候,已有判断和处理 -- 这里也要检查下内存的释放情况
self.width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH))
self.height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
# self.fps = fps # 线程保持最大帧率的刷新画面---过高的帧率会影响CPU性能,但过地的帧率会造成帧积压
self.fps = math.ceil(
self.cap.get(cv2.CAP_PROP_FPS) / float(myCongif.get_data("verify_rate"))) - 1 # 向上取整。
#print(self.width, self.height, self.fps)
else:
raise ValueError("无法打开视频源")
def update(self):
sleep_time = myCongif.get_data("cap_sleep_time")
reconnect_attempts = myCongif.get_data("reconnect_attempts")
while self.running:
try:
self.openViedo_opencv(self.source)
failure_count = 0
self.bok = True
while self.running:
ret, frame = self.cap.read()
if not ret:
failure_count += 1
time.sleep(0.5) #休眠一段时间后重试
if failure_count >= reconnect_attempts:
raise RuntimeError("无法读取视频帧")
continue
self.frame_queue.myappend(frame)
failure_count = 0 #重置计数
# 跳过指定数量的帧以避免积压
for _ in range(self.fps):
self.cap.grab()
except Exception as e:
print(f"发生异常:{e}")
self.cap.release()
self.bok = False
print(f"{self.source}视频流,将于{sleep_time}秒后重连!")
# 分段休眠,检测 self.running
total_sleep = 0
while total_sleep < sleep_time:
if not self.running:
return
time.sleep(2)
total_sleep += 2
#释放cap资源,由release调用实现
#resized_frame = cv2.resize(frame, (int(self.width / 2), int(self.height / 2)))
# with self.read_lock:
# self.frame = frame
# if self.frame_queue.full():
# try:
# #print("队列满---采集线程丢帧")
# self.frame_queue.get(timeout=0.01) #这里不get的好处是,模型线程不会有None
# except queue.Empty: #为空不处理
# pass
# self.frame_queue.put(frame)
# if not self.frame_queue.full():
# self.frame_queue.put(frame)
def read(self):
'''
直接读视频原画面
:param type: 0-大多数情况读取1-绘制区域时读取一帧但当前帧不丢还是回队列
:return:
'''
# with self.read_lock:
# frame = self.frame.copy() if self.frame is not None else None
# if frame is not None:
# return True, frame
# else:
# return False, None
# if not self.frame_queue.empty():
# try:
# frame = self.frame_queue.get(timeout=0.05)
# except queue.Empty:
# #print("cap-frame None")
# return False, None
# else:
# #print("cap-frame None")
# return False, None
ret = False
frame = None
if self.bok: #连接状态再读取
frame = self.frame_queue.mypopleft()
if frame is not None:
ret = True
else:
print("____读取cap帧为空,采集速度过慢___")
return ret, frame
def release(self):
self.running = False
self.thread.join()
self.cap.release()