Summary
Resource cleanup for VideoStream is currently GC/__del__-driven, and it has no close() API. As a result, VideoStreamConcat cannot deterministically release it's child streams and leaks them. This is a possible trigger for the recurring Windows runner segfault, which seems to happen during interpreter shutdown (exit code 139 after all tests passed).
Occurrences
- 0 occurrences in 37 runs before 2026-07-05.
- 9 occurrences total after
tests/test_concat.py landed (4fad8b8), all windows-latest only
- Most recent: 32204313537 (passed then exit 139).
It should be noted that mitigation in (82f96f7, auto_close/close_video_stream) cut the rate roughly 2/3 but did not eliminate it. That helper dispatches on BACKEND_NAME and cannot reach "concat" streams. Additionally, test_concat.py was never migrated, so concat is likely the leak it could not fix.
Issues
VideoStreamConcat (scenedetect/backends/concat.py) has no close() and drops child streams without closing them:
__init__ probes every source and keeps only the first; the rest are dropped unclosed.
read() / seek() / reset() replace self._cap without closing the previous child.
VideoStreamAv.__del__ closes _decoder and _container but never _io, the BufferedReader the stream itself opened (pyav.py:130)
- this is the source of the remaining
ResourceWarnings under -X dev.
- No backend has a deterministic release path callers can use; tests had to reach into private attributes (
tests/helpers.py::close_video_stream).
Proposal
- Add
close() to the VideoStream base class (default no-op or abstract), plus context manager support (__enter__/__exit__) so callers can use with open_video(...) as video:.
- Implement per backend:
- pyav: close
_decoder, _container, and _io (the self-opened file handle); safe to call twice; __del__ delegates to close().
- opencv:
_cap.release().
- moviepy:
_reader.close().
- concat: close the current child, and close the outgoing child in
read()/seek()/reset() when switching sources.
- Replace
tests/helpers.py::close_video_stream internals with the public close(), and extend auto_close usage to tests/test_concat.py.
- Document that
close() is idempotent and that a closed stream cannot be read/seeked (behavior on use-after-close: raise or reopen - to be decided during implementation; raising ValueError like file objects is the least surprising).
This is an additive API, so no deprecations needed.
Goals
The high level items this particular issue is driving forward:
If the segfault ever recurs after this lands, we can try to enable WER dumps for the Python interpreter in the CI job and upload the resulting trace.
Summary
Resource cleanup for
VideoStreamis currently GC/__del__-driven, and it has noclose()API. As a result,VideoStreamConcatcannot deterministically release it's child streams and leaks them. This is a possible trigger for the recurring Windows runner segfault, which seems to happen during interpreter shutdown (exit code 139 after all tests passed).Occurrences
tests/test_concat.pylanded (4fad8b8), all windows-latest onlyIt should be noted that mitigation in (82f96f7,
auto_close/close_video_stream) cut the rate roughly 2/3 but did not eliminate it. That helper dispatches onBACKEND_NAMEand cannot reach"concat"streams. Additionally,test_concat.pywas never migrated, so concat is likely the leak it could not fix.Issues
VideoStreamConcat(scenedetect/backends/concat.py) has noclose()and drops child streams without closing them:__init__probes every source and keeps only the first; the rest are dropped unclosed.read()/seek()/reset()replaceself._capwithout closing the previous child.VideoStreamAv.__del__closes_decoderand_containerbut never_io, theBufferedReaderthe stream itself opened (pyav.py:130)ResourceWarnings under-X dev.tests/helpers.py::close_video_stream).Proposal
close()to theVideoStreambase class (default no-op or abstract), plus context manager support (__enter__/__exit__) so callers can usewith open_video(...) as video:._decoder,_container, and_io(the self-opened file handle); safe to call twice;__del__delegates toclose()._cap.release()._reader.close().read()/seek()/reset()when switching sources.tests/helpers.py::close_video_streaminternals with the publicclose(), and extendauto_closeusage totests/test_concat.py.close()is idempotent and that a closed stream cannot be read/seeked (behavior on use-after-close: raise or reopen - to be decided during implementation; raisingValueErrorlike file objects is the least surprising).This is an additive API, so no deprecations needed.
Goals
The high level items this particular issue is driving forward:
python -X dev -m pytest -vvproduces no unclosed-fileResourceWarnings from scenedetect code paths.with open_video(path) as video:works for all backends including concat.If the segfault ever recurs after this lands, we can try to enable WER dumps for the Python interpreter in the CI job and upload the resulting trace.