欢迎光临
我们一直在努力

纯前端实现视频压缩: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 等格式的在线压缩,完全免费使用。

赞(0)
未经允许不得转载:171主机测评 » 纯前端实现视频压缩:WebAssembly + FFmpeg 实战
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址