Documentation
¶
Overview ¶
Package subsync provides subtitle timing synchronization.
It supports multiple sync strategies:
- Constant-offset alignment (alass algorithm port)
- Framerate correction (known ratios + golden-section search)
- Split-aware DP alignment (handles commercial breaks, different cuts)
- Audio-based sync (energy VAD + FFT cross-correlation, no reference needed)
- Encoding normalization (UTF-16, Windows-1252 → UTF-8)
The entry point is SyncWithOptions, which takes already-parsed cues and returns the winning SyncResult. Callers own file I/O: read the subtitle, NormalizeEncoding it, ParseSRT it, sync, then WriteSRT the corrected cues. internal/search/syncing is the in-repo consumer and shows the full shape (parse -> SyncWithOptions -> WriteSRT -> PostProcess).
Audio-only sync (no reference subtitle) is the same call with a nil reference, EnableAudio, and SyncOptions.VideoPath set.
Index ¶
- Constants
- Variables
- func IsASSContent(data []byte) bool
- func NormalizeEncoding(data []byte) []byte
- func PostProcessBytes(data []byte, opts PostProcessOptions) []byte
- func WriteSRT(w io.Writer, cues []Cue) error
- type AudioSyncHints
- type CandidateSource
- type Confidence
- type ConfidenceCaps
- type Cue
- func ExtractEmbeddedSRT(ctx context.Context, videoPath, lang, excludeLang string, ...) ([]Cue, error)
- func ParseASSDialogue(data []byte) (dialogueCues, maskCues []Cue, err error)
- func ParseSRT(r io.Reader) ([]Cue, error)
- func PostProcess(cues []Cue, opts PostProcessOptions) []Cue
- func ShiftCues(cues []Cue, offset time.Duration) []Cue
- type LangMapper
- type PostProcessOptions
- type Segment
- type SyncMethod
- type SyncOptions
- type SyncResult
- type TimeSpan
- type Transform
- type TransformKind
Constants ¶
const CorrectedCueAgreementMs = 1500
CorrectedCueAgreementMs is the maximum per-cue timing difference, over ALL corrected cue starts and ends in milliseconds, for two candidates to count as agreeing. 1500ms is the package's crosslang anchor-agreement scale; the value is validated against the golden corpus (see corpusbench_test.go).
const MinCuesForSync = 5
MinCuesForSync is the minimum number of subtitle cues required for any timing-based sync strategy to produce meaningful results. Fewer than this provides insufficient signal for correlation or alignment.
Variables ¶
var DefaultConfidenceCaps = ConfidenceCaps{
Audio: 0.9,
Offset: 0.9,
FramerateGSS: 0.85,
FramerateKnown: 0.95,
FramerateFPS: 0.98,
Crosslang: 0.92,
SplitBase: 0.85,
SplitPenaltyPerSegment: 0.05,
SplitMinConf: 0.4,
}
DefaultConfidenceCaps is the production confidence cap table.
Functions ¶
func IsASSContent ¶
IsASSContent reports whether data looks like ASS/SSA subtitle content by checking for the [Script Info] header or Dialogue: lines.
func NormalizeEncoding ¶
NormalizeEncoding detects the encoding of subtitle data and converts it to UTF-8. Handles UTF-8 (with/without BOM), UTF-16 LE/BE, and common single-byte encodings (Windows-1252, ISO-8859-1).
Returns the UTF-8 normalized data with any byte-order mark and embedded NUL bytes removed. When the input is already valid, BOM-free, NUL-free UTF-8 the original slice is returned (not a copy); callers must not mutate the returned slice if they need the original data unchanged. The result is a fixed point: NormalizeEncoding(NormalizeEncoding(x)) always equals NormalizeEncoding(x).
func PostProcessBytes ¶
func PostProcessBytes(data []byte, opts PostProcessOptions) []byte
PostProcessBytes applies encoding normalization and line ending fixes to raw subtitle bytes. Call this before parsing, or on the final output.
Types ¶
type AudioSyncHints ¶
type AudioSyncHints struct {
// DialogueCues are pre-classified ASS dialogue cues from the native
// parser. When set, audioSyncFromPCM uses these directly instead of
// applying text-based heuristics (karaoke pair detection, SDH filtering)
// to the flat cue list. This produces better results for ASS files
// because style-based classification is more accurate than text patterns.
DialogueCues []Cue
// DurationSec is the total media duration in seconds.
DurationSec int
// IsASS indicates the subtitle was ASS-extracted (clean cues, no tag remnants).
IsASS bool
}
AudioSyncHints provides content characteristics for adaptive strategy selection. Zero values use the default strategy.
type CandidateSource ¶ added in v0.1.148
type CandidateSource int
CandidateSource is the stable identity of the strategy that generated a candidate. It is distinct from Transform.Kind (crosslang and the constant offset strategy both apply shift transforms) and from SyncMethod (a string with no ordering). The declared order is the canonical arbitration order: clustering input is sorted by it, and rating ties resolve to the earliest source.
const ( // SourceNone marks a result that did not come from a voted reference // strategy (audio results and no-result placeholders). SourceNone CandidateSource = iota // SourceCrosslang is the cross-language anchor alignment strategy. SourceCrosslang // SourceFramerate is the framerate correction strategy. SourceFramerate // SourceOffset is the constant-offset (alass) strategy. SourceOffset // SourceSplit is the split-aware DP alignment strategy. SourceSplit )
Candidate sources in canonical arbitration order.
func (CandidateSource) String ¶ added in v0.1.148
func (s CandidateSource) String() string
String implements fmt.Stringer. The zero value renders as "none", which the corpus artifacts admit as a winner source.
type Confidence ¶
type Confidence float64
Confidence represents the quality of a sync operation (0.0 to 1.0).
const ConfidenceNone Confidence = 0
ConfidenceNone means no sync was performed or the result is unusable. It is the only named point on the 0.0-1.0 Confidence scale: every other threshold that production reads is a purpose-specific constant (ShouldApplyThreshold here, api.DefaultSyncMinConfidence at the API layer) or a per-strategy ceiling in DefaultConfidenceCaps.
const ShouldApplyThreshold Confidence = 0.5
ShouldApplyThreshold is the exported minimum confidence for a sync result to be considered applicable. Consumers comparing Result.Confidence against a threshold should use this constant instead of a magic 0.5 literal. It is also the sync engine's own default when no explicit threshold is provided (see DefaultSyncOptions and SyncWithOptions). The API layer's DefaultSyncMinConfidence (0.6) is intentionally stricter for user-facing auto-apply decisions.
type ConfidenceCaps ¶
type ConfidenceCaps struct {
Audio Confidence // audio-based sync
Offset Confidence // constant-offset sync
FramerateGSS Confidence // golden-section framerate search
FramerateKnown Confidence // known-ratio framerate match
FramerateFPS Confidence // video FPS confirms the ratio
Crosslang Confidence // cross-language alignment
SplitBase Confidence // split-aware alignment (reduced by segment penalty)
SplitPenaltyPerSegment Confidence // confidence penalty per additional segment
SplitMinConf Confidence // minimum confidence floor for split alignment
}
ConfidenceCaps holds per-strategy confidence ceilings. Each strategy has a different cap reflecting its inherent reliability. The relative ordering is intentional: framerate with FPS confirmation > known ratio > audio/offset > GSS/split.
func (ConfidenceCaps) ForMethod ¶
func (c ConfidenceCaps) ForMethod(m SyncMethod) Confidence
ForMethod returns the confidence cap for the given sync method. For framerate methods, returns FramerateGSS as the conservative default; callers with FPS confirmation should use FramerateFPS directly.
type Cue ¶
Cue represents a single subtitle cue with timing and text content. This is a local definition that mirrors api.SubtitleCue, decoupling the subsync package from the application's domain types.
func ExtractEmbeddedSRT ¶
func ExtractEmbeddedSRT(ctx context.Context, videoPath, lang, excludeLang string, mapper ffmpeg.LangMapper) ([]Cue, error)
ExtractEmbeddedSRT extracts text-based subtitle data from a video file and returns it as parsed SRT cues. Uses ffprobe for track detection and ffmpeg for subtitle extraction.
Track selection priority:
- Matching language (if lang is non-empty)
- Prefer SRT/subrip over ASS/SSA
- Skip forced tracks (sparse cues)
- Skip tracks matching excludeLang
Returns nil cues if no suitable track is found.
func ParseASSDialogue ¶
ParseASSDialogue parses raw ASS content and returns dialogue cues and mask cues separately.
dialogueCues: only cues from classified dialogue styles (for subtitle envelope / correlation signal). maskCues: all cues regardless of style classification (for dialogue mask / time region detection). Even OP/ED/signs mark time regions with audio activity, giving broader mask coverage.
Two-pass: first collects all style names and cue counts, classifies them, then extracts cues.
func PostProcess ¶
func PostProcess(cues []Cue, opts PostProcessOptions) []Cue
PostProcess applies the configured processing steps to subtitle cues. Steps run in order: encoding normalization (on raw bytes before parsing) is handled by the caller; this function operates on parsed cues.
type LangMapper ¶
type LangMapper = ffmpeg.LangMapper
LangMapper is a type alias for ffmpeg.LangMapper, preserving backward compatibility for consumers that reference subsync.LangMapper.
type PostProcessOptions ¶
type PostProcessOptions struct {
// StripHI removes hearing-impaired annotations:
// [sound effects], (music playing), ♪ lyrics ♪, and speaker labels (JOHN:).
StripHI bool
// StripTags removes HTML-like tags: <i>, </i>, <b>, </b>, <u>, </u>, <font ...>, </font>.
StripTags bool
// NormalizeEncoding converts the subtitle to UTF-8 from any detected encoding
// (UTF-16 LE/BE, Windows-1252, ISO-8859-1). Also strips UTF-8 BOM.
NormalizeEncoding bool
// NormalizeLineEndings converts all line endings to CRLF (SRT standard)
// and ensures a single trailing CRLF.
NormalizeLineEndings bool
// CleanWhitespace trims leading/trailing whitespace from each text line
// and removes blank lines and bare dialogue dashes.
CleanWhitespace bool
// RemoveEmpty drops cues that have no text content after all other
// processing steps. Cue numbers are assigned when writing SRT output.
RemoveEmpty bool
}
PostProcessOptions configures subtitle post-processing. All fields default to false (no processing). Enable what you need.
type Segment ¶ added in v0.1.148
Segment describes one contiguous cue range and the constant shift a split-aware correction applies to it. Indexes address the corrected cue slice; EndIdx is exclusive.
type SyncMethod ¶
type SyncMethod string
SyncMethod is a typed string identifying the sync algorithm used.
const ( MethodNone SyncMethod = "none" MethodOffset SyncMethod = "offset" MethodFramerate SyncMethod = "framerate" MethodSplit SyncMethod = "split" MethodAudio SyncMethod = "audio" MethodCrosslang SyncMethod = "crosslang" )
Sync method identifiers.
type SyncOptions ¶
type SyncOptions struct {
// VideoPath is the path to the video file (required for audio sync).
VideoPath string
// AudioHints provides content characteristics for adaptive audio
// sync strategy selection. Optional; zero value uses defaults.
AudioHints AudioSyncHints
// SplitPenalty controls split detection sensitivity (0 = default).
SplitPenalty float64
// MinConfidence is the minimum confidence to apply a sync result.
// Default: 0.5.
MinConfidence Confidence
// EnableFramerate enables framerate correction detection.
EnableFramerate bool
// EnableSplits enables split-aware DP alignment.
EnableSplits bool
// EnableAudio enables audio-based sync (requires video file path).
EnableAudio bool
}
SyncOptions configures the sync behavior.
func DefaultSyncOptions ¶
func DefaultSyncOptions() SyncOptions
DefaultSyncOptions returns sensible defaults.
type SyncResult ¶
type SyncResult struct {
Method SyncMethod // MethodNone, MethodOffset, MethodFramerate, MethodSplit, MethodAudio, MethodCrosslang
Cues []Cue
Transform Transform // descriptor of the correction applied to Cues (voted candidates)
Offset int64 // milliseconds (constant offset applied)
Confidence Confidence // quality of the sync (calibrated; gates read this)
Rate float64 // framerate ratio applied (1.0 = no change)
Source CandidateSource // generating strategy identity (voted candidates)
}
SyncResult holds the output of any sync operation.
func SyncWithOptions ¶
func SyncWithOptions(ctx context.Context, reference, incorrect []Cue, opts *SyncOptions) SyncResult
SyncWithOptions performs multi-strategy subtitle synchronization.
Strategy order:
- If reference subtitle available: try framerate correction, then split-aware alignment, then constant offset
- If no reference but video path provided and audio enabled: try audio-based sync
Returns the best result above the minimum confidence threshold.
func (*SyncResult) Applied ¶
func (r *SyncResult) Applied() bool
Applied returns true if the sync actually changed the subtitle timing. Checks offset (constant shift), rate (framerate correction), and split method (multi-segment alignment where no single offset/rate captures the change).
func (*SyncResult) ShouldApply ¶
func (r *SyncResult) ShouldApply() bool
ShouldApply returns true if the confidence is high enough to use the result. Threshold: ShouldApplyThreshold (moderate confidence or better). This is the post-hoc check used by callers after sync completes.
type Transform ¶ added in v0.1.148
type Transform struct {
Segments []Segment // per-segment shifts (Kind == TransformSegments)
Shift int64 // shift in milliseconds (Kind == TransformShift)
Ratio float64 // framerate ratio (Kind == TransformFramerate)
Kind TransformKind
}
Transform describes the timing correction a SyncResult applies to the incorrect cues. It exists for candidate validation, logging, and the corpus digest. It is deliberately NOT usable as a map key (Segments is a slice, and Ratio would need canonical float equality): cluster membership is decided by comparing corrected cues, never by comparing transforms.
type TransformKind ¶ added in v0.1.148
type TransformKind uint8
TransformKind identifies the shape of the timing correction a sync candidate applies to the incorrect cues.
const ( // TransformNone marks a result that carries no transform descriptor // (results that never enter the vote, and no-op placeholders). TransformNone TransformKind = iota // TransformShift is a single constant time shift of all cues. TransformShift // TransformFramerate is a linear rescale of all cue times by a ratio. TransformFramerate // TransformSegments is a set of per-segment constant shifts // (split-aware alignment). TransformSegments )
Transform kinds.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package crosslang implements cross-language subtitle alignment using anchor-based matching and dynamic programming.
|
Package crosslang implements cross-language subtitle alignment using anchor-based matching and dynamic programming. |
|
Package ffmpeg provides low-level ffmpeg/ffprobe subprocess wrappers for stream probing, subtitle extraction, and PCM audio extraction.
|
Package ffmpeg provides low-level ffmpeg/ffprobe subprocess wrappers for stream probing, subtitle extraction, and PCM audio extraction. |
|
Package fft provides a radix-2 Cooley-Tukey FFT implementation for cross-correlation in the subsync package.
|
Package fft provides a radix-2 Cooley-Tukey FFT implementation for cross-correlation in the subsync package. |
|
Package framerate provides framerate drift detection for subtitle alignment.
|
Package framerate provides framerate drift detection for subtitle alignment. |