104 lines
2.5 KiB
TypeScript
104 lines
2.5 KiB
TypeScript
import { FFmpeg } from '@ffmpeg/ffmpeg'
|
||
import { toBlobURL } from '@ffmpeg/util'
|
||
|
||
let ffmpegInstance: FFmpeg | null = null
|
||
let loadPromise: Promise<FFmpeg> | null = null
|
||
|
||
/**
|
||
* 获取或初始化 FFmpeg 实例(单例模式)
|
||
*/
|
||
export const useFFmpeg = async () => {
|
||
// 如果已经加载过,直接返回
|
||
if (ffmpegInstance && ffmpegInstance.loaded) {
|
||
return ffmpegInstance
|
||
}
|
||
|
||
// 如果正在加载中,等待加载完成
|
||
if (loadPromise) {
|
||
return loadPromise
|
||
}
|
||
|
||
loadPromise = initializeFFmpeg()
|
||
return loadPromise
|
||
}
|
||
|
||
async function initializeFFmpeg(enableMT: boolean = false): Promise<FFmpeg> {
|
||
try {
|
||
const ffmpeg = new FFmpeg()
|
||
|
||
ffmpeg.on('log', ({ message, type }) => {
|
||
console.log(`[ffmpeg - ${type}]`, message)
|
||
})
|
||
|
||
ffmpeg.on('progress', ({ progress, time }) => {
|
||
console.log(`[ffmpeg] P: ${(progress * 100).toFixed(2)}%, T: ${time}ms`)
|
||
})
|
||
|
||
const baseURL = enableMT
|
||
? 'https://cdn.jsdelivr.net/npm/@ffmpeg/core-mt@0.12.10/dist/esm'
|
||
: 'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.10/dist/esm'
|
||
|
||
const coreURL = await toBlobURL(
|
||
`${baseURL}/ffmpeg-core.js`,
|
||
'text/javascript'
|
||
)
|
||
const wasmURL = await toBlobURL(
|
||
`${baseURL}/ffmpeg-core.wasm`,
|
||
'application/wasm'
|
||
)
|
||
|
||
let loadPayload = {
|
||
coreURL,
|
||
wasmURL,
|
||
}
|
||
|
||
if (enableMT) {
|
||
const workerURL = await toBlobURL(
|
||
`${baseURL}/ffmpeg-core.worker.js`,
|
||
'text/javascript'
|
||
)
|
||
Object.assign(loadPayload, { workerURL })
|
||
}
|
||
|
||
const isLoaded = await ffmpeg.load(loadPayload)
|
||
console.log('[FFmpeg] FFmpeg 加载完成,isLoaded:', isLoaded)
|
||
|
||
ffmpegInstance = ffmpeg
|
||
loadPromise = null
|
||
return ffmpeg
|
||
} catch (error) {
|
||
console.error('[FFmpeg] 初始化失败:', error)
|
||
loadPromise = null
|
||
throw error
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理 FFmpeg 资源
|
||
*/
|
||
export const cleanupFFmpeg = () => {
|
||
if (ffmpegInstance && ffmpegInstance.loaded) {
|
||
console.log('[FFmpeg] 清理 FFmpeg 资源...')
|
||
ffmpegInstance.terminate()
|
||
ffmpegInstance = null
|
||
loadPromise = null
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 将 Blob/File 转换为 Uint8Array
|
||
*/
|
||
export const fileToUint8Array = async (
|
||
file: File | Blob
|
||
): Promise<Uint8Array> => {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader()
|
||
reader.onload = (e) => {
|
||
const arrayBuffer = e.target?.result as ArrayBuffer
|
||
resolve(new Uint8Array(arrayBuffer))
|
||
}
|
||
reader.onerror = reject
|
||
reader.readAsArrayBuffer(file)
|
||
})
|
||
}
|