ffmpegaudionormalizationapiloudnorm

How to Normalize Audio with FFmpeg (Volume, Loudnorm, and API)

·Javid Jamae·7 min read
How to Normalize Audio with FFmpeg (Volume, Loudnorm, and API)

FFmpeg audio normalization trips up even experienced developers. The loudnorm filter alone has 6 parameters, a two-pass workflow that requires parsing JSON from stderr, and behavior that changes depending on whether you feed it MP4, WAV, or MKV. Most guides skip the hard parts.

This post covers three approaches to fixing audio levels with FFmpeg: simple volume scaling, broadcast-standard loudnorm normalization (EBU R128), and a cloud API that handles it without installing anything.

Quick answer: normalize audio to -16 LUFS with FFmpeg

ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:v copy output.mp4

This applies EBU R128 loudnorm normalization targeting -16 LUFS (the standard for streaming platforms). The -c:v copy flag passes video through untouched so only the audio gets re-encoded.

If you don't want to install FFmpeg or manage a server, the same operation through the FFmpeg Micro API:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{"url": "https://your-bucket.s3.amazonaws.com/input.mp4"}],
    "outputFormat": "mp4",
    "options": [
      {"option": "-af", "argument": "loudnorm=I=-16:TP=-1.5:LRA=11"},
      {"option": "-c:v", "argument": "copy"}
    ]
  }'

Approach 1: Simple volume adjustment with the volume filter

The volume filter is the simplest way to change audio levels. It multiplies every sample by a fixed factor.

Halve the volume:

ffmpeg -i input.mp4 -af "volume=0.5" -c:v copy output.mp4

Double the volume:

ffmpeg -i input.mp4 -af "volume=2.0" -c:v copy output.mp4

Adjust by decibels (more precise):

ffmpeg -i input.mp4 -af "volume=6dB" -c:v copy output.mp4

The volume filter is predictable but dumb. It doesn't analyze the source audio first, so you can easily clip loud sections or amplify noise in quiet ones. For consistent levels across multiple files, you need actual normalization.

Approach 2: EBU R128 normalization with loudnorm

The loudnorm filter implements the EBU R128 broadcast standard. Instead of blindly scaling amplitude, it measures perceived loudness (LUFS) and adjusts dynamically.

Single-pass normalization (good enough for most cases):

ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:v copy output.mp4

What the parameters mean:

  • I=-16 sets the target integrated loudness to -16 LUFS. YouTube uses -14, Spotify uses -14, podcasts typically use -16 to -19.
  • TP=-1.5 sets the true peak ceiling to -1.5 dBTP. This prevents clipping on lossy codecs like AAC and MP3 that can overshoot 0 dB during decoding.
  • LRA=11 sets the loudness range to 11 LU. This controls how much dynamic range the output keeps. Lower values compress more.

Two-pass normalization (more accurate):

Single-pass loudnorm uses a look-ahead buffer and adjusts in real time. Two-pass analyzes the entire file first, then applies corrections with exact measurements.

Pass 1: Analyze and capture stats.

ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11:print_format=json" -f null - 2>&1 | tail -12

This outputs JSON with measured values like input_i, input_tp, input_lra, input_thresh, output_i, output_tp, output_lra, output_thresh, normalization_type, and target_offset.

Pass 2: Apply the measured values.

ffmpeg -i input.mp4 -af "loudnorm=I=-16:TP=-1.5:LRA=11:measured_I=-19.6:measured_TP=-5.9:measured_LRA=0.6:measured_thresh=-29.6:offset=0.1:linear=true" -c:v copy output.mp4

You need to parse the JSON output from pass 1 and plug those numbers into pass 2. This is where automation scripts get messy, because FFmpeg dumps the JSON to stderr mixed with progress output.

Approach 3: Normalize audio through an API (no installation)

If you're processing videos programmatically (in an n8n workflow, a Make.com scenario, or a backend service), you don't need FFmpeg installed locally.

FFmpeg Micro accepts the same -af filter syntax through its REST API:

curl -X POST https://api.ffmpeg-micro.com/v1/transcodes \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": [{"url": "https://your-bucket.s3.amazonaws.com/input.mp4"}],
    "outputFormat": "mp4",
    "options": [
      {"option": "-af", "argument": "loudnorm=I=-16:TP=-1.5:LRA=11"},
      {"option": "-c:v", "argument": "copy"}
    ]
  }'

The response includes a job ID you can poll:

{
  "id": "b5f5a9c0-9e33-4e77-8a5b-6a0c2cd9c0b3",
  "status": "queued",
  "output_format": "mp4",
  "created_at": "2026-05-20T10:00:00.000Z"
}

Check status with GET /v1/transcodes/{id}. When status is completed, grab the output with GET /v1/transcodes/{id}/download.

No FFmpeg binary to install. No server to scale. The loudnorm filter runs on cloud infrastructure and you get a download URL back.

Which approach should you use?

ScenarioBest approachWhy
Quick fix on one file`volume` filterSimple, predictable, no analysis needed
Batch normalize for a platform (YouTube, Spotify)`loudnorm` single-passEBU R128 standard, good enough for streaming
Mastering or archival quality`loudnorm` two-passMost accurate, preserves dynamics
Automated pipeline (n8n, Make.com, backend)FFmpeg Micro APINo installation, scales with your workflow

Common pitfalls with FFmpeg audio normalization

**Forgetting -c:v copy and re-encoding the entire video.** Without it, FFmpeg re-encodes the video stream too. This takes 10-50x longer and can degrade quality. Always pass -c:v copy when you only need to change the audio.

**Using volume instead of loudnorm for batch processing.** The volume filter applies the same gain to every file. If your inputs vary from -30 LUFS to -10 LUFS, they'll still vary by the same 20 LU after processing. loudnorm measures each file and targets a consistent output level.

Setting the true peak ceiling to 0 dBTP. Lossy codecs like AAC generate intersample peaks during decoding that can exceed the encoded peak by 1-3 dB. A ceiling of -1.5 dBTP or -2.0 dBTP gives headroom for this. Setting 0 dBTP causes audible clipping on some decoders.

Mixing up LUFS targets across platforms. YouTube normalizes to -14 LUFS. Spotify normalizes to -14 LUFS. Podcasts are typically -16 to -19 LUFS. If you normalize to -16 LUFS and upload to YouTube, the platform will bump it up 2 LU, which is usually fine. But if you normalize to -24 LUFS (the EBU R128 broadcast standard), YouTube will boost it significantly and the result can sound unnatural.

Running loudnorm on already-normalized audio. Double-normalizing compresses dynamics further each pass. If your source is already at -16 LUFS, running loudnorm again won't improve anything and may degrade quality. Check with ffmpeg -i input.mp4 -af "loudnorm=print_format=summary" -f null - first.

FAQ

What LUFS level should I target for YouTube?
YouTube normalizes all audio to -14 LUFS. If your content is louder, YouTube turns it down. If it's quieter, YouTube leaves it alone (it doesn't boost). Targeting -14 LUFS means YouTube won't touch your audio. Targeting -16 LUFS is close enough that the difference is barely audible.

Can I normalize audio without re-encoding the video?
Yes. Use -c:v copy to pass the video stream through unchanged. Only the audio gets decoded, filtered, and re-encoded. This is fast because video is the expensive part.

What's the difference between loudnorm and dynaudnorm?
loudnorm targets a specific LUFS level per the EBU R128 standard. It measures the whole file (or uses a look-ahead buffer) and applies gain to hit the target. dynaudnorm adjusts volume frame-by-frame, making quiet parts louder and loud parts quieter. dynaudnorm sounds more "radio-processed" and can introduce pumping artifacts. For most use cases, loudnorm is the right choice.

Does the FFmpeg Micro API support two-pass loudnorm?
The API runs single-pass loudnorm, which uses a real-time look-ahead buffer. For most automated pipelines, single-pass is accurate enough. The difference between single-pass and two-pass is typically less than 0.5 LU on the output.

How do I normalize just the audio track and extract it as MP3?
Use -vn to drop the video and output as MP3: ffmpeg -i input.mp4 -vn -af "loudnorm=I=-16:TP=-1.5:LRA=11" -c:a libmp3lame -q:a 2 output.mp3

Last verified: 2026-05-20 against FFmpeg 7.x and FFmpeg Micro API v1.

About Javid Jamae

Founder & CEO at FFmpeg Micro

Javid is a software engineer, author, and entrepreneur with over 25 years of professional software development experience across enterprise, startup, and consulting environments. He founded FFmpeg Micro to make video processing accessible to developers through a simple, automation-first REST API.

Software EngineeringVideo ProcessingFFmpegCloud ArchitectureAPI DesignAutomation

Ready to process videos at scale?

Start using FFmpeg Micro's simple API today. No infrastructure required.

Get Started Free