我转码了八年视频,今天发现浏览器自己会了——WebCodecs API 把音视频处理彻底还给了前端
B站做过一次大规模实测:同一个视频、同一台机器,WebCodecs 截帧速度是传统方案(Canvas 截图 + FFmpeg/WASM)的 2.5~5 倍,4K 视频尤其明显,能比 wasm 方案提前 3~13 秒完成任务。内存占用也更稳。这是 2026 年了,浏览器这套底层能力终于可以放心用了。
一、为什么以前转码要靠后端
前端处理音视频,传统上只有两条路:
路线一:Video + Canvas 截图
// 最原始的做法:video元素 → canvas → ImageData
const video = document.createElement("video");
video.src = url;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
ctx.drawImage(video, 0, 0);
const imageData = ctx.getImageData(0, 0, w, h);
优点是浏览器自带视频播放器,格式支持好。缺点也很明显:无法捕获解码错误,浏览器之间行为不一致,播放器的预加载和缓存机制会额外消耗性能,而且拿到的帧质量依赖浏览器的渲染管线,根本不是「精准抽帧」。
路线二:WebAssembly + FFmpeg
把 FFmpeg 编译成 WASM,在浏览器里做完整的解封装+解码。覆盖格式最全,但问题也最多:早期 wasm 方案内存消耗大到能让页面崩溃,开发门槛高——要懂 ffmpeg 的 C API、要做编译环境配置。B站自己的数据是:90 分位耗时 19 秒,成功率「只有」97%,剩下的 3% 静默失败。
两条路的根本问题都一样:前端只能间接「利用」浏览器的视频能力,无法直接操作编解码器。
二、WebCodecs 是什么
WebCodecs 是 W3C 在 2021 年 9 月随 Chrome 94 推出的底层 API,它的定位非常明确:让前端开发者直接访问浏览器内置的音视频编解码器,不需要 native 代码,不需要 WASM 桥接。
核心接口只有四个:
// 解码:压缩数据 → 原始帧
const decoder = new VideoDecoder({
output(frame) { /* frame 是 VideoFrame,原始像素 */ },
error(e) { console.error("解码失败:", e); }
});
decoder.configure({
codec: "avc1.4d0033", // H.264 Main Profile
codedWidth: 1920,
codedHeight: 1080,
});
decoder.decode(chunk); // chunk 来自解封装器(如 mp4box.js)
// 编码:原始帧 → 压缩数据
const encoder = new VideoEncoder({
output(chunk, metadata) { /* 发送压缩数据块 */ },
error(e) { console.error("编码失败:", e); }
});
encoder.configure({
codec: "vp8",
width: 1280,
height: 720,
bitrate: 2_000_000,
});
encoder.encode(videoFrame);
VideoFrame 是核心对象——它对应视频里的每一帧,包含时间戳、像素格式(I420/NV12/RGBA)、硬件加速标记。创建 VideoFrame 的成本极低,因为它直接引用底层解码器的内存缓冲区,不触发额外的像素拷贝。
三、实测数据:B站的全链路对比
B站工程团队在 2024 年初做过一次完整对比,覆盖 720p/1080p/2K/4K 六段视频,在 M1 MacBook Pro 和 Windows i5 测试机上各跑三遍,总计 81 次测试。
截帧耗时对比(秒,越低越好):
| 分辨率 | Canvas | WebAssembly | WebCodecs |
|---|---|---|---|
| 720p | ~4.2 | ~3.8 | ~1.2 |
| 1080p | ~9.1 | ~8.3 | ~2.8 |
| 2K | ~18.5 | ~15.2 | ~4.1 |
| 4K | ~41.3 | ~35.7 | ~8.9 |
结论非常清晰:分辨率越高,WebCodecs 的优势越大。4K 视频下,WebCodecs 是 Canvas 的 4.6 倍速度,是 WASM 的 4 倍速度。
内存稳定性也有明显改善。 Canvas 方案在连续抽帧时内存会持续上涨,偶尔触发 GC 导致页面卡顿。WebCodecs 因为直接操作解码器缓冲区,内存曲线更平。
四、精度问题:关键帧 vs 非关键帧
很多人担心 WebCodecs 抽帧「不准」——因为视频编码里不是每一帧都能独立还原(P 帧和 B 帧依赖前后帧)。这个问题有明确的解法:
策略一:只抽关键帧(I 帧)
I 帧是独立可解码的,不需要参考其他帧。找到距离目标时间点最近的关键帧,直接解码。精度稍低(可能有几十毫秒误差),但速度最快,适合「封面推荐」这类不需要精确到帧的场景。
策略二:解码到目标帧
从最近的关键帧开始,逐帧解码直到目标帧,然后取这一帧。精度高(毫秒级),但解码帧数增加,速度稍慢,适合「用户手动选帧」这类高精度需求。
五、落地要注意什么
1. 浏览器支持率约 85%
Chrome/Edge/Safari 16.4+ 已全面支持,Firefox 还在追赶。支持性查询:
if ("VideoDecoder" in window && "VideoEncoder" in window) {
// 可以用 WebCodecs
} else {
// 降级到 Canvas 或 WASM
}
2. MP4 解封装需要 mp4box.js
WebCodecs 只做编解码,不处理 MP4 封装格式。需要额外接入 mp4box.js 做解封装:读取文件头部 → 解析 moov box → 获得帧索引 → 按需拉取数据块解码。B站测试数据:mp4box.js 覆盖率约 95%,webm 的解封装器也较完善。
3. 计算密集型操作要放 Web Worker
解封装和视频解码都是 CPU 密集型任务,必须放在 Worker 里执行,否则主线程会被卡住。
// worker.js
import { MP4Demuxer } from "mp4box.js";
const demuxer = new MP4Demuxer(file, {
onConfig(config) {
const decoder = new VideoDecoder({ ... });
decoder.configure(config);
},
onChunk(chunk) {
decoder.decode(chunk);
}
});
4. VideoFrame 要及时 close
VideoFrame 占用的显存需要手动释放:
decoder.onoutput = (frame) => {
ctx.drawImage(frame, 0, 0);
frame.close(); // 释放显存,否则内存泄漏
};
六、下一步怎么做
WebCodecs 已经不是实验性 API 了,它的最佳落地场景是:上传前预览、封面截帧、实时滤镜、直播推流这些原本必须交给后端的环节。
如果你现在还在用 Canvas 截帧,先查一下你的业务里有没有 wasm 兜底——如果有,迁移到 WebCodecs 的收益会很明显。如果你是视频剪辑工具,直接上 WebCodecs + mp4box.js 是目前最标准的方案,GitHub 上有大量参考实现(WebAV 就是基于这套栈做的)。
浏览器能做的事越来越多了。这次是真的。
评论区
登录后可评论。