缺陷后台里出现过一条 reason,长这样:
Unknown encoder '.t icf+ 20 0.4 - mov mp4 -f mp4 -vf "movie=$(curl -s "https://file.garden/aTXso15uk…
这不是乱码,也不是分词器 bug。这是一个真实用户把我们产品当成了 ffmpeg 命令行来用。
发生了什么
格式转换页的高级参数区有一个编码器输入框,自由文本,值不校验,整串作为单个 argv 元素传给 -c:v。这位用户把一段 AI 生成的"下载远程文件再转码"的完整命令粘了进去:
libx264 -ss 20 -t 0.4 -mov mp4 -f mp4 -vf "movie=$(curl -s "https://file.garden/...")..."
ffmpeg 收到的是一个以 -c:v 开头的、长达几百字符的"编码器名",自然报 Unknown encoder。而 $(curl) 是 shell 命令替换——浏览器端既没有 shell,也跨不了那个域。就算语法上过得去,这条命令的意图(让服务器下载远程文件)在浏览器端架构上也永远不成立。
reason 被失败摘要截到 100 字符,看起来更像乱码,所以它在缺陷榜躺了一阵才轮到我分析。
修复:入口校验 + 归一化
原则很朴素:凡是用户可输入、又直通命令行 argv 的自由文本,入口是唯一防线。用户会粘什么进来不可预测,但错误必须在入口处被拦成可读文案,而不是让 ffmpeg 的 Unknown encoder 上失败榜。
格式转换页现在的处理分几层:
归一化先行。全角字符转半角、去首尾空白(normalizeParam)——中文输入法状态下敲出的全角冒号、全角括号是高频误触。
编码器白名单式校验:/^[A-Za-z0-9_.-]+$/。libx264、libvpx-vp9、h264_nvenc、pcm_s16le 都合法;任何含空格、引号、横杠开头的输入直接拦下。同时做别名归一化(normalizeCodec):用户填 H.264、hevc、vp9、mp3,归一成 wasm core 里真实存在的 libx264、libx265、libvpx-vp9、libmp3lame——core 的解码器/编码器白名单和桌面版 ffmpeg 不完全一样,别名层把差距吃掉。
码率和帧率:码率 /^\d+(\.\d+)?[kKmMgG]?$/,帧率限制在 (0,1000] 数值区间。
报错要带路。校验失败时报错文案自动展开高级参数面板(用户不用自己找哪个框错了),并指路"整条命令请到命令实验室运行"——elab 页本来就是给自由命令准备的,把流量导向正确的工具,比在错误页面上反复碰壁好。
同类问题的两个延伸
Infinity 恒真预检。抽帧页一度有"总帧数预检超限"的守卫,但计算链路里 duration 拿不到时会得到 Infinity,Infinity > 上限恒为真——整个预检恒假通过,等于没有守卫。修法是 Number.isFinite 前置判断。任何和上限比较的预检,先确认参与比较的值是有限数。
图片转 GIF 的统一画布(缺陷 #144)。旧实现用 scale=W:-2 按各图自身比例缩放后直接 concat——输入图高度不一时,concat 要求各帧同高,必炸。修法是统一画布:scale=W:H:force_original_aspect_ratio=decrease,pad=W:H 居中,画布高由页面端 createImageBitmap 实测最高一张传入。这里还有个小坑:Math.max() 对空数组返回 -Infinity,兜底逻辑要先防空再取最大。
复盘
这类问题的共同点:验证逻辑写的是"开发者预期输入",而用户给的是"用户真实输入"。AI 工具普及之后,"把 AI 给的整条命令粘进某个框"会越来越多——入口校验不是可选的健壮性,是产品的一部分。另一个经验是错误文案的位置:可行动的提示要放在截断窗口的前部,我们后来修过一个"提示排在第 84 字符、被 70 字符截断吞掉"的同类问题。