Ahosting Logo
Knowledge Base

How to Convert Video Formats with FFmpeg

Before converting anything, ask whether you need to convert at all. A great many format changes are only a container change. The video and audio inside are already what you need, and only the wrapper is wrong. That takes seconds instead of hours and loses no quality.

ffprobe -hide_banner input.mkv

If that reports H.264 video and AAC audio and you want MP4, you do not need to re-encode. Copy the streams into the new container:

ffmpeg -i input.mkv -c copy output.mp4

No decoding, no quality loss, limited only by disk speed. Try this first every time; when it works it is the whole job.

When you do have to re-encode

Re-encoding is necessary when the codec itself is wrong for the destination, when you need a smaller file, or when the container cannot hold what is inside. The basic form:

ffmpeg -i input.avi -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4

Every part of that matters. -c:v libx264 chooses the video encoder, -crf 23 sets quality, -preset medium sets how hard the encoder works, and the audio options convert sound to AAC at a reasonable bitrate.

Leaving out the audio options is a common mistake: FFmpeg picks a default that may not suit the container, and MP4 in particular is fussy about what it accepts.

CRF, and why not to specify a bitrate

CRF asks for a consistent visual quality and lets the file size follow. For anything watched on demand this is what you want, because simple scenes use fewer bits and complex ones use more, which is exactly right.

  • 18: visually lossless, large files
  • 23; the default, a good balance
  • 28: noticeably smaller, acceptable for most web video

The scale is inverted and logarithmic: lower means better and bigger, and a change of about 6 halves or doubles the size.

Specify a fixed bitrate only when something external requires it: a delivery specification, or a hard bandwidth ceiling. Otherwise CRF produces a better result at a smaller average size.

The first question is whether to convert at allRun ffprobe. Do the codecs inside already match what you need?YesRemuxchange the container only: seconds, and no qualityloss at allNoRe-encodethen choose codec, rate control and containerdeliberatelyA large share of conversions people run are unnecessary, and they cost hours and quality for nothing.

Format combinations that actually work

MP4 with H.264 and AAC. The safe default. Plays in every browser, on every phone, in every player. Choose this unless you have a specific reason not to.

ffmpeg -i input.avi -c:v libx264 -crf 23 -c:a aac -b:a 128k -movflags +faststart output.mp4

-movflags +faststart moves the file's index to the beginning so playback can start before the whole file has downloaded. Without it, a browser may wait for the entire file. For anything served over the web, include it every time.

WebM with VP9 and Opus. Open formats, meaningfully smaller than H.264 at the same quality, and slower to encode.

ffmpeg -i input.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -c:a libopus output.webm

The -b:v 0 is required for VP9 to operate in constant-quality mode; without it the CRF value is ignored and you get a default bitrate instead. This trips people up regularly because the command appears to work.

MKV accepts almost any combination, which makes it useful for archiving and poor for the web.

Common conversions

Extract or change only the audio while leaving the video untouched:

ffmpeg -i input.mkv -c:v copy -c:a aac -b:a 192k output.mp4

Resize while converting, faster than doing both separately, since it encodes fewer pixels:

ffmpeg -i input.mp4 -vf "scale=1280:-2" -c:v libx264 -crf 23 -c:a copy output.mp4

Use -2 rather than -1 so the result is an even number, which H.264 requires.

Convert a whole folder:

for f in *.avi; do
 ffmpeg -i "$f" -c:v libx264 -crf 23 -c:a aac "${f%.avi}.mp4"
done

Quote the variable, or filenames with spaces break the loop.

Changing frame rate and dropping streams

To change frame rate, set it explicitly: converting 60fps to 30fps roughly halves the encoding work:

ffmpeg -i input.mp4 -r 30 -c:v libx264 -crf 23 -c:a copy output.mp4

To remove audio entirely, which is right for silent background video:

ffmpeg -i input.mp4 -an -c:v libx264 -crf 23 output.mp4

When a file has several audio tracks and you want a specific one, list the streams with ffprobe and map by index:

ffmpeg -i input.mkv -map 0:v:0 -map 0:a:1 -c copy output.mp4

Without -map, FFmpeg picks one stream of each type by its own rules, which is frequently not the one you wanted.

What goes wrong

Could not write header. The container cannot hold that codec. Re-encode, or use MKV.

Height not divisible by 2. Use -2 in the scale filter.

The output is enormous. No CRF was set and a default bitrate was used. Add -crf.

The WebM ignored the CRF. -b:v 0 is missing.

Video plays but there is no sound. The audio codec was not converted and the container silently dropped it. Set -c:a aac explicitly.

Browser waits before playing. -movflags +faststart is missing.

Troubleshooting common FFmpeg errors deals with the wider set, and FFmpeg performance optimization explains making these run faster.

For a short looping clip in place of a video file, How to Create Animated GIFs with FFmpeg explains why the obvious command produces a poor result.

Before running any conversion, it is worth asking whether one is needed: if the codecs inside the file already suit the target, the container can be swapped in seconds without touching the video. See How to Remux and Stream Copy Without Re-encoding.

The setting that decides how the result looks and how large it is deserves more than a copied number. There is more on the three approaches and which suits a website in CRF, Two-Pass and Bitrate: What to Use When.

If the colours shift during conversion, the pixels are usually fine and their description is not. Why Converted Video Looks Washed Out or Too Dark explains diagnosing it.