Skip to content

Add close() to VideoStream API; VideoStreamConcat leaks child streams #568

Description

@Breakthrough

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

  1. 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:.
  2. 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.
  3. Replace tests/helpers.py::close_video_stream internals with the public close(), and extend auto_close usage to tests/test_concat.py.
  4. 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:

  • python -X dev -m pytest -vv produces no unclosed-file ResourceWarnings from scenedetect code paths.
  • with open_video(path) as video: works for all backends including concat.
  • Windows segfault rate observed over subsequent runs drops to zero

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.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions