Skip to main content

Command Palette

Search for a command to run...

Limit True Peak to -1 dBTP with ffmpeg Before Uploading

Measure with ebur128, resample to 48 kHz and oversample a limiter so social platforms don't clip your clip.

Updated
•6 min read•View as Markdown
Limit True Peak to -1 dBTP with ffmpeg Before Uploading
M
MixVio AI is a multimodal creative workspace for generating and editing images, video, and audio with leading AI models and practical creative tools.
Cover: "Fix inter-sample peaks before you upload" next to an output frame from a 15-second AI party clip

AI-generated example made with MixVio's Burning Bridges AI effect. The lead is an AI-generated portrait, not a real person.

A clip can look fine in your editor and still clip after upload. Social platforms re-encode audio, and a file whose waveform already touches 0 dBFS often has inter-sample peaks above 0 dB once it is reconstructed or transcoded. You hear that as harsh distortion on the loudest beats.

This tutorial shows an ffmpeg true peak limit workflow I used on a 15-second AI party clip: measure with ebur128, resample to 48 kHz, take off a little gain, and run an oversampled limiter so the result sits at or below -1 dBTP. Everything here is about levels only. It says nothing about music rights, which are a separate question.

The example file

The example clip was made with Burning Bridges AI on MixVio: a 15-second 1280x720 video whose effect attaches the original source track after the visual edit. As delivered, its audio was 44.1 kHz stereo, about -12.6 LUFS integrated, with peaks at +1.1 dBFS, so the source track was already clipping before any platform touched it.

That makes it a good test case: we want to fix the peaks without changing the music, the timing or the video.

Step 1: measure integrated loudness and true peak

ebur128 with peak=true reports EBU R128 loudness and a 4x oversampled true-peak value:

ffmpeg -hide_banner -nostats -i in.mp4 \
  -af ebur128=peak=true -f null - 2>&1 | grep -E "I:|Peak:"

On the example clip:

I:         -12.6 LUFS
Peak:        1.1 dBFS

The Peak line in the true-peak section is the number to watch. Anything above about -1 dBTP is likely to clip after a lossy re-encode. Most platforms also normalise playback to around -14 LUFS, so a -12.6 LUFS file will simply be turned down; there is little to gain from leaving it that loud.

Step 2: understand why the sample peak lies

A sample peak only looks at the stored samples. Between two samples the reconstructed analog waveform can rise higher, especially on bright, dense mixes. AAC and Opus encoders work on that reconstructed signal, so a file with sample peaks at -0.1 dBFS can still produce encoded peaks above 0 dB.

True-peak meters estimate this by oversampling (4x here) before taking the maximum. That is why you should limit at a higher sample rate too: a limiter that only sees 48 kHz samples cannot catch overs it does not see.

Step 3: resample, trim gain, oversample and limit

This keeps the video stream untouched (-c:v copy) and only rebuilds the audio:

ffmpeg -i in.mp4 -c:v copy \
  -af "aresample=48000:resampler=soxr,volume=-1.6dB,\
aresample=192000,alimiter=limit=0.84:level=disabled,\
aresample=48000" \
  -c:a aac -b:a 192k out.mp4

What each part does:

  • aresample=48000:resampler=soxr converts 44.1 kHz to 48 kHz, the rate most video platforms expect, with the high-quality SoX resampler.

  • volume=-1.6dB brings the integrated loudness close to -14 LUFS. This is plain gain; it does not change the mix.

  • aresample=192000 upsamples 4x so the limiter can see inter-sample overs.

  • alimiter=limit=0.84:level=disabled caps the signal at about -1.5 dBFS (0.84 linear). level=disabled stops alimiter from re-normalising the output back up.

  • aresample=48000 returns to the delivery rate.

  • -c:a aac -b:a 192k encodes AAC at a bitrate that leaves headroom for the next encode.

Step 4: measure again

Run the same ebur128 command on out.mp4:

I:         -14.2 LUFS
Peak:       -1.9 dBFS

Integrated loudness moved by the gain change only, and the true peak is now below -1 dBTP. A quick check that the format is what the platform expects:

ffprobe -v error -select_streams a \
  -show_entries stream=sample_rate,channels -of csv=p=0 out.mp4
# 48000,2
Chart: true peak per 50 ms over 15 seconds for the source, the 48 kHz master and the social export, with 0 dBFS and -1 dBTP reference lines

True peak over time for the source (44.1 kHz), the 48 kHz master and the social export. Chart from ffmpeg ebur128 measurements; the final social export measured -14.3 LUFS and -2.4 dBTP.

The chart comes from a slightly different export of the same chain, which is why its numbers differ from my run by a few tenths of a dB. Both sit well under the -1 dBTP line.

Alternative: two-pass loudnorm

If you would rather target loudness directly, loudnorm can do both jobs. Run it once to measure:

ffmpeg -hide_banner -i in.mp4 \
  -af loudnorm=I=-14:TP=-1.5:LRA=11:print_format=summary \
  -f null -

Then feed the measured values back in a second pass with measured_I, measured_TP, measured_LRA, measured_thresh and linear=true. Linear mode applies a single gain where it can, which keeps the dynamics of a music track intact. If the target would push peaks over the ceiling, loudnorm falls back to dynamic mode, so always re-measure.

For short clips that are already loud, the explicit chain in Step 3 is easier to reason about: one gain value, one limiter, nothing adaptive.

Batch it

A tiny shell loop applies the same chain to a folder of exports and prints the result for each file:

#!/usr/bin/env bash
set -euo pipefail
AF="aresample=48000:resampler=soxr,volume=-1.6dB"
AF="$AF,aresample=192000,alimiter=limit=0.84:level=disabled"
AF="$AF,aresample=48000"
mkdir -p fixed
for f in exports/*.mp4; do
  out="fixed/$(basename "$f")"
  ffmpeg -nostdin -loglevel error -y -i "$f" -c:v copy \
    -af "$AF" -c:a aac -b:a 192k "$out"
  printf '%s ' "$out"
  ffmpeg -hide_banner -nostats -i "$out" -af ebur128=peak=true \
    -f null - 2>&1 | grep -E "Peak:" | tail -n 1
done

Adjust volume per file if your sources differ a lot in loudness; the limiter is there to catch peaks, not to do the loudness work.

Things to keep in mind

  • Don't swap the music to fix levels. Gain and limiting change levels, not content. If a platform raises a rights question, that is not something a filter can answer.

  • Keep video untouched. -c:v copy avoids a generation loss on the picture.

  • Leave headroom. -1 dBTP is a common ceiling; -1.5 to -2 dBTP gives extra safety for platforms that re-encode at low bitrates.

  • Check by ear. A limiter working too hard pumps. If you hear it, lower the gain before the limiter instead of pushing the limit lower.

A last note on the example: the effect page states "This is an independent AI effect and is not affiliated with Drake."

Disclosure: written by the MixVio team with AI assistance; the example video was made with MixVio's Burning Bridges AI effect from an AI-generated portrait.