You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

149 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(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}秒后重连!")
# 分段休眠,检测 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
return ret, frame
def release(self):
self.running = False
self.thread.join()
self.cap.release()