FFmpeg.wasm:在浏览器内运行 FFmpeg 的 WebAssembly 技术解析

FFmpeg.wasm Browser

FFmpeg.wasm 是将知名的开源音视频处理工具 FFmpeg 通过 Emscripten 编译为 WebAssembly 的版本。它让浏览器能够在不依赖后端服务器的情况下,直接在客户端执行复杂的视频解码、转码、合并等操作。

项目链接:github.com/ffmpegwasm/ffmpeg.wasm

传统架构 vs FFmpeg.wasm 架构

传统架构(后端转码):
用户 → 上传视频 → 服务器 FFmpeg → 输出 → 用户下载
问题:服务器 CPU 资源消耗大、带宽成本高、隐私疑虑

FFmpeg.wasm 架构:
用户 → 浏览器内 FFmpeg.wasm → 输出 Blob → 用户下载
优点:零服务器消耗、隐私保障、离线可用

技术核心:WebAssembly 是什么?

WebAssembly(Wasm)是一种低层级的二进制格式,设计目标是让编译语言(C、C++、Rust 等)的代码在浏览器中接近原生速度执行。

FFmpeg 的源代码是 C 语言,Emscripten 工具链可以将 C 代码编译成 .wasm 二进制模块,配合 JavaScript 胶水代码(ffmpeg-core.js)在浏览器环境运行。

FFmpeg.wasm 的两种运行模式

单线程模式(Single-threaded)

import { FFmpeg } from '@ffmpeg/ffmpeg';
import { toBlobURL } from '@ffmpeg/util';

const ffmpeg = new FFmpeg();
await ffmpeg.load({
  coreURL: await toBlobURL(
    'https://unpkg.com/@ffmpeg/[email protected]/dist/esm/ffmpeg-core.js',
    'text/javascript'
  ),
  wasmURL: await toBlobURL(
    'https://unpkg.com/@ffmpeg/[email protected]/dist/esm/ffmpeg-core.wasm',
    'application/wasm'
  ),
});
  • 不需要 SharedArrayBuffer
  • 不需要 特殊 HTTP Header(COOP/COEP)
  • 所有操作在单一 JS 线程(Worker)中进行
  • 适合大多数静态网站部署(Cloudflare Pages、GitHub Pages 等)

多线程模式(Multi-threaded)

await ffmpeg.load({
  coreURL: 'ffmpeg-core-mt.js',  // 多线程版 core
  wasmURL: 'ffmpeg-core-mt.wasm',
  workerURL: 'ffmpeg-core-mt.worker.js',
});
  • 使用 SharedArrayBuffer 在多个 Web Worker 之间共享内存
  • 需要服务器设置以下 HTTP Headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
  • 速度显著提升(可利用多核心 CPU)
  • 但设置较为复杂,部分 CDN 不支持

虚拟文件系统(Emscripten FS)

FFmpeg.wasm 没有直接访问本地磁盘的能力。它使用 Emscripten 的虚拟文件系统(VFS) 模拟磁盘操作:

// 将 ArrayBuffer 写入虚拟 FS
await ffmpeg.writeFile('input.ts', new Uint8Array(buffer));

// 运行 FFmpeg 命令(在虚拟 FS 中操作)
await ffmpeg.exec(['-i', 'input.ts', '-c', 'copy', 'output.mp4']);

// 读取输出
const data = await ffmpeg.readFile('output.mp4');

// 清理释放内存(重要!)
await ffmpeg.deleteFile('input.ts');
await ffmpeg.deleteFile('output.mp4');

关键: 虚拟 FS 中的数据存在于 JavaScript 堆(Heap)中,必须在使用完毕后手动删除,否则会占用浏览器内存直到页面关闭。

内存限制与最佳实践

这是 FFmpeg.wasm 最大的先天限制:

项目 限制
JS Heap 上限 约 1-2 GB(依浏览器和 OS 而定)
Wasm 内存模型 32-bit 定址,单一模块上限 4 GB
实际可用 通常 500 MB - 1.5 GB

建议的使用场景

  • ✅ 720p 以下,30 分钟以内的视频
  • ✅ 主要是 concat(分片合并),非重新编码
  • ✅ 公开链接的技术研究与测试
  • ❌ 4K/1080p 长片(内存不足)
  • ❌ 复杂的滤镜和重新编码(速度过慢)

内存管理最佳实践

// 1. 分批下载,避免同时持有所有 ArrayBuffer
const BATCH = 5;
for (let i = 0; i < segments.length; i += BATCH) {
  const batch = await downloadBatch(segments.slice(i, i + BATCH));
  // 立即写入 VFS,可回收 JS 侧的 ArrayBuffer
  for (const [idx, buf] of batch.entries()) {
    await ffmpeg.writeFile(`seg${i + idx}.ts`, new Uint8Array(buf));
  }
}

// 2. 运行完成后立即清理 VFS
await ffmpeg.deleteFile('output.mp4');

与后端 API 的比较

面向 FFmpeg.wasm 后端 API
服务器成本 高(CPU 密集)
隐私性 100%(本地处理) 需信任服务器
处理速度 慢(单核 Wasm) 快(多核 + GPU)
大文件支持 受浏览器内存限制 几乎无限制
部署复杂度 极简(静态资源) 需要服务器维护
最适场景 小型转换、研究工具 生产环境大批量处理

实际应用:本站实现方式

本站的 HLS 研究工具 采用以下组合:

  • @ffmpeg/ffmpeg 0.12.x(单线程,无需 COOP/COEP)
  • @ffmpeg/core 0.12.x(对应的 Wasm Core)
  • 通过 unpkg CDN 动态加载,首次加载约 15-30 秒(Wasm 模块约 30MB)
  • 执行 ffmpeg -f concat -safe 0 -i concat.txt -c copy output.mp4 完成 TS 合并

延伸阅读

想立即测试您的 M3U8 链接吗?

🚀 立即测试 M3U8 在线播放器