Facebook Video
setup and support
Facebook Video is a droplet. There is no window and there are no settings. You drag a video onto the icon and it hands you back a copy Facebook will accept. There is one thing to set up before the first run, and you only ever do it once.
Setting up, about five minutes
Facebook Video does not do the encoding itself. It hands the work to ffmpeg, the free open source tool that sits underneath most video software on earth. ffmpeg is not included in the download, because its licence does not allow it to be bundled inside an app like this one, and it is almost certainly not on your Mac already.
You will need Terminal. It is in your Applications folder, inside a folder called Utilities. Open it and leave it open.
Go to brew.sh in your browser. There is a single install command on that page. Copy it, paste it into Terminal and press Return.
It will ask for your Mac password. As you type, nothing appears on screen. That is normal. Keep typing and press Return.
When it finishes it may print two more commands and ask you to run them. If it does, run them. If it does not, carry on.
Paste this into Terminal and press Return.
brew install ffmpeg
This is the slow one. It can take several minutes and it prints a lot of text. Let it finish.
Paste this into Terminal and press Return.
mkdir -p ~/bin && ln -s "$(which ffmpeg)" ~/bin/ffmpeg
Nothing will appear. Silence means it worked.
That is the setup finished. You can close Terminal and never open it again. Drag a video onto Facebook Video and it will work.
Using it
Drag a video onto the Facebook Video icon. A new file appears beside your original, named the same but ending _facebook.mp4. Your original is untouched. When it is done you get a notification and the folder opens.
You can drop several clips at once, or a whole folder, and it will work through them one after another.
What it is actually doing
- It measures how long your clip is, then works out a video bitrate that will land the finished file near 90 MB. That leaves a margin under Facebook's 100 MB limit without wasting quality by going far below it
- It encodes in two passes. One pass has to guess where in the clip to spend the bitrate. The second pass already knows, so the same file size looks better
- The long edge goes to 1920 pixels, or 1280 on longer clips where the bitrate works out low, because a clean 720p beats a starved 1080p. It never scales a video up
- If your footage is HDR, which most recent iPhone video is, it converts it to normal Rec.709 colour. This is the step that stops video arriving on Facebook looking grey and washed out
- Audio is 128 kbps stereo AAC, and the file is written so it starts playing before it has finished downloading
- It does not trim, crop or reframe. What you drop on it comes back the same shape, only smaller
- It does not touch your original file
- It does not upload anything. You still post the video yourself
The five things that happen
which ffmpeg
Changing the target size
If you want a different size, you can change it yourself. Right click the app, choose Open With, then Automator. You will see the script. Near the top there is a line that reads:
TARGET_MB=90
Change the number, save, and close Automator. Everything else carries on as before.
Editing the app this way breaks Apple's signature on it. That is harmless on your own Mac, but it does mean you should not pass the edited copy on to anyone else.
The script, in full
This is everything Facebook Video does, all 112 lines of it. You should never run something on your own machine without being able to see what it is. Read it, or have someone read it for you, before you do.
#!/bin/zsh
# Facebook Video droplet - Automator "Run Shell Script" body.
# Shell: /bin/zsh Pass input: as arguments
#
# Needs ffmpeg at ~/bin/ffmpeg (one-time setup).
# Makes an H.264 MP4 aimed at about 90 MB, so it stays under
# Facebook's 100 MB limit. Long edge 1920 px (1280 px for long clips),
# never scaled up. iPhone HDR footage is converted to normal colour.
# Writes "<name>_facebook.mp4" beside the original, never overwrites.
FFMPEG="${FFMPEG_PATH:-$HOME/bin/ffmpeg}"
TARGET_MB=90 # aim for this size
LIMIT_MB=100 # warn if a result is bigger than this
MAX_KBPS=6000 # no need to go higher than this for Facebook
AUDIO_KBPS=128
alert() {
osascript - "$1" <<'EOF' >/dev/null 2>&1
on run argv
display dialog (item 1 of argv) buttons {"OK"} default button 1 with title "Facebook Video"
end run
EOF
}
if [ ! -x "$FFMPEG" ]; then
alert "ffmpeg was not found at:
$FFMPEG
Do the one time ffmpeg setup first."
exit 1
fi
lastdir=""
count=0
for f in "$@"; do
dir=$(dirname "$f")
base=$(basename "$f")
name="${base%.*}"
out="$dir/${name}_facebook.mp4"
n=1
while [ -e "$out" ]; do
out="$dir/${name}_facebook_$n.mp4"
n=$((n+1))
done
info=$("$FFMPEG" -hide_banner -i "$f" 2>&1)
secs=$(printf '%s\n' "$info" | awk '/Duration:/ { split($2, t, /[:,]/); print t[1]*3600 + t[2]*60 + t[3]; exit }')
if [ -z "$secs" ] || ! awk -v s="$secs" 'BEGIN { exit !(s > 0) }'; then
alert "Could not read the length of this video:
$f"
continue
fi
# Video bitrate that lands the whole file near TARGET_MB.
vkbps=$(awk -v s="$secs" -v t="$TARGET_MB" -v a="$AUDIO_KBPS" -v m="$MAX_KBPS" \
'BEGIN { v = int(t * 8000 / s - a); if (v > m) v = m; print v }')
if [ "$vkbps" -lt 300 ]; then
alert "This video is too long to fit under ${LIMIT_MB} MB at a watchable quality:
$f"
continue
fi
# Long edge 1920, or 1280 when the bitrate is low (looks better than starved 1080p).
if [ "$vkbps" -lt 2500 ]; then edge=1280; else edge=1920; fi
scale="scale='if(gte(iw,ih),min($edge,iw),-2)':'if(gte(iw,ih),-2,min($edge,ih))'"
# iPhone HDR (HLG / Dolby Vision / PQ) -> normal colour, so it does not look washed out.
if printf '%s\n' "$info" | grep -Eq 'arib-std-b67|smpte2084'; then
vf="zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,$scale,format=yuv420p"
else
vf="$scale,format=yuv420p"
fi
passlog=$(mktemp /tmp/fbvideo_pass.XXXXXX)
errfile=$(mktemp /tmp/fbvideo_err.XXXXXX)
if "$FFMPEG" -hide_banner -nostdin -y -i "$f" -map 0:v:0 -vf "$vf" \
-c:v libx264 -preset medium -b:v "${vkbps}k" -pass 1 -passlogfile "$passlog" \
-an -f mp4 /dev/null > "$errfile" 2>&1 \
&& "$FFMPEG" -hide_banner -nostdin -y -i "$f" -map 0:v:0 -map '0:a:0?' -vf "$vf" \
-c:v libx264 -preset medium -b:v "${vkbps}k" -pass 2 -passlogfile "$passlog" \
-profile:v high -colorspace bt709 -color_primaries bt709 -color_trc bt709 \
-c:a aac -b:a "${AUDIO_KBPS}k" -ac 2 -movflags +faststart "$out" >> "$errfile" 2>&1
then
lastdir="$dir"
count=$((count+1))
size_mb=$(( $(wc -c < "$out" | tr -d ' ') / 1000000 ))
if [ "$size_mb" -ge "$LIMIT_MB" ]; then
alert "Finished, but it came out at ${size_mb} MB, over the ${LIMIT_MB} MB limit:
$out"
fi
else
msg=$(tail -n 5 "$errfile")
rm -f "$out"
alert "Could not convert:
$f
$msg"
fi
rm -f "$errfile" "$passlog"*
done
if [ -n "$lastdir" ]; then
osascript -e "display notification \"$count video(s) ready for Facebook\" with title \"Facebook Video\"" >/dev/null 2>&1
open "$lastdir" 2>/dev/null
fi
Nothing leaves your Mac
No account, no sign in, no telemetry, no licence check. Facebook Video makes no network connections of any kind. It reads the file you drop on it and writes a new one beside it, and that is the whole of it.
The entire thing is a short script, and you are welcome to read it before you run anything.
Still stuck?
Tell me what the box on screen said, word for word. That one sentence is usually enough to work out what happened.