Language: English | Español
A Bash utility that compresses a video to fit within a target file size using two-pass ffmpeg encoding.
Give this script a video and a target size. It calculates the bitrate needed and produces an H.264 + AAC .mp4 file close to that size.
./ffmpeg-filesize.sh input.mov 25mb
./ffmpeg-filesize.sh --template gmail input.mov
./ffmpeg-filesize.sh --output final.mp4 input.mov 24mbIf this is all you need, copy one command and go. Full documentation below.
- What It Does
- How It Works
- Requirements
- Installation
- Usage
- Options
- Platform Targets
- Examples
- Input and Output Formats
- Real-World Results
- Advanced: GPU on macOS
- Important Notes
- See Also
- Credits
- License
You give the script:
- A local video file
- A target size such as
25mb,1gb, or800kb
The script:
- Measures the video duration with
ffprobe - Calculates the total bitrate budget for the requested file size
- Reserves a portion for audio (AAC, configurable)
- Encodes the video using two-pass
ffmpeg - Produces a
.mp4file close to the target size
If the original file is already smaller than the target, the script stops and tells you.
flowchart LR
A["Input Video"] --> B["ffprobe"]
B -->|"duration"| C["Calculate Bitrate"]
G["Target Size"] --> C
H["Audio Bitrate"] --> C
C -->|"safety margin"| D["Pass 1: Analyze"]
D --> E["Pass 2: Encode"]
E --> F["Output .mp4"]
The script estimates the total bitrate from target size / duration, applies a 2% safety margin, subtracts the audio bitrate, and uses the remainder for video. Two-pass encoding distributes bits more intelligently across scenes, making the final size more predictable than single-pass.
bashffmpeg(includesffprobe)
ffmpeg -version
ffprobe -versioncurl -sSL https://raw.githubusercontent.com/andreswatson/ffmpeg-video-filesize/master/ffmpeg-filesize.sh -o /usr/local/bin/ffmpeg-filesize && chmod +x /usr/local/bin/ffmpeg-filesizegit clone https://github.com/andreswatson/ffmpeg-video-filesize.git
cd ffmpeg-video-filesize
chmod +x ffmpeg-filesize.shbrew install ffmpegHomebrew installs both ffmpeg and ffprobe.
Verify everything is ready:
ffmpeg -version
./ffmpeg-filesize.sh --version./ffmpeg-filesize.sh <input-file> <target-size>Target size formats:
| Format | Meaning |
|---|---|
25 |
25 MB |
25mb |
25 MB |
800kb |
800 KB |
1gb |
1 GB |
1.5gb |
1.5 GB |
A plain number without a suffix is treated as MB. Suffixes are case-insensitive (MB, mb, Mb all work).
| Flag | Description | Default |
|---|---|---|
-o, --output <path> |
Output file path | <input>-<size>.mp4 |
-a, --audio-bitrate <kbps> |
AAC audio bitrate in kbps (0 = no audio) | 128 |
-e, --video-encoder <name> |
Video encoder name | libx264 |
-g, --gpu |
Use macOS VideoToolbox H.264 | off |
-p, --preset <preset> |
Encoder preset | medium |
-f, --force |
Overwrite existing output | off |
-t, --template <name> |
Use a built-in size preset (see below) | -- |
-v, --version |
Show version | -- |
-h, --help |
Show help | -- |
Built-in templates for common sharing platforms. Use --template or pass the size directly.
| Platform | Template Name | Target Size | Official Limit | Command |
|---|---|---|---|---|
| WhatsApp media | whatsapp-safe |
60 MB | ~100 MB | ./ffmpeg-filesize.sh -t whatsapp-safe input.mov |
| WhatsApp HD | whatsapp |
95 MB | ~100 MB | ./ffmpeg-filesize.sh -t whatsapp input.mov |
| Gmail attachment | gmail |
24 MB | 25 MB | ./ffmpeg-filesize.sh -t gmail input.mov |
| Email-friendly | email |
20 MB | varies | ./ffmpeg-filesize.sh -t email input.mov |
| Mobile preview | mobile |
16 MB | -- | ./ffmpeg-filesize.sh -t mobile input.mov |
| Quick preview | preview |
8 MB | -- | ./ffmpeg-filesize.sh -t preview input.mov |
Safe targets are set below the official platform limits because real-world encoding can land slightly above the requested size.
Reference links (limits can change over time):
Compress to about 25 MB:
./ffmpeg-filesize.sh input.mov 25mbUse a built-in template:
./ffmpeg-filesize.sh --template gmail input.movLower audio bitrate for more video room:
./ffmpeg-filesize.sh --audio-bitrate 96 input.mov 12mbSlower preset for better compression efficiency:
./ffmpeg-filesize.sh --preset slow input.mov 15mbCustom output path:
./ffmpeg-filesize.sh --output exported/final.mp4 input.mov 700mbRemove audio completely:
./ffmpeg-filesize.sh --audio-bitrate 0 input.mov 6mb| Type | Supported Formats |
|---|---|
| Video input | mp4, mov, mkv, avi, m4v, webm (anything ffmpeg can read) |
| Default output | .mp4 (H.264 + AAC) |
| Custom output | Any container ffmpeg supports via --output (e.g. .mov) |
The input can be any format ffmpeg can read. The default output is always .mp4. Use --output to control the filename and container:
./ffmpeg-filesize.sh --output final.mov input.mp4 1gbFor general ffmpeg format conversion, audio extraction, and GIF creation, see docs/ffmpeg-recipes.md.
Tested with Rick Astley - Never Gonna Give You Up (63.5 MB, AV1, 1920x1440, 3:32):
| Target | Actual Size | Accuracy |
|---|---|---|
24mb (Gmail) |
23.97 MB | 99.9% |
50mb (General) |
49.56 MB | 99.1% |
60mb (WhatsApp safe) |
59.41 MB | 99.0% |
All tests used default settings (libx264, medium preset, 128 kbps AAC audio).
The GIF below was generated from the same source video using plain ffmpeg and kept under 1 MB:
Direct MP4 clip: assets/rick-astley-preview.mp4
The GIF is about 1.7 MB (600px wide). For the full recipe, see docs/ffmpeg-recipes.md.
On macOS, you can use Apple VideoToolbox for hardware-assisted encoding:
./ffmpeg-filesize.sh --gpu input.mov 50mb
./ffmpeg-filesize.sh --video-encoder h264_videotoolbox input.mov 50mbSame video, same target (25mb), same Mac:
| Encoder | Time | Output Size | Notes |
|---|---|---|---|
libx264 (CPU) |
~40s | 24.94 MB | Within target |
h264_videotoolbox (GPU) |
~71s | 26.29 MB | Overshot target |
For strict size-targeted workflows, libx264 (CPU) is the recommended default. GPU encoding can be useful for speed in other scenarios, but for this specific two-pass size-targeting use case, CPU was both faster and more accurate in real tests.
- The final file size is approximate, not guaranteed to match the exact requested value
- If the input file is already below the target, the script exits early
- Very small targets on long videos may produce poor quality or fail
- For strict platform limits, aim below the official maximum
- Presets (
--preset) only apply tolibx264; VideoToolbox ignores them - Target sizes accept
kb,mb,gbsuffixes, case-insensitively
- FFmpeg Recipes and Format Reference -- format conversion, audio extraction, GIF creation
- Contributing
Created by Andres Watson. LinkedIn: https://linkedin.com/in/andreswatson
Originally built in a pre-LLM / pre-AI-coding-tools era as a practical shell workflow for a real file-size problem. This repository shows one straightforward way to do it with ffmpeg and ffprobe.
Licensed under the MIT License. See LICENSE.
