import asyncio import re import time from threading import Thread # import cv2 import numpy as np from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtSlot, QThread, pyqtProperty # from PyQt5.QtGui import QImage # from PyQt5 import QtGui from . import vision_service class VideoStreamer(QObject): newImage = pyqtSignal(object, int, int) newRawImage = pyqtSignal(object) reconnectInitiated = pyqtSignal() reconnect = pyqtSignal() camChangeStart = pyqtSignal() camChangeEnd = pyqtSignal(int) isCamChangingChanged = pyqtSignal(bool) camReconnected = pyqtSignal(int) beginOpenVideoCamera = pyqtSignal(str) def __init__(self, parent: QObject = None): super(QObject, self).__init__(parent) self.__is_changing_camera = False self.__stopped = False self.__grabbed = False self.__is_reconnecting = False self.__frame = np.array([]) self.__cap = None self.__tmp_cap = None self.__rtsp_link = '' self.__tmp_rtsp_link = '' self.ip = '' self.tUpdate = None self.reconnectTimer = None self.t = None @pyqtProperty(bool, notify=isCamChangingChanged) def isChangingCamera(self): return self.__is_changing_camera @pyqtSlot() def beginReconnect(self): print('RECONNECTING') self.stopVideoCamera() self.__is_reconnecting = True # QTimer.singleShot(5000, self.reconnectVideo) t = Thread(target=self.reconnectVideo, args=()) t.daemon = True t.start() # @pyqtSlot() def reconnectVideo(self): rtsp_link = self.__tmp_rtsp_link print(f'reconnecting to {rtsp_link}') self.stopVideoCamera() while True: self.reconnect.emit() cap = vision_service.vision_service(rtsp_link) cap.start_framing() time.sleep(5) if cap.get_is_framing(): print('cam is online') self.camChangeEnd.emit(True) time.sleep(0.1) self.__cap = None fps = cap.get_fps() if fps == 0: if not cap.get_is_framing(): return False else: fps = 60 self.__stopped = True self.tUpdate.stop() self.reconnectTimer.stop() self.__cap = cap self.__rtsp_link = rtsp_link self.t = Thread(target=self.update, args=()) self.t.daemon = True self.camReconnected.emit(int(1000 // fps)) self.__stopped = False if not self.t.is_alive(): self.t.start() self.__is_reconnecting = False return True def update(self): while True: if self.__stopped is True: print('self.__stopped is True:') self.__frame = None break if not self.__cap: print('not self.__cap') time.sleep(0.1) continue if self.__cap.get_is_framing(): self.__frame = self.__cap.get_frame() time.sleep(0.066) else: time.sleep(0.1) if not self.__is_reconnecting: print('not self.__is_reconnecting') self.__is_reconnecting = True self.reconnectInitiated.emit() # TODO send error to client # if self.__frame is False: # print('[Exiting] No more frames to read') # self.__stopped = True # break # self.__cap.release() @pyqtSlot() def run(self) -> None: print('starting QThread') self.tUpdate = QTimer(self) self.reconnectTimer = QTimer(self) # self.tUpdate.moveToThread(self) # self.reconnectTimer.moveToThread(self) self.t = Thread(target=self.update, args=()) self.t.daemon = True # daemon threads run in background self.tUpdate.timeout.connect(self.streamVideo) self.reconnectTimer.timeout.connect(self.reconnectVideo) self.reconnectInitiated.connect(self.beginReconnect) self.camReconnected.connect(self.tUpdate.start) self.beginOpenVideoCamera.connect(self.openVideoCamera) # self.exec_() @pyqtSlot() def streamVideo(self): pass # if (self.__frame is not None) and self.__frame.size != 0: # height, width, channel = self.__frame.shape # bytesPerLine = 3 * width # qImg = QImage(self.__frame.data, width, height, bytesPerLine, QImage.Format_BGR888) # self.newRawImage.emit(self.__frame) # self.newImage.emit(qImg, width, height) def get_frame(self): return self.__frame @pyqtSlot(result=bool) def connectCam(self): print('CONNECTING CAM') fps = self.__tmp_cap.get_fps() print('NEW CAP FPS:', fps) if fps == 0: if not self.__tmp_cap.get_is_framing(): return False else: fps = 60 self.__stopped = True self.tUpdate.stop() self.reconnectTimer.stop() self.__rtsp_link = self.__tmp_rtsp_link self.__cap = self.__tmp_cap self.ip = re.findall("(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", self.__rtsp_link)[0] self.t = Thread(target=self.update, args=()) self.t.daemon = True self.tUpdate.start(int(1000 // fps)) self.__stopped = False if not self.t.is_alive(): self.t.start() self.__is_changing_camera = False self.isCamChangingChanged.emit(self.__is_changing_camera) return True @pyqtSlot(result=bool) def checkCam(self): print('CHECKING CAMERA') if self.__tmp_cap.get_is_framing(): print('camera is framing') self.stopVideoCamera() self.camChangeEnd.emit(True) QTimer.singleShot(100, self.connectCam) return True else: print('camera is not framing') self.camChangeEnd.emit(False) self.__is_changing_camera = False self.isCamChangingChanged.emit(self.__is_changing_camera) return False @pyqtSlot(str, result=bool) def changeCamera(self, rtsp_link): self.__is_changing_camera = True self.isCamChangingChanged.emit(self.__is_changing_camera) self.__tmp_rtsp_link = rtsp_link self.__tmp_cap = vision_service.vision_service(rtsp_link) self.__tmp_cap.start_framing() QTimer.singleShot(5000, self.checkCam) self.camChangeStart.emit() return True # except: # print('cannot stop framing') # return False @pyqtProperty(str) def rtspLink(self): return self.__rtsp_link @pyqtSlot(result=bool) @pyqtSlot(str, result=bool) def openVideoCamera(self, rtsp_link: str = None): if self.__cap is None: if rtsp_link is not None: self.__tmp_rtsp_link = rtsp_link print('rtsp_link:', self.__tmp_rtsp_link) self.__tmp_cap = vision_service.vision_service(self.__tmp_rtsp_link) self.__tmp_cap.start_framing() QTimer.singleShot(5000, self.checkCam) self.camChangeStart.emit() return True return False @pyqtSlot() def clearVideoCamera(self): self.__cap = None self.__rtsp_link = '' print('clearVideoCamera finished') @pyqtSlot(result=bool) def stopVideoCamera(self): self.__stopped = True self.t = Thread(target=self.update, args=()) self.t.daemon = True print('stopping video camera') self.tUpdate.stop() self.reconnectTimer.stop() if not self.__is_reconnecting: QTimer.singleShot(100, self.clearVideoCamera) try: if self.__cap: self.__cap.stop_framing() return True except Exception as e: print('cannot stop framing', e) return False def __del__(self): # self.__cap.release() try: self.__cap.stop_framing() except: print('cannot stop framing') self.tUpdate.stop() self.__stopped = True