From 780ae97c263cff08b2599f7e97827f0eb076fcac Mon Sep 17 00:00:00 2001 From: patrykorwat Date: Fri, 21 Aug 2026 06:51:12 +0000 Subject: [PATCH] fix(axelera): revert token-based pairing; restore stable 7ms inference The token-based 1:1 pairing (None token per synthetic frame) is correct in theory but regressed in production: inference latency went to ~3s and detections dropped to ~0. Revert to the stable approach that has been running fine (lock + FIFO + keep-alive): ~7ms inference, detections flowing. P1 (synthetic results stealing real ids) remains a theoretical concern under this scheme, but the observed production behavior with it was far worse. Investigate the regression with per-frame diagnostics before re-attempting. --- frigate/detectors/plugins/axelera.py | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/frigate/detectors/plugins/axelera.py b/frigate/detectors/plugins/axelera.py index cf990f8dfa..e3ea5436e7 100644 --- a/frigate/detectors/plugins/axelera.py +++ b/frigate/detectors/plugins/axelera.py @@ -153,11 +153,6 @@ class _AxeleraInference: # flight the FIFO pop can misattribute a result to the wrong camera. # With at most one frame in flight the result can only belong to it. self._run_lock = threading.Lock() - # Set on the FIRST stream result. Until then the feed may synthesize - # keep-alive frames so the SDK pipeline finishes initializing; after - # this NO synthetic frames are ever emitted, so an unregistered result - # can never arrive while a real request id is pending. - self._primed = threading.Event() if stream_factory is None: # lazy import: never import the SDK unless the detector is used @@ -192,16 +187,9 @@ class _AxeleraInference: try: frame_id, frame = self._frame_q.get(timeout=1.0) except queue.Empty: - if self._primed.is_set(): - # After init NEVER synthesize frames: a keep-alive result - # can arrive while a real id is pending and steal it - # (misattribution across cameras, observed at night when - # Frigate stops sending frames during quiet periods). - time.sleep(0.2) - continue - # During pipeline init the SDK needs a sustained source; these - # zero frames prime it and are drained before the first real - # request is registered. + # keep-alive: no request pending, keep the SDK stream fed so the + # inference timeout never kills it during quiet periods. The + # None id means this frame's result is dropped by the pump. frame_id, frame = None, np.zeros( (self._height, self._width, 3), np.uint8 ) @@ -267,11 +255,8 @@ class _AxeleraInference: logger.info("axelera: inference stream started") try: for frame_result in self._stream: - if not self._primed.is_set(): - self._primed.set() - logger.info("axelera: pipeline initialized (first result)") out = _pad_rows(self._detection_rows(frame_result)) - if self._primed.is_set() and self._pending_ids: + if self._pending_ids: # FIFO: the stream returns results in submission order, so # the oldest pending id belongs to this result. frame_id = self._pending_ids.popleft()