AudioUtils

FFmpeg Compress Audio: One-Liners for MP3, FLAC, Opus, AAC

FFmpeg one-liners for compressing audio: MP3 with LAME presets, FLAC at max compression, Opus for voice, AAC for video. Every flag explained, plus a bash batch loop and an honest comparison vs browser tools.

FFmpeg is the canonical command-line tool for audio compression. Every browser-based audio tool, every desktop converter, every streaming service's ingest pipeline — most of them are wrappers around FFmpeg under the hood. If you are on the command line and want to shrink an audio file, this is the tool, and one line is usually all it takes.

This guide covers the flags that matter, the one-liners that do the work, and the honest comparison of when CLI beats a browser and when it does not. For the conceptual side of "what audio compression actually is" see audio compression explained.

Install FFmpeg

If you do not already have it:

  • macOS (Homebrew): 'brew install ffmpeg'
  • Windows (Chocolatey): 'choco install ffmpeg' — or download a static build from gyan.dev and add to PATH
  • Ubuntu/Debian: 'sudo apt install ffmpeg'
  • Fedora/RHEL: 'sudo dnf install ffmpeg' (after enabling RPM Fusion)
  • Arch: 'sudo pacman -S ffmpeg'

Verify: 'ffmpeg -version'. You want at least version 4.0; modern versions are 6.x or 7.x. Most distro packages are recent enough.

The Flags You Need

FFmpeg has thousands of flags. For audio compression you need a small handful.

| Flag | What it does | Example | |---|---|---| | -i | Input file | -i input.wav | | -c:a | Audio codec | -c:a libmp3lame | | -b:a | Audio bitrate (constant) | -b:a 192k | | -q:a | Quality level (VBR) for MP3/Vorbis | -q:a 2 | | -compression_level | FLAC compression effort (0–12) | -compression_level 8 | | -application | Opus mode (voip/audio/lowdelay) | -application voip | | -ar | Sample rate | -ar 44100 | | -ac | Channel count (1=mono, 2=stereo) | -ac 1 | | -vn | Strip video (for media containers) | -vn | | -y | Overwrite output without asking | -y | | -map_metadata | Copy or strip metadata | -map_metadata 0 |

The pattern is always: 'ffmpeg [input flags] -i input [output flags] output'.

The Essential One-Liners

Basic MP3 size reduction at 128 kbps

'ffmpeg -i input.mp3 -b:a 128k output.mp3'

This re-encodes the input MP3 to 128 kbps CBR. Use it when you have an MP3 that is too big and you do not have the original WAV. Note: re-encoding lossy to lossy is not free — quality drops slightly. If you have the WAV, encode from there directly.

VBR MP3 with LAME V2 preset (~190 kbps average)

'ffmpeg -i input.wav -c:a libmp3lame -q:a 2 output.mp3'

The '-q:a 2' invokes LAME's V2 preset, the standard transparent setting. File size for a 4-minute song lands around 5–6 MB. For higher quality use '-q:a 0' (V0, ~245 kbps). For smaller files use '-q:a 4' (V4, ~165 kbps). See VBR vs CBR MP3 for the deeper trade-off.

Maximum FLAC compression (lossless)

'ffmpeg -i input.wav -c:a flac -compression_level 8 output.flac'

FLAC's '-compression_level' ranges from 0 (fastest) to 12 (slowest, smallest). Levels 8 through 12 produce nearly identical sizes — 8 is the sweet spot. The output is bit-perfect identical to the input WAV when decoded; the file is roughly 50–60% the WAV size with no quality loss whatsoever. See what is FLAC.

Opus for voice (VoIP-optimized)

'ffmpeg -i input.mp3 -c:a libopus -b:a 64k -application voip output.opus'

Opus is the best low-bitrate codec on the planet. The '-application voip' flag tunes the encoder for speech (narrower frequency emphasis, lower latency). 64 kbps Opus on voice is roughly equivalent to 128 kbps MP3 — half the file size at comparable intelligibility. For music, drop the '-application voip' flag (default is 'audio') and use 96–128 kbps.

AAC for compatibility (iPhone, video)

'ffmpeg -i input.wav -c:a aac -b:a 192k -movflags +faststart output.m4a'

FFmpeg's built-in AAC encoder is decent. For top-tier AAC, compile FFmpeg with libfdk_aac (license-restricted; not in most distro builds). 192 kbps AAC is roughly equivalent to 256 kbps MP3 in audible quality. The '-movflags +faststart' moves the moov atom to the front so playback can begin before the file fully downloads.

Strip a track to mono and downsample (smallest voice files)

'ffmpeg -i input.wav -c:a libmp3lame -b:a 64k -ac 1 -ar 22050 output.mp3'

For voice memos and audiobooks where stereo is wasted and 44.1 kHz is overkill: '-ac 1' forces mono, '-ar 22050' halves the sample rate. A 10-minute voice memo at this setting is around 4.5 MB. Music sounds bad with these settings — voice is fine.

