前言
最近在开发一个 B 站视频项目时,遇到了一个诡异的问题:视频转码功能里,1080p 和 720p 都能稳定正常转码,唯独 480p 持续转码失败。排查了很久才找到根因,今天把这个问题、分析过程和最终解决方案完整记录下来,帮大家避开这个 FFmpeg 坑。
问题描述
项目的视频处理系统,需要用 FFmpeg 将用户上传的视频转码为 HLS 流媒体格式,同时生成三种分辨率的版本:
- ✅ 1080p (1920x1080) ------ 转码完全正常
- ✅ 720p (1280x720) ------ 转码完全正常
- ❌ 480p (854x480) ------ 转码直接失败
故障表现:转码进程异常退出,FFmpeg 日志没有打印明确的错误原因,只能定位到 480p 转码命令执行失败。
代码分析
先看项目中核心的 FFmpeg 转码工具类代码,问题就藏在这里:
java
运行
// FFmpegUtils.java
public boolean transcodeToHLS(String inputPath, String outputDir, String playlistName, int width, int height) {
ProcessBuilder builder = new ProcessBuilder(
"ffmpeg",
"-i", inputPath,
// 问题核心代码
"-vf", "scale=" + width + ":-2",
"-c:v", "libx264",
"-preset", "fast",
"-b:v", "2000k",
"-c:a", "aac",
"-b:a", "128k",
"-hls_time", "10",
"-hls_list_size", "0",
"-hls_segment_filename", outputDir + File.separator + "%03d.ts",
"-y",
m3u8Path
);
}
执行 480p 转码时,scale参数会拼接成 scale=854:-2,这就是失败的导火索。
深入分析:问题的根本原因
1. -2 参数的隐藏陷阱
FFmpeg 的scale=宽度:-2语法中,-2表示:自动计算高度,且高度必须是 2 的倍数 。这个设计是为了适配 x264 编码器对视频尺寸的偶数要求,但它不会保持视频原始宽高比,也不会做比例校验。
当源视频和目标分辨率比例不匹配时,自动计算的高度会偏离预期,直接导致转码异常。
2. 缺少关键的宽高比保护逻辑
对比项目中另一个正常运行的 PowerShell 转码脚本,能一眼看出差距:
powershell
# 正常可用的缩放参数
scale=w=$($q.scale):force_original_aspect_ratio=decrease
# 480p对应参数:scale=w=854:480:force_original_aspect_ratio=decrease
而 Java 代码里只用了scale=854:-2,缺失两个核心配置:
- 没有显式指定目标高度(480);
- 没有添加
force_original_aspect_ratio=decrease(保持原始宽高比)。
3. 为什么偏偏 480p 会出问题?
这是 480p 分辨率的特殊性导致的:
表格
| 分辨率 | 宽度 | 高度 | 比例特性 |
|---|---|---|---|
| 1080p | 1920 | 1080 | 标准 16:9,兼容性拉满 |
| 720p | 1280 | 720 | 标准 16:9,极少异常 |
| 480p | 854 | 480 | 非标准 16:9,兼容性极差 |
标准 480p 是 640x480,而常用的 854x480 是适配 16:9 的非标分辨率。一旦源视频是竖屏、宽银幕、非标比例,-2自动计算的高度就会完全错乱,直接触发转码失败。
解决方案
只需要修改 FFmpeg 的 scale 滤镜参数,就能彻底解决问题。
修复后的代码
java
运行
// 原错误写法
"-vf", "scale=" + width + ":-2"
// 修复后写法(推荐)
"-vf", "scale=w=" + width + ":h=" + height + ":force_original_aspect_ratio=decrease"
保守兼容写法
java
运行
"-vf", "scale=w=854:h=480:force_original_aspect_ratio=decrease"
参数核心解释
w=854:强制视频宽度为 854;h=480:强制视频高度为 480;force_original_aspect_ratio=decrease:保持原始宽高比,只缩小不放大,彻底避免视频拉伸、变形、尺寸异常。
修改后重新执行转码,480p、720p、1080p 全部正常输出,无任何异常。
总结
这个问题本质是FFmpeg scale 滤镜参数使用不规范 导致的。scale=宽度:-2是一个 "偷懒写法",在标准 16:9 的 1080p/720p 下能侥幸运行,但在非标 480p 分辨率下会直接暴露缺陷。
核心经验总结
- 视频缩放必须显式指定宽高 :不要依赖
-2自动计算,避免尺寸错乱; - 强制保留原始宽高比 :必加
force_original_aspect_ratio=decrease,防止视频拉伸; - 480p 需特殊处理:854x480 是非标分辨率,转码时一定要做严格的比例校验。
如果你也在做视频转码相关开发,遇到高低分辨率正常、唯独 480p 失败的情况,优先检查scale参数,90% 的概率是这个问题!
相关技术:Java | FFmpeg | HLS | 视频转码 | 流媒体开发