FFmpeg 是视频处理最常用的开源软件,它功能强大,用途广泛,大量用于视频网站和商业软件(比如 Youtube 和 iTunes),也是许多音频和视频格式的标准编码/解码实现。
借助 WebAssembly 的能力,它现在有了一个 Web 版本:FFMPEG.WASM,让你可以在浏览器里处理视频
官网:
GitHub:
获取FFMPEG.WASM
获取FFMPEG.WASM有多种方法。
我们最终需要的ffmpeg.wasm
核心文件如下:
第一种方式:通过源码编译获取
首先到GitHub克隆源码,然后编译出FFMPEG.WASM,步骤如下:
git clone https://github.com/ffmpegwasm/ffmpeg.wasm.git
cd ffmpeg.wasm
yarn
yarn build
编译好后,可以在dist
中找到 ffmpeg.dev.js
和 ffmpeg.min.js
然后到GitHub Release下载最新的ffmpeg-core.wasm
和 ffmpeg-core.js
。
第二种方式:通过npm安装依赖,从node_modules中提取
安装:
yarn add @ffmpeg/ffmpeg @ffmpeg/core
然后可以从node_modules中找到我们需要的文件:
在浏览器中使用
创建示例文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
<script src="./js/ffmpeg.min.js"></script>
</head>
<body>
<video id="player" controls></video>
<script>
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({
corePath: "./js/ffmpeg-core.js",
log: true,
});
(async () => {
await ffmpeg.load();
const dataInputVideo = await fetchFile('data/input.mp4');
ffmpeg.FS('writeFile', 'input.mp4', dataInputVideo);
await ffmpeg.run('-i', 'input.mp4', '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' }));
})();
</script>
</body>
</html>
需要本地测试的话,可以创建一个express服务server.js
,运行命令node server
(详见:关于SharedArrayBuffer报错的处理)
运行到浏览器,可以看到控制台疯狂输出,说明在对视频做处理:
这个处理是本地执行的,浏览器将wasm
下载到本地,浏览器再执行相关操作。
以上示例,是直接读取了项目下的 data/input.mp4
文件,输出到 output.mp4
,没有实际意义,只是一个演示。
通常我们会通过input框读取用户选择的文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="./js/ffmpeg.min.js"></script>
</head>
<body>
<video id="player" controls></video>
<input type="file" id="uploader" placeholder="choose file">
<script>
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({
corePath: "./js/ffmpeg-core.js",
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>
</html>
在Node.js中使用
const fs = require('fs');
const { createFFmpeg, fetchFile } = require('@ffmpeg/ffmpeg');
const ffmpeg = createFFmpeg({ log: true });
(async () => {
await ffmpeg.load();
ffmpeg.FS('writeFile', 'input.mp4', await fetchFile('./data/input.mp4'));
await ffmpeg.run('-i', 'input.mp4', 'output.mp4');
await fs.promises.writeFile('./output.mp4', ffmpeg.FS('readFile', 'output.mp4'));
process.exit(0);
})();
FFMPEG.WASM使用方式
ffmpeg常用的参数都可以使用,参考:
📃 使用FFMPEG实现音视频处理
通过ffmpeg.run
调用即可,每个参数分开传递。
示例:
// 命令行: ffmpeg -i input.mp4 -s 720x480 output.mp4
await ffmpeg.run('-i', name, '-s', '400x600', 'output.mp4');
不过现在支持的格式有限,并不是所有音视频格式都是支持的,支持列表参考:Proposed Encoders / Decoders Libraries
常见错误
关于SharedArrayBuffer报错的处理
如果直接打开引入FFMPEG.WASM的html页面,浏览器会报以下错误:
Uncaught (in promise) ReferenceError: SharedArrayBuffer is not defined
这是官方的解决方案:
SharedArrayBuffer is only available to pages that are cross-origin isolated. So you need to host your own server with Cross-Origin-Embedder-Policy: require-corp and Cross-Origin-Opener-Policy: same-origin headers to use ffmpeg.wasm.
意思是,需要在服务端配置两个响应头:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
比如我们可以使用express开启一个静态服务:server.js
const express = require('express');
const app = express();
app.use((_, res, next) => {
res.header('Cross-Origin-Opener-Policy', 'same-origin');
res.header('Cross-Origin-Embedder-Policy', 'require-corp');
next();
});
app.use(express.static('./'));
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});
将需要加载的html文件放于server.js
同级目录,运行 node server.js
,默认运行到8080端口下。
效果如下:
error:0308010C:digital envelope routines::unsupported
在运行官方vue示例的时候,可能会报以下错误
error:0308010C:digital envelope routines::unsupported
解决方案为降低Node.js到17以下。
如果想要在Node.js 17以上临时解决,可以临时设置环境变量:
set NODE_OPTIONS=--openssl-legacy-provider # windows
export NODE_OPTIONS=--openssl-legacy-provider # linux