Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译] 构建WebAssembly版本的FFmpeg——ffmpeg.wasm:第六部分:深入研究文件系统 #51

Closed
gogoend opened this issue Aug 2, 2020 · 0 comments

Comments

@gogoend
Copy link
Owner

gogoend commented Aug 2, 2020

原文:https://itnext.io/build-ffmpeg-webassembly-version-ffmpeg-js-part-6-a-deep-dive-into-file-system-56eba10067ca
作者:Jerome Wu
翻译:gogoend

2020/1/14:自 ffmpeg.wasm v0.6.0起,我已经抛弃了 IDBFS 和 NODEFS,并使用postMessage中的 Transferable以解决这个问题。IDBFS的主要问题是文件限制过小(大约200 MB),但你仍然可以阅读本文,将本文当作一个使用这两种文件系统的示例。

前一篇文章:[译] 构建WebAssembly版本的FFmpeg——ffmpeg.wasm:第五部分:ffmpeg.wasm v0.3 —— pre-js 和 流媒体直播

在这一部分你将了解到:

  1. MEMFS、IDBFS 和 NODEFS 的区别
  2. 如何挂载 IDBFS 和 NODEFS
  3. 解决一个现实的问题:ffmepg.js的文件大小限制

MEMFS、IDBFS 和 NODEFS 的区别

默认情况下,当你使用 Emscripten 来转换任何具有文件系统操作的库时, Emscripten 会使用一个名为MEMFS的模拟文件系统,以确保这个库可在浏览器与node.js环境下工作。

使用MEMFS很快捷,但它具有一些缺点:

  1. 由于Emscripten仅可以使用最多2 GB的内存,因此MEMFS很容易导致内存耗尽。
  2. 会在你的主线程与Emscripten产生数据“pass through”的行为(详见“解决一个现实的问题:ffmepg.js的文件大小限制”)

若要解决这些问题,一个方法是使用 IDBFS 和 NODEFS 来充当应用程序的真实文件系统。

在浏览器(以及Web Worker)环境中使用的IDBFS是使用 IndexedDB 作为文件系统来存储你的文件

由于IndexedDB具有一些同步问题,你需要在文件写入后使用FS.syncfs()

在Node.js环境中使用的 NODEFS 是使用Node.js 中的 fs API 来模拟一个文件系统。

详情可参阅:https://emscripten.org/docs/api_reference/Filesystem-API.html

如何挂载IDBFS 和 NODEFS

要挂载 IDBFS 和 NODEFS,你需要使用在第五部分中介绍的--pre-js。这次我们需要覆写一个函数,名为preRun(详见此处)。

下面是一个用法样例:

var logger = function(){}

Module['setLogger'] = function(_logger) { logger = _logger; };
Module['print'] = function(message) { logger(message, 'stdout'); };
Module['printErr'] = function(message) { logger(message, 'stderr'); };
Module['preRun'] = [
  function() {
    FS.mkdir('/data');
    /* Node.js Environment */
    if (typeof process === 'object' && typeof require === 'function') {
      try {
        require('fs').mkdirSync('./data');
      } catch(e) {}
      FS.mount(NODEFS, { root: '.' }, '/data');
    /* Web Worker Environment */
    } else if (typeof importScripts === 'function') {
      FS.mount(IDBFS, {}, '/data');
    }
  },
];

使用其它函数(比如preInit)也应该可以进行这个任务,但在这里我们使用的是preRun。具体取决于你的应用程序。

解决一个现实的问题:ffmepg.js的文件大小限制

有一天,有一个issueffmpeg.wasm不能够处理大文件。要解决这个问题,我们重新审视一下我们的设计:

image

当文件不是很大的时候,这个流程看起来还不错;但当媒体文件达到100 MB后,通过 postMessage() 或 send() 来传递如此巨大的文件看起来很不合理,因此会导致 ffmpeg.wasm 崩溃。

此处的瓶颈是由于我们使用 Web Worker / 子进程来充当传递组件来发送和接收媒体文件而造成的。要解决该问题,我们需要使用一个同时可被 Worker 和 Web Worker / 子进程访问的文件系统,因此我们需要重新来设计一下。这是优化后的设计:

image

这里的想法是,使得 Worker 和 Web Worker / 子进程能够在 IDBFS / NODEFS 中进行读写。这就解决了我们在初始设计中遇到的瓶颈。

虽然看起来更加复杂了,但这解决了在 ffmpeg.wasm 中处理大文件的问题。现在你可以在下方的CodePen中进行尝试。(你可以下载一个 90 MB 的视频文件)

<h3>Upload a video to transcode to mp4 (x264) and play!</h3>
<video id="output-video" controls></video><br/>
<input type="file" id="uploader">
<p id="message" />
html, body {
  margin: 0;
  width: 100%;
  height: 100%
}
body {
  display: flex;
  flex-direction: column;
  align-items: center;
}
const message = document.getElementById('message');
const { createFFmpeg } = FFmpeg;
const ffmpeg = createFFmpeg({
  log: true,
  progress: ({ ratio }) => {
    message.innerHTML = `Complete: ${(ratio * 100.0).toFixed(2)}%`;
  },
});

const transcode = async ({ target: { files }  }) => {
  const { name } = files[0];
  message.innerHTML = 'Loading ffmpeg-core.js';
  await ffmpeg.load();
  message.innerHTML = 'Start transcoding';
  await ffmpeg.write(name, files[0]);
  await ffmpeg.transcode(name,  'output.mp4');
  message.innerHTML = 'Complete transcoding';
  const data = ffmpeg.read('output.mp4');
 
  const video = document.getElementById('output-video');
  video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
}
document.getElementById('uploader').addEventListener('change', transcode);

这种方式十分显著的副作用是,它在用户的IndexedDB(浏览器)和文件系统(Node.js)中存储了大量的数据。记住要尽可能地进行清理。


以上就是第六部分的全部内容,感谢阅读。期待在下一部分中继续与你相见。

仓库:

ffmpeg-core.js: https://github.com/ffmpegwasm/FFmpeg
ffmpeg.wasm: https://github.com/ffmpegwasm/ffmpeg.wasm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant