fix: findCodecConfig 改为纯递归搜索,修复 HEVC description 提取失败
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 47s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m22s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m40s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m13s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m55s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m57s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 44s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 48s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m20s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 8m53s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m4s
CI/CD Pipeline / Integration Tests (push) Successful in 3m17s
CI/CD Pipeline / Unit Tests (push) Successful in 12m10s
CI/CD Pipeline / Production Browser E2E (push) Failing after 664h55m34s
CI/CD Pipeline / PR Build Web Image (push) Failing after 665h6m58s
CI/CD Pipeline / Canary Release to Production (push) Failing after 664h55m35s
CI/CD Pipeline / PR Build API Image (push) Failing after 665h7m0s
CI/CD Pipeline / Deploy Production (push) Failing after 664h55m36s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 664h55m36s
CI/CD Pipeline / Build Production Web Image (push) Failing after 664h55m36s
CI/CD Pipeline / Build Production API Image (push) Failing after 664h55m36s
CI/CD Pipeline / CI Gate (push) Failing after 664h55m36s
CI/CD Pipeline / Frontend Lint (push) Failing after 665h7m46s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 665h7m49s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 665h40m41s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 47s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m22s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m40s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m13s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m55s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m57s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 44s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 48s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m20s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 8m53s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m4s
CI/CD Pipeline / Integration Tests (push) Successful in 3m17s
CI/CD Pipeline / Unit Tests (push) Successful in 12m10s
CI/CD Pipeline / Production Browser E2E (push) Failing after 664h55m34s
CI/CD Pipeline / PR Build Web Image (push) Failing after 665h6m58s
CI/CD Pipeline / Canary Release to Production (push) Failing after 664h55m35s
CI/CD Pipeline / PR Build API Image (push) Failing after 665h7m0s
CI/CD Pipeline / Deploy Production (push) Failing after 664h55m36s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 664h55m36s
CI/CD Pipeline / Build Production Web Image (push) Failing after 664h55m36s
CI/CD Pipeline / Build Production API Image (push) Failing after 664h55m36s
CI/CD Pipeline / CI Gate (push) Failing after 664h55m36s
CI/CD Pipeline / Frontend Lint (push) Failing after 665h7m46s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 665h7m49s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 665h40m41s
根因:旧 findCodecConfig 对 container box 的 fullbox 检测逻辑错误, 导致递归搜索时跳过了子 box 的起始位置,永远找不到 hvcC/avcC。 修复: - 替换为 findCodecConfigRecursive,纯递归搜索所有嵌套 box - 不区分 fullbox/非 fullbox,统一从 offset+8 开始搜索子 box - 返回 hvcC/avcC 的内容(不含 8 字节 box header),符合 WebCodecs 要求 - 添加日志追踪找到的 box 类型和位置
This commit is contained in:
@@ -10,14 +10,25 @@ import type { Movie, Sample } from "mp4box"
|
||||
|
||||
// ── MP4 Box 解析辅助函数 ──
|
||||
|
||||
/** 在指定范围内查找 avcC / hvcC box,返回其数据 */
|
||||
function findCodecConfig(buffer: ArrayBuffer, start: number, end: number): ArrayBuffer | undefined {
|
||||
/**
|
||||
* 递归搜索 box 树,找到 hvcC 或 avcC box 并返回其配置数据(不含 8 字节 box header)
|
||||
*
|
||||
* MP4 box 嵌套结构:moov → trak → mdia → minf → stbl → stsd → hev1 → hvcC
|
||||
* 此函数不区分 fullbox / 普通 box,统一从 offset+8 开始递归搜索子 box
|
||||
* 因为所有 container box(moov/trak/mdia/minf/stbl)的 content 都从 offset+8 开始
|
||||
*/
|
||||
function findCodecConfigRecursive(
|
||||
buffer: ArrayBuffer,
|
||||
start: number,
|
||||
end: number,
|
||||
): ArrayBuffer | undefined {
|
||||
const view = new DataView(buffer)
|
||||
let offset = start
|
||||
|
||||
while (offset < end - 8) {
|
||||
const size = view.getUint32(offset)
|
||||
if (size < 8) break
|
||||
if (size < 8 || offset + size > end) break
|
||||
|
||||
const type = String.fromCharCode(
|
||||
view.getUint8(offset + 4),
|
||||
view.getUint8(offset + 5),
|
||||
@@ -25,53 +36,16 @@ function findCodecConfig(buffer: ArrayBuffer, start: number, end: number): Array
|
||||
view.getUint8(offset + 7),
|
||||
)
|
||||
|
||||
const containerBoxes = ["moov", "trak", "mdia", "minf", "stbl"]
|
||||
if (containerBoxes.includes(type)) {
|
||||
// 动态检测 fullbox:检查 offset+8 处的 version/flags 是否为 0
|
||||
// 如果不是 fullbox,content 从 offset+8 开始;如果是 fullbox,content 从 offset+12 开始
|
||||
const v1 = view.getUint8(offset + 8)
|
||||
const v2 = view.getUint8(offset + 9)
|
||||
const v3 = view.getUint8(offset + 10)
|
||||
const isFullBox = v1 === 0 && v2 === 0 && v3 === 0
|
||||
const contentStart = isFullBox ? offset + 12 : offset + 8
|
||||
const result = findCodecConfig(buffer, contentStart, offset + size)
|
||||
if (result) return result
|
||||
} else if (type === "stsd") {
|
||||
// SampleDescriptionBox 是 fullbox: 8 header + 4 version/flags + 4 entry_count
|
||||
const entryCount = view.getUint32(offset + 12)
|
||||
let entryOffset = offset + 16
|
||||
console.log("[findCodecConfig] stsd found:", {
|
||||
entryCount,
|
||||
stsdOffset: offset,
|
||||
stsdSize: size,
|
||||
})
|
||||
for (let i = 0; i < entryCount && entryOffset < offset + size; i++) {
|
||||
const entrySize = view.getUint32(entryOffset)
|
||||
const entryType = String.fromCharCode(
|
||||
view.getUint8(entryOffset + 4),
|
||||
view.getUint8(entryOffset + 5),
|
||||
view.getUint8(entryOffset + 6),
|
||||
view.getUint8(entryOffset + 7),
|
||||
)
|
||||
console.log("[findCodecConfig] entry:", { index: i, entryOffset, entrySize, entryType })
|
||||
// 视觉样本条目: 8 header + 6 reserved + 2 data_ref_index + remaining
|
||||
// 子 box 从 entryOffset + 16 + 62 开始 (skip reserved + data_ref_index + predefined)
|
||||
// 实际结构: 8(header) + 6(reserved) + 2(data_ref_index) + 16(predefined+reserved) + 2(width) + 2(height) + ...
|
||||
// 子 box 从 entryOffset + 8 + 6 + 2 + 16 + 2 + 2 + 2 + 2 + 4 + 2 + 2 + 2 + 2 = entryOffset + 78
|
||||
// 更简单的做法:扫描 entry 内的子 box
|
||||
const entryEnd = entryOffset + entrySize
|
||||
const subBoxStart = entryOffset + 8 + 78 // VisualSampleEntry 固定字段共 78 字节(ISO 14496-12)
|
||||
const result = findCodecConfig(buffer, subBoxStart, entryEnd)
|
||||
if (result) return result
|
||||
entryOffset += entrySize
|
||||
}
|
||||
} else if (type === "avcC" || type === "hvcC") {
|
||||
// 找到目标 box,返回完整 box(含 header)
|
||||
// 返回完整 box(含 size + type header),WebCodecs HEVC decoder 需要
|
||||
return buffer.slice(offset, offset + size)
|
||||
// 找到目标 codec 配置 box,返回内容(不含 8 字节 header)
|
||||
if (type === "avcC" || type === "hvcC") {
|
||||
console.log("[findCodecConfig] Found", type, "at offset", offset, "size", size)
|
||||
return buffer.slice(offset + 8, offset + size)
|
||||
}
|
||||
|
||||
if (size === 0) break
|
||||
// 递归搜索子 box(所有 box 的子 box 都从 offset+8 开始)
|
||||
const childResult = findCodecConfigRecursive(buffer, offset + 8, offset + size)
|
||||
if (childResult) return childResult
|
||||
|
||||
offset += size
|
||||
}
|
||||
return undefined
|
||||
@@ -261,7 +235,7 @@ export function useCanvasPlayer(
|
||||
view.getUint8(offset + 7),
|
||||
)
|
||||
if (type === "moov") {
|
||||
const result = findCodecConfig(buffer, offset + 8, offset + size)
|
||||
const result = findCodecConfigRecursive(buffer, offset + 8, offset + size)
|
||||
console.log("[useCanvasPlayer] extractCodecDescription:", {
|
||||
moovOffset: offset,
|
||||
moovSize: size,
|
||||
|
||||
Reference in New Issue
Block a user