Skip to content

Commit f934fe6

Browse files
committed
fix: decode HEVC error when start with SEI
1 parent b45bca5 commit f934fe6

File tree

2 files changed

+44
-20
lines changed

2 files changed

+44
-20
lines changed

.changeset/afraid-countries-fail.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@webav/av-cliper': patch
3+
---
4+
5+
fix: decode HEVC error when start with SEI

packages/av-cliper/src/clips/mp4-clip.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -561,12 +561,24 @@ async function mp4FileToSamples(otFile: OPFSToolFile, opts: MP4ClipOpts = {}) {
561561
sampleType: 'video' | 'audio',
562562
) {
563563
// todo: perf 丢弃多余字段,小尺寸对象性能更好
564+
const is_idr =
565+
sampleType === 'video' &&
566+
s.is_sync &&
567+
isIDRFrame(s.data, s.description.type);
568+
let offset = s.offset;
569+
let size = s.size;
570+
if (is_idr) {
571+
// 当 IDR 帧前面携带 SEI 数据可能导致解码失败
572+
// 所以此处通过控制 offset、size 字段 跳过 SEI 数据
573+
const seiLen = seiLenOfStart(s.data, s.description.type);
574+
offset += seiLen;
575+
size -= seiLen;
576+
}
564577
return {
565578
...s,
566-
is_idr:
567-
sampleType === 'video' &&
568-
s.is_sync &&
569-
isIDRFrame(s.data, s.description.type),
579+
is_idr,
580+
offset,
581+
size,
570582
cts: ((s.cts - delta) / s.timescale) * 1e6,
571583
dts: ((s.dts - delta) / s.timescale) * 1e6,
572584
duration: (s.duration / s.timescale) * 1e6,
@@ -1087,28 +1099,24 @@ async function videosamples2Chunks(
10871099
);
10881100
return samples.map((s) => {
10891101
const offset = s.offset - first.offset;
1090-
let sData = data.subarray(offset, offset + s.size);
1091-
if (s.is_idr) sData = removeSEIForIDR(sData);
10921102
return new EncodedVideoChunk({
10931103
type: s.is_sync ? 'key' : 'delta',
10941104
timestamp: s.cts,
10951105
duration: s.duration,
1096-
data: sData,
1106+
data: data.subarray(offset, offset + s.size),
10971107
});
10981108
});
10991109
}
11001110

11011111
return await Promise.all(
11021112
samples.map(async (s) => {
1103-
let sData = await reader.read(s.size, {
1104-
at: s.offset,
1105-
});
1106-
if (s.is_idr) sData = removeSEIForIDR(new Uint8Array(sData));
11071113
return new EncodedVideoChunk({
11081114
type: s.is_sync ? 'key' : 'delta',
11091115
timestamp: s.cts,
11101116
duration: s.duration,
1111-
data: sData,
1117+
data: await reader.read(s.size, {
1118+
at: s.offset,
1119+
}),
11121120
});
11131121
}),
11141122
);
@@ -1224,13 +1232,24 @@ function decodeGoP(
12241232
});
12251233
}
12261234

1227-
// 当 IDR 帧前面携带其它数据(如 SEI)可能导致解码失败
1228-
function removeSEIForIDR(u8buf: Uint8Array) {
1229-
const dv = new DataView(u8buf.buffer, u8buf.byteOffset, u8buf.byteLength);
1230-
if ((dv.getUint8(4) & 0x1f) === 6) {
1231-
return u8buf.subarray(dv.getUint32(0) + 4);
1235+
// 获取起始位置的 SEI 长度
1236+
function seiLenOfStart(
1237+
u8Arr: Uint8Array,
1238+
type: MP4Sample['description']['type'],
1239+
) {
1240+
if (type !== 'avc1' && type !== 'hvc1') return 0;
1241+
1242+
const dv = new DataView(u8Arr.buffer);
1243+
if (type === 'avc1' && (dv.getUint8(4) & 0x1f) === 6) {
1244+
return dv.getUint32(0) + 4;
1245+
}
1246+
if (type === 'hvc1') {
1247+
const nalUnitType = (dv.getUint8(4) >> 1) & 0x3f;
1248+
if (nalUnitType === 39 || nalUnitType === 40) {
1249+
return dv.getUint32(0) + 4;
1250+
}
12321251
}
1233-
return u8buf;
1252+
return 0;
12341253
}
12351254

12361255
function isIDRFrame(u8Arr: Uint8Array, type: MP4Sample['description']['type']) {
@@ -1239,8 +1258,8 @@ function isIDRFrame(u8Arr: Uint8Array, type: MP4Sample['description']['type']) {
12391258
const dv = new DataView(u8Arr.buffer);
12401259
let i = 0;
12411260
for (; i < u8Arr.byteLength - 4; ) {
1242-
if (type === 'avc1') {
1243-
if ((dv.getUint8(i + 4) & 0x1f) === 5) return true;
1261+
if (type === 'avc1' && (dv.getUint8(i + 4) & 0x1f) === 5) {
1262+
return true;
12441263
} else if (type === 'hvc1') {
12451264
const nalUnitType = (dv.getUint8(i + 4) >> 1) & 0x3f;
12461265
if (nalUnitType === 19 || nalUnitType === 20) return true;

0 commit comments

Comments
 (0)