diff --git a/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts b/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts index b3f69f117..699661b86 100644 --- a/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts +++ b/apps/web/src/pages/generate/hooks/useCanvasPlayer.ts @@ -81,6 +81,47 @@ function findCodecConfigRecursive( return undefined } +/** + * 从 MP4 buffer 中解析视频轨道的旋转角度。 + * iOS HEVC 竖屏视频用 rotation=90° 元数据标记, + * mp4box.js 的 track_width/track_height 不反映旋转后的尺寸。 + */ +function extractVideoRotation(buffer: ArrayBuffer): number { + try { + const view = new DataView(buffer) + let offset = 0 + while (offset < buffer.byteLength - 8) { + const size = view.getUint32(offset) + const type = String.fromCharCode( + view.getUint8(offset + 4), + view.getUint8(offset + 5), + view.getUint8(offset + 6), + view.getUint8(offset + 7), + ) + if (size <= 0 || offset + size > buffer.byteLength) break + if (type === "tkhd") { + const version = view.getUint8(offset + 8) + const matrixOffset = offset + (version === 0 ? 28 : 40) + if (matrixOffset + 36 <= offset + size) { + const a = view.getInt32(matrixOffset) + const b = view.getInt32(matrixOffset + 4) + if (Math.abs(b) > 0x00008000) { + return b > 0 ? 90 : 270 + } + if (a < -0x00008000) { + return 180 + } + return 0 + } + } + offset += size + } + } catch { + // 解析失败不阻塞,返回 0 + } + return 0 +} + // ── 帧队列(环形缓冲区) ── interface FrameEntry { frame: VideoFrame @@ -370,6 +411,18 @@ export function useCanvasPlayer( samples: [], } + // ═══ 新增:检测旋转元数据,交换宽高 ═══ + const rotation = extractVideoRotation(buffer) + if (rotation === 90 || rotation === 270) { + const tmpW = meta.videoWidth + meta.videoWidth = meta.videoHeight + meta.videoHeight = tmpW + console.log( + `[useCanvasPlayer] Rotation ${rotation}° detected, swapped dimensions to ${meta.videoWidth}x${meta.videoHeight}`, + ) + } + // ═══ 新增结束 ═══ + // 提取所有 samples mp4File.setExtractionOptions(videoTrack.id ?? 1, null, { nbSamples: Infinity, // 提取所有 sample @@ -479,6 +532,15 @@ export function useCanvasPlayer( }) try { + // ═══ 新增:旋转检测 ═══ + const rotation = extractVideoRotation(_buffer) + if (rotation === 90 || rotation === 270) { + const tmpW = meta.videoWidth + meta.videoWidth = meta.videoHeight + meta.videoHeight = tmpW + } + // ═══ 新增结束 ═══ + await decoder.configure({ codec: meta.codec, codedWidth: meta.videoWidth,