Faststart MP4: moving the moov atom to the front

A faststart MP4 has its index, the moov atom, at the start of the file. FFmpeg moves it there with -movflags +faststart in a second pass.

5 min readSume
All posts

A faststart MP4 is one whose index, the moov atom, sits at the start of the file instead of the end. FFmpeg usually writes the index at the end; add -movflags +faststart and it runs a second pass that moves the moov atom to the beginning, so a player reading the file from the start gets the index first.

The FFmpeg facts come from its formats and ffmpeg documentation, YouTube's from its upload encoding settings, and the Sume facts from the Video trim, Video filter, and Timeline 1.0 docs, all read on 2026-09-28. Anything described as current behavior is read from Sume's code.

What is the moov atom, and why does its position matter?

An MP4 keeps its media data and its index apart. FFmpeg's docs describe a normal MOV/MP4 file as having all the metadata about all packets stored in one location, the moov atom, which is usually written at the end of the file and can be moved to the start for better playback.

A player needs that index to find each frame in the file. With the index at the end, a player streaming the file has to fetch the end before it can start; with faststart, the index arrives in the first bytes, ahead of the media. YouTube's recommended upload settings for MP4 list the moov atom at the front of the file (Fast Start).

How do I make an MP4 faststart with FFmpeg?

Put the flag on the command that writes the MP4. For a file you already have, copy its streams into a new file with the flag, which moves the index without re-encoding anything:

  • -c copy is a stream copy: FFmpeg's docs say there is no decoding or encoding, so no quality loss.
  • When you encode, add -movflags +faststart to the encode command instead of running a second job.
  • FFmpeg's docs say the second pass can take a while and doesn't work in various situations such as fragmented output, which is why it isn't enabled by default.
  • The same docs name the separate qt-faststart tool as another way to move the index.
  • The MP4 muxer's moov_size option reserves space for the moov atom at the beginning instead of placing it at the end. If the space reserved is too small, muxing fails.
ffmpeg -i input.mp4 -c copy -movflags +faststart output.mp4

Does faststart repair an MP4 that won't open?

No. Faststart moves an index that already exists. FFmpeg's docs say a normal MOV/MP4 is undecodable if it is not properly finished, and a file cut off before its index was written has nothing to move. The same docs contrast fragmented MP4, which stores packets and their metadata together, so the file stays decodable even if writing is interrupted; the downside they name is that it is less compatible with other applications.

Are Sume's trimmed and rendered MP4 files faststart?

Yes, from these four tools. In current code, video trim, video filter, Timeline compose, and Timeline render all run FFmpeg with -movflags +faststart, whether they re-encode or not:

From Video trim, Video filter, Timeline 1.0, and Sume's compiler code as it runs today, read 2026-09-28.
Sume toolVideo streamFaststart
Video trim, precision: "keyframe"Stream copy, no re-encodeYes
Video trim, precision: "exact" (the default)Re-encoded with libx264Yes
Video filterRe-encoded with libx264Yes
Timeline composeRe-encoded with libx264Yes
Timeline 1.0 renderRe-encoded with libx264Yes

How do I get a faststart copy of a Sume-hosted MP4?

For a Sume-hosted MP4 that didn't come from those four tools, such as another job's output, trim it over its whole length with precision: "keyframe": start at 0 and end at its duration. The docs describe a keyframe trim as a stream copy, and in current code it is written with +faststart, so the result is the same video with the index at the front. The source is untouched, and the new file comes back as video_url from GET /v1/jobs/:id/result.

  • video_url must be your workspace's media.sume.com artifact or asset, such as an earlier Sume job's output; which URLs each endpoint accepts explains the rule.
  • Trim reads sources up to 1,800 seconds but writes at most 900 seconds per job, so a whole-length copy works for clips up to 900 seconds. In current code a source file over 300 MiB is refused with source_too_large. An end past the source clamps to it and warns trim_clamped_to_source.
  • Trim is billed per job, plus a 5.5% agent fee by default; the docs say to confirm the rate in GET /v1/catalog.
curl -X POST https://api.sume.com/v1/video-trim \
  -H "Authorization: Bearer $SUME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: faststart-copy-001" \
  -d '{
    "video_url": "https://media.sume.com/artifacts/artf_demo/clip.mp4",
    "start": 0,
    "end": 42,
    "precision": "keyframe"
  }'

Sources

Related posts

More in Developers

All Developers posts

Written by Sume