From d9dfe299a44f1a77ad027a8fdc9a2340c2de1dd1 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 22 May 2026 08:46:17 -0500 Subject: [PATCH] drop stale shm cache refs when cached segment size doesn't match requested shape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cached SharedMemoryFrameManager reference can point at a segment whose size no longer matches the requested shape — the segment was unlinked and recreated at a different size in a camera add/remove cycle. This catches both a resolution increase (cached too small) and a decrease (cached too large, pointing at an orphaned inode whose stale bytes would otherwise be misinterpreted at the new shape, producing distorted/miscolored YUV frames). After reopening, if the OS-level segment still doesn't match the requested shape we're in a transient mid-recreate state — either the maintainer hasn't allocated the new segment yet (size too small) or we opened a pre-recycle segment (size too big). Either way, skip the frame and don't cache the mismatched ref. --- frigate/util/image.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/frigate/util/image.py b/frigate/util/image.py index 4054c54182..d2832d97a0 100644 --- a/frigate/util/image.py +++ b/frigate/util/image.py @@ -1091,11 +1091,8 @@ class SharedMemoryFrameManager(FrameManager): try: required = int(np.prod(shape)) shm = self.shm_store.get(name) - if shm is not None and shm.size < required: - # Cached reference points at an older, smaller segment - # that was unlinked and recreated at a larger size in a - # camera add/remove cycle. Drop the stale ref so we - # reopen the current segment. + if shm is not None and shm.size != required: + # stale cached ref from a same-name recreate — drop and reopen try: shm.close() except Exception: @@ -1104,12 +1101,8 @@ class SharedMemoryFrameManager(FrameManager): shm = None if shm is None: shm = UntrackedSharedMemory(name=name) - if shm.size < required: - # Transient mid-recreate state: the OS-level segment - # is still at the previous (smaller) size because the - # maintainer hasn't allocated the new one yet. Don't - # cache it (so the next call re-opens once the - # maintainer has caught up) and skip this frame. + if shm.size != required: + # mid-recreate: OS segment doesn't match shape yet; skip try: shm.close() except Exception: