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.

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 copyis a stream copy: FFmpeg's docs say there is no decoding or encoding, so no quality loss.- When you encode, add
-movflags +faststartto 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-faststarttool as another way to move the index. - The MP4 muxer's
moov_sizeoption 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.mp4Does 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:
| Sume tool | Video stream | Faststart |
|---|---|---|
Video trim, precision: "keyframe" | Stream copy, no re-encode | Yes |
Video trim, precision: "exact" (the default) | Re-encoded with libx264 | Yes |
| Video filter | Re-encoded with libx264 | Yes |
| Timeline compose | Re-encoded with libx264 | Yes |
| Timeline 1.0 render | Re-encoded with libx264 | Yes |
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_urlmust be your workspace'smedia.sume.comartifact 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. Anendpast the source clamps to it and warnstrim_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
- p-limit npm: cap concurrent AI API jobs in Node.js
p-limit runs at most n promise-returning functions at once. For paid AI jobs, wrap the submit and the wait, not just the POST, and set n to your limit.
- Rate limit headers: what limit, remaining and reset mean
Rate limit headers report your request budget: the window's limit, what's left, and when it resets. What each means and how a client should pace itself.
- Retry-After header: how long to wait after a 429 or 503
Retry-After tells a client how long to wait before retrying: a number of seconds or an HTTP date, sent with 429 or 503. How to read it and what to do.
- Retryable HTTP status codes: which errors to retry
Retry network errors, 408, 429, and 5xx with backoff; skip most other 4xx. Retry a POST only with an idempotency key, and read the API's retry flag.
Written by Sume