Ben Santora
ffmpeg
Conventions used below:
in.mp4 = input file
out.mp4 = output file
- Times use
HH:MM:SS or seconds (e.g. 5 or 00:00:05)
1. Trimming / Cutting
| Task | Command |
| Remove first N seconds (re-encode) | ffmpeg -ss 5 -i in.mp4 -c:v libx264 -c:a aac out.mp4 |
| Remove first N seconds (no re-encode, fast, keyframe-snapped) | ffmpeg -ss 5 -i in.mp4 -c copy out.mp4 |
| Cut a middle segment (start to end) | ffmpeg -ss 00:01:00 -to 00:02:30 -i in.mp4 -c copy out.mp4 |
| Cut last N seconds off the end | ffmpeg -sseof -10 -i in.mp4 -c copy out.mp4 |
| Cut by duration instead of end time | ffmpeg -ss 10 -i in.mp4 -t 20 -c copy out.mp4 (start at 10s, keep 20s) |
Note: -ss before -i = fast seek (jumps to nearest keyframe, may be a few frames off). -ss after -i = accurate but slower (decodes from start). -c copy = no re-encoding (instant, lossless, but cut points snap to keyframes). Drop -c copy and add -c:v libx264 -c:a aac for frame-accurate cuts at the cost of re-encoding time.
2. Format / Container Conversion
| Task | Command |
| Convert container only (no re-encode) | ffmpeg -i in.mkv -c copy out.mp4 |
| Convert with re-encode (e.g. old codec to H.264) | ffmpeg -i in.avi -c:v libx264 -c:a aac out.mp4 |
| Convert to GIF | ffmpeg -i in.mp4 -vf "fps=15,scale=480:-1" out.gif |
| Extract audio only | ffmpeg -i in.mp4 -vn -c:a copy out.aac |
| Extract audio, convert to mp3 | ffmpeg -i in.mp4 -vn -c:a libmp3lame -q:a 2 out.mp3 |
| Strip audio, keep video only | ffmpeg -i in.mp4 -an -c:v copy out.mp4 |
3. Resizing / Scaling
| Task | Command |
| Scale to specific width, keep aspect | ffmpeg -i in.mp4 -vf scale=1280:-1 out.mp4 |
| Scale to specific height, keep aspect | ffmpeg -i in.mp4 -vf scale=-1:720 out.mp4 |
| Force exact dimensions (may distort) | ffmpeg -i in.mp4 -vf scale=1280:720 out.mp4 |
| Scale down only if larger (never upscale) | ffmpeg -i in.mp4 -vf "scale='min(1280,iw)':-1" out.mp4 |
4. Joining / Concatenating
| Task | Command |
| Concat identical-codec files (fast, no re-encode) | See concat demuxer below |
| Concat with re-encode (mixed formats) | Use concat filter, not demuxer |
Concat demuxer (files must share codec/resolution):
# list.txt:
file 'clip1.mp4'
file 'clip2.mp4'
ffmpeg -f concat -safe 0 -i list.txt -c copy out.mp4
Concat filter (re-encodes, handles mismatched sources):
ffmpeg -i clip1.mp4 -i clip2.mp4 -filter_complex \
"[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[v][a]" \
-map "[v]" -map "[a]" out.mp4
5. Images ↔ Video
| Task | Command |
| Images to video (fixed duration each) | ffmpeg -framerate 1/5 -i %03d.png -c:v libx264 -pix_fmt yuv420p out.mp4 |
| Images to video, custom per-image duration | Use concat demuxer with duration lines (see your quote-slideshow project) |
| Video to image sequence | ffmpeg -i in.mp4 frame_%04d.png |
| Extract a single frame at a timestamp | ffmpeg -ss 00:00:10 -i in.mp4 -frames:v 1 frame.png |
| Add audio to a silent video | ffmpeg -i video.mp4 -i audio.mp3 -c:v copy -c:a aac -shortest out.mp4 |
6. Audio Adjustments
| Task | Command |
| Change volume | ffmpeg -i in.mp4 -filter:a "volume=1.5" out.mp4 (1.5 = 150%) |
| Normalize audio loudness | ffmpeg -i in.mp4 -filter:a loudnorm out.mp4 |
| Fade audio in/out | ffmpeg -i in.mp4 -af "afade=t=in:st=0:d=3,afade=t=out:st=57:d=3" out.mp4 |
| Merge/replace audio track | ffmpeg -i video.mp4 -i newaudio.mp3 -map 0:v -map 1:a -c:v copy -shortest out.mp4 |
7. Video Adjustments
| Task | Command |
| Change frame rate | ffmpeg -i in.mp4 -r 30 out.mp4 |
| Rotate 90° clockwise | ffmpeg -i in.mp4 -vf "transpose=1" out.mp4 |
| Crop | ffmpeg -i in.mp4 -vf "crop=w:h:x:y" out.mp4 |
| Add text overlay (burned-in) | ffmpeg -i in.mp4 -vf "drawtext=text='Hello':x=10:y=10:fontsize=24:fontcolor=white" out.mp4 |
| Fade video in/out | ffmpeg -i in.mp4 -vf "fade=t=in:st=0:d=2,fade=t=out:st=58:d=2" out.mp4 |
| Speed up / slow down | ffmpeg -i in.mp4 -vf "setpts=0.5*PTS" -af "atempo=2.0" out.mp4 (2x speed) |
8. Inspecting Files
| Task | Command |
| Show all stream/codec info | ffprobe -hide_banner in.mp4 |
| Show duration only | ffprobe -v error -show_entries format=duration -of csv=p=0 in.mp4 |
| Show codec of video stream | ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of csv=p=0 in.mp4 |
9. Quality / Compression
| Task | Command |
| Compress with target quality (CRF, lower=better, 18-28 typical) | ffmpeg -i in.mp4 -c:v libx264 -crf 23 -c:a aac out.mp4 |
| Compress with target file size (2-pass) | Use -b:v with 2-pass encoding — ask if you need this, it's a multi-command process |
| Fast encode, larger file | ffmpeg -i in.mp4 -c:v libx264 -preset ultrafast out.mp4 |
| Slow encode, smaller file, same quality | ffmpeg -i in.mp4 -c:v libx264 -preset slow -crf 23 out.mp4 |
Reading ffmpeg's flag order rule
Like ImageMagick, ffmpeg is order-sensitive but the rule is different: flags before -i apply to input handling (seeking, format forcing); flags after -i apply to output encoding/filtering. -ss is the classic example — its position changes both behavior and performance.
GUI Frontends (if you ever want to point-and-click instead)
These are wrappers around ffmpeg — the engine doesn't change, just the interface:
- LosslessCut — AppImage, great for the exact trim task you started with (frame-accurate cut/trim without re-encoding, visual timeline)
- Shutter Encoder — batch conversion, presets, GUI over most of ffmpeg's common operations
- Avidemux — cutting/filtering with a visual timeline, older but stable
- Curlew — simple GTK-based ffmpeg GUI, fits well with a lightweight WM setup
- HandBrake — encoding/compression focused, not trimming
None of these teach you ffmpeg commands directly, but LosslessCut is worth having installed alongside your terminal workflow — it's genuinely the fastest way to do the trim-first-5-seconds job when you don't want to think about keyframes.