From 94ea579df31a93f8e7e3804e60f23e46d212640c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=8F=E6=9D=B0?= Date: Wed, 19 Aug 2026 18:06:35 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Canvas=20description=E7=BC=93=E5=AD=98+?= =?UTF-8?q?=E5=85=B3=E9=94=AE=E5=B8=A7=E5=AE=B9=E9=94=99+data=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 descriptionCache ref,从第一个分片提取后缓存供后续分片使用 - configure 前添加调试日志确认 description 是否正确传递 - EncodedVideoChunk data 字段:sample.data.buffer → sample.data - 添加关键帧容错:跳过第一个 sync 之前的非关键帧 - destroy 时清理 descriptionCache --- .../pages/generate/hooks/useCanvasPlayer.ts | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts b/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts index f11c4a73e..f68f65cf2 100644 --- a/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts +++ b/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts @@ -204,6 +204,7 @@ export function useCanvasPlayer( const videoDimRef = useRef<{ width: number; height: number }>({ width: 0, height: 0 }) const isDestroyedRef = useRef(false) const lastProgressUpdateRef = useRef(0) + const descriptionCache = useRef>(new Map()) // 计算总时长 const totalDuration = segments.reduce((sum, seg) => sum + (seg.endTime - seg.startTime), 0) @@ -277,7 +278,20 @@ export function useCanvasPlayer( } // 提取编解码器配置数据(HEVC 必需,H.264 也需要) - const description = extractCodecDescription(buffer) + let description = extractCodecDescription(buffer) + + // 如果当前分片没有 description,尝试从缓存获取(后续分片可能不含 moov box) + if (!description) { + for (const cached of descriptionCache.current.values()) { + description = cached + break + } + } + + // 如果找到了 description,缓存起来供后续分片使用 + if (description) { + descriptionCache.current.set(segment.assetId, description) + } meta = { assetId: segment.assetId, @@ -338,6 +352,14 @@ export function useCanvasPlayer( }, }) + console.log("[useCanvasPlayer] configure:", { + codec: meta.codec, + description: meta.description, + descriptionByteLength: meta.description?.byteLength, + videoWidth: meta.videoWidth, + videoHeight: meta.videoHeight, + }) + try { await decoder.configure({ codec: meta.codec, @@ -388,16 +410,23 @@ export function useCanvasPlayer( // 等待 samples 收集完成(mp4box start() 是同步的) await new Promise((resolve) => setTimeout(resolve, 10)) - // 送入解码器 + // 送入解码器(跳过第一个关键帧之前的非关键帧) + let hasKeyFrame = false for (const sample of samplesCollected) { if (!sample.data || isDestroyedRef.current) continue if (decoder.state === "closed") break + if (!hasKeyFrame && !sample.is_sync) { + console.warn("[useCanvasPlayer] Skipping non-keyframe chunk before first sync") + continue + } + if (sample.is_sync) hasKeyFrame = true + const chunk = new EncodedVideoChunk({ type: sample.is_sync ? "key" : "delta", timestamp: (sample.cts / meta.timescale) * 1_000_000, duration: (sample.duration / meta.timescale) * 1_000_000, - data: sample.data.buffer as ArrayBuffer, + data: sample.data, }) try { @@ -590,6 +619,7 @@ export function useCanvasPlayer( frameQueueRef.current.clear() segmentDataRef.current.clear() segmentMetaRef.current = [] + descriptionCache.current.clear() }, []) // ── 预加载下一个片段的数据 ── -- 2.54.0