Normalize audio loudness with FFmpeg
loudnorm brings a file to a loudness target measured the way streaming platforms measure it (EBU R128, in LUFS). The commands below were run on a test file sitting at -21.8 LUFS and each result was re-measured with ebur128.
Verified with FFmpeg 9.0.1 on 26 August 2026. Search intent: ffmpeg normalize audio.
ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11,aresample=48000" -c:v copy -c:a aac -b:a 160k output.mp4One pass to -16 LUFS integrated with a true-peak ceiling of -1.5 dBTP, video copied untouched. The test file went from -21.8 LUFS to -16.0 LUFS. aresample=48000 matters: without it loudnorm handed the encoder 96 kHz audio.
Flags, explained
- loudnorm=I=-16
- Target integrated loudness in LUFS. -14 for streaming platforms, -16 for podcasts, -23 for broadcast.
- TP=-1.5
- True-peak ceiling in dBTP. Keeps inter-sample peaks below -1.5 dB so lossy encoders do not clip.
- LRA=11
- Target loudness range in LU. Only matters in dynamic mode; a short synthetic file measures 0.
- aresample=48000
- Bring the audio back to 48 kHz. loudnorm works internally at a high sample rate and, on this build, output 96 kHz without it.
- linear=true + measured_*
- Second-pass mode: apply one constant gain computed from the first-pass measurements instead of adapting over time.
- -c:v copy
- Video is not touched, so the job costs only the audio decode and encode.
- volume=3dB
- Fixed gain. Accepts dB or a linear factor (volume=1.4).
Variants
Two passes: measure first, then apply a linear gain
ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json" -f null -The first pass prints a JSON block (input_i, input_tp, input_lra, input_thresh, target_offset). Feed those numbers back with linear=true so the whole file gets one constant gain instead of a gain that moves over time. On the test file: input_i -21.75, input_tp -16.32, input_lra 0.00, input_thresh -31.75, target_offset 0.04.
Second pass with the measured values
ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-21.75:measured_TP=-16.32:measured_LRA=0:measured_thresh=-31.75:offset=0.04:linear=true,aresample=48000" -c:v copy -c:a aac -b:a 160k output.mp4Result: -16.0 LUFS integrated, peak -10.6 dBFS, 48 kHz. Same loudness as the one-pass run, but the dynamics of the source are preserved exactly.
Podcast or music file to the streaming target (-14 LUFS)
ffmpeg -i input.mp3 -af "loudnorm=I=-14:TP=-1:LRA=11,aresample=44100" -c:a libmp3lame -q:a 2 output.mp3A very quiet test file (-40.1 LUFS) came out at -13.7 LUFS. Streaming services generally normalise around -14 LUFS, podcasts around -16, broadcast at -23.
Plain gain when you only need a few dB
ffmpeg -i input.mp4 -af "volume=3dB" -c:v copy -c:a aac -b:a 160k output.mp4No measurement, no limiter: +3 dB moved the file from -21.8 to -18.8 LUFS and the peak from -16.3 to -13.5 dBFS. Check the peak first (next variant) so the gain does not clip.
Measure loudness and peak without writing a file
ffmpeg -i input.mp4 -af "ebur128=peak=true" -f null -Prints integrated loudness (I), loudness range (LRA) and true peak. The test file reported I: -21.8 LUFS, Peak: -16.3 dBFS. volumedetect is the older alternative and reports mean_volume and max_volume in dB.
Pitfalls we hit
- Without aresample the output sample rate changes: the one-pass command produced 96 kHz AAC (1,126 frames for 12 s instead of 564). Most players cope, some hardware does not, and the file is larger for nothing.
- One-pass loudnorm is dynamic (the JSON says normalization_type: dynamic): the gain moves during the file. For music or anything mastered, run two passes with linear=true.
- Normalising loudness and normalising peaks are different things. A file peaking at -0.1 dBFS can still be quiet; volume= only shifts the peak, loudnorm targets perceived loudness and limits the peak.
- Loudness range on short or synthetic material comes out as 0 LU; the LRA target then does nothing. Measure real speech or music before judging the settings.
- volume=3dB on a file that already peaks near 0 dBFS clips. Read max_volume with volumedetect or Peak with ebur128 first.
How we verified it
Run with FFmpeg 9.0.1 on 26 August 2026 against a 12 s H.264/AAC file measured at -21.8 LUFS, peak -16.3 dBFS. One-pass loudnorm: -16.0 LUFS, 96 kHz without aresample, 48 kHz with it. Two-pass with the printed measurements and linear=true: -16.0 LUFS, peak -10.6 dBFS. Quiet MP3 at -40.1 LUFS with I=-14: -13.7 LUFS. volume=3dB: -18.8 LUFS, peak -13.5 dBFS.
The same edit as a typed operation
normalize_audio takes a named target (streaming -14 LUFS, podcast -16, broadcast -23), measures and applies the gain in one job, keeps the sample rate and the video stream, and returns a signed URL. The target is validated before anything renders.
{
"inputs": [{ "id": "main", "url": "https://example.com/video.mp4" }],
"options": { "target": "streaming" }
}FAQ
Related commands
- ffmpeg extract audioExtract audio from a video with FFmpegRead
- ffmpeg remove silenceRemove silence from audio with FFmpegRead
- ffmpeg silencedetectDetect silence with FFmpegRead