Table of Contents

ffmpeg

in=... ; out=... # or eg. out="${in/%.mov/-small.mov}"
ffmpeg -i "$in"

webm to mp4

Example:

ffmpeg -i "$in" -tune film -crf 20 -preset slow -b:a 192k -movflags +faststart "${in%%.webm}.mp4"

h264

-c:v libx264 -pix_fmt yuv420p -tune film -crf 20 -g 50
-preset slow # (veryfast fast medium slow slower veryslow)

Limit bitrate

Can be used with -crf

-maxrate 6M -bufsize 6M

Or set an average bitrate, without using -crf (see Limiting the output bitrate)

-b:v 4M -maxrate 6M -bufsize 10M

prores

-c:v prores_ks -profile:v 1 -vendor apl0
-c:a pcm_s16le

resize

See https://trac.ffmpeg.org/wiki/Scaling for details.

If using -s WxH, you need to calculate the right sizes to preserve the aspect ratio. It's easier and generally better to use -vf scale=..., which offers various shortcuts, and allows selecting the resizing algorithm.

Examples:

-vf "scale=hd1080"
-vf "scale=hd1080:flags=lanczos" # same, but with lanczos algorithm instead of default bilinear
-vf scale=640:-2      # width 640px, height as needed but divisible by 2
-vf "scale=iw/2:ih/2" # half width and height of original
-vf "scale=iw/2:-2"   # same (half width and height), but height divisible by 2
etc.

Deinterlace

See https://ffmpeg.org/ffmpeg-filters.html#yadif :

yadif=mode:parity:deint
defaults: 0(send_frame=one frame for each frame) : -1(auto=auto detection of field order) : 0(all=all frames)
-vf "yadif=0:-1:0"

Extract part

start=00:05:12.000    # or just seconds: start=312
duration=00:01:05.000 # or just seconds: duration=65

ffmpeg -i "$in" -ss $start -t $duration -c:v copy ...

If -ss $start is before the -i input, it may be faster in some cases. But with codecs like h264, the resulting output may also miss the needed keyframes at the start, and the beginning of the video may be corrupted.

Extract single frames

All frames as .png:

in="file.mov"; out="${in%.*}.%06d.png"
ffmpeg -i "$in" -f image2 "$out"

Here a frame at 5 seconds, and a frame at 300 seconds from the start

in="file.mov"
for start in 5 300; do ffmpeg -ss $start -i "$in" -r 1 -vframes 1 -f image2 "${in/%.mov/}-at-$start-sec.png"; done

Output one image for every I-frame:

ffmpeg -i input.flv -vf "select='eq(pict_type,PICT_TYPE_I)'" -vsync vfr thumb%04d.png

Find all .mov files under some directory, and extract a single Jpeg:

pos=2 # get frame at $pos seconds from start of file
search_in=/mnt/x/y
ext="mov"
 
find "$search_in" -type f -name "*.$ext" -not -name ".*" \
| while read f; do
    n=$(basename "$f")
    [ -f "$n" ] && n="$n-$RANDOM"
    ffmpeg -nostdin -hide_banner -loglevel error -ss $pos -i "$f" \
      -r 1 -vframes 1 -f image2 "$n.jpg" \
    && echo "OK $n.jpg"
done

Output all frames for a given duration (in seconds):

in="file.mkv"
start=00:09:38.000
duration=20
out="${in%%.mkv}-$start.%04d.png"
ffmpeg -ss $start -i "$in" -t $duration -f image2 "$out"

See also ffmpeg's Create a thumbnail image every X seconds of the video.

Images to video

With numbered frames, the numbering must start at 0: "frame0000.png", "frame0001.png", "frame0002.png", ...

Animated webp

Frames to animated .webp :

in="test-tiff%08d.tif"; out="tiff-2.webp"
ffmpeg -r 25 -i "$in" -c:v libwebp_anim -lossless 1 -loop 0 -preset default -an -vsync 0 -s 480x270 "$out"

