Skip to content

Instantly share code, notes, and snippets.

@ohjho
Last active October 24, 2023 20:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ohjho/737c04037747a5eba9fa13429faea444 to your computer and use it in GitHub Desktop.
Save ohjho/737c04037747a5eba9fa13429faea444 to your computer and use it in GitHub Desktop.

Installation

MacOS

Get FFMPEG with all the options which is a little more complicated in brew because the Homebrew team removed all options from the core formula:

brew update
brew uninstall --force --ignore-dependencies ffmpeg
brew install chromaprint amiaopensource/amiaos/decklinksdk
brew tap homebrew-ffmpeg/ffmpeg
brew upgrade homebrew-ffmpeg/ffmpeg/ffmpeg $(brew options homebrew-ffmpeg/ffmpeg/ffmpeg | grep -vE '\s' | grep -- '--with-' | grep -vi chromaprint | tr '\n' ' ')

Ubuntu

sudo apt update
sudo apt install ffmpeg

To validate that it's installed properly: ffmpeg -version

Conversion without lose of Quality

ffmpeg -i file.mov -c copy out.mp4

you can remove audio by using the -an flag

Quality & Encoding

  • MJPEG (motion-jpeg): an example here and if you are making videos from JPEG images, try -c:v copy as recommended here
  • x264: the unofficial cheatsheet on stackoverflow
  • x265 (aka High Efficiency Video Coding): full tutorial here on OTTVerse

Conversion with Compression

ffmpeg -i input.mov -vcodec libx264 -crf 20 output.mp4

The CRF, which, in this case, is 20, can range from 18 to 24, where a higher number will compress the output to a smaller size.

The video codec used here is x264, to check what encoding input.mov is do:

ffprobe -v error -select_streams v:0 -show_entries stream=codec_name -of default=nokey=1:noprint_wrappers=1 input.mov

Reference from here

Slicing Video

or more commonly referred to as trimming

use the -ss option to specify a start timestamp, and the -t option to specify the encoding duration. The timestamps need to be in HH:MM:SS.xxx format or in seconds

Example for clipping 10 seconds, 30 seconds from the start:

ffmpeg -ss 00:00:30.0 -i input.wmv -c copy -t 00:00:10.0 output.wmv
ffmpeg -ss 30 -i input.wmv -c copy -t 10 output.wmv

Note that -t is an output option and always needs to be specified after -i. Use the -to arg to specify the target end time

Also note that if you specify -ss before -i, -to will have the same effect as -t, i.e. it will act as a duration.

ffmpeg -i input.mp4 -vf scale=$w:$h output.mp4

where $w is width and $h is height (e.g. -vf scale=640:480 will resize the video to 480p

To get the width, height of a video, you could(ref):

ffprobe -v error -select_streams v -show_entries stream=width,height -of csv=p=0:s=x input.mp4

image of resolution standards

for small resolutions, check here or here.

ffmpeg -i <input> -filter:v fps=fps=30 <output>

Slow Down/ Speed Up Video

using PTS filter, to slow down video to half the speed:

ffmpeg -i input.mkv -filter:v "setpts=2.0*PTS" output.mkv

to double the speed (frames might be dropped, however, which can be avoided by increasing the FPS: see here):

ffmpeg -i input.mkv -filter:v "setpts=0.5*PTS" output.mkv

Time Lapse from a Video

To take every 15th frame, use

ffmpeg -i in.mp4 -vf select='not(mod(n,15))',setpts=N/FRAME_RATE/TB out.mp4

Another method is to use the framestep filter

ffmpeg -i in.mp4 -vf framestep=15,setpts=N/FRAME_RATE/TB out.mp4

Video to Gif

see this useful post or even this one that combines with a gifsicle compression:

ffmpeg -i input.mp4 -s 1400x800 -pix_fmt rgb24 -r 30 -f gif output.gif 
gifsicle output.gif --optimize=3 --delay=3 --colors 64 -o output.gif
  • the -s params is optional but it's for the output video width, height
  • the -r params controls the FPS
  • --optimize is the compression level in gifsicle, 3 being the highest
  • --colors set how many color is in the gif (less being more compressed, see the GIF Compression section below)

But to do the Boomerang effect, you might want something like this where input.mp4 is the segment that you want to loop and rewind (source):

ffmpeg -i input.mp4 -filter_complex "[0]reverse[r];[0][r]concat=n=2:v=1:a=0" output.gif

GIF compression

it's best handled by gifsicle (brew install gifsicle), see this solution for reference.

Getting Information like FPS

ffmpeg -i filename

For more see this post

for Frame Count

ffprobe -v error -select_streams v:0 -count_frames -show_entries stream=nb_read_frames  input.mp4

see here for details

but to be faster, you probably wanna count packets instead which is significantly faster from experience:

ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 input.mp4

The font file path you need is probably gonna be at /usr/share/fonts on Linux or '/Library/Fonts/' on MacOS

ffmpeg -i input.mp4 -vf "drawtext=fontfile=Arial.ttf: text='Frame\: %{frame_num}': start_number=0: x=(w-tw)/2: y=h-(2*lh): fontcolor=black: fontsize=20: box=1: boxcolor=white: boxborderw=5" -c:a copy output.mp4

This create a video that's like this:

frame number overlay demo

ffmpeg -i input.mp4 -q:a 0 -map a output_file.m4a
  • -q:a: variable bitrate with 0 being the highest quality
  • -map a: select audio stream only
  • file extension, in general m4a is a good idea because ffmpeg should be smart enough to guess your intent and figure out the right codec of your output audio file and encapsulate the raw AAC into the output M4A container
 ffmpeg -i /input/track1.m4a -i ~/input/track2.m4a -filter_complex '[0:0][1:0]concat=n=2:v=0:a=1[out]' -map '[out]' output/track.m4a

for adding a third track simple replace [0:0][1:0]concat=n=2 with [0:0][1:0][2:0]concat=n=3

Selecting a Single Frame from a Video

ffmpeg -ss 01:23:45 -i input -frames:v 1 -q:v 2 output.jpg
  • the -ss flag indicates the timestamp of the frame in hh:mm:ss
  • for details see this stackoverflow post

Convert a Directory of Images to Video

using 30 fps (for details see this stackoverflow post):

ffmpeg -framerate 30 -pattern_type glob -i '*.jpg'   -c:v libx264 -pix_fmt yuv420p out.mp4

the command expects that in your working directories there are a list of JPEGs nicely named by frame number and ideally zero padded. In case you want to make them zero padded, this stackoverflow answer might help.

if you bump into the height not divisible by 2 error you might want to add a pad filter: -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" as explained here.

Finally, if you want to do this with the ffmpeg-python package, this example might help.

Joining Two Videos

Side-by-side

See this very detailed guide or borrow from this solution:

ffmpeg \
  -i input1.mp4 \
  -i input2.mp4 \
  -filter_complex vstack=inputs=2 \
  output.mp4

Keyframes

this explains video keyframes in the context of compression in details. To see how keyframes are extracted, this post is helpful. And to see how many keyframes are in your video, check this

Video Cropping

For cropping video in the same way one would on images, see this post

Rotation

rotation are set in the metadata of the video

to add rotation data, use ffmpeg:

ffmpeg -i input.mp4 -metadata:s:v rotate=180 -vcodec copy -acodec copy output.mp4

this should be lossless because of -vcodec copy, for more details see this stackoverflow post

to update rotation data, use exiftools (not tested, install with sudo apt install libimage-exiftool-perl):

exiftool -rotation=180 input.mp4

see reference here

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment