Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
A quick guide to using FFmpeg to create cross-device web videos.

Video conversion with FFmpeg

Install

On mac:

  1. Download the latest release.
  2. Extract the binary and place it in /usr/local/bin.

Command basics

Most basic conversion using Terminal: ffmpeg -i input.mov output.webm.

Strip audio

Flag: -an.

Scale video

Example flag: -vf scale=1280:-2.

-1 for the width or height will keep it in ratio to the other specified dimension.

-2 will keep it in ratio to the other specified dimension, but, to ensure it is divisible by 2 (a requirement for certain encodings such as yuv420p) the width or height will be adjusted if necessary.

Docs here.

Faster processing

Flag: -threads 0.

Allow your CPU to use an optimal number of threads.

Convert to WebM

Guide here.

Audio example

ffmpeg -i input.mov -c:v libvpx -qmin 0 -qmax 25 -crf 4 -b:v 1M -vf scale=1280:-2 -c:a libvorbis -threads 0 output.webm

Mute example

ffmpeg -i input.mov -c:v libvpx -qmin 0 -qmax 25 -crf 4 -b:v 1M -vf scale=1280:-2 -an -threads 0 output.webm

Convert to MP4

Guide here.

QuickTime compatibility

Flag: -pix_fmt yuv420p.

Note: Requires dimensions to be divisible by 2.

All device compatibility

Flag: -profile:v baseline -level 3.0.

Android in particular doesn't support higher profiles.

Quality

Example flag: -crf 20.

0 is lossless, 23 is default, and 51 is worst possible. 18-28 is a sane range.

Fast start

Flag: -movflags +faststart.

Moves some data to the beginning of the file, allowing the video to be played before it is completely downloaded.

Audio example

ffmpeg -i input.mov -c:v libx264 -pix_fmt yuv420p -profile:v baseline -level 3.0 -crf 22 -preset veryslow -vf scale=1280:-2 -c:a aac -strict experimental -movflags +faststart -threads 0 output.mp4

Mute example

ffmpeg -i input.mov -c:v libx264 -pix_fmt yuv420p -profile:v baseline -level 3.0 -crf 22 -preset veryslow -vf scale=1280:-2 -an -movflags +faststart -threads 0 output.mp4

Bash script

  1. In a folder, make sure your master videos are named how you want your exports to be named.
  2. Save the bash script below as video4web.sh in the folder.
  3. In Terminal, cd to the folder.
  4. Run chmod +x video4web.sh.
  5. Run the script using the parameters documented. E.g. ./video4web.sh mov 1280.
#!/bin/bash

# Generates a cover image along with mute web-ready WebM and MP4 files for each master video in a folder.
# See: https://gist.github.com/jaydenseric/220c785d6289bcfd7366.

# Parameter 1: Input video format (e.g. "mov").
# Parameter 2: Output width in pixels (e.g. "1280").
# Example use: "./video4web.sh mov 1280".

for i in *.$1
do
  # Generate cover image
  ffmpeg -i $i -vframes 1 -vf scale=$2:-2 -q:v 1 ${i%$1}jpg
  # Generate WebM
  ffmpeg -i $i -c:v libvpx -qmin 0 -qmax 25 -crf 4 -b:v 1M -vf scale=$2:-2 -an -threads 0 ${i%$1}webm
  # Generate MP4
  ffmpeg -i $i -c:v libx264 -pix_fmt yuv420p -profile:v baseline -level 3.0 -crf 22 -preset veryslow -vf scale=$2:-2 -an -movflags +faststart -threads 0 ${i%$1}mp4
done
@micschk
Copy link

micschk commented Mar 6, 2017

Thanks for this script, works nicely! I've just had to put quotes around the paths to make this work with files containing spaces, eg "/my/file with spaces.mov"

for i in *.$1
do
  # Generate cover image
  ffmpeg -i "$i" -vframes 1 -vf scale=$2:-2 -q:v 1 "${i%$1}jpg"
  # Generate WebM
  ffmpeg -i "$i" -c:v libvpx -qmin 0 -qmax 25 -crf 4 -b:v 1M -vf scale=$2:-2 -an -threads 0 "${i%$1}webm"
  # Generate MP4
  ffmpeg -i "$i" -c:v libx264 -pix_fmt yuv420p -profile:v baseline -level 3.0 -crf 22 -preset veryslow -vf scale=$2:-2 -an -movflags +faststart -threads 0 "${i%$1}mp4"
done

@ai
Copy link

ai commented Mar 5, 2019

We can start to generate AV1 instead of WebM https://evilmartians.com/chronicles/better-web-video-with-av1-codec

@zachstronaut
Copy link

zachstronaut commented Mar 12, 2019

Thank you! Tried about 4 or 5 different stackoverflow suggestions and none of them worked in 2019! (Even the ones that still ran in ffmpeg did not produce a video that would play on iOS.)

@anonyco
Copy link

anonyco commented Jun 7, 2020

Please do not use crf for web videos. The constraining factor for web videos is the time they take to download, not the total size of the video. It would suck if the video suddenly froze and paused simply because, due to ffmpeg's assessment of the necessary quality, ffmpeg upped the bit rate at that point in the movie. Instead, use constant bit rates for both the audio and the video.

@jonshipman
Copy link

jonshipman commented Apr 5, 2021

Also, replace the $2 with ${2:-1280} to default to 1280, allowing just the extension to be passed. You can also replace $1 with ${1:-mp4} to just run the script with no parameters to default to "mp4" and 1280.

@varenc
Copy link

varenc commented Oct 6, 2021

There's some extraneous flags passed to ffmpeg in these examples which I think make it extra confusing to beginners.

Here's a paired down version of the audio example that should have the same behavior, minus extra flags:
ffmpeg -i input.mov -c:v libx264 -pix_fmt yuv420p -profile:v baseline -level 3.0 -crf 22 -preset veryslow -vf scale=1280:-2 -c:a aac -movflags +faststart output.mp4

  • Removed -threads 0 since that just means "use all available threads" and that's the default.
  • Also removed -strict experimental since with a modern version of ffmpeg there's no need for experimental features here.
  • I'd also recommend tweaking the scaling filter to use a better scaling algorithm. Use scale=1280:-2:flags=lanczos for slightly higher quality scaling at the cost of a minor bump in CPU. (negligible compared to encoding)

@vjayer
Copy link

vjayer commented Nov 23, 2022

Please do not use crf for web videos. The constraining factor for web videos is the time they take to download, not the total size of the video. It would suck if the video suddenly froze and paused simply because, due to ffmpeg's assessment of the necessary quality, ffmpeg upped the bit rate at that point in the movie. Instead, use constant bit rates for both the audio and the video.

Vimeo and Crunchyroll and Funimation uses x264 with CRF and a cap max rate. For pure streaming it's better to use HLS and web player that will implement an appropriate buffer size and a thread to make sure it's always filled, instead of relying on browser downloading and real time playback. For smallish videos or crf value that results in moderate rates, in practice VBR is not a problem for regular browser downloading and streaming since you're downloading the whole file at full speed anyways often much faster than the max bitrate of video.

@vjayer
Copy link

vjayer commented Nov 23, 2022

do you know of a way to quickly apply the shorthand -ac codec options to all or multiple tracks? I'm trying to avoid getting into the complex stream mapping options....

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