"use client" import { ChangeEvent, useEffect, useRef, useState } from "react" import type { WorkerMessage, WorkerResponse, } from "@/app/component/label/video/types/libav" export default function Home() { const workerRef = useRef(null) const [ready, setReady] = useState(false) const [logs, setLogs] = useState([]) const [videoUrl, setVideoUrl] = useState(null) const [processing, setProcessing] = useState(false) const addLog = (msg: string) => { setLogs((prev) => [...prev.slice(-10), msg]) // 只保留最近10条 } useEffect(() => { // 初始化 Worker // Next.js 会自动检测到这个语法并单独打包 Worker workerRef.current = new Worker( new URL("./libav.worker.ts", import.meta.url) ) // 设置消息监听 workerRef.current.onmessage = (event: MessageEvent) => { const { type } = event.data switch (type) { case "READY": setReady(true) addLog("FFmpeg core loaded and ready.") break case "LOG": if ("message" in event.data) addLog(event.data.message) break case "DONE": if ("data" in event.data) { const url = URL.createObjectURL(event.data.data) setVideoUrl(url) setProcessing(false) addLog("Transcoding complete!") } break case "ERROR": if ("error" in event.data) { addLog(`Error: ${event.data.error}`) setProcessing(false) } break } } // 告诉 Worker 加载 libav // 这里的路径必须指向 public 目录下的实际文件 // 使用 default-cli 变体以确保包含 MP4/H.264 等常见格式的解封装器和解码器 const LIBAV_PATH = "/libav/libav-6.8.8.0-default.js" workerRef.current.postMessage({ type: "LOAD", config: { corePath: location.origin + LIBAV_PATH }, } as WorkerMessage) return () => { workerRef.current?.terminate() } }, []) const handleFileChange = (e: ChangeEvent) => { const file = e.target.files?.[0] if (!file || !workerRef.current || !ready) return setProcessing(true) setVideoUrl(null) setLogs([]) addLog(`Starting upload: ${file.name}`) workerRef.current.postMessage({ type: "TRANSCODE", file: file, } as WorkerMessage) } return (

Next.js + Libav.js + Worker

Status:{" "} {ready ? ( Ready ) : ( Loading Core... )}

Processing: {processing ? "Yes" : "No"}

{videoUrl && (

Result:

)}
{logs.map((log, i) => (
{log}
))}
) }