Skip to content

Validate integrated FFmpeg audio core hardening stack - #147

Draft
Blackspirits wants to merge 1 commit into
audit/ffmpeg-audio-clock-failover-8dabfrom
audit/ffmpeg-audio-core-integration-2c44
Draft

Blackspirits wants to merge 1 commit into
audit/ffmpeg-audio-clock-failover-8dabfrom
audit/ffmpeg-audio-core-integration-2c44

Conversation

@Blackspirits

@Blackspirits Blackspirits commented Sep 16, 2026

Copy link
Copy Markdown
Owner

Purpose

Integration-only validation of the current FFmpeg audio core hardening stack.

The base is #144, so it already includes the serial-fenced sink/reset/start chain:

This integration commit layers the audited player behavior from:

It is audit evidence only and is not a merge candidate.

Current upstream SubtitleEdit/subtitleedit main was rechecked at 6c6b83dd043d8d38fb7a2b1800fdb9465526833b; the latest upstream change is unrelated Burn-in/encoder work.

Why this integration matters

These PRs were developed on different branches but all mutate the same audio transaction:

  • committed/requested seek serial;
  • selected audio stream;
  • sink reset serial/generation;
  • audio anchor/master clock;
  • resampler tail and EOF accounting;
  • speed-specific PCM conversion.

Independent CI cannot prove that the old/new contracts compose.

Integration conflicts found and resolved

Pre-PR adversarial reconstruction found several real composition bugs that were not visible in the isolated PRs:

  1. Old parameterless sink reset was reintroduced.
    Fail closed when FFmpeg seek fails #117 still called the pre-Fence FFmpeg audio writes by seek serial #125 _audioSink.Reset() after queue flush. The integrated tree now performs exactly one serial-aware Reset(serial) before serial commit.

  2. Fix FFmpeg EOF handling for unknown durations #116's helper still used the old Write(pcm) contract.
    WriteAudioChunk() now uses Write(pcm, serial).

  3. Normal PCM bypassed Fix FFmpeg EOF handling for unknown durations #116 EOF accounting after Fall back to wall clock after FFmpeg audio sink failure #144 was layered.
    The direct Fall back to wall clock after FFmpeg audio sink failure #144 write path meant normal decoded PCM no longer updated queuedAudioEnd and also bypassed Fix FFmpeg EOF handling for unknown durations #116's gain path. Both normal PCM and resampler-tail PCM now pass through the same WriteAudioChunk().

  4. Fall back to wall clock after FFmpeg audio sink failure #144's packet-drop failover did not compose with Fix FFmpeg EOF handling for unknown durations #116 unknown-duration EOF.
    Dropping compressed audio packets after device failure avoids backpressure, but also discards the timestamps needed to discover the end of an unknown-duration audio stream. The integrated tree now enters decode-only mode: decoder/resampler timing and EOF accounting continue, while native PCM output is skipped. This also covers failure during final resampler drain without losing the observed media end.

  5. Audio EAGAIN ownership from Retry FFmpeg decoder packets rejected with EAGAIN #119 was absent from the Fall back to wall clock after FFmpeg audio sink failure #144 ancestry.
    The integrated tree retains a rejected packet until receive-side progress makes it legal to resend. Sink failover counts as an interruption, so a device failure does not loop/retry the retained packet indefinitely.

Composition invariants

  1. Seek is transactional.
    Native seek must succeed before audio track, sink serial, clock/EOF state and packet queues commit.

  2. Audio-track switch is transactional.
    The old track keeps routing until seek succeeds; failed seek rolls back only the failed latest request.

  3. Sink ownership is serial-fenced.
    Every normal PCM write and resampler-tail write carries the current seek serial.

  4. One accepted PCM path owns audio accounting.
    Gain, sink write, failover classification and queuedAudioEnd update are performed by one helper.

  5. EAGAIN retains packet ownership exactly once.
    Rejected input is resent only after decoder output and only while the serial remains current.

  6. EOF is not published before decoder + resampler drain.
    After a real sink failure, decode-only mode still drains decoder/resampler timing so unknown-duration media retains an observed end even though PCM is no longer sent to the device.

  7. Unknown duration remains data-driven.
    Playback end uses observed decoder/sink state rather than treating a partial sibling stream duration as authoritative.

  8. Speed changes do not reinterpret elapsed wall-clock time.
    Position is captured before mutating the playback rate.

  9. Runtime sink failure does not freeze or truncate unknown-duration playback.
    The last audible media position becomes the wall-clock base; decoding continues without native output so packet backpressure is avoided while timestamps and EOF remain observable.

Scope / branch state

Acceptance

  • full restore/build/test suite must pass on this exact integrated tree;
  • no new warning;
  • retry step must remain unused;
  • final adversarial review must re-check all nine composition invariants.

AI assistance: ChatGPT was used for cross-branch semantic reconstruction, detection and repair of integration-only sink-reset/write/EOF/EAGAIN conflicts, discovery of the packet-drop vs unknown-duration composition bug, decode-only recovery design, and integration acceptance design.

Final CI

Authoritative run: SubtitleEdit#289 on 017540a328cfb460a90cd3b6bace32d698edb1c7

  • SeConv: 488 passed / 2 skipped / 0 failed
  • LibUiLogic: 905 passed / 0 skipped / 0 failed
  • LibSE: 2017 passed / 0 skipped / 0 failed
  • UI: 5285 passed / 9 skipped / 0 failed
  • build: 0 errors / 7 pre-existing warnings
  • retry step skipped
  • failure-artifact upload skipped

Final adversarial integration review was performed on this exact HEAD after CI; all nine composition invariants remain satisfied.

@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.

Final adversarial integration review on 017540a328cfb460a90cd3b6bace32d698edb1c7 after CI SubtitleEdit#289: no blocker found. Verified transactional seek/audio-track commit, serial-fenced sink reset/write, one-path PCM accounting, EAGAIN exact ownership, decoder+resampler EOF ordering, selected-stream duration semantics, speed-position preservation, wall-clock failover, and decode-only continuation after sink failure so unknown-duration EOF remains observable. No merge/promotion 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.

Final adversarial integration review on 017540a328cfb460a90cd3b6bace32d698edb1c7 after CI SubtitleEdit#289: no blocker found. Verified transactional seek/audio-track commit, serial-fenced reset/write, unified normal+resampler PCM accounting, audio-side EAGAIN ownership, speed re-anchor before rate mutation, and the final decode-only sink-failure path. The integration-only packet-drop vs unknown-duration EOF bug was fixed before this run: after device failure the decoder/resampler continue producing timing and EOF while native PCM output is skipped, so wall-clock playback does not freeze or truncate unknown-duration audio. No merge/promotion 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