Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.DS_Store

.venv
__pycache__

client_app/target
43 changes: 43 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"cmake.sourceDirectory": "/Users/lazycodebaker/Documents/Code/Personal/C++/HyperSight/core",
"files.associations": {
"string": "cpp",
"__bit_reference": "cpp",
"__locale": "cpp",
"__split_buffer": "cpp",
"__verbose_abort": "cpp",
"cmath": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"execution": "cpp",
"memory": "cpp",
"fstream": "cpp",
"initializer_list": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"locale": "cpp",
"mutex": "cpp",
"new": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"typeinfo": "cpp",
"variant": "cpp",
"vector": "cpp",
"cstddef": "cpp",
"algorithm": "cpp",
"__node_handle": "cpp",
"array": "cpp",
"deque": "cpp",
"queue": "cpp",
"stack": "cpp",
"string_view": "cpp",
"unordered_map": "cpp"
}
}
5 changes: 5 additions & 0 deletions ai_engine/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
build
tools/*
!tools/README.md

pkgs/opencv
1 change: 1 addition & 0 deletions ai_engine/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
53 changes: 53 additions & 0 deletions ai_engine/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
```sh
$ uv sync --all-packages
```

# M2_Processing_Node

### start the RTSP server, this allows OpenCV to push frames to it, and M2 Nodes to pull frames from it

I chose to use MediaMTX because it has the ability to receive RTSP. Developing our own server can be done later once I gathered enough information and skills required. Afaik, handling all RTSP sessions takes some considerations.

```sh
$ ./tools/mediamtx apps/m2_processing_node/mediamtx.yml
2025/04/12 17:53:06 INF MediaMTX v1.11.3
2025/04/12 17:53:06 INF configuration loaded from <>/HyperSight/ai_engine/apps/m2_processing_node/mediamtx.yml
2025/04/12 17:53:06 INF [RTSP] listener opened on :8554 (TCP)
```

### the nodes process frames from rtsp://localhost:**8554**/cam1 and will push them to rtsp://localhost:**9554**/cam1
There is a race in this script, where the ingestion while-loop was running faster than the output, causing it to write duplicated frames. This is being worked on.
```sh
$ uv run start-node
```

# M3_WebRTC

### MacOS

```sh
$ brew install gstreamer
```

### start the RTSP server, this allows nodes to push frames to it, and M3 to pull frames from it

```sh
$ ./tools/mediamtx apps/m3_processing_node/mediamtx.yml
2025/04/12 17:53:06 INF MediaMTX v1.11.3
2025/04/12 17:53:06 INF configuration loaded from <>/HyperSight/ai_engine/apps/m3_webrtc/mediamtx.yml
2025/04/12 17:53:06 INF [RTSP] listener opened on :9554 (TCP)
```

### M3_WebRTC is hard-coded to pull from rtsp://localhost:9554/cam1 using Gstreamer

It is now a mini-HTTP server, which supports middlewares for Auth Token validation. The frames pulled from RTSP Server will then be forwarded via RTC tracks.

I am working on a Configurator for service discovery, all uris are hard-coded for now.

```sh
$ uv run start-webrtc
```

### test-client.html has a quick&dirty sample code to demonstrate.

Firefox has a bug: https://github.com/aiortc/aiortc/issues/481, you should open the file on another browser.
5 changes: 5 additions & 0 deletions ai_engine/apps/configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import yaml

def load_config(file_path: str):
with open(file_path, "r") as file:
return yaml.safe_load(file)
1 change: 1 addition & 0 deletions ai_engine/apps/m2_processing_node/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
2 changes: 2 additions & 0 deletions ai_engine/apps/m2_processing_node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## This section is to be deleted
Things arent working yet
2 changes: 2 additions & 0 deletions ai_engine/apps/m2_processing_node/config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
server:
port: 5006
2 changes: 2 additions & 0 deletions ai_engine/apps/m2_processing_node/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
server:
port: 5006
70 changes: 70 additions & 0 deletions ai_engine/apps/m2_processing_node/frame_processor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import logging
import subprocess
import time
from pathlib import Path
from threading import Thread

import cv2
from pkgs.shared.configuration import load_config

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("HyperSight_WebRTC")

# TECHDEBT:
width, height, fps = 480, 640, 25

def capture(uri_source: str, uri_sink: str):
global width, height, fps

_in = cv2.VideoCapture(uri_source, cv2.CAP_FFMPEG)

ffmpeg_cmd = [
"ffmpeg",
"-s",
f"{width}x{height}",
"-r",
f"{fps}",
"-i",
"-",
"-an",
"-pix_fmt",
"rgb24",
"-c:v",
"libx264",
"rtsp://localhost:9554/cam1",
]
process = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE)

while True:
ret, frame = _in.read()
if not ret:
break

# TODO: process here
processed_frame = frame.copy()

process.stdin.write(processed_frame.tobytes())
process.stdin.flush()

# BUG: it still happens, maybe the while loop isn't sufficient
# try to mitigate duplicated frame, because while loop is faster
time.sleep(1.0 / fps)

_in.release()
process.stdin.close()
process.wait()


def main():
config = load_config(str(Path(__file__).parent / "config.yml"))

uris = {("rtsp://localhost:8554/cam1", "rtsp://localhost:9554/cam1")}

threads = set[Thread]()
for uri in uris:
t = Thread(target=capture, args=(uri[0], uri[1]))
threads.add(t)
t.start()

for t in threads:
t.join()
Loading