Skip to content

Your first FFmpeg encode

current

If your FFmpeg build includes libx264 and libopus (most distribution packages and the common Windows and macOS builds do), this is a good general-purpose encode:

Terminal window
ffmpeg -i input.mkv -c:v libx264 -preset slow -crf 20 -c:a libopus -b:a 128k output.mkv

It produces H.264 video, which plays on practically everything, with Opus audio in Matroska. If FFmpeg stops with Invalid channel layout 5.1(side), see copy or re-encode the audio; using -c:a copy instead is the quick fix. The rest of this page covers what the two numbers mean, how to test settings on a short clip, how to choose streams when the source has several, and when to copy audio instead of re-encoding it.

Terminal window
ffprobe -hide_banner input.mkv

-hide_banner skips FFmpeg’s build-configuration header. The stream list looks like this (trimmed):

Stream #0:0: Video: h264 (High), yuv420p(tv, bt709, progressive), 1920x1080, 23.98 fps
Stream #0:1(eng): Audio: ac3, 48000 Hz, 5.1(side), fltp, 640 kb/s (default)
Stream #0:2(jpn): Audio: flac, 48000 Hz, stereo, s16
Stream #0:3(eng): Subtitle: subrip (srt)

Note the audio and subtitle languages and the video pixel format. This recipe is for ordinary 8-bit SDR video. If you see yuv420p10le together with bt2020 and smpte2084 or arib-std-b67, the source is HDR: use HEVC or AV1 and follow preserve color and HDR instead.

-crf sets the quality target. The encoder spends whatever bitrate each scene needs to reach it, so file size depends on the content. Lower is better quality and bigger. For x264 the default is 23, and about ±6 halves or doubles the bitrate. Values around 17–18 usually look the same as the source; most encodes land between 18 and 24. Pick the highest value that still looks good to you. CRF scales differ between encoders: x265, SVT-AV1 and others use different numbers for similar quality (see quality controls).

-preset trades encoding time for compression. The x264 presets run from ultrafast to placebo; the default is medium. Slower presets find better ways to code the same picture, so at the same CRF you get a smaller file or more retained detail. slow is a common choice; veryslow is worth it for encodes you keep. placebo costs a lot of time for almost nothing.

-tune is optional. film, animation and grain adjust x264’s deblocking, psychovisual and adaptive-quantization settings for that kind of content; use grain for grainy film sources you want to keep grainy.

Encode a minute from a demanding part of the source before committing to a full run:

Terminal window
ffmpeg -ss 10:00 -i input.mkv -t 60 -c:v libx264 -preset slow -crf 20 -c:a libopus -b:a 128k test-crf20.mkv

-ss before -i seeks quickly in the input, and -t 60 stops after 60 seconds. Because the video is re-encoded, the cut is frame-accurate. Make two or three versions at different CRF values, then watch them at normal speed and pause on dark scenes, faces, grain and fast motion. Your eyes are the final judge; compare encoding quality covers metrics when you want numbers as well.

Without -map, FFmpeg keeps one video stream (the highest resolution), one audio stream (the one with the most channels) and the first subtitle stream the output format’s default subtitle encoder can handle. Within a file, a stream flagged default wins over a bigger one, so a default stereo track beats a non-default 5.1 track. Everything else is dropped without a warning. When writing Matroska, text subtitles are converted to ASS; MP4 output gets no subtitles at all.

As soon as you use -map, only the streams you list are written:

Terminal window
ffmpeg -i input.mkv -map 0:v:0 -map 0:a:1 -map '0:s?' \
-c:v libx264 -preset slow -crf 20 -c:a libopus -b:a 128k -c:s copy output.mkv

0:a:1 is the second audio stream of the first input (counting from zero within each type), so with the input above this keeps the Japanese track. 0:s? takes every subtitle stream; the ? means “if there are any”, and the quotes stop shells such as zsh from treating ? as a wildcard. -c:s copy keeps the subtitles in their original format. To keep every stream, use -map 0. The audio and subtitles guide covers languages, default/forced flags and fonts.

Use -c:a copy when the source audio already plays where you need it. It costs no quality and no time. Re-encoding pays off for lossless or very large tracks (FLAC, PCM, TrueHD, DTS-HD MA), where Opus saves most of the space. Re-encoding a lossy track such as AC-3 or AAC adds another generation of loss, so do it only for size or compatibility.

Opus at 128 kbit/s is close to transparent for stereo music; use roughly 256 kbit/s for 5.1. If the source layout is 5.1(side), libopus refuses it with Invalid channel layout 5.1(side). Convert it to plain 5.1:

Terminal window
ffmpeg -i input.mkv -map 0:v:0 -map 0:a:0 -c:v libx264 -preset slow -crf 20 \
-c:a libopus -b:a 256k -af aformat=channel_layouts=5.1 -mapping_family 1 output.mkv

aformat moves the side channels to the back positions; nothing is downmixed. -mapping_family 1 switches libopus to its surround encoder, which uses masking between channels to spend the bits better. -af applies to every audio stream you encode, so with several audio tracks use -filter:a:N instead (see the audio and subtitles guide).

For MP4 output aimed at older devices, use AAC (-c:a aac -b:a 192k) instead of Opus.

libx264 keeps the input’s bit depth and chroma format. A 10-bit or 4:2:2 source therefore becomes High 10 or High 4:2:2 H.264, which most hardware decoders and browsers cannot play. Add -pix_fmt yuv420p when you need 8-bit 4:2:0 output that plays everywhere.

For MP4 files that will be streamed over the web, add -movflags +faststart. It moves the index to the start of the file so playback can begin before the whole file has downloaded.

MessageCause and fix
Unknown encoder 'libx264'Your FFmpeg build lacks that library. Install a build that includes it.
Stream map '...' matches no streamsThat stream does not exist. Check the ffprobe list, or add ? if the stream is optional.
Invalid channel layout 5.1(side) for specified mapping familylibopus does not accept that layout. Add -af aformat=channel_layouts=5.1.
Automatic encoder selection failed or Could not find tag for codec subrip (MP4)MP4 cannot hold that subtitle format. Use -c:s mov_text for text subtitles, or leave them out.
Colors look washed out or too darkUsually an HDR source or a range/matrix mismatch. See preserve color and HDR.