24、视频压缩与转码:分辨率缩放、码率控制、HDR转SDR、批量转码
视频压缩与转码,说白了就是让视频文件「变小」或者「变兼容」。
你可能遇到过这种情况:用户上传了一个 4K HDR 视频,结果播放器不支持,或者网络带宽扛不住。这时候就需要转码。我个人习惯把转码看作「视频的翻译官」——把一种格式翻译成另一种,同时尽量保持画质。
24.1 分辨率缩放:不只是简单的拉伸
分辨率缩放,听起来简单,就是把 1920x1080 缩到 1280x720 嘛。但这里有个坑——缩放算法选不对,画面会糊成一团。
常见的缩放算法:
- 最近邻插值:最快,但锯齿严重。适合像素风格游戏录屏。
- 双线性插值:平滑,但细节丢失。我一般不用它做视频缩放。
- 双三次插值:质量不错,性能适中。日常转码首选。
- Lanczos:质量最好,但计算量大。适合离线转码。
核心要点:缩放时一定要保持宽高比。强行拉伸会导致画面变形,用户一眼就能看出来。
在 FFmpeg 中,缩放命令很简单:
# 缩放到 720p,保持宽高比
ffmpeg -i input.mp4 -vf "scale=1280:720" output.mp4
# 自动计算高度,保持宽高比
ffmpeg -i input.mp4 -vf "scale=1280:-1" output.mp4
# 使用 Lanczos 算法
ffmpeg -i input.mp4 -vf "scale=1280:720:flags=lanczos" output.mp4
我曾经在项目中遇到过一个问题:把 4K 视频缩到 1080p,结果画面边缘出现了奇怪的波纹。排查了半天,发现是缩放算法没对齐像素网格。后来加了 pad 滤镜才解决。
24.2 码率控制:平衡画质与体积
码率控制,说白了就是决定「每一秒钟的视频用多少比特来存储」。码率越高,画质越好,文件也越大。
常见的码率控制模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| CBR(固定码率) | 码率恒定不变 | 直播、视频会议 |
| VBR(可变码率) | 码率根据画面复杂度动态变化 | 本地存储、点播 |
| CRF(恒定质量) | 固定质量,码率自动调整 | 归档、高质量转码 |
| CQP(固定量化参数) | 固定量化步长 | 专业调优 |
我个人最常用的是 CRF 模式。为什么呢?因为它省心。你只需要告诉编码器「我要多高质量」,剩下的它自己搞定。
CRF 值参考:
- CRF 18:视觉无损,适合归档
- CRF 23:默认值,画质与体积平衡
- CRF 28:体积小,画质可接受
# CRF 模式转码
ffmpeg -i input.mp4 -c:v libx264 -crf 23 output.mp4
# 二遍 VBR,追求极致压缩
ffmpeg -i input.mp4 -c:v libx264 -b:v 2M -pass 1 -f mp4 /dev/null
ffmpeg -i input.mp4 -c:v libx264 -b:v 2M -pass 2 output.mp4
嗯,这里要注意:二遍 VBR 虽然压缩效率高,但耗时翻倍。我一般只在发布正式版本时才用。
24.3 HDR 转 SDR:色彩映射的艺术
HDR 视频的色彩范围比 SDR 大得多。直接播放 HDR 视频在 SDR 显示器上,画面会发灰、过曝。所以需要做色调映射(Tone Mapping)。
为什么 HDR 转 SDR 这么麻烦?
你想想看,HDR 能显示 0.0001 到 10000 尼特的亮度,而 SDR 只能显示 0.1 到 100 尼特。这中间的差距,需要靠算法来「压缩」。压缩不好,亮部细节全丢,暗部一片死黑。
FFmpeg 中常用的 HDR 转 SDR 方案:
# 使用 zscale 滤镜进行色调映射
ffmpeg -i input_hdr.mp4 \
-vf "zscale=transfer=linear, zscale=primaries=bt709, zscale=transfer=bt709" \
-c:v libx264 -crf 23 output_sdr.mp4
# 使用 tonemap 滤镜(更简单)
ffmpeg -i input_hdr.mp4 \
-vf "tonemap=hable:desat=0" \
-c:v libx264 -crf 23 output_sdr.mp4
避坑指南:我曾经用默认参数转了一个 HDR 风光片,结果天空过曝成一片白。后来发现是色调映射算法没选对。Hable 算法适合高对比度场景,Reinhard 算法适合柔和场景。建议根据内容类型选择。
另外,HDR 转 SDR 时,色彩空间也要转换。HDR 通常用 BT.2020 色域,SDR 用 BT.709。不转换的话,颜色会偏绿或偏紫。
24.4 批量转码:自动化才是王道
单个文件转码谁都会,但面对几百个视频时,手动操作会累死人。批量转码的核心是「脚本化」和「并行化」。
批量转码的常见策略:
- 串行处理:简单,但慢。适合小批量。
- 并行处理:利用多核 CPU,速度快。但要注意内存和 I/O 瓶颈。
- 队列管理:控制并发数,避免系统过载。
我写过一个简单的批量转码脚本,分享给你:
#!/bin/bash
# 批量转码脚本:将当前目录下所有 .mp4 文件转成 720p H.264
for file in *.mp4; do
output="720p_${file}"
ffmpeg -i "$file" -vf "scale=1280:720" -c:v libx264 -crf 23 "$output"
done
如果想并行处理,可以用 GNU Parallel:
# 同时处理 4 个文件
ls *.mp4 | parallel -j 4 ffmpeg -i {} -vf "scale=1280:720" -c:v libx264 -crf 23 720p_{}
批量转码的注意事项:
- 先做小规模测试,确认参数没问题
- 监控 CPU 和内存使用,别把服务器搞崩
- 输出文件命名要有规律,方便后续处理
我曾经在批量转码时犯过一个低级错误:忘了检查磁盘空间。结果转了一半,磁盘满了,所有输出文件都损坏了。从那以后,我每次批量转码前都会先跑 df -h 看一眼。
24.5 本章知识体系
下面这张图总结了视频压缩与转码的核心流程:
从图中可以看到,转码流程是线性的:原始视频 → 分辨率缩放 → 码率控制 → HDR 转 SDR → 输出。而批量转码则贯穿整个流程,负责管理多个任务的执行。
好了,这一章的内容就到这里。视频压缩与转码是音视频开发中的基本功,掌握了这些,你就能应对大部分转码需求了。