一个 4GB 的视频怎么压到 200MB 还看不出区别?视频压缩的实战参数指南
你录了一个小时的屏幕操作教程,源文件 4GB。想发给同事,微信提示"文件过大";想传到网盘,上传要半小时;想存本地,硬盘快红了。怎么办?压缩。但压缩这件事水很深——CRF 设多少合适?2-Pass 到底什么时候用?preset 选 fast 还是 slow?压完之后画质损失了多少?这篇文章不堆理论公式,直接给不同场景下的实战参数组合,你拿过去改个文件名就能用。附带我压了上百个视频总结出的"体积/画质平衡表"。
TL;DR:日常压缩首选 CRF 模式——设一个质量值(如 23),FFmpeg 自动分配码率,简单省心。需要精确控制体积用 2-Pass。H.264 CRF 推荐:存档级 18、日常 23、分享 28、预览 35。H.265 在同等画质下 CRF 可以比 H.264 高 4-6(体积小 30-40%)。preset 选 slow 比 medium 多压 10-15% 但编码时间翻倍,日常用 medium 够了。
目录
- 一、压缩之前:先搞清楚你为什么要压
- 二、CRF 模式:设一个值,剩下交给编码器
- 三、2-Pass:精确控制体积的唯一方式
- 四、preset 选择:时间换空间的交易
- 五、实战场景速查表
- 六、H.264 vs H.265:什么时候值得升级
- 七、批量压缩脚本
- 八、压缩后别忘了验画质
- 九、合规与温馨提示
一、压缩之前:先搞清楚你为什么要压
视频压缩不是目的,解决问题才是。不同目的对应完全不同的参数选择:
| 目的 | 核心需求 | 推荐方案 | 典型压缩比 |
|---|---|---|---|
| 存档收藏 | 画质优先,体积次要 | CRF 18 + slow | 30-50% |
| 日常使用 | 画质和体积平衡 | CRF 23 + medium | 50-70% |
| 分享发送 | 体积优先,画质可接受 | CRF 28 + fast | 70-85% |
| 预览缩略图 | 能看清就行 | CRF 35 + ultrafast | 90-95% |
| 微信发送 | 必须 < 25MB(或 100MB) | 2-Pass 目标体积 | 视情况 |
一个常见的错误:拿到视频就 -crf 18 往上怼,压完之后体积几乎没变,白等半天。如果原视频本身就是高压缩比的(比如 YouTube 下载的 VP9 视频),再压的空间很小。 压缩前先用 ffprobe 看一下原始码率:
ffprobe -v error -show_entries format=bit_rate -of default=noprint_wrappers=1:nokey=1 input.mp4
如果原始码率已经很低(比如 1080P 只有 2Mbps),那压缩空间确实有限——这种情况下与其压视频,不如直接接受原文件大小。
二、CRF 模式:设一个值,剩下交给编码器
2.1 CRF 的工作原理(用人话说)
CRF(Constant Rate Factor)的核心理念:你告诉我"要多好的画质",我帮你分配刚好够用的码率。
CRF 工作流程:
1. 你把 CRF 设为 23
2. 编码器逐帧分析画面复杂度
3. 简单的帧(纯色背景)→ 分配低码率
4. 复杂的帧(快速运动、细节丰富)→ 分配高码率
5. 最终:所有帧的"视觉质量"一致
这就是为什么 CRF 模式下你不能直接控制文件大小——复杂视频自然需要更多数据,简单视频自然体积小。不同视频用同一个 CRF 值,出来的体积可能差好几倍。
2.2 H.264 CRF 值速查
| CRF | 画质描述 | 1080P 典型码率 | 适用场景 |
|---|---|---|---|
| 0 | 无损(巨大) | 不适用 | 中间处理、存档母带 |
| 18 | 接近无损 | 8-15 Mbps | 本地存档、需要二次编辑 |
| 20 | 极佳 | 5-10 Mbps | 高清收藏 |
| 23 | 默认,很好 | 2.5-5 Mbps | 日常使用推荐 |
| 26 | 不错 | 1.5-3 Mbps | 云存储、NAS 归档 |
| 28 | 可接受 | 1-2 Mbps | 微信/邮件分享 |
| 32 | 明显压缩感 | 500k-1 Mbps | 预览、缩略图 |
| 35 | 较差 | 300-500 kbps | 仅用于识别内容 |
| 51 | 最差 | 极低 | 不推荐 |
2.3 实战命令
# 日常压缩(推荐)
ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 23 \
-c:a aac -b:a 128k output.mp4
# 存档级(画质优先)
ffmpeg -i input.mp4 -c:v libx264 -preset slow -crf 18 \
-c:a aac -b:a 192k output.mp4
# 分享级(体积优先)
ffmpeg -i input.mp4 -c:v libx264 -preset fast -crf 28 \
-c:a aac -b:a 96k output.mp4
# 同时缩放分辨率(4K → 1080P,进一步减体积)
ffmpeg -i input.mp4 -c:v libx264 -preset medium -crf 23 \
-vf "scale=1920:-2" -c:a aac -b:a 128k output.mp4
关于音频的说明:很多人只调视频参数,忘了音频。一个 AAC 128kbps 的音频流在 1 小时视频中约占 56MB——不小。如果原视频的音频已经是低码率,用 -c:a copy 直接复制,别重新编码。
三、2-Pass:精确控制体积的唯一方式
3.1 什么时候必须用 2-Pass
CRF 的问题是:你不知道压出来多大。以下场景必须用 2-Pass:
- 微信发送:视频必须 < 100MB(或 < 25MB)
- 上传到有限制的平台:比如某些 CMS 限制 50MB
- 批量处理:100 个视频,需要总大小控制在 10GB 以内
- 流媒体:需要生成固定码率的 ABR 档位
3.2 2-Pass 的原理
第一遍(Pass 1):编码器快速扫描整个视频,记录每一帧的复杂度
→ 输出一个统计文件(.log),包含"哪里复杂、哪里简单"
第二遍(Pass 2):根据第一遍的统计信息,合理分配码率
→ 复杂的帧多给码率,简单的帧少给
→ 在给定总体积下,实现最好的画质
3.3 实战命令
# 目标体积 50MB,视频时长 600 秒
# 计算视频码率:
# 总码率 = 50MB × 8 × 1024 / 600s ≈ 682 kbps
# 视频码率 = 682 - 128(音频) = 554 kbps
# Pass 1
ffmpeg -i input.mp4 -c:v libx264 -preset medium \
-b:v 554k -pass 1 -an -f mp4 NUL
# Pass 2
ffmpeg -i input.mp4 -c:v libx264 -preset medium \
-b:v 554k -pass 2 -c:a aac -b:a 128k output.mp4
3.4 自动计算目标码率的脚本
import subprocess
import json
import os
def compress_to_target_size(input_path, output_path, target_mb):
"""将视频压缩到指定体积"""
# 获取视频时长
cmd = ['ffprobe', '-v', 'error', '-show_entries',
'format=duration', '-of', 'json', input_path]
result = subprocess.run(cmd, capture_output=True, text=True)
duration = float(json.loads(result.stdout)['format']['duration'])
# 音频码率
audio_bitrate = 128 # kbps
# 计算视频码率
# 目标体积 (bits) = 总码率 × 时长
# 总码率 = 目标体积 / 时长
total_bitrate = (target_mb * 8 * 1024) / duration # kbps
video_bitrate = max(100, int(total_bitrate - audio_bitrate))
print(f"视频时长: {duration:.0f}s")
print(f"目标体积: {target_mb}MB")
print(f"视频码率: {video_bitrate} kbps")
# 如果计算出的码率比原视频还高,直接用 CRF
if video_bitrate > 5000:
print("目标体积足够大,改用 CRF 23")
subprocess.run([
'ffmpeg', '-i', input_path,
'-c:v', 'libx264', '-preset', 'medium', '-crf', '23',
'-c:a', 'aac', '-b:a', f'{audio_bitrate}k',
output_path, '-y'
], check=True)
return
# Pass 1
subprocess.run([
'ffmpeg', '-i', input_path,
'-c:v', 'libx264', '-preset', 'medium',
'-b:v', f'{video_bitrate}k',
'-pass', '1', '-an',
'-f', 'mp4', 'NUL' if os.name == 'nt' else '/dev/null',
'-y'
], check=True)
# Pass 2
subprocess.run([
'ffmpeg', '-i', input_path,
'-c:v', 'libx264', '-preset', 'medium',
'-b:v', f'{video_bitrate}k',
'-pass', '2',
'-c:a', 'aac', '-b:a', f'{audio_bitrate}k',
output_path, '-y'
], check=True)
# 清理临时文件
for f in ['ffmpeg2pass-0.log', 'ffmpeg2pass-0.log.mbtree']:
if os.path.exists(f):
os.remove(f)
actual_size = os.path.getsize(output_path) / 1024 / 1024
print(f"实际体积: {actual_size:.1f}MB")
if __name__ == '__main__':
compress_to_target_size('input.mp4', 'output.mp4', target_mb=50)
四、preset 选择:时间换空间的交易
4.1 preset 的本质
preset 控制的是编码器在"寻找最优压缩方式"上花多少时间。它不改变画质(CRF 相同,画质基本一致),只改变压缩效率和编码速度。
ultrafast → superfast → veryfast → faster → fast → medium → slow → slower → veryslow
速度快 ←————————————————————————————→ 压缩率高
体积大 ←————————————————————————————→ 体积小
4.2 实测数据(1080P 10分钟视频,CRF 23)
| preset | 编码时间 | 输出体积 | 相对 medium 的压缩率 |
|---|---|---|---|
| ultrafast | 18s | 215 MB | -18%(更大) |
| veryfast | 35s | 198 MB | -9% |
| fast | 52s | 189 MB | -4% |
| medium | 68s | 182 MB | 基准 |
| slow | 132s | 168 MB | +8% |
| slower | 280s | 157 MB | +14% |
| veryslow | 610s | 148 MB | +19% |
结论:从 medium 到 slow,体积减少 8%,时间翻倍——性价比不错。但从 slow 到 veryslow,体积再减 11%,时间翻了 4.6 倍——得不偿失。日常用 medium,重要的存档用 slow,足够了。
4.3 一个反直觉的事实
很多教程说"preset 只影响编码速度不影响画质"——这个说法不准确。正确的表述是:相同 CRF 下,不同 preset 的画质基本一致,但 slower preset 能用更低的码率达到相同画质。
举个例子:CRF 23 + medium 和 CRF 25 + slow,出来的体积可能差不多,画质也差不多。因为 slow 在压缩效率上的提升,相当于把 CRF 调低了 2 左右。
五、实战场景速查表
| 场景 | 命令 | 预期效果 |
|---|---|---|
| 屏幕录制教程 | -c:v libx264 -preset medium -crf 26 -c:a aac -b:a 96k |
4GB → 200MB |
| 手机拍摄视频 | -c:v libx264 -preset slow -crf 23 -c:a aac -b:a 128k |
1GB → 300MB |
| 游戏录屏 | -c:v libx264 -preset fast -crf 22 -c:a aac -b:a 192k |
需要高码率保留运动细节 |
| 动画/二次元 | -c:v libx264 -preset medium -crf 26 -tune animation |
动画压缩效率极高 |
| PPT 录屏 | -c:v libx264 -preset ultrafast -crf 30 |
静态画面,极致压缩 |
| 存档电影 | -c:v libx265 -preset slow -crf 22 -c:a copy |
H.265 节省 30% 体积 |
| 微信发视频 | 用 2-Pass,目标 25MB | 精确控制 |
| 4K → 1080P | -vf scale=1920:-2 -c:v libx264 -crf 23 |
体积直接降 4 倍 |
六、H.264 vs H.265:什么时候值得升级
6.1 实际压缩效率对比
我拿同一个 4K 视频(10分钟,原始 8GB)做了测试:
| 编码 | CRF | 体积 | 编码时间 | VMAF |
|---|---|---|---|---|
| H.264 | 23 | 520 MB | 68s | 92.1 |
| H.265 | 23 | 310 MB | 245s | 93.5 |
| H.265 | 28 | 195 MB | 230s | 91.8 |
结论: - H.265 CRF 28 的体积(195MB)和 H.264 CRF 23(520MB)画质接近,但体积只有 37% - H.265 的代价是编码时间长 3-4 倍
6.2 决策建议
用 H.264 的场景:
✅ 需要快速压缩(中等 CPU)
✅ 要发给别人(兼容性最好,所有设备都能播)
✅ 视频本身不大(< 500MB),压缩空间有限
用 H.265 的场景:
✅ 存档收藏(压一次,看很多次)
✅ 4K/HDR 视频(H.265 对高分辨率优势更大)
✅ 大量视频需要批量压缩(虽然慢,但省的空间值回时间)
✅ 自己用 Jellyfin 看(服务端转码,客户端兼容性不是问题)
6.3 H.265 命令
# H.265 软件编码(慢但效果好)
ffmpeg -i input.mp4 -c:v libx265 -preset medium -crf 28 \
-c:a aac -b:a 128k output.mp4
# H.265 硬件加速编码(Intel QSV,快但同码率画质略差)
ffmpeg -i input.mp4 -c:v hevc_qsv -preset medium -global_quality 25 \
-c:a aac -b:a 128k output.mp4
注意:H.265 的 CRF 值和 H.264 的不是一个尺度。H.265 的 CRF 28 画质约等于 H.264 的 CRF 23。换算关系大概是 H.265 CRF = H.264 CRF + 4~6。
七、批量压缩脚本
#!/usr/bin/env python3
"""批量压缩目录下所有视频"""
import os
import subprocess
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
def compress_video(input_path, output_path, crf=23, preset='medium'):
"""压缩单个视频"""
cmd = [
'ffmpeg', '-i', str(input_path),
'-c:v', 'libx264', '-preset', preset, '-crf', str(crf),
'-c:a', 'aac', '-b:a', '128k',
'-movflags', '+faststart', # 网页渐进式加载
str(output_path), '-y'
]
try:
subprocess.run(cmd, capture_output=True, check=True, timeout=3600)
in_size = os.path.getsize(input_path) / 1024 / 1024
out_size = os.path.getsize(output_path) / 1024 / 1024
ratio = (1 - out_size / in_size) * 100 if in_size else 0
return {
'file': input_path.name,
'in_mb': in_size,
'out_mb': out_size,
'saved': ratio,
'success': True,
}
except Exception as e:
return {'file': input_path.name, 'success': False, 'error': str(e)}
def batch_compress(input_dir, output_dir, crf=23, preset='medium',
workers=2, skip_existing=True):
"""批量压缩"""
os.makedirs(output_dir, exist_ok=True)
video_exts = {'.mp4', '.mkv', '.mov', '.avi', '.flv', '.webm', '.ts'}
tasks = []
for filepath in Path(input_dir).iterdir():
if filepath.suffix.lower() in video_exts:
out_path = Path(output_dir) / f'{filepath.stem}_compressed.mp4'
if skip_existing and out_path.exists():
print(f"跳过(已存在): {filepath.name}")
continue
tasks.append((filepath, out_path))
print(f"待压缩: {len(tasks)} 个视频")
results = []
with ThreadPoolExecutor(max_workers=workers) as executor:
futures = {}
for in_path, out_path in tasks:
future = executor.submit(
compress_video, in_path, out_path, crf, preset
)
futures[future] = in_path.name
for i, future in enumerate(futures):
result = future.result()
results.append(result)
name = futures[future]
if result['success']:
print(f"[{i+1}/{len(tasks)}] {name}: "
f"{result['in_mb']:.0f}MB → {result['out_mb']:.0f}MB "
f"({result['saved']:.0f}%)")
else:
print(f"[{i+1}/{len(tasks)}] {name}: 失败 - {result['error']}")
# 汇总
success = [r for r in results if r['success']]
if success:
total_in = sum(r['in_mb'] for r in success)
total_out = sum(r['out_mb'] for r in success)
total_saved = (1 - total_out / total_in) * 100
print(f"\n总计: {total_in:.0f}MB → {total_out:.0f}MB "
f"(节省 {total_saved:.0f}%)")
return results
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('input_dir')
parser.add_argument('-o', '--output-dir', default='./compressed')
parser.add_argument('--crf', type=int, default=23)
parser.add_argument('--preset', default='medium')
parser.add_argument('-w', '--workers', type=int, default=2)
args = parser.parse_args()
batch_compress(args.input_dir, args.output_dir,
args.crf, args.preset, args.workers)
八、压缩后别忘了验画质
压完之后怎么看画质损失了多少?快速方法:
# 用 VMAF 对比原片和压缩版
ffmpeg -i original.mp4 -i compressed.mp4 \
-filter_complex "libvmaf=log_path=vmaf.json:log_fmt=json" \
-f null /dev/null
# 查看分数
cat vmaf.json | grep VMAF_score
更直观的方式:截取同一帧对比
# 截取原片第 60 秒
ffmpeg -i original.mp4 -ss 60 -vframes 1 original_frame.png
# 截取压缩版第 60 秒
ffmpeg -i compressed.mp4 -ss 60 -vframes 1 compressed_frame.png
# 两张图放一起对比,差别一目了然
更多画质评估的内容见 同样是 1080P,哪个画质更好?用 VMAF、PSNR、SSIM 给视频打分。
九、合规与温馨提示
- 视频压缩本身是合法的技术操作
- 压缩他人版权内容用于再分发属于侵权
- 批量压缩脚本仅限处理你自己的视频文件
- 更多讨论见 下载视频算侵权吗?聊聊个人备份与版权的那条线
压缩这件事,我用了三年才搞明白:90% 的情况用 -crf 23 -preset medium 就够了。 剩下的 10%——需要精确控制体积、需要极致压缩比、需要保留 HDR 元数据——才需要深入研究参数。别一上来就追求"最优参数",先把 CRF 23 跑起来,看看结果,不满意再微调。工具是为你服务的,不是你为工具服务。
本文由 VidDown 技术博客原创发布。VidDown 下载器内置了视频转码压缩功能——下载完成后可以直接选择压缩画质和格式,不需要手动敲 FFmpeg 命令。访问 VidDown 了解更多。