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() }, []) // ── 预加载下一个片段的数据 ──