fix: 移除 rotation 元数据检测,从 VideoFrame 获取实际尺寸
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
- 删除 extractVideoRotation 函数 - 删除 demuxSegment 和 decodeSegment 中的旋转检测+宽高交换代码 - 删除 decoder.configure() 中的 codedWidth/codedHeight 参数 - 在 VideoDecoder output 回调中从第一帧获取实际帧尺寸 - 核心思路:不再依赖 mp4box.js 的 track_width/track_height 和 rotation 元数据, 改为从 VideoDecoder 实际输出的 VideoFrame 获取真实尺寸
This commit is contained in:
@@ -81,47 +81,6 @@ 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
|
||||
@@ -411,18 +370,6 @@ 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
|
||||
@@ -510,6 +457,11 @@ export function useCanvasPlayer(
|
||||
// 配置解码器(每个片段可能需要不同的 codec/分辨率)
|
||||
const decoder = new VideoDecoder({
|
||||
output: (frame: VideoFrame) => {
|
||||
// 从第一帧获取实际尺寸
|
||||
if (videoDimRef.current.width === 0 || videoDimRef.current.height === 0) {
|
||||
videoDimRef.current = { width: frame.codedWidth, height: frame.codedHeight }
|
||||
console.log(`[useCanvasPlayer] Actual frame size: ${frame.codedWidth}x${frame.codedHeight}`)
|
||||
}
|
||||
const localTime = frame.timestamp / 1_000_000
|
||||
const globalTime = localTime + meta.globalStartTime
|
||||
frameQueueRef.current.push({
|
||||
@@ -532,19 +484,8 @@ 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,
|
||||
codedHeight: meta.videoHeight,
|
||||
...(meta.description ? { description: meta.description } : {}),
|
||||
})
|
||||
decoderRef.current = decoder
|
||||
|
||||
Reference in New Issue
Block a user