前端使用@ffmpeg/ffmpeg转换视频

974 阅读5分钟

建议放弃

建议放弃

建议放弃

建议放弃

如果前端有这个需求,基本上建议放弃。

  1. 转换效率太低;这个应该是官方给的性能图ffmpegwasm.netlify.app/docs/perfor…
  2. 浏览器内 sharedarraybuffer 常规被禁用;开启后可能得不偿失

如果非要用这个插件实现这个功能的话。我简单的写一下我踩坑的一些资料吧。。

我使用的环境是vue vue-cli(vue-cli是个很大的问题)

下面这段可以直接不看

自己已知的版本和会遇到的问题

  • 9.0 基本上遇到的问题就是 Sharedarraybuffer 不存在。这个是因为浏览器安全策略。开启需要配置请求头+https。配置上基本不会报错了,正常使用就行。但是目测所有类似于跨域请求的内容都会失败。(比如正常可跨域加载其他域名的图片,开启这个之后基本也会被拦截掉,感觉至少在正常浏览器内,正常业务场景内,这个基本没有实用价值)
// vue.config.js
  devServer: {
    https: true,
    headers: {
      'Cross-Origin-Embedder-Policy': 'require-corp',
      'Cross-Origin-Opener-Policy': 'same-origin',
    }
  }
  • 10-12版本。 貌似剔除了 Sharedarraybuffer 的基础存在(除非专门使用多线程处理时会依然会使用,但是起码理论上不开启上面的也能简单而缓慢的使用ffmpeg

因为官网12版本给的例子基本上都是vite + 其他技术的demo。而且我也没找到其他相关资料。一开始打算直接用vue-cli能不能直接服用。

不能;

11-12版本里面用了很多新特性,比如 #号的私有变量,import.meta.url 等内容。直接按照正常传统import { ffmpeg } from '@ffmpeg/ffmpeg'的引入方式, 基本上会抛出Module parse failed: Unexpected character '#'#号不识别的错误,

  1. 需要修改babel.config.js的配置来尝试编译node_modules里面的代码(正常不编译的,默认node_modules里面的都是编译过的)
  2. 把包手动复制到node_modules外。让babel可以正常处理#号的问题

解决之后基本上就是 提示 import.meta.url不存在的问题。这东西vue-cli(自带的@vue/cli-plugin-babel)处理不了,需要安装一个一个新的babel npm install --save-dev @babel/plugin-syntax-import-meta再修改babel.config.js

反正稀里糊涂处理完了。还会有一个worker创建不了的问题。需要安装一个npm install --save-dev worker-loader打包配置单独打一下woker(貌似是这样,但是例子给的都是vite相关的,我也不知道怎么弄)

无视掉上面的内容。直接用原始的方式引入

// 我安装的是12版本的,如果10以下版本可能是另外的情况
npm install @ffmpeg/ffmpeg @ffmpeg/util @ffmpeg/core @ffmpg/core-mt

我猜测:

  1. @ffmpeg/ffmpeg 是用来驱动 ffmpeg的
  2. @ffmpeg/util 是新版本用来提供的一个工具函数
  3. @ffmpeg/core 是真正处理视频文件的(单线程,未使用Sharedarraybuffer,这个非必要,看自己)
  4. @ffmpg/core-mt 是真正处理视频文件的(多线程,使用了Sharedarraybuffer,这个更是非必要,看自己)
// 直接安装这两个也够;await ffmpeg.load()的时候什么参数都不写就行
npm install @ffmpeg/ffmpeg @ffmpeg/util

安装完成之后直接从node_modules里面复制出来。 粘贴到/public/static/ffmpeg目录下。 正常当代的vue-cli都有这个文件夹,基本上就是打包完成后会直接复制粘贴的一个文件夹。

<!-- public/index.html 引用文件(umd) ->
<script src="/static/ffmpeg/ffmpeg/dist/umd/ffmpeg.js"></script>
<script src="/static/ffmpeg/util/dist/umd/index.js"></script>

然后到正常的vue页面里面

// template
// 勤劳点就直接写一个 input上传文件的,不勤劳自己随便整个文件吧
<input type="file"@input="handleInput">
<video :src="videoSrc" style="width: 100vw" autoplay></video>

随便写个方法

async handleInput(e) {
    const file = e.target.files[0] // 获取文件
    
    // 引入ffmpeg
    const { fetchFile } = FFmpegUtil
    const { FFmpeg } = FFmpegWASM
    
    const ffmpeg = new FFmpeg() // 创建实例
    
    ffmpeg.on('log', (e) => console.log(e.message)) // 绑定一个实时监听的
    
    // 加载处理核心(我猜)
    await ffmpeg.load({
        // 其实load什么都不传递也行。插件默认会去找网上cdn对应的核心
        // 如果传递了 coreURL 和 wasmURL 就会用本地传递的
        // coreURL: '/static/ffmpeg/core/dist/umd/ffmpeg-core.js',
        // wasmURL: '/static/ffmpeg/core/dist/umd/ffmpeg-core.wasm'
        
        // 或者用下面这两个
        // 下面这两个 是多线程的版本。正常直接来说会直接提示 Sharedarraybuffer 不存在,所以需要按照上面的方法,给页面加上限制的请求头(我没试)
        // coreURL: '/static/ffmpeg/core-mt/dist/umd/ffmpeg-core.js',
        // worker: '/static/ffmpeg/core-mt/dist/umd/ffmpeg-core.worker.js'
    })
    
    await ffmpeg.writeFile(file.name, await fetchFile(file)) // 准备写的文件
    await ffmpeg.exec(['-i', file.name, `${file.name}.mp4`]) // 处理文件,里面写的就是正常的 ffmpeg用的公式。
    const data = await ffmpeg.readFile(`${file.name}.mp4`) // 拿出处理完的文件
    this.videoSrc = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })) // 放在video内播放

复制粘贴代码就应该正常播放了。但是速度出奇的慢。

写文章的原因:

  1. 资料过少;写给其他像和我一样想尝试前端转视频的萌新
  2. 有限的资料解决方式都不全面;比如说有人安装了11要降级到9的,解决完了又遇到Sharedarraybuffer不存在,一环扣一环,搜了半天,最后改写请求头才能使用的情况,但因为严格的安全限制其实并没有实际价值
  3. 直接简单复制粘贴这段代码,起码有个demo,然后给产品看说转的太慢,劝他改需求放弃就行了。

都说用了wasm之后浏览器性能贼强。但是目测下来还是出奇的慢。我也怀疑过是不是自己使用方法有问题,但是官方给的性能就是 正常5秒能做到的 浏览器要2分钟。去魅了去魅了

如果有啥真的性能强的东西麻烦踢一下