ffmpeg.wasm 以图片组输出时,如何提取这些文件?

起因

正在做视频上传功能,其中视频压制这方面使用了ffmpeg.wasm,就在前端做掉了。还需要截取视频的缩略图thumbnails。

项目使用的播放器是DPlayer,DPlayer的文档在thumbnails这边是这样写的并提供了DPlayer-thumbnails

image.png

通过这个工具可以得到如下的雪碧图。

image.png

阅读了下DPlayer-thumbnails的代码,也是通过ffmpeg。然后又测试了一下,它是无论是什么长度的视频都取100张的缩略图,然后制成雪碧图。
(一开始我以为是每秒截一张,这个方案很好,在进度条上显示缩略图时就计算百分比就好了。)

既然这样,是否可以一样使用ffmpeg.wasm在前端进行同样的操作。得出100张缩略图,可以供用户选择视频封面,并且制成雪碧图上传它。

经过

先用ffmpeg测试一下

//既然是在视频总长中截取100张图片,那么就是视频总时长/100
//我的这个测试视频是160秒,至于读取不同视频的时长那后面再说吧。
ffmpeg -i ../../test.mp4 -vf fps=1/(160/100) ../../thumbnail/thumbnail%d.png

能得出以下内容

image.png

好啊,很好啊。但是要怎么把他们取出来呢。

ffmpeg.wasm的文档是这样的

//Browser
<body>
  <video id="player" controls></video>
  <input type="file" id="uploader">
  <script src="ffmpeg.min.js"></script>
  <script>
    const { createFFmpeg, fetchFile } = FFmpeg;
    const ffmpeg = createFFmpeg({ log: true });
    const transcode = async ({ target: { files } }) => {
      const { name } = files[0];
      await ffmpeg.load();
      ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
      await ffmpeg.run('-i', name,  'output.mp4');
      const data = ffmpeg.FS('readFile', 'output.mp4');
      const video = document.getElementById('player');
      video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
    }
    document
      .getElementById('uploader').addEventListener('change', transcode);
  </script>
</body>

//Node.JS
const fs = require('fs');
const { createFFmpeg, fetchFile } = require('@ffmpeg/ffmpeg');

const ffmpeg = createFFmpeg({ log: true });

(async () => {
  await ffmpeg.load();
  ffmpeg.FS('writeFile', 'test.avi', await fetchFile('./test.avi'));
  await ffmpeg.run('-i', 'test.avi', 'test.mp4');
  await fs.promises.writeFile('./test.mp4', ffmpeg.FS('readFile', 'test.mp4'));
  process.exit(0);
})();

单个文件是可以取出来的(比如只压制一个视频),但像这里生成了很多图片,是多个文件该怎么取出来呢?

问题

截取缩略图这块我是这样写的

var in_name = "upload.mp4";
var out_name = "thumbnail%d.png";

(async () => {
    await ffmpeg.load();
    ffmpeg.FS('writeFile', in_name, await fetchFile(original_video_file));
    await ffmpeg.run('-i', in_name, '-vf' , 'fps=1/(160/100)' , out_name);
    var new_file = ffmpeg.FS('readFile', out_name);
    var thumbnail = new Blob([new_file.buffer], { type: 'image/png' });
    console.log(thumbnail);
})();

报了以下错误

image.png

想了想原因,应该就是这个写法不能读取多个文件的原因,报错也在ffmpeg.FS这里看看下文档吧。可惜没有给出这样的写法。

image.png

然后又去稍稍的了解了下emscripten(毕竟文档里有这个链接),可惜头都看的大了都没找着。
https://emscripten.org/docs/api_reference/Filesystem-API.html

各位大神是否有办法呢!如果网络上有这方面的讲解或者这个问题的其他解法,请麻烦告诉我也可以节省时间重复回答。辛苦了!

阅读 3.2k
2 个回答
✓ 已被采纳

找到办法了,实际是读取文件问题。
按照原本的代码的输出的文件会存到MEMFS的工作根目录,而读取这些文件也应该是像这样。

ffmpeg.FS('readFile', 'thumbnails1.png');
ffmpeg.FS('readFile', 'thumbnails2.png');
ffmpeg.FS('readFile', 'thumbnails3.png');

那么可以先新建个目录,指定输出到这里
ffmpeg.FS('mkdir','/thumbnails');

我一开始是不知道这些文件存到哪里去了,头大... 到处问了一遍,没什么结果看看作者博客吧。看到了这个,是可以读取指定目录中的文件的。
一开始想着读取到了这些文件就可以读出来了,后来一想这文件名在输出时就固定了,知道路径文件名就可以直接读了,套个循环就可以拿出来。
image.png

var in_name = "upload.mp4";
var out_name = "thumbnails/thumbnails%d.png";

(async () => {
    await ffmpeg.load();
    //先在文件系统中新建一个thumbnails目录
    ffmpeg.FS('mkdir','/thumbnails');
    ffmpeg.FS('writeFile', in_name, await fetchFile(original_video_file));
    await ffmpeg.run('-i', in_name, '-vf' , 'fps=1/(160/100)' , out_name);

    //读取指定文件夹中的内容
    console.log(ffmpeg.FS('readdir', '/thumbnails'));
    //查看第一张图片
    var new_file = ffmpeg.FS('readFile', 'thumbnails/thumbnails1.png');
    console.log(new Blob([new_file.buffer], { type: 'image/png' }));
})();

image.png
能读到第一张那后面的也不成问题了。

生成的时候 abc%d.png
读取的时候你得指定 a1.png a2.png 这样的文件名

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题