ffmpegHelper.ts文件
import { createFFmpeg, FFmpeg } from '@ffmpeg/ffmpeg';
export class FFmpegWrapper {
private ffmpeg: FFmpeg;
public isLoaded: boolean = false;
constructor() {
this.ffmpeg = createFFmpeg({
log: import.meta.env.VITE_APP_ENV !== 'production'
});
}
async load(): Promise<void> {
if (!this.isLoaded) {
await this.ffmpeg.load();
this.isLoaded = true;
}
}
async transcodeVideoToMP4(remoteVideoUrl: string, inputType: string = 'avi'): Promise<Uint8Array> {
if (!this.isLoaded) {
throw new Error('FFmpeg 未初始化,请先调用 load() 方法。');
}
try {
// 获取远程文件[citation:1]
const response = await fetch(remoteVideoUrl);
if (!response.ok) {
throw new Error(`获取视频失败: ${response.status} ${response.statusText}`);
}
const videoArrayBuffer = await response.arrayBuffer();
// 根据输入类型设置文件名
const inputFileName = `input.${inputType}`;
const outputFileName = 'output.mp4';
// 写入文件到FFmpeg的虚拟文件系统[citation:1]
this.ffmpeg.FS('writeFile', inputFileName, new Uint8Array(videoArrayBuffer));
// 执行转码命令[citation:1]
await this.ffmpeg.run(
'-i',
inputFileName,
'-c:v',
'libx264', // 视频编码器
'-c:a',
'aac', // 音频编码器
'-preset',
'medium', // 编码速度预设
'-crf',
'23', // 质量系数
'-movflags',
'+faststart', // 优化网络播放
outputFileName
);
// 读取输出文件
const data = this.ffmpeg.FS('readFile', outputFileName);
// 清理文件系统
this.cleanupFiles([inputFileName, outputFileName]);
return data;
} catch (error) {
this.cleanupFiles([`input.${inputType}`, 'output.mp4']);
throw error;
}
}
async transcodeVideoToMP4Url(remoteVideoUrl: string, inputType: string = 'avi'): Promise<string> {
const data = await this.transcodeVideoToMP4(remoteVideoUrl, inputType);
// 创建可播放的URL
const blob = new Blob([data.buffer], { type: 'video/mp4' });
return URL.createObjectURL(blob);
}
private cleanupFiles(fileNames: string[]): void {
fileNames.forEach((fileName) => {
try {
this.ffmpeg.FS('unlink', fileName);
} catch (error) {
console.warn(`清理文件 ${fileName} 失败:`, error);
}
});
}
}
一、FFmpeg 初始化失败: ReferenceError: SharedArrayBuffer is not defined
解决方法:
Vite 配置 (vite.config.js) : 增加
export default defineConfig({
server: {
// ffmpeg需要跨域隔离
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'credentialless' // 无需第三方配合
}
}
})
这个只能本地运行起作用
如果发到线上需要配置nginx :
nginx
server {
location / {
# 启用跨域隔离
add_header Cross-Origin-Opener-Policy same-origin;
add_header Cross-Origin-Embedder-Policy credentialless;
# 其他配置...
}
}
'Cross-Origin-Embedder-Policy': 'credentialless'设置成credentialless才能不影响到第三方的,否则第三方的图片等资源无法播放