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 核心实体映射

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 MB1024 MB
内存增长启用 (ALLOW_MEMORY_GROWTH)禁用 (推荐用于 Pthreads)
线程池N/A32 个线程 (PTHREAD_POOL_SIZE=32)
二进制输出ffmpeg-core.js, ffmpeg-core.wasm额外添加 ffmpeg-core.worker.js

4. 数据流与执行

核心模块对 FFmpeg 命令采用请求 - 响应式的执行方式。

执行序列图

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 堆的情况下处理大文件。