diff --git a/profiles/hong.json b/profiles/hong.json new file mode 100644 index 0000000..aebc4bf --- /dev/null +++ b/profiles/hong.json @@ -0,0 +1 @@ +{"video_device": {"friendly_name": "WARRKY USB 3.0", "format": "mjpeg", "resolution": "1920x1080", "fps": "60.000"}, "esp32_serial": "usb-Espressif_USB_JTAG_serial_debug_unit_CC:8D:A2:0F:C0:08-if00"} \ No newline at end of file diff --git a/webui/ipkvm/__init__.py b/webui/ipkvm/__init__.py index 363dc95..8553967 100644 --- a/webui/ipkvm/__init__.py +++ b/webui/ipkvm/__init__.py @@ -1,5 +1,98 @@ +from os import name, listdir from flask import Flask +import json +import logging ui = Flask(__name__) +logger = ui.logger +logger.setLevel(logging.INFO) -from ipkvm import routes \ No newline at end of file +def new_profile(): + device_list = video.create_device_list() + print(f"Detected {len(device_list)} video devices on your system.") + print("Please enter the number of your preferred video device.") + for i, device in enumerate(device_list): + print(f"{i + 1}. {device.friendly_name}") + + device = int(input("> ")) - 1 + + if len(device_list[device].video_formats) > 1: + print("Please enter your preferred video input format: ") + for i, format in enumerate(device_list[device].video_formats): + print(f"{i + 1}. {format}") + + format = list(device_list[device].video_formats.keys())[int(input("> ")) - 1] + + else: + format = next(iter(device_list[device].video_formats)) + print(f"Video input format auto-detected as {format}!") + + print("Please enter the number of your preferred video resolution.") + + for i, resolution in enumerate(device_list[device].video_formats[format]): + print(f"{i + 1}. {resolution}") + + resolution = list(device_list[device].video_formats[format].keys())[int(input("> ")) - 1] + + print("Please enter the number of your preferred video refresh rate.") + + for i, fps in enumerate(device_list[device].video_formats[format][resolution]): + print(f"{i + 1}. {fps}") + + fps = str(device_list[device].video_formats[format][resolution][int(input("> ")) - 1]) + + if name == "posix": + serial_devices = listdir("/dev/serial/by-id/") + + else: + serial_devices = [] + + if len(serial_devices) > 1: + print("Please enter the number of your preferred ESP32 serial device.") + for i, serial_device in enumerate(serial_devices): + print(f"{i + 1}. {serial_device}") + + serial_device = serial_devices[int(input("> ")) - 1] + + elif len(serial_devices) == 1: + print(f"ESP32 auto-detected as {serial_devices[0]}!") + serial_device = serial_devices[0] + + else: + raise RuntimeError("No valid ESP32 devices connected!") + + print("Please enter your new profile name.") + profile_name = input("> ") + + profile: dict[str, str | dict[str, str]] = { + "video_device": { + "friendly_name": device_list[device].friendly_name, + "format": format, + "resolution": resolution, + "fps": fps + }, + + "esp32_serial": serial_device + } + + with open(f"profiles/{profile_name}.json", 'w') as file: + json.dump(profile, file) + + return profile + +if len(listdir("profiles")) == 0: + print("No profiles found, entering runtime profile configuration...") + profile = new_profile() + +elif len(listdir("profiles")) == 1: + print(f"Only one profile found, autoloading {listdir("profiles")[0]}...") + with open(f"profiles/{listdir("profiles")[0]}", 'r') as file: + profile = json.load(file) + print(profile) + +from ipkvm.util import video +from ipkvm import feed + +frame_buffer = feed.FrameBuffer() + +from ipkvm import routes diff --git a/webui/ipkvm/feed.py b/webui/ipkvm/feed.py new file mode 100644 index 0000000..1b00daa --- /dev/null +++ b/webui/ipkvm/feed.py @@ -0,0 +1,80 @@ +from os import name +import threading +import av, av.container +import cv2 +from ipkvm import profile +from ipkvm.util import video +from ipkvm import logger +import time + +class FrameBuffer(threading.Thread): + def __init__(self): + super().__init__() + self.buffer_lock = threading.Lock() + self.cur_frame = None + self.new_frame = threading.Event() + self.start() + + def run(self): + self.capture_feed() + + + def capture_feed(self): + device = self.acquire_device() + print(device) + time.sleep(5) + while True: + # try: + # for frame in device.decode(video=0): + # frame = frame.to_ndarray(format='rgb24') + # ret, self.cur_frame = cv2.imencode('.jpg', frame) + # print(ret) + # cv2.imwrite("test.jpg", frame) + # except av.BlockingIOError: + # pass + + success, frame = device.read() + if not success: + break + else: + ret, buffer = cv2.imencode('.jpg', frame) + self.cur_frame = buffer.tobytes() + self.new_frame.set() + + + # def acquire_device(self): + # device_list = video.create_device_list() + # device_path = "" + # for device in device_list: + # if device.friendly_name == profile["video_device"]["friendly_name"]: + # device_path = device.path +# + # if name == "posix": + # return av.open(device_path, format="video4linux2", container_options={ + # "framerate": profile["video_device"]["fps"], + # "video_size": profile["video_device"]["resolution"], + # "input_format": profile["video_device"]["format"] + # }) +# + # else: + # raise RuntimeError("We're on something other than Linux, and that's not yet supported!") + + def acquire_device(self): + device_list = video.create_device_list() + device_path = "" + for device in device_list: + if device.friendly_name == profile["video_device"]["friendly_name"]: + device_path = device.path + + if name == "posix": + device = cv2.VideoCapture(device_path) # Use default webcam (index 0) + + else: + raise RuntimeError("We're on something other than Linux, and that's not yet supported!") + + device.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG")) + device.set(cv2.CAP_PROP_FRAME_WIDTH, int(profile["video_device"]["resolution"].split('x')[0])) + device.set(cv2.CAP_PROP_FRAME_HEIGHT, int(profile["video_device"]["resolution"].split('x')[1])) + device.set(cv2.CAP_PROP_FPS, float(profile["video_device"]["fps"])) + + return device \ No newline at end of file diff --git a/webui/ipkvm/routes.py b/webui/ipkvm/routes.py index ad58390..e39ab79 100644 --- a/webui/ipkvm/routes.py +++ b/webui/ipkvm/routes.py @@ -1,36 +1,14 @@ from ipkvm import ui +from ipkvm import frame_buffer from flask import Response - -import cv2 - -camera = cv2.VideoCapture(0) # Use default webcam (index 0) - -# Get some basic properties -width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH)) -height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT)) -fps = camera.get(cv2.CAP_PROP_FPS) -format = camera.get(cv2.CAP_PROP_FORMAT) - -camera.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) -camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) -camera.set(cv2.CAP_PROP_FPS, 60) - -print(f"Current resolution: {width}x{height}") -print(f"Current FPS: {fps}") -print(f"Current format: {format}") +import time def generate_frames(): while True: - success, frame = camera.read() - if not success: - break - else: - # Encode frame as JPEG - ret, buffer = cv2.imencode('.jpg', frame) - frame = buffer.tobytes() - yield (b'--frame\r\n' - b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n') - + frame_buffer.new_frame.wait() + frame_buffer.new_frame.clear() + yield (b'--frame\r\n' + b'Content-Type: image/jpeg\r\n\r\n' + frame_buffer.cur_frame + b'\r\n') @ui.route('/video_feed') def video_feed(): diff --git a/webui/ipkvm/templates/index.html b/webui/ipkvm/templates/index.html new file mode 100644 index 0000000..03ed53c --- /dev/null +++ b/webui/ipkvm/templates/index.html @@ -0,0 +1,22 @@ + + +
+