Support AVC in Annex B format, add AVCDecoderConfigurationRecord extraction logic, add Annex B to AVCC converter, add support for Matroska Clusters with undefined size, & a couple other improvements

This commit is contained in:
Vanilagy
2025-05-11 20:47:37 +02:00
parent 7ae47c0b23
commit f1eed5aa9e
15 changed files with 795 additions and 263 deletions
+18 -52
View File
@@ -1,5 +1,5 @@
import { parseOpusTocByte } from '../codec';
import { assert, ilog, readBits, toDataView } from '../misc';
import { assert, Bitstream, ilog, toDataView } from '../misc';
export const OGGS = 0x5367674f; // 'OggS'
@@ -96,40 +96,6 @@ export const extractSampleMetadata = (
// Based on vorbis_parser.c from FFmpeg.
export const parseModesFromVorbisSetupPacket = (setupHeader: Uint8Array) => {
class Bitstream {
bytes: Uint8Array;
pos: number;
constructor(bytes: Uint8Array) {
this.bytes = bytes;
this.pos = 0;
}
read(n: number): number {
const result = readBits(this.bytes, this.pos, this.pos + n);
this.pos += n;
return result;
}
skip(n: number): void {
this.pos += n;
}
getBitsLeft(): number {
return this.bytes.length * 8 - this.pos;
}
getBitCount(): number {
return this.pos;
}
clone(): Bitstream {
const clone = new Bitstream(this.bytes);
clone.pos = this.pos;
return clone;
}
}
// Verify that this is a Setup header.
if (setupHeader.length < 7) {
throw new Error('Setup header is too short.');
@@ -150,14 +116,14 @@ export const parseModesFromVorbisSetupPacket = (setupHeader: Uint8Array) => {
}
// Initialize a Bitstream on the reversed buffer.
const bs = new Bitstream(revBuffer);
const bitstream = new Bitstream(revBuffer);
// --- Find the framing bit.
// In FFmpeg code, we scan until get_bits1() returns 1.
let gotFramingBit = 0;
while (bs.getBitsLeft() > 97) {
if (bs.read(1) === 1) {
gotFramingBit = bs.getBitCount();
while (bitstream.getBitsLeft() > 97) {
if (bitstream.readBits(1) === 1) {
gotFramingBit = bitstream.pos;
break;
}
}
@@ -170,23 +136,23 @@ export const parseModesFromVorbisSetupPacket = (setupHeader: Uint8Array) => {
let modeCount = 0;
let gotModeHeader = false;
let lastModeCount = 0;
while (bs.getBitsLeft() >= 97) {
const tempPos = bs.pos;
const a = bs.read(8);
const b = bs.read(16);
const c = bs.read(16);
while (bitstream.getBitsLeft() >= 97) {
const tempPos = bitstream.pos;
const a = bitstream.readBits(8);
const b = bitstream.readBits(16);
const c = bitstream.readBits(16);
// If a > 63 or b or c nonzero, assume we’ve gone too far.
if (a > 63 || b !== 0 || c !== 0) {
bs.pos = tempPos;
bitstream.pos = tempPos;
break;
}
bs.skip(1);
bitstream.skipBits(1);
modeCount++;
if (modeCount > 64) {
break;
}
const bsClone = bs.clone();
const candidate = bsClone.read(6) + 1;
const bsClone = bitstream.clone();
const candidate = bsClone.readBits(6) + 1;
if (candidate === modeCount) {
gotModeHeader = true;
lastModeCount = modeCount;
@@ -201,16 +167,16 @@ export const parseModesFromVorbisSetupPacket = (setupHeader: Uint8Array) => {
const finalModeCount = lastModeCount;
// --- Reinitialize the bitstream.
bs.pos = 0;
bitstream.pos = 0;
// Skip the bits up to the found framing bit.
bs.skip(gotFramingBit);
bitstream.skipBits(gotFramingBit);
// --- Now read, for each mode (in reverse order), 40 bits then one bit.
// That one bit is the mode blockflag.
const modeBlockflags = Array(finalModeCount).fill(0) as number[];
for (let i = finalModeCount - 1; i >= 0; i--) {
bs.skip(40);
modeBlockflags[i] = bs.read(1);
bitstream.skipBits(40);
modeBlockflags[i] = bitstream.readBits(1);
}
return { modeBlockflags };