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(10) #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}秒后重连!") time.sleep(sleep_time) #释放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 return ret, frame def release(self): self.running = False self.thread.join() self.cap.release()