
使用 webcodecs api 直接编码 webgl 帧为 h.264,并手动构建 mp4 容器,是当前浏览器端最高效、原生支持、跨平台(含 android chrome)的视频生成方案。
在 Web 应用中将 WebGL 渲染画面实时录制为 MP4 视频,长期以来受限于性能与兼容性——WASM 版 FFmpeg 体积大、启动慢、移动端耗电高;老旧的第三方 MP4 封装库(如已归档的 wasm-mp4-encoder)缺乏维护,且在现代 Android Chrome(v110+)中因 WebAssembly 线程或内存模型变更而失效。真正高性能、低延迟、可生产落地的解法,是拥抱浏览器原生能力:WebCodecs API + 手动 MP4 muxing。
✅ 核心优势:为什么 WebCodecs 是最优选?
- 零依赖、零加载:内置于 Chromium(Chrome / Edge / Android Chrome)、Firefox(部分支持)、Safari(逐步推进),无需下载 MB 级 WASM 模块;
- 硬件加速直通:通过 hardwareAcceleration: 'prefer-hardware'(需配合 latencyMode: 'realtime')可触发 GPU 编码器,帧率稳定、CPU 占用极低;
- 流式处理友好:VideoEncoder 支持异步 encode() + output 回调,天然适配逐帧捕获(readPixels → VideoFrame → 编码)流水线;
- Android 全面兼容:自 Chrome 94 起已在 Android 12+ 设备稳定支持,实测 Android 13(Chrome 124)完全可用。
? 实战三步走:从 Canvas 到可播放 MP4
第一步:高效捕获帧并构造 VideoFrame
避免 readPixels 后冗余转换。推荐直接使用 canvas> 元素作为 VideoFrame 输入源(无需 ImageData 中转):
// 假设 yourWebGLCanvas 已渲染完毕
const frame = new VideoFrame(yourWebGLCanvas, {
timestamp: performance.now() * 1000, // μs 精度时间戳
visibleRect: new DOMRect(0, 0, canvas.width, canvas.height)
});⚠️ 注意:确保 Canvas 的 alpha: false(WebGL 上下文创建时设置),否则透明通道会强制启用 BGRA 转换,降低性能。
第二步:配置并启动 VideoEncoder
关键配置决定质量与性能平衡:
const encoder = new VideoEncoder({
output: handleChunk,
error: e => console.error("Encoder error:", e.message)
});
encoder.configure({
codec: "avc1.42C01E", // H.264 Baseline Profile Level 3.0 (兼容性最佳)
width: canvas.width,
height: canvas.height,
bitrateMode: "variable",
bitrate: 2_000_000, // 2 Mbps(按需调整)
framerate: 30,
latencyMode: "realtime", // 必选!启用低延迟编码路径
avc: { format: "annexb" } // 输出带 NALU 起始码(00 00 00 01),MP4 muxing 必需
});第三步:封装为 MP4 —— 手写原子(Atom)而非依赖黑盒库
MP4 本质是“盒子嵌套”结构。最小可播放 MP4 至少需以下原子:
- ftyp(文件类型声明)
- moov(元数据:mvhd, trak, mdia, minf, stbl 等)
- mdat(实际 H.264 数据)
你无需实现全部——可复用成熟轻量库完成核心 muxing:
- ✅ MP4Box.js(推荐):专注 MP4 操作,支持 addTrack() + addFrame() 流式写入,自动处理 SPS/PPS 注入、PTS/DTS 计算、stss(关键帧表)生成;
- ✅ mux.js:Video.js 生态,API 简洁,适合简单场景;
- ❌ 避免自行手写全 MP4 结构(如原始答案中的 stss 示例),易出错且维护成本高。
使用 MP4Box.js 封装示例:
import MP4Box from 'mp4box';
const mp4boxFile = MP4Box.createFile();
mp4boxFile.enableLogging(false);
// 添加 H.264 轨道(传入 SPS/PPS 及编码参数)
const track = mp4boxFile.addTrack("video/h264", {
width: canvas.width,
height: canvas.height,
timescale: 1000,
bitrate: 2_000_000
});
// 在 handleChunk 中注入帧数据
function handleChunk(encodedFrame, metadata) {
if (metadata.decoderConfig) {
// 注入 SPS/PPS(仅首次)
const sps = new Uint8Array(metadata.decoderConfig.description);
mp4boxFile.setVideoTrackMetadata(track.id, sps, null);
}
// 添加帧(自动处理关键帧标记、时间戳)
const sample = {
data: encodedFrame,
pts: encodedFrame.timestamp, // μs
cts: 0,
dts: encodedFrame.timestamp,
is_sync: metadata.keyFrame || false,
duration: 1000 / 30 * 1000 // 30fps → 33333 μs/frame
};
mp4boxFile.addSample(track.id, sample);
}
// 结束后导出 Blob
function exportMP4() {
const buffer = mp4boxFile.flush();
const blob = new Blob([buffer], { type: "video/mp4" });
const url = URL.createObjectURL(blob);
// 下载或播放...
}? 关键注意事项
- 内存管理:每次 VideoFrame 使用后务必调用 .close(),否则导致内存泄漏;
- 帧率控制:WebGL 渲染帧率可能高于目标视频帧率(如 60fps → 30fps),需主动丢帧(检查 encoder.encodeQueueSize > 2);
- SPS/PPS 处理:H.264 编码器首帧输出包含 SPS/PPS,必须在 MP4 的 moov 中正确写入,否则无法播放;
- 移动端限制:Android Chrome 对 VideoEncoder 的 bitrate 上限较保守(建议 ≤ 4Mbps),过高会导致编码失败;
- 回退方案:对不支持 WebCodecs 的浏览器(如 Safari
✅ 总结
放弃臃肿的 WASM FFmpeg,拥抱 WebCodecs 是浏览器端视频编码的范式升级。它以原生性能、低延迟、高兼容性,成为 WebGL 录屏、实时可视化导出、Web 游戏录像等场景的首选技术栈。结合 MP4Box.js 这类专业 muxing 库,你能在 200 行核心代码内,构建出媲美桌面软件的 Web 视频生成能力——轻量、可靠、面向未来。











