fix(adapters): recreate decode iterator after EAGAIN so stream resumes - #9
Merged
Merged
Conversation
The previous fix caught BlockingIOError / OSError(EAGAIN) and retried, but next() was called on the SAME decode iterator. PyAV's decode() is a Python generator, and generators become exhausted after any exception propagates out of them — so every subsequent next() raised StopIteration. The capture thread then hit 'except StopIteration: break' and exited silently (no health event), leaving latest_frame frozen on whatever was set last (usually the very first frame). This matched the reported symptom: 'health events show connected but preview is frozen on the first frame.' Fix: track frame_iter explicitly, set it to None on any transient- errno path, and recreate via iter(self._input.decode(video=0)) at the top of the next loop iteration. PyAV's decode() is a fresh generator per call that shares the underlying container state, so recreation resumes packet reading without losing the session. StopIteration now cleanly exits (live cameras only emit it on true end-of-stream / device disconnect). Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
After PR #8 merged, the viewer's camera preview cards populate with the first frame only and then freeze — despite health events showing
connectedand no error events. The capture thread exits silently after the first EAGAIN retry cycle.Root cause
PR #8 added EAGAIN handling but called
next()on the same decode iterator after catching the exception. PyAV'scontainer.decode()is a Python generator, and generators become permanently exhausted after any exception propagates out of them.The flow was:
next(frame_iter)→ first frame ✓ (camera warmed up)next(frame_iter)→BlockingIOError(35)(between frames) — iterator diestime.sleep(0.001); continuenext(frame_iter)→StopIteration(iterator is dead)except StopIteration: break→ capture thread exits silently,latest_framefrozenFix
Track
frame_iterexplicitly. Set it toNoneon every transient-errno retry, and recreate at the top of the next loop iteration:PyAV's
container.decode()returns a fresh generator per call that shares the underlying container's packet queue, so recreation resumes reading without losing session state.StopIterationis now only hit on true end-of-stream (device disconnect) and exits cleanly.Test plan
TestDecoderResiliencetests from PR fix(adapters): tolerate EAGAIN/EINTR in UVC capture loop #8)examples/iphone_mac_webcam/record.py— preview updates live at 30 fps, no freeze🤖 Generated with Claude Code