画涂一
遇事不决,量子力学。
页面
分类
从外网爆🔥的视频压缩工具说起-FFmpeg.wasm
作者: aedelnz
时间:
分类: 默认
阅读:33
总字数:7156
累计调用:

前言

炸裂!💥🔥 《硅谷》压缩技术好像现世了!!!将视频压缩 80~90% 几乎不损失效果?基于 FF.gif

工具地址:tools.rotato.app/compress

最近有一个视频压缩网站忽然爆火外网,据说能够将视频压缩到原视频的10~20%,而且几乎不损失清晰度,不影响视频播放效果。

这个工具无需注册,可以免费使用;视频处理我们一般都会使用FFmpeg,这个工具也不例外,官方说明了是基于FFmpeg的:

image.png

但是如果是服务端调用FFmpeg,那网站的开发者真是大善人了,查看网站的请求,发现:

image.png

该网站是使用FFmpeg.wasm在浏览器端进行视频压缩的,这样消耗的就是客户端的计算资源,也没有上传下载文件的需求了。

FFmpeg.wasm

FFmpeg是一个音视频处理的C语言工具库,市面上几乎所有音视频处理软件都是基于FFmpeg实现的;而FFmpeg.wasm是一个基于WebAssembly技术实现的,可以在浏览器中运行 FFmpeg 命令行工具,实现音视频处理功能的库。

在浏览器端使用

使用FFmpeg.wasm其实比较简单,首先安装相关类库:

npm install @ffmpeg/ffmpeg @ffmpeg/util

然后使用官方的示例代码

// import { FFmpeg } from '@ffmpeg/ffmpeg';
// import { fetchFile, toBlobURL } from '@ffmpeg/util';
function() {
    const [loaded, setLoaded] = useState(false);
    const ffmpegRef = useRef(new FFmpeg());
    const videoRef = useRef(null);
    const messageRef = useRef(null);

    const load = async () => {
        const baseURL = 'https://unpkg.com/@ffmpeg/core@0.12.6/dist/umd'
        const ffmpeg = ffmpegRef.current;
        ffmpeg.on('log', ({ message }) => {
            messageRef.current.innerHTML = message;
            console.log(message);
        });
        // toBlobURL is used to bypass CORS issue, urls with the same
        // domain can be used directly.
        await ffmpeg.load({
            coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
            wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
        });
        setLoaded(true);
    }

    const transcode = async () => {
        const ffmpeg = ffmpegRef.current;
        await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));
        await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']);
        const data = await ffmpeg.readFile('output.mp4');
        videoRef.current.src =
            URL.createObjectURL(new Blob([data.buffer], {type: 'video/mp4'}));
    }

    return (loaded
        ? (
            <>
                <video ref={videoRef} controls></video><br/>
                <button onClick={transcode}>Transcode webm to mp4</button>
                <p ref={messageRef}></p>
                <p>Open Developer Tools (Ctrl+Shift+I) to View Logs</p>
            </>
        )
        : (
            <button onClick={load}>Load ffmpeg-core (~31 MB)</button>
        )
    );
}

官方提供的是一个React的示例,核心流程为:

第1步,调用load方法加载相关资源:

await ffmpeg.load({
   coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
   wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
});

第2步,写入需要处理的文件

await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));

第3步,执行命令

await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']);

第4步,获取处理完成的文件

const data = await ffmpeg.readFile('output.mp4');
需要注意:如果使用vite构建项目需要使用esm的资源代替umd unpkg.com/@ffmpeg/cor… => unpkg.com/@ffmpeg/cor…

多线程处理

FFmpeg.wasm提供单线程和多线程两个版本,官方表示多线程版本会比单线程版本快2倍左右;使用多线程版本的流程如下:

首先,修改引用的资源,并且需要额外引入workerURL:

await ffmpeg.load({
   coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
   wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
   workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript'),
});

其次,多线程版本使用到SharedArrayBuffer,采用 SharedArrayBuffer,需要页面 Header 添加

Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

但是,在实际使用多线程版本的过程还存在其他问题:

在Chrome浏览器中无法执行命令,并且没有错误提示

具体现象为在执行第一帧之后就卡住,同时也不报错,官方有多个相关issue,并且一直没有关闭;可能原因是 wasm 采用的线程数量过大,使得其使用的内存超过 chrome 的限制,导致无法执行;不过无法判断 wasm 是否是通过 navigator.hardwareConcurrency (用户计算机的处理器数)来确定线程数量。

部分用户通过添加线程控制,比如 -threads 4,就可以顺利执行多线程指令;但是有部分还是反馈无法执行。

所以:

谨慎使用多线程版本

性能对比

#FFmpegcore v0.12.3core-mt v0.12.3
Avg5.2 sec128.8 sec (0.04x)60.4 sec (0.08x)
Max5.3 sec130.7 sec63.9 sec
Min5.1 sec126.6 sec59 sec

相较于C语言版本的FFmpeg,无论是单核还是多核版本的FFmpeg.wasm都远远不如,存在着10-20倍的性能差距。

但是在Web端,如果使用服务端调用FFmpeg的方式处理视频,存在消耗服务器资源消耗带宽,这些可都是money,都是真金白银,而且如果服务器资源有限,会出现排队等待的情况。

而如果是在浏览器端执行就能避免上述问题,而且并不是所有操作都是耗时的,有些操作比如:

  1. 格式转换:当使用 -c copy 选项进行容器格式转换而无需重新编码时,操作会非常快。
  2. 视频裁剪:使用 -ss(开始时间)和 -t(持续时间)选项来裁剪视频片段时,如果视频已经被解码到内存中,这个操作可以很快完成。
  3. 音频提取:从视频中提取音频流(例如,使用 -vn 忽略视频并复制音频流)通常很快,尤其是当音频编码不需要重新编码时。

总结

FFmpeg.wasm存在着很多问题,但是它的存在让前端能够具有完备的音视频处理的能力;尽管性能比较差,至少已经解决了有没有的问题,Web可以根据自己的具体的业务使用相关功能,所以:

Fabrice Bellard牛逼

地址:https://juejin.cn/post/7401027594231431208

暂无评论
添加新评论