FFmpeg is a cross‑platform collection of libraries and a powerful
command‑line tool (ffmpeg
) for recording, converting, streaming and analysing
audio & video content. It supports hundreds of formats and codecs, offers a rich
filtergraph engine, and is script‑friendly — making it a cornerstone of multimedia
automation in Bash workflows.
Latest stable release: 7.0.2 “Dijkstra” (Aug 03 2024)
brew install ffmpeg --with-sdl2 --with-fdk-aac --with-opencore-amr
sudo apt update
sudo apt install ffmpeg
choco install ffmpeg
# or download the full/static build from gyan.dev
Check your installation quickly with ffmpeg -version
.
The CLI follows a consistent pattern:
ffmpeg [global_options] -i input
[input_options] …
[output_options] …
output
ffmpeg -i input.mov -c:v libx264 -preset slow -crf 22 -c:a aac -b:a 192k output.mp4
ffmpeg -i video.mp4 -vn -c:a libmp3lame -qscale:a 2 podcast.mp3
ffmpeg -i source.mkv -c copy target.mp4
for f in *.wav; do
ffmpeg -i "$f" -c:a flac "${f%.wav}.flac"
done
Option -crf (0‑51) trades quality for size in constant‑rate‑factor encodes.
ffmpeg -i in.mp4 -vf "scale=1280:720:flags=lanczos,pad=1280:720:0:0:black" \
-c:v libx264 -crf 20 out.mp4
ffmpeg -i bg.mp4 -i logo.png -filter_complex \
"[0:v][1:v] overlay=10:10,format=yuv420p" \
-c:v libx264 -crf 23 -c:a copy branded.mp4
ffmpeg -i song.mp3 -af "atrim=0:30,afade=t=out:st=25:d=5" \
short_fade.mp3
Complex filtergraphs can be scripted via files: -filter_complex_script
.
key=value
sets title, artist, etc.-i in.mp4 -i chapters.txt -map 0 -map_metadata 1 -c copy out.mp4
ffmpeg -f avfoundation -framerate 60 -i "1:0" -c:v hevc_videotoolbox -b:v 8M \
-c:a aac -pix_fmt yuv420p screen.mov
ffmpeg -f x11grab -video_size 1366x768 -i :0.0 -t 5 -vf \
"fps=10,scale=600:-1:flags=lanczos" capture.gif
ffmpeg -re -i local.mp4 -c:v copy -c:a aac -b:a 128k \
-f flv rtmp://a.rtmp.youtube.com/live2/YOUR_STREAM_KEY
# Sender
ffmpeg -re -i talk.mp4 -c copy -f mpegts "srt://listener_ip:2025?pkt_size=1316"
# Receiver
ffmpeg -i "srt://0.0.0.0:2025?mode=listener" -c copy live.ts
ffmpeg -i master.mp4 -filter_complex \
"[0:v]split=3[v1][v2][v3];
[v1]scale=640:-2:flags=lanczos[v1out];
[v2]scale=1280:-2:flags=lanczos[v2out];
[v3]scale=1920:-2:flags=lanczos[v3out]" \
-map "[v1out]" -c:v:0 libx264 -b:v:0 800k -maxrate:v:0 856k -bufsize:v:0 1200k \
-map "[v2out]" -c:v:1 libx264 -b:v:1 1400k -maxrate:v:1 1498k -bufsize:v:1 2100k \
-map "[v3out]" -c:v:2 libx264 -b:v:2 3000k -maxrate:v:2 3210k -bufsize:v:2 4500k \
-map 0:a -c:a aac -b:a 128k -f hls -hls_time 6 -hls_playlist_type vod \
-hls_segment_filename "v%v/file%03d.ts" -master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0 v:1,a:0 v:2,a:0" v%v/prog.m3u8
Platform | Encoder flag | Note |
---|---|---|
Intel Quick Sync | -c:v h264_qsv |
Use -look_ahead 1 for VBR mode |
NVIDIA NVENC | -c:v h264_nvenc |
-preset p7 = best quality |
Apple Silicon VTB | -c:v hevc_videotoolbox |
Keep -pix_fmt yuv420p for compatibility |
Combine -hwaccel for decoding with a HW encoder for full pipe offload.
Precise framing using the crop
filter
ffmpeg -i input.mp4 -vf "crop=<width>:<height>:<x>:<y>" \
-c:v libx264 -crf 22 -c:a copy output.mp4
width and height define the new frame size,
while x and y set the top-left offset.
Arithmetic expressions are accepted (e.g. in_w/2
, in_h-100
).
ffmpeg -i clip.mp4 -vf "crop=min(in_w\,in_h):min(in_w\,in_h)" \
-c:v libx264 -crf 20 -c:a copy square.mp4
ffmpeg -i landscape.mp4 -vf "crop=ih*9/16:ih:(iw-ih*9/16)/2:0" \
-c:v libx264 -crf 21 -c:a copy vertical.mp4
for vid in *.mp4; do
ffmpeg -i "$vid" -vf "crop=1280:720:0:0" -c:v libx265 -crf 28 \
-c:a copy "cropped_$vid"
done
A recursive script for cropping multiple JPEG files
find . -type f -iname "*.jpg" -exec sh -c '
for img; do
out="${img%.*}_cropped.jpg"
ffmpeg -y -i "$img" -vf "crop=300:300:0:0" "$out"
done
' sh {} +
find . -type f -iname "*.jpg"
: Recursively finds all .jpg
files, case-insensitive, from the current directory downward.-exec sh -c '…' sh {} +
: Executes a subshell that loops over the found files as arguments.for img; do …; done
: Iterates over all matched JPEG paths.out="${img%.*}_cropped.jpg"
: Constructs the output filename by removing the extension and appending _cropped.jpg
.ffmpeg -y -i "$img" -vf "crop=300:300:0:0" "$out"
: Applies a 300x300 top-left crop and writes to the new file.
You can adjust the crop=width:height:x:y
values to suit your framing.
This script preserves the original image and outputs side-by-side copies.
parallel
parallel -j4 'ffmpeg -i {} -c:v libx265 -crf 28 {.}.hevc.mp4' ::: *.mp4
# capture one JPEG every minute
*/1 * * * * ffmpeg -y -f v4l2 -i /dev/video0 -vframes 1 ~/timelapse/%Y%m%d_%H%M.jpg
# assemble at midnight:
0 0 * * * ffmpeg -r 30 -pattern_type glob -i "~/timelapse/$(date +\%Y\%m\%d)_*.jpg" \
-c:v libx264 -pix_fmt yuv420p "timelapse_$(date +\%Y\%m\%d).mp4"
check_and_fix(){
local f="$1"
if ffprobe -v error "$f" &>/dev/null; then
echo "✓ $f looks good"
else
echo "✗ $f broken ⇒ re‑encode"
ffmpeg -i "$f" -c:v libx264 -c:a aac -movflags +faststart "fixed_$f"
fi
}
for vid in *.mp4; do check_and_fix "$vid"; done
ffmpeg -y -i in -c:v libvpx-vp9 -b:v 0 -pass 1 -an -f null /dev/null
,
second run with -pass 2
and audio matching flags.