Files
Amorano-Jovi_Capture/core/stream.py
T
Alexander G. Morano 335ae69a71 added flip and reverse
fix camera scan for first time with no auto-scan
2025-02-25 04:24:50 -05:00

180 lines
5.2 KiB
Python

"""
Jovi_Capture - http://www.github.com/amorano/Jovi_Capture
Core
"""
import time
import threading
from typing import Any
import cv2
from cozy_comfyui import logger
# ==============================================================================
# === CLASS ===
# ==============================================================================
class MediaStreamBase:
def __init__(self, fps:float=30, timeout:float=5) -> None:
self.__fps = fps
self.__timeout = min(30, max(0.5, timeout))
self.__running = True
self.__quit = False
self.__frame = None
self.__source = None
self.__url = None
self.__reverse: bool = False
self.__flip: bool = False
self.__height = 0
self.__width = 0
self.__thread = threading.Thread(target=self.__run, daemon=True)
self.__thread.start()
def __run(self) -> None:
start_time = time.perf_counter()
while not self.__quit:
if not self.__source or not self.__source.isOpened():
logger.error("waiting on device")
time.sleep(0.5)
if time.perf_counter() - start_time > self.__timeout:
logger.error("could not capture device")
self.__quit = True
continue
delta = 1. / self.__fps
if self.__running:
while True:
ret, frame = self.__source.read()
if ret and frame is not None and frame.sum() > 0:
if self.__flip:
frame = cv2.flip(frame, 0)
if self.__reverse:
frame = cv2.flip(frame, 1)
self.__frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGRA)
break
elif time.perf_counter() - start_time > self.__timeout:
logger.error("could not capture frame")
self.__quit = True
break
"""
if not ret:
count = int(self.__source.get(cv2.CAP_PROP_FRAME_COUNT))
pos = int(self.__source.get(cv2.CAP_PROP_POS_FRAMES))
if pos >= count:
self.__source.set(cv2.CAP_PROP_POS_FRAMES, 0)
ret, self.__frame = self.__source.read()
https://www.learningcontainer.com/wp-content/uploads/2020/05/sample-mp4-file.mp4
"""
elapsed = time.perf_counter() - start_time
time.sleep(max(delta - elapsed, 0))
start_time = time.perf_counter()
self.__end()
def __repr__(self) -> str:
return self.__class__.__name__
def __end(self) -> None:
if self.__thread:
try:
self.__thread.join(timeout=self.__timeout)
except RuntimeError as e:
pass
self.__quit = True
if self.__source is not None:
self.__source.release()
self.__source = None
def play(self) -> None:
self.__running = True
def pause(self) -> None:
self.__running = False
@property
def url(self) -> str:
return self.__url
@url.setter
def url(self, url:str) -> None:
if url == self.__url:
return
if self.__source is not None:
self.__source.release()
self.__source = None
new_source = cv2.VideoCapture(url, cv2.CAP_ANY)
if new_source.isOpened():
self.__source = new_source
self.__url = url
logger.info(f"captured url: {self.__url}")
else:
logger.error(f"failed to open source: {url}")
@property
def source(self) -> cv2.VideoCapture:
return self.__source
@property
def frame(self) -> Any:
return self.__frame
@property
def fps(self) -> float:
return self.__fps
@fps.setter
def fps(self, val: float) -> None:
self.__fps = min(60, max(1, val))
@property
def running(self) -> bool:
return self.__running
@property
def timeout(self) -> float:
return self.__timeout
@timeout.setter
def timeout(self, timeout: float) -> None:
self.__timeout = min(30, max(1, timeout))
@property
def width(self) -> int:
return self.__width
@width.setter
def width(self, width:int) -> None:
if self.__source is not None:
self.__width = max(1, width)
self.__source.set(cv2.CAP_PROP_FRAME_WIDTH, self.__width)
@property
def height(self) -> int:
return self.__height
@height.setter
def height(self, height:int) -> None:
if self.__source is not None:
self.__height = max(1, height)
self.__source.set(cv2.CAP_PROP_FRAME_HEIGHT, self.__height)
@property
def flip(self) -> bool:
return self.__flip
@flip.setter
def flip(self, flip: bool) -> None:
self.__flip = flip
@property
def reverse(self) -> bool:
return self.__reverse
@flip.setter
def reverse(self, reverse: bool) -> None:
self.__reverse = reverse