Skip to content

Interrupt blocked FFmpeg I/O when a load is closed or replaced - #99

Draft
Blackspirits wants to merge 2 commits into
audit/ffmpeg-load-cancel-base-c77cfrom
fix/ffmpeg-io-interrupt-c77c
Draft

Interrupt blocked FFmpeg I/O when a load is closed or replaced#99
Blackspirits wants to merge 2 commits into
audit/ffmpeg-load-cancel-base-c77cfrom
fix/ffmpeg-io-interrupt-c77c

Conversation

@Blackspirits

@Blackspirits Blackspirits commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Summary

The FFmpeg player performs avformat_open_input, avformat_find_stream_info and av_read_frame on worker threads. FFmpeg I/O is blocking by default, while Session.Dispose currently gives workers 5 seconds to stop and then releases their native resources.

A close/replacement therefore needs a way to interrupt blocking FFmpeg I/O, especially because NativeMediaPath.ForMpv deliberately leaves URLs unchanged and the player can receive network-backed media.

This change, layered on the generation-safe load ownership from #95 and partial-construction cleanup from #98:

  • gives each load a tiny shared cancellation state;
  • cancels the previous state before replacing/closing a load;
  • pre-allocates AVFormatContext and installs AVIOInterruptCB before avformat_open_input, as required by FFmpeg for protocol-level interruption;
  • keeps the callback state alive with a GCHandle;
  • makes the callback abort when the load has been closed/replaced;
  • keeps the handle alive through avformat_close_input, then releases it;
  • lets a cancelled/stale load fail without publishing over a newer session.

Evidence

FFmpeg documents AVIOInterruptCB as the callback used to abort blocking I/O; returning 1 causes the blocked function to return AVERROR_EXIT. AVFormatContext.interrupt_callback is explicitly caller-supplied for demuxing and should be installed before avformat_open_input.

Validation

AI assistance: ChatGPT was used to audit FFmpeg blocking-I/O teardown semantics and design the callback lifetime/cancellation contract.

@Blackspirits Blackspirits left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent adversarial re-check: the FFmpeg player accepts URLs unchanged and can block in avformat_open_input/find_stream_info/av_read_frame. The callback is installed on a preallocated AVFormatContext before open, uses a GCHandle-backed cancellation state that outlives native I/O, and is signalled by close/replacement before disposal. Real open failures remain logged while expected cancellation is quiet. FFmpeg's failure contract for a preallocated AVFormatContext is compatible with the cleanup path. Full CI #34779660270 passed restore, build and the complete suite on the first run; UITests reported 5,151 passed, 9 skipped, 0 failed. Retry was not used. Residual risk remains only if a protocol/driver ignores the interrupt callback; that is being handled separately rather than weakening this PR. Keep draft; no merge performed.

@Blackspirits Blackspirits left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent adversarial re-check: the player accepts URL-backed media unchanged, while FFmpeg open/stream-info/demux calls are blocking by default. The final patch installs AVIOInterruptCB before avformat_open_input, keeps callback state alive with GCHandle through avformat_close_input, cancels old load state before close/replacement, and distinguishes expected cancellation from real open failures. The stale-load generation gate from #95 remains the publication authority. Full CI #34779660270 passed restore, build and the complete suite on the first run; UITests reported 5,151 passed, 9 skipped, 0 failed. Retry was not used. No blocker identified in the callback lifetime/cancellation contract. Keep draft; no merge performed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant