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.

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.mp4

Parameter Breakdown for the Second Pass