Extract and compress audio from video

'ffmpeg -i input.mp4 -vn -c:a libmp3lame -q:a 2 output.mp3'

The '-vn' flag drops the video stream entirely. The audio gets re-encoded to MP3 V2. For lossless extraction (no re-encode), use '-c:a copy' instead, but the output codec must match what is already in the container.

Bash Batch Loop

For folders of files, a shell loop beats opening a GUI every time:

``` for f in *.wav; do ffmpeg -i "$f" -c:a libmp3lame -q:a 2 "${f%.wav}.mp3" done ```

This converts every .wav in the current directory to an MP3 at LAME V2, preserving filenames. The '${f%.wav}.mp3' strips the .wav extension and appends .mp3. Quote the variable as "$f" to handle filenames with spaces.

For parallel processing on multi-core machines, GNU parallel makes it trivial:

``` ls *.wav | parallel ffmpeg -i {} -c:a libmp3lame -q:a 2 {.}.mp3 ```

This runs N jobs simultaneously where N = number of CPU cores. Install with 'brew install parallel' or 'apt install parallel'.

VBR vs CBR Quality Reference

LAME quality presets in FFmpeg ('-q:a' values):

| -q:a value | LAME preset | Average bitrate | Best for | |---|---|---|---| | 0 | V0 | ~245 kbps | Audiophile, archival from masters | | 2 | V2 | ~190 kbps | Default for music — transparent | | 4 | V4 | ~165 kbps | Casual listening | | 6 | V6 | ~130 kbps | Voice with some music tolerance | | 9 | V9 | ~65 kbps | Lowest quality, voice only |

For Opus, '-b:a' alone is enough:

| Bitrate | Best for | |---|---| | 32 kbps | Speech in a low-bandwidth pinch | | 64 kbps | High-quality voice | | 96 kbps | Music, transparent for most listeners | | 128 kbps | Music, transparent for trained ears |

For format ranges by use case see audio bitrate explained.

Honest Verdict: When CLI Beats the Browser, and When It Doesn't

FFmpeg wins when:

  • You have dozens or hundreds of files to process. A shell loop with parallel hits CPU saturation; the browser handles one file at a time.
  • You need specific encoder flags (libfdk_aac, custom Opus tuning, multi-pass encoding) the browser tool does not expose.
  • You are automating as part of a pipeline — CI builds, podcast publishing scripts, server-side transcoding.
  • The file is multi-gigabyte and would not fit in browser memory.
  • You are already in a terminal and do not want a context switch.

The browser wins when:

  • It is a one-off file. By the time you remember the right flags, you would already be done in a browser.
  • You are on a device without FFmpeg — Chromebook, locked-down work laptop, mobile.
  • The audio is sensitive and you prefer not to involve the OS-level filesystem path your shell history records.
  • You want visual feedback — a progress bar, a sane filename suggestion, a download button.

For one-off compression, /audio-compressor and the per-format tools at /compress-mp3, /compress-wav, /compress-m4a, /compress-ogg, and /compress-flac are the lower-friction option. Behind the scenes they run the same LAME/FLAC/Opus encoders FFmpeg wraps, compiled to WebAssembly. The output is byte-identical for the same input and settings.

Common FFmpeg Pitfalls

  • Forgetting '-c:a' on a transcode: FFmpeg defaults to a sensible codec based on the output extension, but for non-standard combinations (e.g., '.m4a' with non-AAC audio) you should specify explicitly.
  • Using '-b:a' with a VBR-only codec: Opus uses '-b:a' as a target. MP3 with LAME accepts both '-b:a' (CBR) and '-q:a' (VBR); use one or the other, not both.
  • Re-encoding when 'copy' would do: 'ffmpeg -i in.mp4 -vn -c:a copy out.m4a' extracts the audio with no re-encoding — instant, lossless. Only use the libmp3lame route when you actually need to change the format or bitrate.
  • Forgetting '-y' in scripts: without '-y', FFmpeg prompts on overwrite, which hangs unattended scripts.
  • Sample rate mismatches: if the input is 48 kHz and you target an output container that expects 44.1 kHz (rare, but happens with some MP3 players), add '-ar 44100' explicitly.

Verifying Output

After encoding, sanity-check with 'ffprobe':

'ffprobe -v error -show_entries stream=codec_name,sample_rate,bit_rate,channels output.mp3'

This prints codec, sample rate, bitrate, and channel count. If the bitrate is way off your target, something went wrong — usually a typo in '-b:a' (FFmpeg silently falls back to defaults).

Cross-Reference

For the conceptual breakdown of what audio compression actually is, including the file-size vs dynamic-range distinction, see audio compression explained. For the GUI-first equivalent, see how to compress audio in Audacity. For the trade-off between MP3, AAC, and the rest see what is MP3 and audio bitrate explained.