Default `subs_then_webrtc` VAD aligns against forced-only embedded subtitle stubs, producing ±60 s offsets on in-sync subtitles
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 7.9k
- Forks
- 323
- PR merge metrics
- No merged PRs in 30d
Description
Summary
With the default --vad subs_then_webrtc, VideoSpeechTransformer.try_fit_using_embedded_subs uses an embedded text subtitle stream from the reference video as the alignment source whenever one exists. It picks the stream with the largest last-cue time (np.argmax(embedded_subs_times)) and never checks how many cues the stream has, whether it is flagged forced, or what language it is.
Most WEB-DL and many Blu-ray remuxes carry a forced-only English track with 1 to 60 cues (signs, foreign lines). Against such a track the FFT alignment has almost nothing to correlate, so ffsubsync returns a large offset pinned at or near --max-offset-seconds (60 s), often with a negative score, for a subtitle file that is in fact in sync. It then logs ...success!, writes the shifted file, and exits 0.
Reproduction
A feature-length WEB-DL mkv with two English audio streams and one embedded subrip English stream flagged forced, under 50 cues. The external English .srt has well over a thousand cues and plays in sync.
ffsubsync video.mkv -i video.en.srt -o out.srt --max-duration-seconds 1200
score: -72067.320 offset seconds: -59.440 framerate scale factor: 1.042
ffsubsync video.mkv -i video.en.srt -o out.srt --max-duration-seconds 1200 --vad webrtc
score: 33134.000 offset seconds: 0.030 framerate scale factor: 1.000
ffsubsync video.mkv -i video.en.srt -o out.srt --max-duration-seconds 1200 --reference-stream a:1
score: 18426.000 offset seconds: 0.000 framerate scale factor: 1.000
Across a library of several thousand external subtitles, roughly three in four of the files the default mode flagged as badly out of sync re-measured as fine (within 1 s, or a clean 25/23.976 fps scale with a high positive score) once --vad webrtc was forced. Every video in that set had a forced-only embedded stream of a few dozen cues or fewer; videos with a full embedded track measured the same in both modes.
Version: 0.5.1 (master is identical at time of writing).
Why it matters
Batch tools built on ffsubsync (subtitle managers, media-server plugins) run it unattended with default flags and overwrite the input. With the default VAD, any library containing forced-only tracks gets its good subtitles shifted by up to 60 s, and because the output is deterministic the next run shifts them again. --skip-sync-on-low-quality is opt-in, and the exit code stays 0 when sync_was_successful is false, so callers get no signal.
Proposed fix
In speech_transformers.py:
- Have
_probe_embedded_subtitle_streamsalso requeststream_disposition=forcedandstream_tags=language, and skip streams withforced=1. - In
try_fit_using_embedded_subs, pick the stream by cue count rather than by last-cue time, and log which stream was used and how many cues it had. - If the best remaining stream has fewer than a threshold of cues (I suggest 50, exposed as
--min-embedded-subtitle-cues), raise so the caller falls back to the audio VAD, as it already does when no stream exists.
Optionally, when the subs-based alignment scores below zero, retry with the audio VAD before giving up. I have a patch for 1 to 3 and can open a PR if this approach is acceptable.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in speech_transformers.py, reading _probe_embedded_subtitle_streams and try_fit_using_embedded_subs, then reproduce the issue with the supplied ffsubsync commands. Trace how forced streams, cue counts, and low-quality alignment results reach the audio VAD fallback. Done means forced-only or too-short embedded tracks are not selected, the chosen stream is logged, and the reproduction avoids the large offset.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- audio-video-rtc, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100