Cattura: rimuovi videorate e limita la coda all'ultimo frame
La pipeline GStreamer non forza più la frequenza con videorate e usa un appsink con max-buffers=1, drop e enable-last-sample=false: si elabora solo il frame più recente invece di duplicare frame o accumulare ritardo. Il demone rinnova inoltre l'ultimo frame inviato ogni 10 s quando la sorgente a frequenza variabile non produce nuovi campioni (schermata statica), evitando il timeout del firmware Adalight, e usa time.monotonic per il pacing di invio al posto di time.time.
This commit is contained in:
@@ -79,10 +79,9 @@ class Capture:
|
|||||||
self.gw = leds * sub
|
self.gw = leds * sub
|
||||||
self.gh = rows
|
self.gh = rows
|
||||||
desc = (f"pipewiresrc path={node} do-timestamp=true ! "
|
desc = (f"pipewiresrc path={node} do-timestamp=true ! "
|
||||||
f"videoconvert ! videoscale ! videorate ! "
|
f"videoconvert ! videoscale ! "
|
||||||
f"video/x-raw,format=RGB,width={self.gw},height={self.gh},"
|
f"video/x-raw,format=RGB,width={self.gw},height={self.gh} ! "
|
||||||
f"framerate={int(fps)}/1 ! "
|
f"appsink name=sink emit-signals=false max-buffers=1 drop=true sync=false enable-last-sample=false")
|
||||||
f"appsink name=sink emit-signals=false max-buffers=2 drop=true sync=false")
|
|
||||||
self.pipe = Gst.parse_launch(desc)
|
self.pipe = Gst.parse_launch(desc)
|
||||||
self.sink = self.pipe.get_by_name("sink")
|
self.sink = self.pipe.get_by_name("sink")
|
||||||
|
|
||||||
|
|||||||
+12
-4
@@ -208,6 +208,7 @@ def capture_loop(state, strip):
|
|||||||
last_write = 0.0
|
last_write = 0.0
|
||||||
t_log = time.time()
|
t_log = time.time()
|
||||||
n_none = 0
|
n_none = 0
|
||||||
|
last_frame = None
|
||||||
while True:
|
while True:
|
||||||
cfg = state.snapshot()
|
cfg = state.snapshot()
|
||||||
if not cfg["running"]:
|
if not cfg["running"]:
|
||||||
@@ -215,6 +216,7 @@ def capture_loop(state, strip):
|
|||||||
cap.stop()
|
cap.stop()
|
||||||
cap = None
|
cap = None
|
||||||
prev = None
|
prev = None
|
||||||
|
last_frame = None
|
||||||
time.sleep(0.15)
|
time.sleep(0.15)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -239,10 +241,15 @@ def capture_loop(state, strip):
|
|||||||
|
|
||||||
frame = cap.frame(200)
|
frame = cap.frame(200)
|
||||||
if frame is None:
|
if frame is None:
|
||||||
|
# Una sorgente a frequenza variabile può fermarsi su una schermata
|
||||||
|
# statica; rinnova il frame per il timeout del firmware Adalight.
|
||||||
|
if last_frame is not None and time.monotonic() - last_write >= 10:
|
||||||
|
if strip.write(last_frame):
|
||||||
|
last_write = time.monotonic()
|
||||||
n_none += 1
|
n_none += 1
|
||||||
time.sleep(0.005)
|
time.sleep(0.005)
|
||||||
if time.time() - t_log >= 10:
|
if time.time() - t_log >= 10:
|
||||||
log(f"diagnostica: 10 s senza frame (timeout={n_none}), "
|
log(f"diagnostica: attese cattura negli ultimi 10 s (timeout={n_none}), "
|
||||||
f"fps={state.fps} sent={sent}")
|
f"fps={state.fps} sent={sent}")
|
||||||
t_log, n_none = time.time(), 0
|
t_log, n_none = time.time(), 0
|
||||||
continue
|
continue
|
||||||
@@ -270,12 +277,13 @@ def capture_loop(state, strip):
|
|||||||
frame_bits = (6 + 3 * cfg["leds"]) * 10
|
frame_bits = (6 + 3 * cfg["leds"]) * 10
|
||||||
min_interval = max(1.0 / max(1, cfg["target_fps"]),
|
min_interval = max(1.0 / max(1, cfg["target_fps"]),
|
||||||
1.7 * frame_bits / max(9600, cfg["baud"]))
|
1.7 * frame_bits / max(9600, cfg["baud"]))
|
||||||
wait = min_interval - (time.time() - last_write)
|
wait = min_interval - (time.monotonic() - last_write)
|
||||||
if wait > 0:
|
if wait > 0:
|
||||||
time.sleep(wait)
|
time.sleep(wait)
|
||||||
ok = strip.write(adalight_frame(px))
|
last_frame = adalight_frame(px)
|
||||||
|
ok = strip.write(last_frame)
|
||||||
if ok:
|
if ok:
|
||||||
last_write = time.time()
|
last_write = time.monotonic()
|
||||||
sent += 1
|
sent += 1
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now - t_win >= 1.0:
|
if now - t_win >= 1.0:
|
||||||
|
|||||||
Reference in New Issue
Block a user