Animated .gif to .webp (copied from Convert GIF to WebM with ffmpeg)

ffmpeg -i my-animation.gif -c vp9 -b:v 0 -crf 41 my-animation.webm

Jpegs to mp4

1 .jpg image = 1 frame :

in="image%03d.jpg" # image000.jpg to image999.jpg
out="image-to-video.mp4"
ffmpeg -framerate 25 -i "$in" -c:v libx264 -crf 18 -pix_fmt yuv420p "$out"

To start on a specific numbered frame, use -start_number. Example staring at "image150.jpg":

in="image%03d.jpg" # image000.jpg to image999.jpg
out="image-to-video.mp4"
ffmpeg -start_number 150 framerate 25 -i "$in" -c:v libx264 -crf 18 -pix_fmt yuv420p "$out"

Slideshow with 1 .jpg = 1 second :

fps=25
out="slideshow-glob.mp4"
ffmpeg -framerate 1 -pattern_type glob -i '*.jpeg' -c:v libx264 -crf 20 -g $fps -pix_fmt yuv420p -r $fps "$out"

If the size of the frames is uneven (eg. 512x459), it needs to be set to an even size, or there will be this error:

height not divisible by 2 (512x459)
Error initializing output stream 0:0 -- Error while opening encoder for output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or height

In this case, specify the size. For example: -s 512x460

Audio

Audio AAC

-c:a aac -b:a 192k

AC3 5.1 to Stereo

Using defaults:

ffmpeg -i input.ac3 -ac 2 output.ac3

Or manual:

... -af "pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR" ...

See also: https://forum.videohelp.com/threads/404278-Ac3-5-1-audio-track-convert-to-stereo

Remap 2 mono audio to stereo

I think that since -map is used, -map:v must be added too or the output is without video.

-filter_complex "[0:a:0][0:a:1]amerge,channelmap=channel_layout=stereo[st]" -map 0:v -map "[st]"

FLAC

FLAC to MP3

dir="/path/to/output_dir"; mkdir -p "$dir"
for f in *.flac; do
  f2=$(basename "${f%.flac}.mp3"); \
  out="$dir/$f2";                  \
  ffmpeg -i "$f" -c:a libmp3lame -q:a 0 -c:v copy "$out"
done

-q:a 0 with libmp3lame makes it use best quality VBR. See https://trac.ffmpeg.org/wiki/Encode/MP3

-c:v copy makes it copy album art if there is any (instead of converting it to .png)

The metadata from FLAC is preserved by default, and written to a ID3v2.4 header. To write an ID3v2.3 header instead, add the -id3v2_version 3 option.

WAV to FLAC

Can be done with flac instead of ffmpeg:

for f in *.wav; do
  flac "$f" --keep-foreign-metadata -V -o "${f%%wav}.flac"
done

Detect silence

volumedetect

This was slow? 330 mn. for a 167 mn. / 600GB file with 16 audio channels. Not slow on 30GB 16channels quicktime. But doesn't separate channels!

time ffmpeg -i "$in" -map 0:a -af "volumedetect" -f null - 2>&1 | tee "$in-volumedetect.txt"

astats

ffmpeg -i "$in" -vn -sn -dn -map 0:a -af "astats=measure_overall=none" -f null - 2>&1

See also https://ffmpeg.org/ffmpeg-filters.html#toc-ebur128-1

EBU stats

For a file with 12 mono audio tracks:

for t in {0..11}; do ffmpeg -nostats -i "$in" -map 0:a:$t -filter_complex ebur128 -f null - > $(printf "ebur-$in-a%02d.txt" $t) 2>&1 ; done

faststart

