I grew up around audio and apprenticed in a studio. FFmpeg was how I got to play with audio on my own: convert, cut, rip a track, resample, batch a whole folder of takes. It’s bailed me out more times than I can count and is still one of the most useful tools I’ve learned - so this is my recipe box, the commands I keep coming back to.

FFmpeg can convert, cut, resize, filter, mux, and record essentially any audio/video. It looks cryptic because it’s one tool for a thousand jobs - but almost every command is the same shape. Learn the shape and the recipes below cover the rest.

The mental model

ffmpeg [global opts] -i INPUT [per-output opts] OUTPUT
  • Options before -i apply to reading the input (e.g. seek).
  • Options after the input apply to writing the output (codecs, filters).
  • -c copy = stream copy: no re-encode → instant and lossless (only remuxes). Drop it and ffmpeg re-encodes (slow, lossy, but lets you change quality/size).
  • -y overwrites the output without asking; -c:v / -c:a pick video/audio codec.

Two things that confuse everyone:

  • -crf is backwards: lower = higher quality + bigger file (x264 sane range 18–28, 23 default).
  • -ss placement: before -i = fast keyframe seek; after -i = slow but frame-accurate.

Inspect anything first:

ffprobe -hide_banner input.mp4          # codecs, resolution, duration, bitrate

Convert & remux

# Re-encode to MP4 (H.264 + AAC) - universal, web-safe
ffmpeg -i in.mov -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k out.mp4
 
# Change container only, NO re-encode (instant, lossless) - e.g. MKV → MP4
ffmpeg -i in.mkv -c copy out.mp4

Cut & trim

# Fast, lossless cut (snaps to nearest keyframe)
ffmpeg -ss 00:01:30 -i in.mp4 -t 30 -c copy clip.mp4        # 30s starting at 1:30
 
# Frame-accurate cut (re-encodes; -ss AFTER -i)
ffmpeg -i in.mp4 -ss 00:01:30 -to 00:02:00 -c:v libx264 -crf 20 clip.mp4

Resize, framerate, rotate

ffmpeg -i in.mp4 -vf "scale=1280:-2" out.mp4     # width 1280, height auto (-2 = keep AR, even)
ffmpeg -i in.mp4 -r 30 out.mp4                    # force 30 fps
ffmpeg -i in.mp4 -vf "transpose=1" out.mp4        # rotate 90° CW (2 = CCW); hflip / vflip to mirror

Audio: extract, strip, volume, normalize

ffmpeg -i in.mp4 -vn -c:a copy audio.m4a          # rip audio, no re-encode
ffmpeg -i in.mp4 -vn -q:a 2 audio.mp3             # to MP3 (q:a 0=best … 9=worst)
ffmpeg -i in.mp4 -an -c:v copy muted.mp4          # remove audio
ffmpeg -i in.mp3 -af "volume=1.5" louder.mp3      # +50% volume
ffmpeg -i in.mp3 -af loudnorm out.mp3             # EBU R128 loudness normalize

Compress (shrink a big file)

ffmpeg -i big.mp4 -c:v libx264 -crf 28 -preset slow -c:a aac -b:a 96k small.mp4

Push crf up (28–32) and use a slower -preset for smaller files; slower preset = better compression at the same quality, just more CPU time.

Frames, thumbnails & GIFs

ffmpeg -ss 00:00:05 -i in.mp4 -frames:v 1 thumb.jpg          # one frame at 5s
ffmpeg -i in.mp4 -vf "fps=1" frame_%04d.png                  # a frame every second
 
# High-quality GIF (two-pass palette - the trick to non-ugly gifs)
ffmpeg -i in.mp4 -vf "fps=15,scale=480:-1:flags=lanczos,palettegen" palette.png
ffmpeg -i in.mp4 -i palette.png \
  -filter_complex "fps=15,scale=480:-1:flags=lanczos[x];[x][1:v]paletteuse" out.gif

Join & merge

# Concatenate clips with the SAME codec (lossless) - via a list file
printf "file 'a.mp4'\nfile 'b.mp4'\n" > list.txt
ffmpeg -f concat -safe 0 -i list.txt -c copy joined.mp4
 
# Mux separate video + audio into one file
ffmpeg -i video.mp4 -i audio.aac -c copy -map 0:v:0 -map 1:a:0 -shortest out.mp4

Overlays & subtitles

# Watermark a logo bottom-right, 10px margin
ffmpeg -i in.mp4 -i logo.png -filter_complex "overlay=W-w-10:H-h-10" out.mp4
 
# Burn subtitles INTO the video (hard subs)
ffmpeg -i in.mp4 -vf "subtitles=subs.srt" out.mp4
 
# Add subtitles as a soft, toggleable track (no re-encode)
ffmpeg -i in.mp4 -i subs.srt -c copy -c:s mov_text out.mp4

Speed up / slow down

# 2× faster (video + audio kept in sync)
ffmpeg -i in.mp4 -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" \
  -map "[v]" -map "[a]" fast.mp4

setpts multiplier is the inverse of speed (0.5 = 2×); atempo is the speed directly (valid 0.5–2.0, chain two for more, e.g. atempo=2.0,atempo=2.0 = 4×).

Notes

  • -c copy is your friend - if you’re only cutting or changing the container, never re-encode. It’s instant and preserves quality perfectly.
  • -filter_complex vs -vf: -vf is a simple single-stream video filter; -filter_complex is for multiple inputs/outputs (overlays, merging, a/v filters together).
  • Hardware encoding (-c:v h264_nvenc on Nvidia, h264_videotoolbox on macOS) is far faster than libx264 but slightly lower quality per bit - great for bulk jobs.
  • Add -hide_banner to quiet the version spam; -v error to only see real errors.