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