fix: 解析 MP4 rotation 元数据,修复竖屏视频显示为横屏
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 1m22s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m58s
CI/CD Pipeline / Build Staging API Image (push) Successful in 2m16s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m40s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m18s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m42s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 56s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 50s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 7m13s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m32s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m16s
CI/CD Pipeline / Integration Tests (push) Successful in 2m57s
CI/CD Pipeline / Unit Tests (push) Successful in 11m58s
CI/CD Pipeline / Production Browser E2E (push) Failing after 650h5m47s
CI/CD Pipeline / Deploy Production (push) Failing after 650h5m48s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 650h5m49s
CI/CD Pipeline / Build Production API Image (push) Failing after 650h5m49s
CI/CD Pipeline / Build Production Web Image (push) Failing after 650h5m49s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 650h17m47s
CI/CD Pipeline / PR Build API Image (push) Failing after 650h17m49s
CI/CD Pipeline / Frontend Lint (push) Failing after 650h17m49s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 650h17m51s
CI/CD Pipeline / Canary Release to Production (push) Failing after 650h39m33s
CI/CD Pipeline / CI Gate (push) Failing after 650h39m35s
CI/CD Pipeline / PR Build Web Image (push) Failing after 650h51m35s

- 新增 extractVideoRotation 函数,从 tkhd box 解析旋转角度
- demuxSegment 中检测 90°/270° 旋转,交换宽高
- decodeSegment 中同样检测旋转,确保 configure 使用正确尺寸
- 修复 iPhone 竖屏视频在竖屏容器中显示为一小块横屏画面的问题
This commit is contained in:
xiaoxia
2026-08-20 13:08:19 +08:00
parent 85afadd166
commit 3c42541ff7
@@ -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,