纯前端使用ffmpeg实现视频压缩的具体方法及踩坑

作者:工具松鼠时间:2026-10-31 08:54:54浏览:332

本文详细记录在纯前端环境下使用ffmpeg.js进行视频压缩的完整流程。重点解决SharedArrayBuffer跨域隔离报错,对比降级、Origin Trials及COOP/COEP头部配置三种方案,最终给出在ThinkPHP项目中通过npm本地集成ffmpeg资源并正确配置响应头的稳定实现方案。

实现需求

业务场景要求用户上传视频文件,并在前端完成压缩处理,同时允许用户自定义压缩强度。经过多方资料检索与对比,最终确定采用ffmpeg作为核心处理工具。本文将详细阐述具体实现步骤,并整理开发过程中遇到的各类问题及解决方案。

ffmpeg视频压缩转码

利用ffmpeg进行视频压缩的代码逻辑相对直观,以下直接展示核心实现代码。

HTML结构部分

<h3>视频前端压缩</h3>
<video id="output-video" controls></video><br/>
<input type="file" id="uploader">
<p id="message"></p>

JavaScript逻辑部分

    // 引入ffmpeg.min.js
    <script src="https://unpkg.com/@ffmpeg/ffmpeg@0.9.5/dist/ffmpeg.min.js"></script>
    <script>
		const { createFFmpeg, fetchFile } = FFmpeg;
		const message = document.getElementById('message');
		const ffmpeg = createFFmpeg({
			log: true,
			progress: ({ ratio }) => {
				message.innerHTML = `完成率: ${(ratio * 100.0).toFixed(2)}%`;
			},
		});
		const transcode = async ({ target: { files }  }) => {
			const { name } = files[0];
			message.innerHTML = '正在加载 ffmpeg-core.js';
			await ffmpeg.load();
			message.innerHTML = '开始压缩';
            ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
			// '-b','2000000'  值越小  压缩率越大
			await ffmpeg.run('-i', name,'-b','2000000','output.mp4');
			message.innerHTML = '压缩完成';
			const data = ffmpeg.FS('readFile', '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);
	</script>

此处引用的ffmpeg大神封装好的CDN资源历经多次尝试才确定可用,此前搜索到的多个版本均存在兼容性问题,故此处不再赘述其他无效版本。

尽管代码行数不多,但在运行初期,随着控制台日志逐行打印,曾一度以为即将成功。然而,不出所料地遇到了第一个阻碍。

实现需求

解决SharedArrayBuffer报错:

背景:

实现需求

经过大量资料查阅,目前主要存在以下几种解决思路。

1.SharedArrayBuffer 降级 ArrayBuffer

if(!crossOriginIsolated) {
  SharedArrayBuffer = ArrayBuffer;
}

首先需验证跨域隔离策略是否已生效,可通过检查窗口及worker上下文中的crossOriginIsolated属性确认可用性,若不可用则需进行降级处理。

采用上述方法确实消除了SharedArrayBuffer相关的报错,但随之引发了新的错误。

实现需求

error:bad memory 错误:内存不足

随后再次尝试寻找解决方案,由于处理逻辑过于复杂且难以落地,该方案实际效果有限,未能从根本上解决问题,耗费了较多时间。

2.Chrome浏览器添加Chrome Origin Trials

1)注册页面获得 Token

访问 https://developer.chrome.com/origintrials/#/registration 进行注册。

2)Token 放置页面 meta 标签或者响应头 Origin-Trial

设置 http-equiv="origin-trial" content="注册后获得的Token"

<meta http-equiv="origin-trial" content="注册后获得的Token">

最终配置效果如下所示。

实现需求

该方法实施简单直接,但局限性在于仅支持Chrome浏览器,其他浏览器仍会抛出相同报错。

3.设置COOP和COEP头部

以下内容均围绕解决SharedArrayBuffer报错展开,篇幅较长且包含部分冗余信息,主要记录实际开发中遇到的具体问题及排查过程。

SharedArrayBuffer - JavaScript | MDN (mozilla.org)

参考官网提供的官方解决方案:

实现需求

由于当前项目基于ThinkPHP框架开发,加之前端技术栈限制,对于如何在后端正确配置Header存在困惑。

经过反复搜索验证,决定先在本地环境中进行测试。本项目采用Vue2构建,本地测试版本虽为独立分支,但核心逻辑与生产环境基本一致。

1.npm下载ffmpeg资源包

2.上代码

<template>
    <!-- tempalte部分 -->
    <h3>视频前端压缩</h3>
    <video id="output-video" controls :src="vedioSrc"></video><br/>
    <input type="file" id="uploader" @change="initFfmpeg">
    <h5 id="message">{{ message }}</h5>
</template>
<script>
// @ is an alias to /src
//引入
import { createFFmpeg, fetchFile } from "@ffmpeg/ffmpeg";
export default {
    name: "HelloWorld",
    components: {},
    data() {
        return {
            message: null,
            vedioSrc: '',
        };
    },
    methods: {
        //初始化
        initFfmpeg() {
            let file = document.querySelector("#uploader").files[0];
            console.log(file);
            const ffmpeg = createFFmpeg({
                corePath: "ffmpeg-core.js",
                log: true,
            });
            //设置进度条
            ffmpeg.setProgress(({ ratio }) => {
                console.log(ratio);
                this.percentage = Math.floor(ratio * 100);
            });
            //开始压缩
            const transcode = async (file) => {
                const { name } = file;
                this.message = "Loading ffmpeg-core.js";
                await ffmpeg.load();
                ffmpeg.FS("writeFile", name, await fetchFile(file));
                this.message = "Start transcoding";
                // '-b','2000000'  值越小  压缩率越大
                await ffmpeg.run("-i", name, "-b", "700000", "output.mp4");
                this.message = "压缩完成";
                const data = ffmpeg.FS("readFile", "output.mp4");
                this.fileBytes = data.byteLength;
                //把压缩后的视频进行回显
                this.vedioSrc = URL.createObjectURL(
                    new Blob([data.buffer], { type: "video/mp4" })
                );

            };
            transcode(file);
        },
    },
};
</script>

