FFmpeg Loudnorm Second Pass Guide
Using the loudnorm filter in FFmpeg via a two-pass
process allows you to achieve highly accurate audio normalization to
industry standards like EBU R128. While a single-pass scan estimates
loudness on the fly, the second pass uses precise statistics gathered
from the first pass to adjust the audio without unwanted dynamic
compression. This guide details how to extract the measured audio
statistics in the first pass and correctly apply them in the second pass
to output perfectly leveled audio.
Step 1: Run the First Pass to Gather Statistics
To get the necessary measurements, you must run the
loudnorm filter on your source file and direct the output
to print in JSON format. Because we only need the data, we can discard
the video/audio output by exporting to a null muxer.
Run the following command in your terminal:
ffmpeg -i input.mp4 -af loudnorm=print_format=json -f null -After the command runs, look at the end of the terminal output. You will see a JSON block containing the measured statistics of your input file. It will look similar to this:
[parsed_loudnorm_0 @ 0x7fa89bc05ec0]
{
"input_i" : "-14.35",
"input_tp" : "-0.50",
"input_lra" : "7.20",
"input_thresh" : "-24.62",
"output_i" : "-24.12",
"output_tp" : "-10.27",
"output_lra" : "4.90",
"output_thresh" : "-34.36",
"normalization_type" : "dynamic",
"target_offset" : "0.12"
}Keep this JSON data open, as you will need the input_*
values and the target_offset value for the second pass.
Step 2: Map the JSON Values to Loudnorm Parameters
In the second pass, you must map the output values from the first
pass to the corresponding input parameters of the loudnorm
filter.
input_imaps tomeasured_iinput_tpmaps tomeasured_tpinput_lramaps tomeasured_lrainput_threshmaps tomeasured_threshtarget_offsetmaps tooffset
You will also define your target loudness parameters. The default
targets are integrated loudness (I) of -24 LUFS, True Peak
(TP) of -1.0 dBTP, and Loudness Range (LRA) of
7.0 LU. For online platforms like YouTube or Spotify, a target of -14
LUFS is commonly preferred.
Step 3: Run the Second Pass
Run the second command using the mapped values. You must also set
linear=true in this step. This instructs the filter to
perform a linear normalization using the exact data from the first pass,
preventing unnecessary dynamic compression.
If your target is -16 LUFS (common for podcasts), and you are using the example JSON statistics from Step 1, your command will look like this:
ffmpeg -i input.mp4 -af loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-14.35:measured_TP=-0.50:measured_LRA=7.20:measured_thresh=-24.62:offset=0.12:linear=true output.mp4Parameter Breakdown for the Second Pass
I: Your target integrated loudness (e.g.,-16).TP: Your target maximum True Peak (e.g.,-1.5).LRA: Your target maximum loudness range (e.g.,11).measured_Ithroughmeasured_thresh: The exact values measured from your first pass input.offset: The gain offset calculated in the first pass, which bridges the difference between the input and target levels.linear=true: Forces FFmpeg to use linear scaling instead of dynamic range compression wherever possible.