How Subtitle Timestamps Are Aligned After Video Segment Concatenation in Video-Use

Video-use aligns subtitle timestamps correctly by applying the subtitles filter after concatenation, allowing ffmpeg to automatically synchronize cues to the final continuous PTS timeline.

The video-use repository solves a common video editing challenge: ensuring that subtitle files remain perfectly synchronized even when the final video is assembled from multiple trimmed, speed-adjusted, or reordered segments. This article explains the exact mechanism used in helpers/render.py to achieve this alignment without manual timestamp arithmetic.

Why Subtitle Alignment Is Tricky With Concatenated Segments

When you edit video by cutting and rearranging segments, each piece carries its own Presentation Timestamp (PTS) values. If you naively apply subtitles to individual segments before merging, the timestamps would drift or break entirely because:

  • Each segment's internal clock starts at zero or some arbitrary offset
  • Trimming and speed changes alter the duration and PTS values
  • The final assembled timeline is a composite that doesn't match any single source

Video-use avoids these pitfalls through a carefully constructed ffmpeg filter-complex graph.

The Filter-Graph Architecture in helpers/render.py

The core solution resides in helpers/render.py, where the rendering engine builds a single ffmpeg command that processes all segments through one continuous pipeline.

Step 1: Process Each Segment Individually

Each input segment undergoes its own transformations—trimming, speed adjustments, PTS shifts—within the filter graph:


# Conceptual structure of the filter graph

[segment_0] trim=start=...:end=...,setpts=PTS-STARTPTS[v0];
[segment_1] trim=start=...:end=...,setpts=PTS-STARTPTS[v1];

# ... additional segment processing

Step 2: Concatenate Into Unified Timeline

All processed video streams feed into the concat filter, which produces a single output with normalized, continuous PTS values:

f"[base][over1][over2]...concat=n={n_segments}:v=1:a=1[video][audio]"

The concat filter guarantees that:

  • Segment boundaries align seamlessly
  • The output timeline represents the final edited sequence, not the source material
  • Audio and video remain synchronized throughout

Step 3: Apply Subtitles to Final Timeline

Here's the critical insight: the subtitles filter is appended after concatenation, receiving the unified timeline:


# Resolve and escape the subtitle path for ffmpeg

subs_abs = str(subtitles_path.resolve()).replace(":", r"\:").replace("'", r"\'")

# Complete filter graph: concatenation → subtitles → output

ffmpeg_cmd = [
    "ffmpeg",
    "-i", base_path,
    "-filter_complex",
    f"[base][over1][over2]...concat=n={n_segments}:v=1:a=1[video][audio];"
    f"{current}subtitles='{subs_abs}':force_style='{SUB_FORCE_STYLE}'[outv]",
    "-map", "[outv]",
    "-map", "[audio]",
    "-c:v", "libx264",
    "-c:a", "aac",
    out_path,
]

By positioning subtitles downstream of concat, ffmpeg automatically maps the original SRT or ASS timestamps onto the already-merged video timeline. No recalculation of subtitle times is necessary—ffmpeg handles the internal PTS-to-timecode synchronization.

Runtime Usage

Generate final output with embedded, correctly-aligned subtitles:

python helpers/render.py my_edit.edl -o final.mp4 --build-subtitles

To render without subtitles, simply omit the flag—the subtitles filter is excluded entirely:

python helpers/render.py my_edit.edl -o final.mp4 --no-subtitles

Supporting Components

Several helper modules contribute to timestamp integrity across the pipeline:

File Contribution to Alignment
helpers/render.py Builds the ffmpeg filter graph; orchestrates concat-then-subtitles ordering
helpers/timeline_view.py Visualizes segment start/end calculations for verification
helpers/grade.py Applies audio-video leveling while preserving PTS continuity

Summary

  • Subtitles are applied post-concatenation, ensuring they reference the final unified timeline rather than individual segment clocks
  • The concat filter normalizes PTS values across all edited segments automatically
  • Zero manual timestamp arithmetic is required—ffmpeg's internal synchronization handles the mapping
  • This architecture is implemented in helpers/render.py with straightforward runtime control via --build-subtitles and --no-subtitles flags

Frequently Asked Questions

Does video-use modify the original subtitle file's timestamps?

No. The original SRT or ASS file remains unchanged. According to the video-use source code, the subtitles filter reads the file and performs runtime synchronization against the video's PTS. The subtitle timestamps in the file are interpreted as absolute timecodes relative to the concatenated output timeline.

What happens if segments have different frame rates or timebases?

ffmpeg's concat filter handles timebase normalization when all inputs share compatible properties. Video-use processes segments to ensure compatibility before concatenation. The PTS values are adjusted accordingly, and the final timeline presents a consistent timebase to the subtitles filter.

Can I use this approach with ASS subtitle styling?

Yes. The code explicitly supports ASS files through the force_style parameter, which allows overriding default styling. The path resolution and escaping logic in helpers/render.py handles both SRT and ASS formats identically for timestamp alignment purposes.

Is there any performance penalty for this filter ordering?

Minimal. The subtitles filter operates on the final video dimensions and frame rate, so its computational cost is identical regardless of concatenation complexity. The primary processing overhead occurs during segment decoding and the concat operation itself, not during subtitle overlay.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →