H.265/HEVC (High Efficiency Video Coding) delivers the same visual quality as H.264 in files 40β50 % smaller. FFmpeg with the `libx265` encoder is the most powerful way to leverage this codec from the command line.
## H.264 vs H.265 vs AV1: quick comparison
| Codec | Year | Compression | Speed | Compatibility | Licensing |
|---|---|---|---|---|---|
| H.264 (libx264) | 2003 | Good | Very fast | Universal | Patented |
| **H.265 (libx265)** | **2013** | **Very good** | **Medium** | **Wide** | **Patented** |
| AV1 (libaom) | 2018 | Excellent | Slow | Growing | Free |
| VP9 | 2013 | Very good | Slow | Wide | Free |
H.265 is the sweet spot between universal compatibility and compression efficiency.
## Basic MP4 β H.265 conversion
```bash
# Re-encode to H.265 with default quality
ffmpeg -i input.mp4 -c:v libx265 -c:a copy output_h265.mp4
# Specify CRF (Constant Rate Factor)
# 0 = lossless | 18 = near-lossless | 28 = default | 51 = worst
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -c:a copy output.mp4
# Recommended for web (good quality, reduced size)
ffmpeg -i input.mp4 -c:v libx265 -crf 24 -preset medium -c:a aac -b:a 128k output.mp4
```
## The CRF parameter: quality control
```bash
# Visual comparison of CRF values
ffmpeg -i input.mp4 -c:v libx265 -crf 18 crf18_near_lossless.mp4
ffmpeg -i input.mp4 -c:v libx265 -crf 23 crf23_high.mp4
ffmpeg -i input.mp4 -c:v libx265 -crf 28 crf28_default.mp4
ffmpeg -i input.mp4 -c:v libx265 -crf 35 crf35_compressed.mp4
ls -lh *.mp4 # Compare sizes
```
**Practical CRF guide:**
- **18β20**: master files, no perceptible loss
- **21β26**: high-quality online distribution (YouTube, Vimeo)
- **27β32**: streaming, general download
- **33β40**: mobile, very limited bandwidth
## Presets: speed vs compression
```bash
# Available presets (fastest β slowest):
# ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow
# ultrafast β minimal compression, maximum speed (testing only)
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset ultrafast output.mp4
# medium β recommended balance for production
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset medium output.mp4
# slow β better compression, ideal for final deliverables
ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset slow output.mp4
# Rule: same CRF + slower preset = smaller file at equal quality
```
## Hardware acceleration (GPU)
```bash
# NVIDIA NVENC (NVIDIA GPUs)
ffmpeg -i input.mp4 -c:v hevc_nvenc -cq 28 -preset p4 output_nvenc.mp4
# cq: Constant Quality (NVENC equivalent of CRF)
# preset: p1 (fast) β p7 (slow/best quality)
# Intel Quick Sync Video (QSV)
ffmpeg -i input.mp4 -c:v hevc_qsv -global_quality 28 output_qsv.mp4
# Apple VideoToolbox (M1/M2/M3 Mac)
ffmpeg -i input.mp4 -c:v hevc_videotoolbox -q:v 60 output_mac.mp4
# AMD AMF (AMD GPUs on Windows)
ffmpeg -i input.mp4 -c:v hevc_amf -quality quality output_amd.mp4
# Check available encoders on your system
ffmpeg -encoders 2>/dev/null | grep hevc
```
## Two-pass encoding
Two-pass encoding improves quality consistency when targeting a specific bitrate:
```bash
# Pass 1 β analysis
ffmpeg -i input.mp4 -c:v libx265 -b:v 2M -x265-params pass=1 -an -f null /dev/null
# Pass 2 β encoding
ffmpeg -i input.mp4 -c:v libx265 -b:v 2M -x265-params pass=2 -c:a aac -b:a 128k output.mp4
```
## Batch convert a directory (Bash)
```bash
#!/bin/bash
mkdir -p output_h265
for f in *.mp4; do
name="${f%.mp4}"
echo "-> Converting: $f"
ffmpeg -i "$f" \
-c:v libx265 \
-crf 26 \
-preset medium \
-c:a aac \
-b:a 128k \
-movflags +faststart \
"output_h265/${name}_h265.mp4" \
-y
done
echo "Conversion complete"
echo "Original sizes:"
du -sh *.mp4
echo "H.265 sizes:"
du -sh output_h265/*.mp4
```
## Python with subprocess
```python
import subprocess
from pathlib import Path
def compress_h265(input_path, output_path, crf=26, preset='medium'):
"""Compress video to H.265 using FFmpeg from Python."""
cmd = [
'ffmpeg',
'-i', str(input_path),
'-c:v', 'libx265',
'-crf', str(crf),
'-preset', preset,
'-c:a', 'aac',
'-b:a', '128k',
'-movflags', '+faststart',
str(output_path),
'-y',
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(f"ERROR: {result.stderr[-500:]}")
return False
return True
# Batch conversion
directory = Path('input_videos')
for video in sorted(directory.glob('*.mp4')):
output = video.parent / 'h265' / video.name
output.parent.mkdir(exist_ok=True)
ok = compress_h265(video, output, crf=26)
if ok:
orig = video.stat().st_size / 1024 / 1024
comp = output.stat().st_size / 1024 / 1024
savings = (1 - comp / orig) * 100
print(f"OK: {video.name} β {orig:.1f}MB β {comp:.1f}MB ({savings:.0f}% smaller)")
```
## Useful additional options
```bash
# Limit max resolution (scale down if wider than 1080p)
ffmpeg -i input.mp4 \
-vf "scale='min(1920,iw)':'min(1080,ih)':force_original_aspect_ratio=decrease" \
-c:v libx265 -crf 26 output.mp4
# Trim first 60 seconds
ffmpeg -i input.mp4 -t 60 -c:v libx265 -crf 26 first60s.mp4
# -movflags +faststart for web streaming (moves MP4 metadata to front)
ffmpeg -i input.mp4 -c:v libx265 -crf 26 -movflags +faststart output.mp4
# Inspect output video info
ffprobe -v quiet -print_format json -show_streams output.mp4 | python3 -m json.tool
```
## When to use H.265
- **Archive / master**: CRF 18β22, preset slow
- **Online distribution**: CRF 24β28, preset medium
- **Mobile streaming**: CRF 30β35, preset fast
- **Quick conversion (testing)**: preset ultrafast
For **maximum compatibility** (legacy players, smart TVs), use H.264. For **best compression with modern compatibility**, H.265 is the optimal choice in 2024.
Guide