Implement Ogg demuxer

This commit is contained in:
Vanilagy
2025-02-04 16:59:25 +01:00
parent 35c4d384cf
commit 50442691a5
11 changed files with 1423 additions and 24 deletions
+122
View File
@@ -0,0 +1,122 @@
import { readBits } from '../misc';
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;
}
}
// Based on vorbis_parser.c from FFmpeg.
export const parseModesFromSetupPacket = (setupHeader: Uint8Array) => {
// Verify that this is a Setup header.
if (setupHeader.length < 7) {
throw new Error('Setup header is too short.');
}
if (setupHeader[0] !== 5) {
throw new Error('Wrong packet type in Setup header.');
}
const signature = String.fromCharCode(...setupHeader.slice(1, 7));
if (signature !== 'vorbis') {
throw new Error('Invalid packet signature in Setup header.');
}
// Reverse the entire buffer.
const bufSize = setupHeader.length;
const revBuffer = new Uint8Array(bufSize);
for (let i = 0; i < bufSize; i++) {
revBuffer[i] = setupHeader[bufSize - 1 - i]!;
}
// Initialize a Bitstream on the reversed buffer.
const bs = 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();
break;
}
}
if (gotFramingBit === 0) {
throw new Error('Invalid Setup header: framing bit not found.');
}
// --- Search backwards for a valid mode header.
// We try to “guess” the number of modes by reading a fixed pattern.
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);
// If a > 63 or b or c nonzero, assume we’ve gone too far.
if (a > 63 || b !== 0 || c !== 0) {
bs.pos = tempPos;
break;
}
bs.skip(1);
modeCount++;
if (modeCount > 64)
break;
const bsClone = bs.clone();
const candidate = bsClone.read(6) + 1;
if (candidate === modeCount) {
gotModeHeader = true;
lastModeCount = modeCount;
}
}
if (!gotModeHeader) {
throw new Error('Invalid Setup header: mode header not found.');
}
if (lastModeCount > 63) {
throw new Error(`Unsupported mode count: ${lastModeCount}.`);
}
const finalModeCount = lastModeCount;
// --- Reinitialize the bitstream.
bs.pos = 0;
// Skip the bits up to the found framing bit.
bs.skip(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);
}
return { modeBlockflags };
};