import math import queue import cv2 import threading import time from myutils.ConfigManager import myCongif import subprocess as sp class VideoCaptureWithFPS: '''视频捕获的封装类,是一个通道一个''' def __init__(self, source): self.source = source self.width = None self.height = None # 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.cap = cv2.VideoCapture(self.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)) print(self.width,self.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.fps) self.running = True self.frame_queue = queue.Queue(maxsize=1) #self.frame = None #self.read_lock = threading.Lock() self.thread = threading.Thread(target=self.update) self.thread.start() def update(self): icount = 0 while self.running: ret, frame = self.cap.read() if not ret: icount += 1 if icount > 5: #重连 self.cap.release() self.cap = cv2.VideoCapture(self.source) #self.cap = cv2.VideoCapture(self.pipeline, cv2.CAP_GSTREAMER) 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)) print(self.width,self.height) # self.fps = fps # 线程保持最大帧率的刷新画面---过高的帧率会影响CPU性能,但过地的帧率会造成帧积压 self.fps = math.ceil( self.cap.get(cv2.CAP_PROP_FPS) / float(myCongif.get_data("verify_rate"))) -1 # 向上取整。 icount = 0 else: #self.frame = None sleep_time = myCongif.get_data("cap_sleep_time") print(f"{self.source}视频流,将于{sleep_time}秒后重连!") time.sleep(sleep_time) continue #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) # 跳过指定数量的帧以避免积压 for _ in range(self.fps): self.cap.grab() # time.sleep(self.fps) #按照视频源的帧率进行休眠 #print("Frame updated at:",time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) 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 return True, frame def release(self): self.running = False self.thread.join() self.cap.release()