Your source file played perfectly, but the moment you ran it through FFmpeg the audio and video came out of sync. This is one of the most confusing FFmpeg problems because the conversion itself looks successful — there’s no error, the file plays, the picture is fine, but the sound no longer lines up. The cause is almost always something the conversion carried over or failed to rebuild: broken timestamps copied straight across, a variable-frame-rate source, or a container remux that didn’t reconcile the two clocks. This article isolates each cause and gives you a runnable fix per cause.

Tested with: FFmpeg 8.1


What You’ll Learn

  • Why sync breaks after conversion even when the source was fine
  • How stream copy (-c copy) carries over bad or variable timestamps unchanged
  • Why a variable-frame-rate (VFR) source produces sync drift in a copied output
  • Four runnable fixes: full re-encode, normalize audio timing, rebuild timestamps, and force constant frame rate
  • How to pick the right fix for your specific symptom

This problem is different from a constant offset that was present from the start (the audio was always 200 ms late). For a fixed lead/lag, see Fix Audio Delay Manually. For the general toolbox of sync options, see Fix Audio Sync. This article is specifically about sync that appeared after conversion.


Cause 1: Stream Copy Carried Over Bad Timestamps

-c copy (stream copy) is fast and lossless because it doesn’t decode anything — it just lifts the packets out of one container and drops them into another. The catch is that it also copies the presentation timestamps (PTS) exactly as they were. If the source had irregular, missing, or non-monotonic timestamps, those problems travel straight into the output, and the new container may interpret them differently than the old one did.

This is the classic “it played fine before, but -c copy broke the sync” scenario. The player that opened the source was tolerant of the messy timestamps; the new container plus a stricter player is not.

The most reliable fix is to re-encode, which forces FFmpeg to decode and re-lay both streams on a clean timeline.

ffmpeg -i input.mp4 -c:v libx264 -crf 20 -c:a aac output.mp4
  • -c:v libx264 -crf 20 — re-encode the video (CRF 20 is visually near-lossless)
  • -c:a aac — re-encode the audio
  • No -c copy, so FFmpeg rebuilds the timeline from decoded frames

Re-encoding is the heaviest option but the one most likely to fix a stubborn case, because it removes the reliance on the source’s original timestamps entirely.


Cause 2: Normalize Audio Timing Without a Full Re-Encode

If the video itself is fine and only the audio timing is the problem, you don’t have to re-encode the video. The aresample filter with async=1 tells FFmpeg to fill or trim the audio (by inserting or dropping samples) so its timestamps stay aligned with the video clock. (async=1 only enables fill/trim; to actually stretch or squeeze the audio you’d use a value greater than 1.)

ffmpeg -i input.mp4 -af aresample=async=1 -c:v copy output.mp4
  • -af aresample=async=1 — resample audio, filling/trimming to keep it aligned to its timestamps
  • -c:v copy — copy the video untouched (fast, lossless)
  • The audio is re-encoded by necessity, since the filter rewrites samples

This keeps the (often large) video stream copied while only the audio is reprocessed, so it’s much faster than a full re-encode. It’s a good first attempt when the picture looks correct and only the sound has slipped.


Cause 3: Rebuild Missing Timestamps with +genpts

Sometimes the real problem is that the source has no usable timestamps at all for one of the streams, or the timestamps are non-monotonic. When that happens, copying them across (-c copy) just propagates the gap. The +genpts flag asks FFmpeg to generate presentation timestamps where they’re missing, which often repairs the alignment without any re-encoding.

ffmpeg -fflags +genpts -i input.mp4 -c copy output.mp4
  • -fflags +genpts — generate missing PTS values during demuxing (must come before -i)
  • -c copy — still no re-encode, so it stays fast and lossless

Because +genpts is applied at the input/demux stage, it must appear before the -i input.mp4. This is the lightest-weight fix: if it works, you keep both streams bit-for-bit identical and only the timing metadata is corrected.


Cause 4: The Source Was Variable Frame Rate (Force CFR)

The single most common root cause of “sync was fine, then broke after conversion” is a variable-frame-rate (VFR) source. Screen recorders, phone cameras, and game-capture tools frequently record VFR: the gap between frames changes throughout the clip. Audio, by contrast, runs on a steady sample clock. When you copy a VFR video into a container that the new player treats as constant-frame-rate, the two clocks slowly diverge and the audio drifts further off as the clip goes on.

The fix is to convert the video to constant frame rate (CFR) while re-encoding, so every frame sits at a predictable interval that matches the audio clock.

ffmpeg -i input.mp4 -fps_mode cfr -r 30 -c:v libx264 -c:a aac output.mp4
  • -fps_mode cfr — force constant frame rate output (FFmpeg 8.x option)
  • -r 30 — target 30 frames per second (set this to your source’s nominal rate)
  • -c:v libx264 -c:a aac — re-encode both streams onto the clean CFR timeline

Note: In FFmpeg 8.x the option is -fps_mode cfr. The older -vsync cfr still works but is deprecated — prefer -fps_mode.

If your problem is specifically VFR, this is the proper fix, and it’s worth understanding the topic on its own. See the dedicated guide: Convert Variable Frame Rate to Constant. If the audio drifts progressively (fine at the start, worse by the end) rather than being uniformly off, that is the drift pattern covered in Fix Audio Drift.


Which Fix Should I Use?

SymptomLikely causeRecommended fix
Off right after a -c copy remuxBad timestamps carried overRe-encode: -c:v libx264 -crf 20 -c:a aac
Picture fine, only audio slippedAudio timing-af aresample=async=1 -c:v copy
-c copy output won’t sync, source has odd timestampsMissing/non-monotonic PTS-fflags +genpts -i ... -c copy
Drifts worse over time, source is a screen/phone captureVFR source-fps_mode cfr -r 30 (re-encode)

Start with the lightest fix that matches your symptom. If +genpts doesn’t resolve it, move up to aresample=async=1, and if the source is VFR, go straight to forcing CFR.


FAQ

Why was the file in sync before I converted it?

Because stream copy (-c copy) carries the source timestamps over unchanged, and the new container plus player can interpret them differently than the original did. The source wasn’t “perfect” — its player was just tolerant of messy timestamps. Re-encoding or rebuilding timestamps with -fflags +genpts removes that dependency.

Do I have to re-encode to fix this?

Not always. Try -fflags +genpts -i input.mp4 -c copy first — it generates missing timestamps without re-encoding. If only the audio slipped, -af aresample=async=1 -c:v copy re-encodes just the audio. A full re-encode is the last resort for stubborn cases.

What’s the difference between this and a constant audio delay?

A constant delay means the audio was a fixed amount early or late from the very start, and you correct it with a single offset — see Fix Audio Delay Manually. The problem here is sync that appeared after conversion, usually from carried-over timestamps or a VFR source, and it often gets worse over time rather than staying a flat offset.

How do I know if my source is VFR?

A VFR file shows a different r_frame_rate versus avg_frame_rate when inspected with ffprobe. The full detection and conversion walkthrough is in Convert Variable Frame Rate to Constant.

Is -vsync cfr the same as -fps_mode cfr?

Functionally yes, but -vsync is deprecated in FFmpeg 8.x. Use -fps_mode cfr going forward. The old flag still works for now, but new commands should use -fps_mode.



Tested with FFmpeg 8.1 — verified with our command-check script
Primary sources: ffmpeg.org/ffmpeg.html / ffmpeg.org/ffmpeg-filters.html#aresample