hls.js
Version:
JavaScript HLS client using MediaSourceExtension
154 lines (134 loc) • 3.9 kB
text/typescript
/**
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
*/
import { logger } from '../../utils/logger';
class ExpGolomb {
private data: Uint8Array;
public bytesAvailable: number;
private word: number;
private bitsAvailable: number;
constructor(data: Uint8Array) {
this.data = data;
// the number of bytes left to examine in this.data
this.bytesAvailable = data.byteLength;
// the current word being examined
this.word = 0; // :uint
// the number of bits left to examine in the current word
this.bitsAvailable = 0; // :uint
}
// ():void
loadWord(): void {
const data = this.data;
const bytesAvailable = this.bytesAvailable;
const position = data.byteLength - bytesAvailable;
const workingBytes = new Uint8Array(4);
const availableBytes = Math.min(4, bytesAvailable);
if (availableBytes === 0) {
throw new Error('no bytes available');
}
workingBytes.set(data.subarray(position, position + availableBytes));
this.word = new DataView(workingBytes.buffer).getUint32(0);
// track the amount of this.data that has been processed
this.bitsAvailable = availableBytes * 8;
this.bytesAvailable -= availableBytes;
}
// (count:int):void
skipBits(count: number): void {
let skipBytes; // :int
count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable);
if (this.bitsAvailable > count) {
this.word <<= count;
this.bitsAvailable -= count;
} else {
count -= this.bitsAvailable;
skipBytes = count >> 3;
count -= skipBytes << 3;
this.bytesAvailable -= skipBytes;
this.loadWord();
this.word <<= count;
this.bitsAvailable -= count;
}
}
// (size:int):uint
readBits(size: number): number {
let bits = Math.min(this.bitsAvailable, size); // :uint
const valu = this.word >>> (32 - bits); // :uint
if (size > 32) {
logger.error('Cannot read more than 32 bits at a time');
}
this.bitsAvailable -= bits;
if (this.bitsAvailable > 0) {
this.word <<= bits;
} else if (this.bytesAvailable > 0) {
this.loadWord();
} else {
throw new Error('no bits available');
}
bits = size - bits;
if (bits > 0 && this.bitsAvailable) {
return (valu << bits) | this.readBits(bits);
} else {
return valu;
}
}
// ():uint
skipLZ(): number {
let leadingZeroCount; // :uint
for (
leadingZeroCount = 0;
leadingZeroCount < this.bitsAvailable;
++leadingZeroCount
) {
if ((this.word & (0x80000000 >>> leadingZeroCount)) !== 0) {
// the first bit of working word is 1
this.word <<= leadingZeroCount;
this.bitsAvailable -= leadingZeroCount;
return leadingZeroCount;
}
}
// we exhausted word and still have not found a 1
this.loadWord();
return leadingZeroCount + this.skipLZ();
}
// ():void
skipUEG(): void {
this.skipBits(1 + this.skipLZ());
}
// ():void
skipEG(): void {
this.skipBits(1 + this.skipLZ());
}
// ():uint
readUEG(): number {
const clz = this.skipLZ(); // :uint
return this.readBits(clz + 1) - 1;
}
// ():int
readEG(): number {
const valu = this.readUEG(); // :int
if (0x01 & valu) {
// the number is odd if the low order bit is set
return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2
} else {
return -1 * (valu >>> 1); // divide by two then make it negative
}
}
// Some convenience functions
// :Boolean
readBoolean(): boolean {
return this.readBits(1) === 1;
}
// ():int
readUByte(): number {
return this.readBits(8);
}
// ():int
readUShort(): number {
return this.readBits(16);
}
// ():int
readUInt(): number {
return this.readBits(32);
}
}
export default ExpGolomb;