add model mamager

This commit is contained in:
Comma Device
2026-03-28 16:31:33 +08:00
parent d9b6874c5e
commit fb784b084c
99 changed files with 6033 additions and 1610 deletions

Binary file not shown.

View File

@@ -48,6 +48,10 @@ namespace Path {
return "/tmp/comma_download_cache" + Path::openpilot_prefix() + "/";
}
inline std::string model_root() {
return Hardware::PC() ? Path::comma_home() + "/media/0/models" : "/data/media/0/models";
}
inline std::string shm_path() {
#ifdef __APPLE__
return"/tmp";

View File

@@ -63,3 +63,9 @@ class Paths:
if PC and platform.system() == "Darwin":
return "/tmp" # This is not really shared memory on macOS, but it's the closest we can get
return "/dev/shm"
@staticmethod
def model_root() -> str:
if PC:
return str(Path(Paths.comma_home()) / "media" / "0" / "models")
return "/data/media/0/models"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -143,6 +143,9 @@ procs = [
PythonProcess("fleet_manager", "selfdrive.frogpilot.fleetmanager.fleet_manager", check_fleet),
PythonProcess("carrot_man", "selfdrive.carrot.carrot_man", always_run),#, enabled=not PC),
PythonProcess("carrot_server", "selfdrive.carrot.carrot_server", always_run),
# Model Manager (always run so process is visible; full download/list when offroad)
PythonProcess("models_manager", "selfdrive.models_manager.manager", always_run),
#Xiaoge data broadcaster (conditional on ShareData param)
PythonProcess("xiaoge_data", "selfdrive.carrot.xiaoge_data", enable_xiaoge_data),
# c3x lite

Binary file not shown.

View File

@@ -1,6 +1,11 @@
import asyncio
import av
import subprocess
from fractions import Fraction
from typing import Optional
from aiortc.mediastreams import VideoStreamTrack
from teleoprtc.tracks import TiciVideoStreamTrack
from cereal import messaging
@@ -41,3 +46,147 @@ class LiveStreamVideoStreamTrack(TiciVideoStreamTrack):
def codec_preference(self) -> str | None:
return "H264"
def _read_fb_virtual_size(path: str = "/sys/class/graphics/fb0/virtual_size") -> Optional[tuple[int, int]]:
try:
with open(path, "r", encoding="utf-8") as f:
s = f.read().strip()
w_s, h_s = s.split(",", 1)
w, h = int(w_s), int(h_s)
if w > 0 and h > 0:
return (w, h)
except Exception:
pass
return None
class ScreenCaptureVideoStreamTrack(VideoStreamTrack):
"""
Best-effort full device screen capture for WebRTC.
Implementation notes:
- Grabs framebuffer via ffmpeg fbdev input (typically /dev/fb0 on openpilot devices).
- Outputs raw BGR frames and lets aiortc encode (CPU heavy, keep fps/resolution low).
"""
kind = "video"
def __init__(self, fb_path: str = "/dev/fb0", fps: int = 10, scale: float = 0.5):
super().__init__()
self._fb_path = fb_path
self._fps = max(1, int(fps))
self._scale = float(scale) if scale is not None else 1.0
src_size = _read_fb_virtual_size() or (1920, 1080)
src_w, src_h = src_size
out_w = max(16, int((src_w * self._scale) // 2 * 2))
out_h = max(16, int((src_h * self._scale) // 2 * 2))
self._w, self._h = out_w, out_h
self._frame_bytes = self._w * self._h * 3 # bgr24
self._proc: subprocess.Popen[bytes] | None = None
self._use_black_frames = False
self._pts = 0
self._time_base = Fraction(1, 90000)
self._pts_step = int(90000 / self._fps)
def _ensure_proc(self) -> subprocess.Popen[bytes]:
if self._use_black_frames:
raise RuntimeError("screen capture disabled; using black frames")
if self._proc is not None and self._proc.poll() is None:
return self._proc
# Restart process
if self._proc is not None:
try:
self._proc.kill()
except Exception:
pass
src_size = _read_fb_virtual_size() or (1920, 1080)
src_w, src_h = src_size
vf = f"scale={self._w}:{self._h},format=bgr24"
cmd = [
"ffmpeg",
"-hide_banner",
"-loglevel", "error",
"-f", "fbdev",
"-framerate", str(self._fps),
"-video_size", f"{src_w}x{src_h}",
"-i", self._fb_path,
"-vf", vf,
"-f", "rawvideo",
"-pix_fmt", "bgr24",
"pipe:1",
]
# stdout is raw frames; stderr suppressed by -loglevel error
try:
self._proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
except FileNotFoundError:
# ffmpeg missing
self._use_black_frames = True
raise
assert self._proc.stdout is not None
return self._proc
async def recv(self) -> av.VideoFrame:
# If ffmpeg/fbdev isn't available, still produce frames so the browser gets a track.
if self._use_black_frames:
await asyncio.sleep(1.0 / self._fps)
frame = av.VideoFrame(self._w, self._h, "bgr24")
for p in frame.planes:
p.update(bytes(p.buffer_size))
frame.pts = self._pts
frame.time_base = self._time_base
self._pts += self._pts_step
return frame
try:
proc = self._ensure_proc()
assert proc.stdout is not None
# Read one full frame worth of bytes
buf = bytearray()
while len(buf) < self._frame_bytes:
chunk = await asyncio.to_thread(proc.stdout.read, self._frame_bytes - len(buf))
if not chunk:
# ffmpeg ended or stalled; restart and retry
try:
proc.kill()
except Exception:
pass
self._proc = None
await asyncio.sleep(0.05)
proc = self._ensure_proc()
assert proc.stdout is not None
buf.clear()
continue
buf.extend(chunk)
frame = av.VideoFrame(self._w, self._h, "bgr24")
frame.planes[0].update(bytes(buf))
except Exception:
# Any failure (fb0 missing, bad fbdev support, etc.) falls back to black frames
self._use_black_frames = True
await asyncio.sleep(1.0 / self._fps)
frame = av.VideoFrame(self._w, self._h, "bgr24")
for p in frame.planes:
p.update(bytes(p.buffer_size))
frame.pts = self._pts
frame.time_base = self._time_base
self._pts += self._pts_step
return frame
async def stop(self) -> None:
try:
if self._proc is not None and self._proc.poll() is None:
self._proc.kill()
except Exception:
pass
self._proc = None
await super().stop()