FFmpeg 核心模块
FFmpeg Core Module 是 FFmpeg 的 WebAssembly 编译版本,构成了 ffmpeg.wasm 的基础。它提供核心的音频和视频处理能力,使 FFmpeg 的完整功能能够直接在 Web 浏览器中运行。本页面涵盖了单线程和多线程核心模块的结构、构建过程及运行时特性。
1. 概述
FFmpeg Core Module 提供两种变体:
- 单线程版 (
@ffmpeg/core):兼容所有现代浏览器,使用弹性内存模型。 - 多线程版 (
@ffmpeg/core-mt):利用 Web Workers 和SharedArrayBuffer进行并行处理,需要固定大小的内存分配。
这两个版本都封装了通过 Emscripten 编译的 FFmpeg C 代码库,并链接了一个 JavaScript 胶水层 (bind.js),用于将高级 JS 调用转换为低级 WebAssembly 函数执行。
2. 架构
下图将核心模块的自然语言概念与仓库中的具体代码实体连接起来。
FFmpeg 核心实体映射
2.1 WebAssembly 核心接口
核心模块通过 createFFmpegCore 工厂函数进行初始化。初始化后,它返回一个符合 FFmpegCoreModule 接口的对象,提供执行命令和与虚拟文件系统交互的方法。
3. 编译过程
该模块使用 emcc 并根据构建脚本中定义的特殊标志进行编译。
3.1 Emscripten 标志与绑定
编译使用了以下关键配置:
- MODULARIZE:将输出包装在工厂函数 (
createFFmpegCore) 中。 - ENVIRONMENT=worker:优化构建以在 Web Workers 中执行。
- Bindings:使用
--pre-js注入文件src/bind/ffmpeg/bind.js以提供 JavaScript API。 - Exports:
_ffmpeg、_ffprobe、_malloc和_abort等函数被显式导出,以便从 JavaScript 访问。
3.2 线程模型差异
构建系统根据环境变量 FFMPEG_MT 和 FFMPEG_ST 生成两个不同的二进制文件。
| 特性 | 单线程 (FFMPEG_ST) | 多线程 (FFMPEG_MT) |
|---|---|---|
| 初始内存 | 32 MB | 1024 MB |
| 内存增长 | 启用 (ALLOW_MEMORY_GROWTH) | 禁用 (推荐用于 Pthreads) |
| 线程池 | N/A | 32 个线程 (PTHREAD_POOL_SIZE=32) |
| 二进制输出 | ffmpeg-core.js, ffmpeg-core.wasm | 额外添加 ffmpeg-core.worker.js |
4. 数据流与执行
核心模块对 FFmpeg 命令采用请求 - 响应式的执行方式。
执行序列图
4.1 字符串处理
由于 WebAssembly 在线性内存堆上运行,JavaScript 字符串必须转换为 C 风格的指针。bind.js 层提供了 stringToPtr 和 stringsToPtr 工具,它们使用 Module["_malloc"] 分配内存并使用 Module["stringToUTF8"] 写入数据。
4.2 日志与进度
- 日志:核心模块捕获来自 C 代码的
stdout和stderr,并通过setLogger将其重定向到logger函数。 - 进度:C 代码调用
receiveProgress(通过 Emscripten 绑定),这会触发带有Progress对象的progress回调,该对象包含当前的time和progress比率。
5. 多线程资源定位(Hash Hack)
在多线程环境中,ffmpeg-core.worker.js 由浏览器启动。如果资源托管在 CDN 上或作为 Blobs,Worker 可能无法找到 .wasm 文件。ffmpeg.wasm 通过将 wasmURL 和 workerURL 编码到主脚本 URL 的哈希片段中来解决此问题。
bind.js 中的 _locateFile 函数在运行时解码此信息:
function _locateFile(path, prefix) {
const mainScriptUrlOrBlob = Module["mainScriptUrlOrBlob"];
if (mainScriptUrlOrBlob) {
const { wasmURL, workerURL } = JSON.parse(
atob(mainScriptUrlOrBlob.slice(mainScriptUrlOrBlob.lastIndexOf("#") + 1))
);
if (path.endsWith(".wasm")) return wasmURL;
if (path.endsWith(".worker.js")) return workerURL;
}
return prefix + path;
}6. 虚拟文件系统 (VFS)
核心模块将 Emscripten 的 FS 库暴露出来,用于与内存文件系统交互。这允许 JavaScript 在执行前写入输入文件,并在执行后读取输出文件。
暴露的关键 FS 方法:
writeFile(path, data):将Uint8Array或string写入 VFS。readFile(path, opts):从 VFS 读取数据。mount(type, config, path):挂载专用文件系统(如WORKERFS),以便在不将数据复制到 WASM 堆的情况下处理大文件。