此处偏离主题,我们仍需回归核心,即解决SharedArrayBuffer报错问题。

定位到项目根目录下的vue.config.js配置文件

实现需求

在此处配置前文所述的解决SharedArrayBuffer所需的头部信息。

    devServer: {
        headers: {
            "Cross-Origin-Opener-Policy": "same-origin",
            "Cross-Origin-Embedder-Policy": "require-corp",
        },
    }

随后执行代码 npm run serve...启动服务

结果符合预期,视频压缩功能成功运行。

实现需求

在Edge浏览器中测试通过,报错问题彻底解决,视频体积从6M压缩至3M,压缩效果显著且画质差异不明显。代码中可通过参数 -b '2000000' 调整码率,最大值为2000000,数值越大压缩率越高,最小值需自行测试确定。ffmpeg官网提供了丰富的使用方法,功能十分强大。

带着谨慎的态度在Chrome浏览器中进行测试,结果再次符合预期地失败了。

至此,所有浏览器均能成功压缩视频。但在喜悦之余,考虑到当前仅为本地Vue项目测试,上线部署后是否还会存在其他未知问题仍不确定。

前文提及项目基于ThinkPHP,作为前端开发人员,对于如何将本地测试环境与ThinkPHP后端有效结合感到困惑,缺乏相关经验。

经过反复思考,意识到在本地测试前,核心难点在于解决SharedArrayBuffer报错,只要攻克此关,后续流程应无大碍。于是开始搜索ThinkPHP环境下配置Header的具体方法,以下是配置过程中遇到的具体问题。

4.配置header信息

实现需求

首次尝试在指定的配置文件中添加设置,这也是搜索到的常见方案之一。

中间经历多次试错,最终结论是该方法在此场景下不可行。

但是!!!今日尝试另一种方法,确实有效。

解决SharedArrayBuffer报错:

定位到页面控制器,直接在该文件中配置,竟然成功生效。作为前端人员,虽不完全理解后端逻辑,但结果有效。

代码如下:

        header('Cross-Origin-Opener-Policy: same-origin');
        header('Cross-Origin-Embedder-Policy: require-corp');

实现需求

此次真正解决了报错问题,但视频压缩依然失败。原因在于:

该配置影响了跨域资源的加载,包括iframe及script标签引用的资源,导致页面所有资源失效。由于本项目依赖ffmpeg的CDN资源,直接导致无法使用。即便下载该文件,ffmpeg.min.js内部仍包含CDN链接,造成引用断裂。

过程令人几乎崩溃。

经过大量冗长叙述,最后直接展示最终解决方案。

**重点重点,最后实现方案!!!!**

核心思路是沿用本地测试时的方法,通过npm下载ffmpeg包。

在ThinkPHP项目中使用npm

确保开发环境已安装Node.js和npm。可在命令行中输入node -v和npm -v以检查安装状态。

进入ThinkPHP 5项目根目录,打开命令行或终端,确保当前路径位于项目根目录下。

运行以下命令安装Node.js的包管理器依赖

npm install

如需安装其他特定npm包,可在项目根目录创建package.json文件,并在其中的dependencies或devDependencies字段中添加所需依赖项。

将本地测试时的package.json内容直接复制到项目根目录创建的package.json上内容直接复制到项目根目录新建的package.json文件中。

需注意,ThinkPHP 5本身不直接与npm交互,而是通过前端资源集成方式实现。这意味着需在ThinkPHP项目根目录下创建与前端资源相关的目录(例如public/static),并将前端资源放置其中。随后,可在ThinkPHP模板中引用这些前端资源。

最后执行npm install下载包,并在代码中正确引用。

<script type="text/javascript" src="/node_modules/@ffmpeg/ffmpeg/dist/ffmpeg.min.js"></script>

完整代码

html部分

            <h3>视频前端压缩</h3>
            <video id="output-video" controls></video><br/>
            <input type="file" id="uploader">
            <p id="message"></p>

js部分

<script type="text/javascript" src="/node_modules/@ffmpeg/ffmpeg/dist/ffmpeg.min.js"></script>
<script>
	const { createFFmpeg, fetchFile } = FFmpeg;
	const message = document.getElementById('message');
	const ffmpeg = createFFmpeg({
		log: true,
		progress: ({ ratio }) => {
			message.innerHTML = `完成率: ${(ratio * 100.0).toFixed(2)}%`;
		},
	});
	const transcode = async ({ target: { files }  }) => {
		const { name } = files[0];
		message.innerHTML = '正在加载 ffmpeg-core.js';
		await ffmpeg.load();
		message.innerHTML = '开始压缩';
        ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
		// '-b','2000000'  值越小  压缩率越大
		await ffmpeg.run('-i', name,'-b','2000000','output.mp4');
		message.innerHTML = '压缩完成';
		const data = ffmpeg.FS('readFile', '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);
</script>

最终问题得到解决,关键在于正确配置header,无需其他额外配置,所有浏览器均可成功执行压缩。

总结

至此,关于纯前端使用ffmpeg实现视频压缩的具体方法及踩坑经验介绍完毕,更多相关前端ffmpeg实现视频压缩内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章