mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-02 21:33:52 +02:00
Implement Ogg demuxer
This commit is contained in:
@@ -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 };
|
||||
};
|
||||
Reference in New Issue
Block a user