Why does my video freeze while the voiceover keeps playing?

Timeline holds the last frame when slots end before audio.duration_seconds, or leave a gap. Plan reports it free as video_coverage_shorter_than_audio.

4 min readSume
All posts

The picture freezes because the video slots in your Timeline document cover fewer seconds than audio.duration_seconds, and the renderer holds the previous frame to reach the spine length instead of failing. The render still succeeds, and warnings[] says which of three causes it was.

Timeline 1.0 treats the audio spine as the clock. The output is exactly audio.duration_seconds long, and every video slot is placed on that clock by its declared start and duration. Anything the slots do not cover is filled with a hold of the frame before it.

The three causes, by warning code

Each cause has its own code in the render result. The first two come from the numbers in your document alone, so the free POST /v1/timeline-1.0/plan call returns them before you spend anything. The third needs the real clip length, which only the render probes.

We ran the repository's own plan builder on a 12-second spine with two 4-second slots, the second one starting at 6 seconds and carrying a 0.3-second fade, at 25 fps. It reported a 2-second gap before the second slot and a coverage end of 10 seconds against a 12-second spine.

Why a Timeline render holds a frame (read 2026-10-03)
Warning codeTriggerWhat you seeFix
timeline_gap_filledA slot's start is later than the previous slot's start plus durationPrevious frame holds through the gap, then the next slot starts on timeSet the next start to the previous end, or lengthen the previous duration
video_coverage_shorter_than_audioLast slot ends before audio.duration_secondsLast frame holds to the end of the spineLengthen the last slot or add another
segment_source_short_paddedA clip is shorter than source_in plus durationThat slot's last frame holds, position unchangedTrim the slot shorter or use a longer clip, see the short-source post

Why it is a warning and not an error

The audio is authoritative. A voiceover that must not be cut is more valuable than a refusal, so the compiler keeps the spine intact and fills the picture. The warning text in our run read: "Video coverage ends at 10.000s; holding last frame to audio spine 12.000s." The context object carries coverage_seconds, audio_duration_seconds and hold_seconds, so a script can act on it.

The check runs one way only. The request schema refuses a final slot that ends more than 0.5 seconds after the spine, with invalid_segment_timing. A slot that ends early is accepted and held. So a document can fail for being too long but never for being too short, which is why the freeze surprises people.

Catch it before you render

Declared starts are authoritative, so the arithmetic is easy to do client-side. This function lists every gap and the tail hold for a list of slots, using the same rule the compiler applies. For two 4-second slots with starts 0 and 6 on a 12-second spine it prints the 2-second gap and the 2-second tail.

def coverage_gaps(slots, spine):
    gaps, end = [], 0.0
    for s in slots:
        if s["start"] > end + 1e-6:
            gaps.append(("gap", end, s["start"]))
        end = s["start"] + s["duration"]
    if end + 1e-6 < spine:
        gaps.append(("tail", end, spine))
    return gaps

slots = [{"start": 0, "duration": 4}, {"start": 6, "duration": 4}]
print(coverage_gaps(slots, 12))

Run that, or call the plan endpoint, then fix the numbers. If you want a deliberate freeze, such as a held title card under a long sentence, leave the gap in on purpose and ignore the warning. The coverage_seconds field on the plan response gives the end of the last slot directly. A plan response cannot see source lengths, so the third cause still has to be read from the render result.

Sources

Related posts

More in Media tools

All Media tools posts

Written by Sume