纯前端实现视频压缩:WebAssembly + FFmpeg 实战
前言
传统在线视频压缩工具需要把文件上传到服务器,用户等待上传 → 服务器处理 → 再下载,流程长、服务器成本高,还存在隐私泄露风险。
本文介绍如何利用 FFmpeg 的 WebAssembly 版本,在浏览器本地实现视频压缩,**文件全程不上传服务器**。
技术选型
- **@ffmpeg/ffmpeg**:FFmpeg 的 WASM 编译版本,在浏览器中运行完整的 FFmpeg 命令行
- **@ffmpeg/util**:工具库,提供 `fetchFile` 等便捷方法
- **SharedArrayBuffer**:WASM 多线程支持,提升编解码性能
核心实现
1. 初始化 FFmpeg 引擎
import { FFmpeg } from '@ffmpeg/ffmpeg'
import { fetchFile } from '@ffmpeg/util'
const ffmpeg = new FFmpeg()
await ffmpeg.load()
FFmpeg WASM 核心文件约 31MB,首次需要从 CDN 加载,之后会被浏览器缓存。
2. 视频信息读取
压缩前需要获取视频的宽高、时长等信息,用于计算压缩参数:
async function getVideoInfo(file: File) {
const inputName = 'input' + getExtension(file.name)
await ffmpeg.writeFile(inputName, await fetchFile(file))
await ffmpeg.exec(['-i', inputName])
// 从 stderr 中解析视频元数据
}
3. 自适应压缩算法
核心思路:根据目标文件大小反推需要的编码参数。
async function compressToTargetSize(file: File, targetSizeMB: number) {
/* 1. 读取视频信息 */
const info = await getVideoInfo(file)
/* 2. 根据目标大小计算目标比特率 */
const targetBitrate = Math.floor((targetSizeMB * 8192) / info.duration)
/* 3. 根据压缩率动态决定是否缩小分辨率 */
const ratio = targetSizeMB / (file.size / 1024 / 1024)
let scaleFilter = ''
if (ratio < 0.1) scaleFilter = 'scale=640:360'
else if (ratio < 0.4) scaleFilter = 'scale=1280:720'
/* 4. 构建 FFmpeg 命令 */
const args = ['-i', inputName, '-c:v', 'libx264']
if (scaleFilter) args.push('-vf', scaleFilter)
args.push('-b:v', `${targetBitrate}k`, '-y', outputName)
/* 5. 执行压缩 */
await ffmpeg.exec(args)
/* 6. 读取结果 */
const data = await ffmpeg.readFile(outputName)
return new Blob([data])
}
4. 同等画质优先复制(关键优化)
如果源文件的目标格式与源编码一致,可以直接**复制视频流**,实现秒级处理:
// MP4 → MP4 转换时,尝试直接复制视频流
if (isSameContainer) {
await ffmpeg.exec(['-i', inputName, '-c:v', 'copy', '-c:a', 'copy', '-y', outputName])
}
5. 进度追踪
通过监听 FFmpeg 的日志输出,解析处理进度:
ffmpeg.on('progress', ({ progress, time }) => {
console.log(`压缩进度: ${(progress * 100).toFixed(1)}%`)
})
完整 Composables 封装
将上述逻辑封装为 Vue Composables,方便在组件中复用:
export function useVideoCompress() {
const isProcessing = ref(false)
const progress = ref({ percent: 0, time: 0 })
const error = ref<string | null>(null)
async function compressAuto(file: File): Promise<Blob> {
isProcessing.value = true
error.value = null
// ... 上述核心逻辑
}
return { isProcessing, progress, error, compressAuto }
}
组件中使用
<script setup>
import { useVideoCompress } from './composables/useVideoCompress'
const { isProcessing, progress, error, compressAuto } = useVideoCompress()
async function handleCompress(file) {
const blob = await compressAuto(file)
const url = URL.createObjectURL(blob)
// 提供给用户下载
}
</script>
部署注意
FFmpeg WASM 需要 `SharedArrayBuffer` 支持,服务器响应头需配置:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Vite 开发环境可在 `vite.config.ts` 中配置:
export default defineConfig({
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp'
}
}
})
总结
纯前端 WASM 方案相比传统服务端方案有几个明显优势:
| 对比项 | 纯前端 WASM | 传统服务端 |
| 文件上传 | 不上传 | 必须上传 |
| 隐私安全 | 文件不离设备 | 存储在第三方 |
| 服务器成本 | 零 | 高 |
| 文件大小限制 | 无 | 通常有限制 |
这套方案已在 91AI工具 生产环境中运行,支持 MP4/WebM/MOV/AVI/MKV 等格式的在线压缩,完全免费使用。

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐
所有评论(0)