How Output-Timeline SRT Offsets Prevent Subtitle Misalignment in Video-Use

Output-timeline SRT offsets accumulate segment durations to shift subtitle timestamps forward, ensuring cues align with the final concatenated video rather than resetting to zero at each cut.

When concatenating multiple video segments into a single output file, subtitle timestamps must be adjusted to reflect the new continuous timeline. In the browser-use/video-use repository, the build_master_srt function in helpers/render.py solves this by calculating and applying per-segment offsets to prevent subtitle overlap and drift.

The Problem: Why Subtitles Drift in Multi-Segment Videos

Without timeline adjustments, each video segment's subtitles maintain timestamps relative to their individual start points (time 0). When these segments are concatenated into a final video, every segment's subtitles would begin at the video's start time, causing massive overlap and complete misalignment with the actual audio. The temporal relationship between spoken words and on-screen text breaks entirely, rendering the subtitles unusable.

The Solution: Per-Segment Offset Accumulation

The build_master_srt function implements a running offset accumulator to track the absolute position of each segment within the final timeline.

The seg_offset variable starts at 0.0 seconds at the beginning of the rendering process (render.py:L2627). This variable represents the cumulative duration of all previously processed segments.

As the function iterates through the Edit Decision List (EDL), it increments the offset by each segment's duration:

seg_offset = 0.0  # Initialize at timeline start

for r in edl["ranges"]:
    # Process segment extraction and word chunks...

    seg_offset += seg_duration  # Advance offset for next segment (render.py:L2733)

This accumulated value represents the exact moment in the final video where the current segment begins.

How Timestamp Shifting Works

When converting word-level transcripts into subtitle cues, the system calculates both local (segment-relative) and output (timeline-relative) timestamps.

For each two-word chunk processed within a segment:

  1. Local timestamps (local_start, local_end) are computed relative to the segment's beginning
  2. Output timestamps are derived by adding the accumulated seg_offset to the local values

# Inside build_master_srt (helpers/render.py)

out_start = max(0.0, local_start - seg_start) + seg_offset
out_end   = max(0.0, local_end   - seg_start) + seg_offset
entries.append((out_start, out_end, text))

This calculation (render.py:L3162-L3164) ensures that if a word appears 5 seconds into its source segment, and that segment starts 120 seconds into the final video, the subtitle appears at 125 seconds in the output file.

Robustness and Quality Controls

Beyond basic offset calculation, the implementation includes safeguards to ensure SRT validity:

  • Zero-length protection: The code enforces a minimum duration of 0.4 seconds for any cue (if out_end <= out_start: out_end = out_start + 0.4), preventing subtitle player errors from instantaneous timestamps
  • Chronological sorting: All entries are sorted by start time before writing (entries.sort(key=lambda e: e[0])) to ensure proper SRT sequence
  • Text normalization: Punctuation normalization and uppercase conversion standardize the output appearance

Implementation Workflow

To generate a properly aligned master subtitle file, invoke the build process after video extraction:


# Typical render workflow (helpers/render.py)

if args.build_subtitles:
    subs_path = edit_dir / "master.srt"
    # Build master SRT using per-source transcripts and accumulated offsets

    build_master_srt(edl, edit_dir, subs_path)  # Offsets applied here

The function processes the EDL ranges sequentially, maintaining the seg_offset state throughout the loop to ensure continuous timeline coherence.

Summary

  • Output-timeline offsets convert segment-relative timestamps to absolute positions in the concatenated video timeline
  • The seg_offset accumulator in build_master_srt tracks running duration by adding each segment's length before processing the next
  • Timestamp shifting occurs at render.py:L3162-L3164, where local word timings are offset by the accumulated segment duration
  • Zero-length protection ensures all cues have valid durations, while chronological sorting guarantees SRT specification compliance
  • This approach eliminates the overlap and misalignment that would otherwise occur when stitching multiple source clips into a single output file

Frequently Asked Questions

How does the offset calculation handle gaps between segments?

The offset accumulator (seg_offset) only increments by the actual duration of processed segments, not by any timeline gaps. If your EDL includes intentional pauses or black frames between segments, those must be accounted for in the segment duration itself or handled separately in the timeline construction, as the subtitle offset strictly follows the video segment timing.

Can this method handle overlapping segments in the EDL?

According to the video-use source code, the build_master_srt function processes EDL ranges sequentially. While the offset accumulator continues forward regardless of EDL structure, overlapping source ranges would result in subtitle timestamps that reflect the sequential processing order rather than the source timecode. For complex EDLs with overlapping sources, ensure the ranges are flattened or ordered appropriately before subtitle generation.

What happens if a segment has no transcribed words?

If a segment contains no words in its transcript, the inner loop simply produces no entries for that segment range, but the seg_offset accumulator still advances by seg_duration at the end of the iteration (render.py:L2733). This maintains proper synchronization for subsequent segments, ensuring their subtitles appear at the correct absolute time even after silent segments.

Is the 0.4-second minimum duration configurable?

The zero-length protection value of 0.4 seconds appears as a hardcoded constant in the current implementation (render.py). Users requiring different minimum durations would need to modify the source code directly where the guard clause checks if out_end <= out_start: out_end = out_start + 0.4.

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 →