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