mirror of
https://jihulab.com/mr-one/openpilot.git
synced 2026-10-27 21:13:42 +08:00
ui: match C3XL display rate and gate stream encoding
This commit is contained in:
+6
-2
@@ -15,6 +15,9 @@ export VECLIB_MAXIMUM_THREADS=1
|
||||
# headroom for this until ui is moved to the CPU.
|
||||
export QCOM_PRIORITY=12
|
||||
|
||||
# C3XL panel runs at ~31.6Hz; targeting 60 causes constant false FPS warnings.
|
||||
export FPS=30
|
||||
|
||||
if [ -z "$AGNOS_VERSION" ]; then
|
||||
export AGNOS_VERSION="19.7-c3xl-dev"
|
||||
fi
|
||||
@@ -24,8 +27,9 @@ export STAGING_ROOT="/data/safe_staging"
|
||||
# Comma4-UI-Streamer (MJPEG UI stream) - set STREAM=0 to disable
|
||||
export STREAM=1
|
||||
export STREAM_PORT=8082
|
||||
export STREAM_QUALITY=50
|
||||
export STREAM_FPS=10
|
||||
export STREAM_QUALITY=40
|
||||
export STREAM_FPS=5
|
||||
export STREAM_MAX_WIDTH=540
|
||||
|
||||
# --- system time (this hardware has no battery-backed RTC) ---
|
||||
# On a cold boot there is no sane clock: AGNOS restores a stale timestamp (or the epoch),
|
||||
|
||||
@@ -24,16 +24,35 @@ class StreamState:
|
||||
self.frame = b""
|
||||
self.lock = threading.Lock()
|
||||
self.event = threading.Event()
|
||||
self.clients = 0
|
||||
self.snapshot_pending = False
|
||||
|
||||
def update(self, jpeg):
|
||||
with self.lock:
|
||||
self.frame = jpeg
|
||||
self.snapshot_pending = False
|
||||
self.event.set()
|
||||
|
||||
def get(self):
|
||||
with self.lock:
|
||||
return self.frame
|
||||
|
||||
def client_enter(self):
|
||||
with self.lock:
|
||||
self.clients += 1
|
||||
|
||||
def client_exit(self):
|
||||
with self.lock:
|
||||
self.clients = max(0, self.clients - 1)
|
||||
|
||||
def request_snapshot(self):
|
||||
with self.lock:
|
||||
self.snapshot_pending = True
|
||||
|
||||
def has_demand(self):
|
||||
with self.lock:
|
||||
return self.clients > 0 or self.snapshot_pending
|
||||
|
||||
def wait(self, t=2.0):
|
||||
self.event.wait(t)
|
||||
self.event.clear()
|
||||
@@ -237,6 +256,7 @@ class StreamHandler(BaseHTTPRequestHandler):
|
||||
self.send_header("Cache-Control", "no-cache")
|
||||
self.send_header("Access-Control-Allow-Origin", "*")
|
||||
self.end_headers()
|
||||
self.server._state.client_enter()
|
||||
try:
|
||||
while True:
|
||||
self.server._state.wait(2.0)
|
||||
@@ -247,7 +267,11 @@ class StreamHandler(BaseHTTPRequestHandler):
|
||||
self.wfile.write(b"\r\n")
|
||||
except (BrokenPipeError, ConnectionResetError):
|
||||
pass
|
||||
finally:
|
||||
self.server._state.client_exit()
|
||||
elif self.path == "/snapshot":
|
||||
self.server._state.request_snapshot()
|
||||
self.server._state.wait(1.0)
|
||||
f = self.server._state.get()
|
||||
if f:
|
||||
self.send_response(200)
|
||||
@@ -423,6 +447,9 @@ def _encode_worker():
|
||||
raw, w, h, quality = item
|
||||
try:
|
||||
img = Image.frombytes("RGBA", (w, h), raw).transpose(Image.FLIP_TOP_BOTTOM).convert("RGB")
|
||||
max_width = int(os.getenv("STREAM_MAX_WIDTH", "540"))
|
||||
if img.width > max_width:
|
||||
img.thumbnail((max_width, max_width * img.height // img.width), Image.Resampling.BILINEAR)
|
||||
buf = io.BytesIO()
|
||||
img.save(buf, "JPEG", quality=quality)
|
||||
if _state is not None:
|
||||
@@ -503,7 +530,7 @@ def capture_frame(app, quality=50, target_fps=10):
|
||||
_app = app
|
||||
"""Call from the render loop: snapshot texture, queue raw for async JPEG encode."""
|
||||
global _counter
|
||||
if _state is None or app._render_texture is None:
|
||||
if _state is None or app._render_texture is None or not _state.has_demand():
|
||||
return
|
||||
_counter += 1
|
||||
skip = max(1, int(getattr(app, "_target_fps", 60)) // max(1, int(target_fps)))
|
||||
|
||||
Reference in New Issue
Block a user