feat(ffmpeg): add ffmpeg-core.wasm for video processing capabilities
This commit is contained in:
103
composables/useFFmpeg.ts
Normal file
103
composables/useFFmpeg.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user