ffmpeg command to remove HDR
I used this
ffmpeg command to remove HDR from an iPhone 13 Pro
time ffmpeg -i /private/tmp/lightroom/IMG_5744.MOV -c:v h264_videotoolbox -maxrate 2600K -bufsize 2600K -vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,scale='trunc(iw/2):trunc(ih/2)',format=yuv420p" -c:a aac -pix_fmt yuv420p aperture2.mp4
Sorry that command’s so unwieldy. If you’re going to run it, I’d recommend copying it into your code editor instead of trying to bash it into shape at the terminal, especially if you don’t have mouse support in your shell.
Why though? I transcoded a video from my phone using ffmpreg to post on Discord and it looked like shit, so I did some research.
Notes on the Command
This uses the zscale filter that I’m not super familiar with, and manipulates the colorspace of the images, a process I’m also not super familiar with.
format stuff below mentions three colon-separated numbers,
like “4:4:4” and “4:2:0”.
where for multiple pixels,
you store different amounts of luminance (like brightness)
and color channels to save space.
“4:4:4” stores everything,
“4:2:0” saves a ton of space/bandwidth and
is basically the standard for final playback.
There’s a lot of stuff below about color spaces and matrices and ranges and whatnot. The quick version is that you gotta do it because HDR is necessarily complicated because image processing in 2023 is complicated because the human vision system is complicated.
The Annotated Command
time- I want to see how long it takes
ffmpeg- I’m running it; pretty sure I did
brew install ffmpegto get it
-i /private/tmp/lightroom/IMG_5744.MOV- this is the video file; I dragged it out of
Photos.appinto Yoink then from Yoink into Finder, and finally from Finder into Terminal, which inserts the correctly-escaped path
-c:v h264_videotoolbox- I generally want to use the hardware encoder on this machine (m1 pro) because it’s fast; if you’re not on macOS with hardware accelerated video encoding you’ll have to use a different ffmpeg video encoder
-maxrate 2600K -bufsize 2600K- these settings match what my mastodon server expects;
maxratecontrols the maximum number of bytes/sec it’ll output (2.6mb in this case), and
bufsizeis the size of a buffer to enforce that rate over. A bigger
bufsizegives the encoder more flexibility in adjusting per-frame sizes, but may affect playability with small buffers (i.e. on a small device or when a connection is just barely fast enough). Some more information about ffmpeg bitrates is available.
-vf- the next command is a video filter (which I’ll annotate the comma-delimited parts separately)
zscale=t=linear:npl=100- Transfer the images to a linear brightness scale, with a Nominal Peak Luminance of 100
format=gbrpf32le- change the pixel format to something presumably easier or more accurate to work with, “IEEE-754 single precision planar GBR 4:4:4, 96bpp, little-endian”
zscale=p=bt709- set the color Primaries to BT.709
tonemap=tonemap=hable:desat=0- use the Hable tonemapping algorithm to scoot all the colors into the a useful range, and don’t desaturate anything that would be blown out towards white
zscale=t=bt709:m=bt709:r=tv- Transfer the images to BT.709, set the color matrix to BT.709, and set the color range to a standard TV range (yeah television, as opposed to Personal Computer) of 16-235 instead of 0-255
scale='trunc(iw/2):trunc(ih/2)'- size the image down to half of each dimension,
truncrounds the “Input Width / 2” (and Input Height) towards zero so weird stuff with half-pixels doesn’t happen if the original has an odd dimension
format=yuv420p- change the pixel format to YUV 4:2:0
-c:a aac- compress the audio with AAC
-pix_fmt yuv420p- save it as YUV 4:2:0
aperture2.mp4- the output filename
ffmpeg is really powerful and useful, but frustratingly it seems like one of those tools where everyone’s learned it from lore and rumors than the documentation.
Hopefully by writing this I help solve the problem for myself, and incidentally I hope you’ve found it useful too.