Move "moov" atom to start of file (https://ffmpeg.org/ffmpeg-formats.html#Options-8). (Implies file copy after encoding)

-movflags faststart

Change framerate

-r 25 or -vf "fps=fps=25"

Example to check what it does:

ffmpeg -f lavfi -i testsrc=duration=10:size=854x480:rate=60 \
-vf "drawtext=text=%{n}:fontsize=72:r=60:x=(w-tw)/2: y=h-(2*lh):fontcolor=white:box=1:boxcolor=0x00000099" test.mp4

Add pseudo-timecode

Example with Fuji .MOV files, to add the "Create Date" time as a timecode:

find . -type f | perl -ple 's/^.*\.(\w+)/$1/' | sort -u
dest=/mnt/share/temp
mkdir -p "$dest"
find . -type f -name "*.MOV" \
| while read f; do \
    b=$(basename "$f");
    t=$(exiftool -CreateDate "$f" | awk '{print $NF}');
    tc="$t:00";
    echo "====== $f $tc";
    ffmpeg -loglevel warning -i "$f" -codec copy -map_metadata 0 -movflags use_metadata_tags -timecode "$tc" -f mov "$dest/$b" </dev/null || break;
    touch -r "$f" "$dest/$b";
    echo;
  done

If the exiftool command gives the error "End of processing at large atom (LargeFileSupport not enabled)", add the -api largefilesupport=1 option so the exiftool line becomes:

t=$(exiftool -api largefilesupport=1 -CreateDate "$f" | awk '{print $NF}');

Diff

From https://reto.ch/training/2017/201706/FFmpeg.html

f1=... # file 1
f2=... # file 2
t="-t 60" # limit time
out=delta.mp4
ffmpeg -i "$f1" -i "$f2" $t \
  -filter_complex "[1]format=yuva444p,lut=c3=128,negate[1_with_alpha_channel];[0][1_with_alpha_channel]overlay" "$out"

How to add resize? How to add different start times for input files?

Or see https://stackoverflow.com/questions/25774996/how-to-compare-show-the-difference-between-2-videos-in-ffmpeg :

ffmpeg -i "$f1" -i "$f2" $t \
  -filter_complex "blend=all_mode=difference" \
  -c:v libx264 -crf 18 -c:a none "$out"

And FFmpeg Filtering Guide

Or http://dericed.com/2012/display-video-difference-with-ffmpegs-overlay-filter/

ffmpeg "$f1" -i "$f2" $t \
  -filter_complex '[1:v]format=yuva444p,lut=c3=128,negate[video2withAlpha],[0:v][video2withAlpha]overlay[out]' \
  -map [out] "$out"

Stream MD5

$  ffmpeg -i "$filename" -map 0:a -codec copy -f md5 "$filename.md5"
$  ffmpeg -i "$filename" -map 0:a -codec copy -hide_banner -loglevel warning -f md5 -
$  ffmpeg -i "$filename" -map 0 -c copy -f streamhash -hash md5 -
...
0,v,MD5=50224fec84bc6dfde90d742bcf1d2e01
1,a,MD5=1d2a32ed72798d66e0110bd02df2be65
$  ffmpeg -i "$f" -map 0 -c copy -f streamhash -hash md5 "$f.stream.md5"

See also:

Other options

-nostdin

Verbosity:

-hide_banner -loglevel warning -stats
# or
-hide_banner -loglevel info -stats

Edit metadata

See How do I name an audio track with ffmpeg:

ffmpeg -i input.mp4 -map 0 -c copy -metadata:s:a:0 title="One" -metadata:s:a:1 title="Two" -metadata:s:a:0 language=eng -metadata:s:a:1 language=spa output.mp4

Examples

Audio + still image to video

ffmpeg -loop 1 -r 1 -i "$in_img" -i "$in_a" \
 -c:v libx264 -pix_fmt yuv420p -crf 22 -preset veryfast -tune stillimage \
 -c:a copy -shortest "$out"

Resize, remap audio, change fps

Source is 4K (3840x2160), 50fps progressive, 8 mono tracks.

Wanted output : HD 1920x1080, 25 fps, 1 stereo audio from source tracks 1 + 2.

ffmpeg -i "$in" -vf "scale=hd1080" -r 25 \
-c:v libx264 -pix_fmt yuv420p -tune film -crf 20 -g 50 \
-filter_complex "[0:a:0][0:a:1]amerge,channelmap=channel_layout=stereo[st]" \
-map 0:v -map "[st]" \
-c:a aac -b:a 192k \
"$out"

Sample output:

ffmpeg -i "$in" -t 300 -vf "scale=hd1080" -r 25 -c:v libx264 -pix_fmt yuv420p -tune film -preset fast -crf 20 -g 50 -filter_complex "[0:a:0][0:a:1]amerge,channelmap=channel_layout=stereo[st]" -map 0:v -map "[st]" -c:a aac -b:a 192k "$out"
 
ffmpeg version 4.2.1-static https://johnvansickle.com/ffmpeg/  Copyright (c) 2000-2019 the FFmpeg developers
...
[mxf @ 0x6225a40] decoding for stream 0 failed
Guessed Channel Layout for Input Stream #0.1 : mono
Guessed Channel Layout for Input Stream #0.2 : mono
...
Guessed Channel Layout for Input Stream #0.16 : mono
Input #0, mxf, from 'fdv20190808pgm.mxf':
  Metadata:
    operational_pattern_ul: 060e2b34.04010101.0d010201.01010900
    uid             : bd7136e0-ba0f-11e9-8fed-ac1f6b48c8ac
    generation_uid  : bd7136e0-ba0f-11e9-8fee-ac1f6b48c8ac
    company_name    : oc
    product_name    : OCtk
    product_version : 2.10
    product_uid     : 3a4fe380-0d01-11e4-869f-3cd92b5c1dfc
    modification_date: 2019-08-08T21:07:17.708000Z
    material_package_umid: 0x060A2B340101010501010D2013000000BD6EECF0BA0F11E98FB2AC1F6B48C8AC
    timecode        : 21:07:16:08
  Duration: 02:47:16.24, start: 0.000000, bitrate: 524301 kb/s
    Stream #0:0: Video: h264 (High 4:2:2 Intra), yuv422p10le(progressive), 3840x2160 [SAR 1:1 DAR 16:9], 50 fps, 50 tbr, 50 tbn, 100 tbc
    Metadata:
      file_package_umid: 0x060A2B340101010501010D2013000000BD6AF550BA0F11E98F81AC1F6B48C8AC
    Stream #0:1: Audio: pcm_s24le, 48000 Hz, mono, s32 (24 bit), 1152 kb/s
    Metadata:
      file_package_umid: 0x060A2B340101010501010D2013000000BD6AF550BA0F11E98F81AC1F6B48C8AC
    Stream #0:2: Audio: pcm_s24le, 48000 Hz, mono, s32 (24 bit), 1152 kb/s
    ...
    Metadata:
      file_package_umid: 0x060A2B340101010501010D2013000000BD6AF550BA0F11E98F81AC1F6B48C8AC
    Stream #0:16: Audio: pcm_s24le, 48000 Hz, mono, s32 (24 bit), 1152 kb/s
    Metadata:
      file_package_umid: 0x060A2B340101010501010D2013000000BD6AF550BA0F11E98F81AC1F6B48C8AC
    Stream #0:17: Data: none
    Metadata:
      file_package_umid: 0x060A2B340101010501010D2013000000BD6AF550BA0F11E98F81AC1F6B48C8AC
      data_type       : vbi_vanc_smpte_436M
Stream mapping:
  Stream #0:1 (pcm_s24le) -> amerge:in0 (graph 0)
  Stream #0:2 (pcm_s24le) -> amerge:in1 (graph 0)
  Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))
  channelmap (graph 0) -> Stream #0:1 (aac)
...