From 4232f9b51d1ae9e4981339503e8ecf7e000589e4 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Sun, 22 Jun 2025 07:53:35 +0100 Subject: [PATCH 01/18] build: auto indent with 2 spaces in VS Code --- .vscode/settings.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 4c2e136ee..2477f5be1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,6 @@ { + "editor.tabSize": 2, + "editor.insertSpaces": true, "eslint.useFlatConfig": true, "eslint.workingDirectories": [{ "mode": "auto" }], "eslint.options": { From a79b137c97d58cc6162c26a379bfef60e4a4c0c2 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Sun, 22 Jun 2025 07:54:25 +0100 Subject: [PATCH 02/18] feat: allow HTML formatted elements --- demo/boxHtmlTable.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo/boxHtmlTable.js b/demo/boxHtmlTable.js index 8bfef6151..7cee67458 100644 --- a/demo/boxHtmlTable.js +++ b/demo/boxHtmlTable.js @@ -59,7 +59,8 @@ function generateBoxTable(box, excluded_fields, additional_props, no_header) { if (i % 4 === 3) html += ' '; } } else { - if (box[prop].hasOwnProperty('toString') && typeof box[prop].toString === 'function') + if (typeof box[prop].toHTML === 'function') html += box[prop].toHTML(); + else if (box[prop].hasOwnProperty('toString') && typeof box[prop].toString === 'function') html += box[prop].toString(); else html += box[prop]; } From 5e521fa4a7cc45fe35aff0e39674b6ad7f9bbde0 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Sun, 22 Jun 2025 07:54:57 +0100 Subject: [PATCH 03/18] feat: Bitstream reader supporting non-aligned values --- src/BitBuffer.ts | 316 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 src/BitBuffer.ts diff --git a/src/BitBuffer.ts b/src/BitBuffer.ts new file mode 100644 index 000000000..5c6de9aab --- /dev/null +++ b/src/BitBuffer.ts @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2023. Paul Higgs + * License: BSD-3-Clause (see LICENSE file) + * + * + * reads bits and bytes from a buffer that may not contain aligned values + */ + +import { Endianness } from '#/DataStream'; + +class State { + rbyte: number; + rbit: number; + wbyte: number; + wbit: number; + end: number; + read_error: boolean; + write_error: boolean; + + constructor() { + this.rbyte = this.rbit = this.rbyte = this.rbit = this.end - 0; + this.read_error = this.write_error = false; + } +} + +export class BitBuffer { + private endianness: Endianness; + private _buffer: Array; + private _buffer_size: number; + private _state: State; + private _big_endian = true; // results are returned Big Endian + + constructor(stream?: Uint8Array, endianness?: Endianness) { + this._state = new State(); + this.load(stream ? stream : new Uint8Array([])); + this.endianness = endianness ? endianness : this._ENDIANNESS(); + } + + load(stream: Uint8Array): void { + this._buffer = [...stream]; + this._buffer_size = stream.length; + this._state.rbit = this._state.rbyte = 0; + this._state.wbit = 0; + this._state.wbyte = this._state.end = this._buffer_size; + this._state.read_error = this._state.write_error = false; + } + appendUint8(byte: number): void { + this._buffer.push(byte); + this._buffer_size += 1; + this._state.end = this._state.wbyte = this._buffer_size; + } + + getBit(): number { + //! Read the next bit and advance the read pointer. + if (this._state.read_error || this.endOfRead()) { + this._state.read_error = true; + return 0; + } + const bit: number = + (this._buffer[this._state.rbyte] >> + (this._big_endian ? 7 - this._state.rbit : this._state.rbit)) & + 0x01; + if (++this._state.rbit > 7) { + this._state.rbyte++; + this._state.rbit = 0; + } + return bit; + } + + peekBit(): number { + //! Read the next bit and but dont advance the read pointer. + if (this._state.read_error || this.endOfRead()) { + this._state.read_error = true; + return 0; + } + const bit: number = + (this._buffer[this._state.rbyte] >> + (this._big_endian ? 7 - this._state.rbit : this._state.rbit)) & + 0x01; + return bit; + } + + endOfRead(): boolean { + return this._state.rbyte === this._state.wbyte && this._state.rbit === this._state.wbit; + } + + getBool(): boolean { + return this.getBit() !== 0; + } + + private _rdb(bytes: number): number { + let i: number, res: number; + // eslint-disable-next-line no-loss-of-precision + const ff = 0xffffffffffffffff; + if (this._state.read_error) return ff; + if (this._state.rbit === 0) { + // Read buffer is byte aligned. Most common case. + if (this._state.rbyte + bytes > this._state.wbyte) { + // Not enough bytes to read. + this._state.read_error = true; + return ff; + } else { + for (res = 0, i = 0; i < bytes; i++) res = (res << 8) + this._buffer[this._state.rbyte + i]; + this._state.rbyte += bytes; + return res; + } + } else { + // Read buffer is not byte aligned, use an intermediate aligned buffer. + if (this.currentReadBitOffset() + 8 * bytes > this.currentWriteBitOffset()) { + // Not enough bytes to read. + this._state.read_error = true; + return ff; + } else { + for (res = 0, i = 0; i < bytes; i++) { + if (this._big_endian) + res = + (res << 8) + + ((this._buffer[this._state.rbyte] << this._state.rbit) | + (this._buffer[this._state.rbyte + 1] >> (8 - this._state.rbit))); + else + res = + (res << 8) + + ((this._buffer[this._state.rbyte] >> this._state.rbit) | + (this._buffer[this._state.rbyte + 1] << (8 - this._state.rbit))); + this._state.rbyte++; + } + return res; + } + } + return ff; // we should never get here!! + } + + getUint8() { + return this._rdb(1); + } + + getUint16() { + return this._big_endian ? this._GetUInt16BE(this._rdb(2)) : this._GetUInt16LE(this._rdb(2)); + } + private _ByteSwap16 = function (x) { + return (x << 8) | (x >> 8); + }; + private _CondByteSwap16BE = function (val) { + return this._OSisLittleEndian() ? this._ByteSwap16(val) : val; + }; + private _CondByteSwap16LE = function (val) { + return this._OSisLittleEndian() ? val : this._ByteSwap16(val); + }; + private _GetUInt16BE = function (val) { + return this._CondByteSwap16BE(val); + }; + private _GetUInt16LE = function (val) { + return this._CondByteSwap16LE(val); + }; + + getUint24() { + return this._big_endian ? this._GetUInt24BE(this._rdb(3)) : this._GetUInt24LE(this._rdb(3)); + } + + private _ByteSwap24 = function (x) { + return ((x & 0xff0000) >> 16) | (x & 0xff00) | (x & (0xff << 16)); + }; + private _CondByteSwap24BE = function (val) { + return this._OSisLittleEndian() ? this._ByteSwap24(val) : val; + }; + private _CondByteSwap24LE = function (val) { + return this._OSisLittleEndian() ? val : this._ByteSwap24(val); + }; + private _GetUInt24BE = function (val) { + return this._CondByteSwap24BE(val); + }; + private _GetUInt24LE = function (val) { + return this._CondByteSwap24LE(val); + }; + + _getUint32() { + return this._big_endian ? this._GetUInt32BE(this._rdb(4)) : this._GetUInt32LE(this._rdb(4)); + } + + private _ByteSwap32(x) { + return (x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | (x >> 24); + } + private _CondByteSwap32BE(val) { + return this._OSisLittleEndian() ? this._ByteSwap32(val) : val; + } + private _CondByteSwap32LE(val) { + return this._OSisLittleEndian() ? val : this._ByteSwap32(val); + } + private _GetUInt32BE(val) { + return this._CondByteSwap32BE(val); + } + private _GetUInt32LE(val) { + return this._CondByteSwap32LE(val); + } + + getBits(bits: number): number { + // No read if read error is already set or not enough bits to read. + if ( + this._state.read_error || + this.currentReadBitOffset() + bits > this.currentWriteBitOffset() + ) { + this._state.read_error = true; + return 0; + } + let val = 0; + if (this._big_endian) { + // Read leading bits up to byte boundary + while (bits > 0 && this._state.rbit !== 0) { + val = (val << 1) | this.getBit(); + --bits; + } + + // Read complete bytes + while (bits > 7) { + val = (val << 8) | this._buffer[this._state.rbyte++]; + bits -= 8; + } + + // Read trailing bits + while (bits > 0) { + val = (val << 1) | this.getBit(); + --bits; + } + } else { + // Little endian decoding + let shift = 0; + + // Read leading bits up to byte boundary + while (bits > 0 && this._state.rbit !== 0) { + val |= this.getBit() << shift; + --bits; + shift++; + } + + // Read complete bytes + while (bits > 7) { + val |= this._buffer[this._state.rbyte++] << shift; + bits -= 8; + shift += 8; + } + + // Read trailing bits + while (bits > 0) { + val |= this.getBit() << shift; + --bits; + shift++; + } + } + return val; + } + + skipBits(bits: number): boolean { + if (this._state.read_error) { + // Can't skip bits and bytes if read error is already set. + return false; + } + const rpos = 8 * this._state.rbyte + this._state.rbit + bits; + const wpos = 8 * this._state.wbyte + this._state.wbit; + if (rpos > wpos) { + this._state.rbyte = this._state.wbyte; + this._state.rbit = this._state.wbit; + this._state.read_error = true; + return false; + } + this._state.rbyte = rpos >> 3; + this._state.rbit = rpos & 7; + return true; + } + + skipBit(): boolean { + return this.skipBits(1); + } + + getUE(): number { + // read in an unsigned Exp-Golomb code; + if (this.getBit() === 1) return 0; + let zero_count = 1; + while (this.peekBit() === 0) { + this.getBit(); + zero_count++; + } + return this.getBits(zero_count + 1) - 1; + } + + byte_alignment() { + while (!this._state.read_error && this._state.rbit !== 0) this.skipBit(); + } + + private _OSisLittleEndian() { + return this.endianness === Endianness.LITTLE_ENDIAN; + } + + private _ENDIANNESS(): Endianness { + const buf = new ArrayBuffer(4); + const u32data = new Uint32Array(buf); + const u8data = new Uint8Array(buf); + u32data[0] = 0xcafebabe; + return u8data[3] === 0xca ? Endianness.BIG_ENDIAN : Endianness.LITTLE_ENDIAN; + } + + currentReadByteOffset() { + return this._state.rbyte; + } + currentReadBitOffset() { + return 8 * this._state.rbyte + this._state.rbit; + } + currentWriteByteOffset() { + return this._state.wbyte; + } + currentWriteBitOffset() { + return 8 * this._state.wbyte + this._state.wbit; + } + bitsRemaining() { + return this.currentWriteBitOffset() - this.currentReadBitOffset(); + } +} From 9477c35fd20402fef016cf50c627f953700f3989 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Sun, 22 Jun 2025 08:06:49 +0100 Subject: [PATCH 04/18] feat: add parsing of AVS3 audio configuration --- entries/all-boxes.ts | 1 + src/boxes/dca3.ts | 269 +++++++++++++++++++++++++ src/boxes/sampleentries/sampleentry.ts | 8 + 3 files changed, 278 insertions(+) create mode 100644 src/boxes/dca3.ts diff --git a/entries/all-boxes.ts b/entries/all-boxes.ts index 87749bf09..3a35c38a7 100644 --- a/entries/all-boxes.ts +++ b/entries/all-boxes.ts @@ -18,6 +18,7 @@ export * from '#/boxes/cprt'; export * from '#/boxes/cslg'; export * from '#/boxes/ctts'; export * from '#/boxes/dac3'; +export * from '#/boxes/dca3'; export * from '#/boxes/dec3'; export * from '#/boxes/defaults'; export * from '#/boxes/dfLa'; diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts new file mode 100644 index 000000000..a65e7dd6c --- /dev/null +++ b/src/boxes/dca3.ts @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2023. Paul Higgs + * License: BSD-3-Clause (see LICENSE file) + */ + +import { Box } from '#/box'; +import { MP4BoxStream } from '#/stream'; +import { BitBuffer } from '#/BitBuffer'; + +type DescriberFunction = (n: number) => string; + +class DescribedValue { + private value: number; + private description: string; + + constructor(value: number, descriptionFn?: DescriberFunction) { + this.value = value; + this.description = descriptionFn ? descriptionFn(value) : null; + } + toString() { + return this.value + (this.description ? ' (' + this.description + ')' : ''); + } + get() { + return this.value; + } +} + +function AVS3Acodec(codec_id: number) { + const codecs = ['General High Rate', 'Lossless', 'General Full Rate']; + return codec_id < codecs.length ? codecs[codec_id] : 'undefined'; +} + +function AVS3Achannel_number(channel_number_index: number) { + const configs = [ + 'Mono', + 'Stereo', + '5.1', + '7.1', + '10.2', + '22.2', + '4.0/FOA', + '5.1.2', + '5.1.4', + '7.1.2', + '7.1.4', + '3rd HOA', + '2nd HOA', + ]; + return channel_number_index < configs.length ? configs[channel_number_index] : 'undefined'; +} + +function AVS3Asampling_frequency(sampling_frequency_index: number) { + const frequencies = [192000, 96000, 48000, 44100, 32000, 24000, 22050, 16000, 8000]; + return sampling_frequency_index < frequencies.length + ? frequencies[sampling_frequency_index] + 'Hz' + : 'reserved'; +} + +function AVS3Aresolution(resolution: number) { + switch (resolution) { + case 0: + return '8 bits/sample'; + case 1: + return '16 bits/sample'; + case 2: + return '24 bits/sample'; + } + return 'reserved'; +} + +function AVS3Anntype(nn_type: number) { + switch (nn_type) { + case 0: + return 'basic neural network'; + case 1: + return 'low-complexity neural network'; + } + return 'reserved'; +} + +function AVS3Acodingprofile(conding_profile: number) { + switch (conding_profile) { + case 0: + return 'basic framework'; + case 1: + return 'object metadata framework'; + case 2: + return 'HOA data coding framework'; + } + return 'reserved'; +} + +class AVS3data { + toHTML(data): string { + let res = ''; + const props = Object.getOwnPropertyNames(data); + if (props) + props.forEach(function (val) { + let fmt_val = ''; + if (Array.isArray(data[val])) { + for (let i = 0; i < data[val].length; i++) { + const hex = data[val][i].toString(16); + fmt_val += hex.length === 1 ? '0' + hex : hex; + if (i % 4 === 3) fmt_val += ' '; + } + } else fmt_val = data[val]; + res += '' + val + '' + fmt_val + ''; + }); + return '' + res + '
'; + } +} + +interface GAconfig { + sampling_frequency_index?: DescribedValue; + nn_type?: DescribedValue; + content_type?: number; + channel_number_index?: DescribedValue; + number_objects?: number; + hoa_order?: number; + total_bitrate?: number; + resolution?: DescribedValue; +} +class AVS3GAConfig extends AVS3data { + data: GAconfig; + constructor(bit_reader: BitBuffer) { + super(); + this.data = {}; + this.parse(bit_reader); + } + parse(bit_reader: BitBuffer) { + this.data.sampling_frequency_index = new DescribedValue( + bit_reader.getBits(4), + AVS3Asampling_frequency, + ); + this.data.nn_type = new DescribedValue(bit_reader.getBits(3), AVS3Anntype); + bit_reader.skipBits(1); + this.data.content_type = bit_reader.getBits(4); + if (this.data.content_type === 0) { + this.data.channel_number_index = new DescribedValue( + bit_reader.getBits(7), + AVS3Achannel_number, + ); + bit_reader.skipBits(1); + } else if (this.data.content_type === 1) { + this.data.number_objects = bit_reader.getBits(7); + bit_reader.skipBits(1); + } else if (this.data.content_type === 2) { + this.data.channel_number_index = new DescribedValue( + bit_reader.getBits(7), + AVS3Achannel_number, + ); + bit_reader.skipBits(1); + this.data.number_objects = bit_reader.getBits(7); + bit_reader.skipBits(1); + } else if (this.data.content_type === 3) { + this.data.hoa_order = bit_reader.getBits(4); + } + this.data.total_bitrate = bit_reader.getUint16(); + this.data.resolution = new DescribedValue(bit_reader.getBits(2), AVS3Aresolution); + } + toHTML(): string { + return super.toHTML(this.data); + } +} + +interface GHconfig { + sampling_frequency_index?: number; + anc_data_index?: number; + coding_profile?: DescribedValue; + bitstream_type?: number; + channel_number_index?: number; + bitrate_index?: number; + raw_frame_length?: number; + resolution?: DescribedValue; + addition_info?: Array; +} +class AVS3GHConfig extends AVS3data { + data: GHconfig; + constructor(bit_reader: BitBuffer) { + super(); + this.data = {}; + this.parse(bit_reader); + } + parse(bit_reader: BitBuffer) { + this.data.sampling_frequency_index = bit_reader.getBits(4); + this.data.anc_data_index = bit_reader.getBit(); + this.data.coding_profile = new DescribedValue(bit_reader.getBits(3), AVS3Acodingprofile); + this.data.bitstream_type = bit_reader.getBits(1); + this.data.channel_number_index = bit_reader.getBits(7); + this.data.bitrate_index = bit_reader.getBits(4); + this.data.raw_frame_length = bit_reader.getUint16(); + this.data.resolution = new DescribedValue(bit_reader.getBits(2), AVS3Aresolution); + const addition_info_length = bit_reader.getUint16(); + if (addition_info_length > 0) { + this.data.addition_info = []; + for (let i = 0; i < addition_info_length; i++) + this.data.addition_info.push(bit_reader.getUint8()); + } + } + toHTML(): string { + return super.toHTML(this.data); + } +} + +interface LLconfig { + sampling_frequency_index?: number; + sampling_frequency?: number; + anc_data_index?: number; + coding_profile?: DescribedValue; + channel_number?: number; + resolution?: DescribedValue; + addition_info?: Array; +} +class AVS3LLConfig extends AVS3data { + data: LLconfig; + constructor(bit_reader: BitBuffer) { + super(); + this.data = {}; + this.parse(bit_reader); + } + parse(bit_reader: BitBuffer) { + this.data.sampling_frequency_index = bit_reader.getBits(4); + if (this.data.sampling_frequency_index === 0xf) + this.data.sampling_frequency = bit_reader.getUint24(); + this.data.anc_data_index = bit_reader.getBit(); + this.data.coding_profile = new DescribedValue(bit_reader.getBits(3), AVS3Acodingprofile); + this.data.channel_number = bit_reader.getUint8(); + this.data.resolution = new DescribedValue(bit_reader.getBits(2), AVS3Aresolution); + const addition_info_length = bit_reader.getUint16(); + if (addition_info_length > 0) { + this.data.addition_info = []; + for (let i = 0; i < addition_info_length; i++) + this.data.addition_info.push(bit_reader.getUint8()); + } + bit_reader.skipBits(2); // reserved + } + toHTML(): string { + return super.toHTML(this.data); + } +} + +export class dca3Box extends Box { + static override readonly fourcc = 'dca3' as const; + box_name = 'AVS3AConfigurationBox' as const; + + private audio_codec_id: DescribedValue; + private Avs3AudioGAConfig?: AVS3GAConfig; + private Avs3AudioGHConfig?: AVS3GHConfig; + private Avs3AudioLLConfig?: AVS3LLConfig; + + parse(stream: MP4BoxStream) { + const bit_reader = new BitBuffer(); + for (let i = 0; i < this.size - this.hdr_size; i++) bit_reader.appendUint8(stream.readUint8()); + this.audio_codec_id = new DescribedValue(bit_reader.getBits(4), AVS3Acodec); + + switch (this.audio_codec_id.get()) { + case 2: + this.Avs3AudioGAConfig = new AVS3GAConfig(bit_reader); + break; + case 0: + this.Avs3AudioGHConfig = new AVS3GHConfig(bit_reader); + break; + case 1: + this.Avs3AudioLLConfig = new AVS3LLConfig(bit_reader); + break; + } + bit_reader.byte_alignment(); + } +} diff --git a/src/boxes/sampleentries/sampleentry.ts b/src/boxes/sampleentries/sampleentry.ts index da7690062..fef76dd58 100644 --- a/src/boxes/sampleentries/sampleentry.ts +++ b/src/boxes/sampleentries/sampleentry.ts @@ -376,6 +376,14 @@ export class fLaCSampleEntry extends AudioSampleEntry { static override readonly fourcc = 'fLaC' as const; } +export class av3aSampleEntry extends AudioSampleEntry { + static override readonly fourcc = 'av3a' as const; +} + +export class a3asSampleEntry extends AudioSampleEntry { + static override readonly fourcc = 'a3as' as const; +} + // Encrypted sample entries export class encvSampleEntry extends VisualSampleEntry { static override readonly fourcc = 'encv' as const; From 62e9384768a1a1b30176a0e76f318baf090493e7 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Sun, 22 Jun 2025 10:12:03 +0100 Subject: [PATCH 05/18] feat: export items to be reused in AVS3 Video parsing --- src/BitBuffer.ts | 2 +- src/boxes/dca3.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/BitBuffer.ts b/src/BitBuffer.ts index 5c6de9aab..09a036b4d 100644 --- a/src/BitBuffer.ts +++ b/src/BitBuffer.ts @@ -173,7 +173,7 @@ export class BitBuffer { return this._CondByteSwap24LE(val); }; - _getUint32() { + getUint32() { return this._big_endian ? this._GetUInt32BE(this._rdb(4)) : this._GetUInt32LE(this._rdb(4)); } diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index a65e7dd6c..3f4830acd 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -7,9 +7,9 @@ import { Box } from '#/box'; import { MP4BoxStream } from '#/stream'; import { BitBuffer } from '#/BitBuffer'; -type DescriberFunction = (n: number) => string; +export type DescriberFunction = (n: number) => string; -class DescribedValue { +export class DescribedValue { private value: number; private description: string; @@ -90,7 +90,7 @@ function AVS3Acodingprofile(conding_profile: number) { return 'reserved'; } -class AVS3data { +export class AVS3data { toHTML(data): string { let res = ''; const props = Object.getOwnPropertyNames(data); From eec80814b697a6eb651eb71934a4dc9afeffb268 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Mon, 23 Jun 2025 06:01:03 +0100 Subject: [PATCH 06/18] feat: add parsing for AVS3 video using some functions in common with AVS3 audio --- entries/all-boxes.ts | 1 + src/boxes/av3c.ts | 510 +++++++++++++++++++++++++ src/boxes/avs-common.ts | 78 ++++ src/boxes/dca3.ts | 65 ++-- src/boxes/sampleentries/sampleentry.ts | 31 +- 5 files changed, 638 insertions(+), 47 deletions(-) create mode 100644 src/boxes/av3c.ts create mode 100644 src/boxes/avs-common.ts diff --git a/entries/all-boxes.ts b/entries/all-boxes.ts index 3a35c38a7..a5765fd40 100644 --- a/entries/all-boxes.ts +++ b/entries/all-boxes.ts @@ -2,6 +2,7 @@ export * from '#/boxes/a1lx'; export * from '#/boxes/a1op'; export * from '#/boxes/auxC'; export * from '#/boxes/av1C'; +export * from '#/boxes/av3c'; export * from '#/boxes/avcC'; export * from '#/boxes/btrt'; export * from '#/boxes/ccst'; diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts new file mode 100644 index 000000000..1d165816b --- /dev/null +++ b/src/boxes/av3c.ts @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2025. Paul Higgs + * License: BSD-3-Clause (see LICENSE file) + */ + +import { Box } from '#/box'; +import { MP4BoxStream } from '#/stream'; +import { BitBuffer } from '#/BitBuffer'; + +import { DescribedValue, AVS3data, HexadecimalValue, BinaryValue } from './avs-common'; + +interface ReferencePicture { + library_index_flag?: number; + referenced_library_picture_index?: number; + abs_delta_doi?: number; + sign_delta_doi?: number; +} + +class ReferencePictureSet { + list: number; + set: number; + pics: Array; + reference_to_library_enable_flag?: number; + private library_enable_flag_set: boolean; + + constructor(list: number, set: number) { + this.list = list; + this.set = set; + this.pics = []; + this.library_enable_flag_set = false; + } + set_reference_to_library_enable_flag(flag: number) { + this.library_enable_flag_set = true; + this.reference_to_library_enable_flag = flag; + } + get_reference_to_library_enable_flag(): number { + return this.library_enable_flag_set ? this.reference_to_library_enable_flag : 0; + } + push(pic) { + this.pics.push(pic); + } + toString() { + let ret = '{'; + if (this.library_enable_flag_set) + ret += 'reference_to_library_enable_flag: ' + this.reference_to_library_enable_flag; + this.pics.forEach(e => { + ret += (ret.length > 3 ? ', ' : '') + JSON.stringify(e).replace(/"/g, ''); + }); + ret += '}'; + return ret; + } +} + +class ReferencePictureList { + list?: number; + sets?: Array; + + constructor(list: number) { + this.list = list; + this.sets = []; + } + push(set: ReferencePictureSet) { + this.sets.push(set); + } + toString() { + if (this.sets.length === 0) return '(empty)'; + const l: Array = []; + this.sets.forEach(set => { + l.push(set.toString()); + }); + return l.join(', '); + } +} + +class WeightQuantMatrix { + WeightQuantMatrix4x4: Array>; + WeightQuantMatrix8x8: Array>; + + constructor(reader: BitBuffer) { + this.WeightQuantMatrix4x4 = []; + this.WeightQuantMatrix8x8 = []; + + for (let sizeId = 0; sizeId < 2; sizeId++) { + const this_size: Array> = []; + const WQMSize = 1 << (sizeId + 2); + for (let i = 0; i < WQMSize; i++) { + const iVal: Array = []; + for (let j = 0; j < WQMSize; j++) iVal.push(reader.getUE()); + this_size.push(iVal); + } + if (sizeId === 0) this.WeightQuantMatrix4x4 = this_size; + else this.WeightQuantMatrix8x8 = this_size; + } + } + toString() { + let str = ''; + if (this.WeightQuantMatrix4x4.length) + str += '4x4: ' + JSON.stringify(this.WeightQuantMatrix4x4); + if (this.WeightQuantMatrix8x8.length) + str += (str.length > 2 ? ',\n' : '') + '8x8: ' + JSON.stringify(this.WeightQuantMatrix8x8); + return str; + } +} + +const MAIN_8 = 0x20, + MAIN_10 = 0x22, + HIGH_8 = 0x30, + HIGH_10 = 0x32; +const RESERVED = 'Reserved', + FORBIDDEN = 'Forbidden'; +const AVS3profiles = [ + // Table B.1 of T/AI 109.2 + { profile: MAIN_8, description: 'Main 8 bit' }, + { profile: MAIN_10, description: 'Main 10 bit' }, + { profile: HIGH_8, description: 'High 8 bit' }, + { profile: HIGH_10, description: 'High 10 bit' }, + { profile: 0x00, description: FORBIDDEN }, +]; +const AVS3levels = [ + // Table B.1 of T/AI 109.2 + { level: 0x50, description: '8.0.30' }, + { level: 0x52, description: '8.2.30' }, + { level: 0x51, description: '8.4.30' }, + { level: 0x53, description: '8.6.30' }, + { level: 0x54, description: '8.0.60' }, + { level: 0x56, description: '8.2.60' }, + { level: 0x55, description: '8.4.60' }, + { level: 0x57, description: '8.6.60' }, + { level: 0x58, description: '8.0.120' }, + { level: 0x5a, description: '8.2.120' }, + { level: 0x59, description: '8.4.120' }, + { level: 0x5b, description: '8.6.120' }, + { level: 0x60, description: '10.0.30' }, + { level: 0x62, description: '10.2.30' }, + { level: 0x61, description: '10.4.30' }, + { level: 0x63, description: '10.6.30' }, + { level: 0x64, description: '10.0.60' }, + { level: 0x66, description: '10.2.60' }, + { level: 0x65, description: '10.4.60' }, + { level: 0x67, description: '10.6.60' }, + { level: 0x68, description: '10.0.120' }, + { level: 0x6a, description: '10.2.120' }, + { level: 0x69, description: '10.4.120' }, + { level: 0x6b, description: '10.6.120' }, + { level: 0x10, description: '2.0.15' }, + { level: 0x12, description: '2.0.30' }, + { level: 0x14, description: '2.0.60' }, + { level: 0x20, description: '4.0.30' }, + { level: 0x22, description: '4.0.60' }, + { level: 0x40, description: '6.0.30' }, + { level: 0x42, description: '6.2.30' }, + { level: 0x41, description: '6.4.30' }, + { level: 0x43, description: '6.6.30' }, + { level: 0x44, description: '6.0.60' }, + { level: 0x46, description: '6.2.60' }, + { level: 0x45, description: '6.4.60' }, + { level: 0x47, description: '6.6.60' }, + { level: 0x48, description: '6.0.120' }, + { level: 0x4a, description: '6.2.120' }, + { level: 0x49, description: '6.4.120' }, + { level: 0x4b, description: '6.6.120' }, + { level: 0x00, description: FORBIDDEN }, +]; +const AVS3precisions = [ + // Table 45 of T/AI 109.2 + { precision: 1, description: '8-bit' }, + { precision: 2, description: '10-bit' }, +]; +const AVS3framerates = [ + '', + '24/1.001', + '24', + '25', + '30/1.001', + '30', + '50', + '60/1.001', + '60', + '100', + '120', + '200', + '240', + '300', + '120/1.001', +]; +const AVS3ratios = ['', '1.0', '4:3', '16:9', '2.21:1']; + +const AVS3Vconfiguration = (version: number) => (version !== 1 ? 'not supported' : ''); + +function AVS3profile(profile: number) { + const t = AVS3profiles.find(function (e) { + return e.profile === profile; + }); + return t === undefined ? RESERVED : t.description; +} + +function AVS3level(level: number) { + const t = AVS3levels.find(function (e) { + return e.level === level; + }); + return t === undefined ? RESERVED : t.description; +} + +function AVS3precision(precision: number) { + const t = AVS3precisions.find(function (e) { + return e.precision === precision; + }); + return t === undefined ? RESERVED : t.description; +} + +const AVS3chroma = (chroma: number) => (chroma === 1 ? '4:2:0' : RESERVED); + +const AVS3aspectratio = (ratio: number) => + ratio === 0 ? FORBIDDEN : ratio >= AVS3ratios.length ? RESERVED : AVS3ratios[ratio]; + +const AVS3framerate = (framerate: number) => + framerate === 0 + ? FORBIDDEN + : framerate >= AVS3framerates.length + ? RESERVED + : AVS3framerates[framerate] + ' fps'; + +interface SequenceHeaderElements { + video_sequence_start_code?: HexadecimalValue; + profile_id?: HexadecimalValue; + level_id?: HexadecimalValue; + progressive_sequence?: number; + field_coded_sequence?: number; + library_stream_flag?: number; + library_picture_enable_flag?: number; + duplicate_sequence_number_flag?: number; + horizontal_size?: number; + vertical_size?: number; + chroma_format?: BinaryValue; + sample_precision?: BinaryValue; + encoding_precision?: BinaryValue; + aspect_ratio?: BinaryValue; + frame_rate_code?: BinaryValue; + bit_rate_lower?: number; + bit_rate_upper?: number; + low_delay?: number; + temporal_id_enable_flag?: number; + max_dpb_minus1?: number; + bbv_buffer_size?: number; + rpl1_index_exist_flag?: number; + rpl1_same_as_rpl0_flag?: number; + num_ref_pic_list_set0?: number; + rpl0?: ReferencePictureList; + num_ref_pic_list_set1?: number; + rpl1?: ReferencePictureList; + num_ref_default_active_minus1_0?: number; + num_ref_default_active_minus1_1?: number; + log2_lcu_size_minus2?: number; + log2_min_cu_size_minus2?: number; + log2_max_part_ratio_minus2?: number; + max_split_times_minus6?: number; + log2_min_qt_size_minus2?: number; + log2_max_bt_size_minus2?: number; + log2_max_eqt_size_minus3?: number; + weight_quant_enable_flag?: number; + load_seq_weight_quant_data_flag?: number; + weight_quant_matrix?: WeightQuantMatrix; + st_enable_flag?: number; + sao_enable_flag?: number; + alf_enable_flag?: number; + affine_enable_flag?: number; + smvd_enable_flag?: number; + ipcm_enable_flag?: number; + amvr_enable_flag?: number; + num_of_hmvp_cand?: number; + umve_enable_flag?: number; + emvr_enable_flag?: number; + intra_pf_enable_flag?: number; + tscpm_enable_flag?: number; + dt_enable_flag?: number; + log2_max_dt_size_minus4?: number; + pbt_enable_flag?: number; + pmc_enable_flag?: number; + iip_enable_flag?: number; + sawp_enable_flag?: number; + asr_enable_flag?: number; + awp_enable_flag?: number; + etmvp_mvap_enable_flag?: number; + dmvr_enable_flag?: number; + bio_enable_flag?: number; + bgc_enable_flag?: number; + inter_pf_enable_flag?: number; + inter_pfc_enable_flag?: number; + obmc_enable_flag?: number; + sbt_enable_flag?: number; + ist_enable_flag?: number; + esao_enable_flag?: number; + ccsao_enable_flag?: number; + ealf_enable_flag?: number; + ibc_enable_flag?: number; + isc_enable_flag?: number; + num_of_intra_hmvp_cand?: number; + fimc_enable_flag?: number; + nn_tools_set_hook?: number; + num_of_nn_filter_minus1?: number; + output_reorder_delay?: number; + cross_patch_loop_filter_enable_flag?: number; + ref_colocated_patch_flag?: number; + stable_patch_flag?: number; + uniform_patch_flag?: number; + patch_width_minus1?: number; + patch_height_minus1?: number; +} + +class AVS3SequenceHeader extends AVS3data { + data: SequenceHeaderElements; + + constructor(bit_reader: BitBuffer) { + super(); + this.data = {}; + this.load(bit_reader); + } + load(bit_reader: BitBuffer) { + this.data.video_sequence_start_code = new HexadecimalValue(bit_reader.getUint32()); + this.data.profile_id = new HexadecimalValue(bit_reader.getUint8(), AVS3profile); + this.data.level_id = new HexadecimalValue(bit_reader.getUint8(), AVS3level); + this.data.progressive_sequence = bit_reader.getBit(); + this.data.field_coded_sequence = bit_reader.getBit(); + this.data.library_stream_flag = bit_reader.getBit(); + if (!this.data.library_stream_flag) { + this.data.library_picture_enable_flag = bit_reader.getBit(); + if (this.data.library_picture_enable_flag) + this.data.duplicate_sequence_number_flag = bit_reader.getBit(); + } + bit_reader.skipBit(); // marker_bit + + this.data.horizontal_size = bit_reader.getBits(14); + bit_reader.skipBit(); // marker_bit + + this.data.vertical_size = bit_reader.getBits(14); + this.data.chroma_format = new BinaryValue(bit_reader.getBits(2), 2, AVS3chroma); + this.data.sample_precision = new BinaryValue(bit_reader.getBits(3), 3, AVS3precision); + + if (this.data.profile_id.get() === MAIN_10 || this.data.profile_id.get() === HIGH_10) + this.data.encoding_precision = new BinaryValue(bit_reader.getBits(3), 3, AVS3precision); + bit_reader.skipBit(); // marker_bit + + this.data.aspect_ratio = new BinaryValue(bit_reader.getBits(4), 4, AVS3aspectratio); + this.data.frame_rate_code = new BinaryValue(bit_reader.getBits(4), 4, AVS3framerate); + bit_reader.skipBit(); // marker_bit + + this.data.bit_rate_lower = bit_reader.getBits(18); + bit_reader.skipBit(); // marker_bit + + this.data.bit_rate_upper = bit_reader.getBits(12); + this.data.low_delay = bit_reader.getBit(); + this.data.temporal_id_enable_flag = bit_reader.getBit(); + bit_reader.skipBit(); // marker_bit + + this.data.bbv_buffer_size = bit_reader.getBits(18); + bit_reader.skipBit(); // marker_bit + + this.data.max_dpb_minus1 = bit_reader.getBits(4); + this.data.rpl1_index_exist_flag = bit_reader.getBit(); + this.data.rpl1_same_as_rpl0_flag = bit_reader.getBit(); + bit_reader.skipBit(); // marker_bit + + const reference_picture_list = function ( + list: number, + rpls: number, + library_picture_enable_flag: number, + ) { + const this_set = new ReferencePictureSet(list, rpls); + if (library_picture_enable_flag) + this_set.set_reference_to_library_enable_flag(bit_reader.getBit()); + const num_of_ref_pic = bit_reader.getUE(); + for (let i = 0; i < num_of_ref_pic; i++) { + const this_pic: ReferencePicture = {}; + let LibraryIndexFlag = 0; + if (this_set.get_reference_to_library_enable_flag()) + LibraryIndexFlag = this_pic.library_index_flag = bit_reader.getBit(); + if (LibraryIndexFlag !== 0) this_pic.referenced_library_picture_index = bit_reader.getUE(); + else { + this_pic.abs_delta_doi = bit_reader.getUE(); + if (this_pic.abs_delta_doi > 0) this_pic.sign_delta_doi = bit_reader.getBit(); + } + this_set.push(this_pic); + } + return this_set; + }; + + this.data.num_ref_pic_list_set0 = bit_reader.getUE(); + this.data.rpl0 = new ReferencePictureList(0); + for (let j = 0; j < this.data.num_ref_pic_list_set0; j++) + this.data.rpl0.push(reference_picture_list(0, j, this.data.library_picture_enable_flag)); + + if (!this.data.rpl1_same_as_rpl0_flag) { + this.data.num_ref_pic_list_set1 = bit_reader.getUE(); + this.data.rpl1 = new ReferencePictureList(1); + for (let j = 0; j < this.data.num_ref_pic_list_set1; j++) + this.data.rpl1.push(reference_picture_list(1, j, this.data.library_picture_enable_flag)); + } + + this.data.num_ref_default_active_minus1_0 = bit_reader.getUE(); + this.data.num_ref_default_active_minus1_1 = bit_reader.getUE(); + this.data.log2_lcu_size_minus2 = bit_reader.getBits(3); + this.data.log2_min_cu_size_minus2 = bit_reader.getBits(2); + this.data.log2_max_part_ratio_minus2 = bit_reader.getBits(2); + this.data.max_split_times_minus6 = bit_reader.getBits(3); + this.data.log2_min_qt_size_minus2 = bit_reader.getBits(3); + this.data.log2_max_bt_size_minus2 = bit_reader.getBits(3); + this.data.log2_max_eqt_size_minus3 = bit_reader.getBits(2); + bit_reader.skipBit(); // marker_bit + + this.data.weight_quant_enable_flag = bit_reader.getBit(); + if (this.data.weight_quant_enable_flag) { + this.data.load_seq_weight_quant_data_flag = bit_reader.getBit(); + if (this.data.load_seq_weight_quant_data_flag) + this.data.weight_quant_matrix = new WeightQuantMatrix(bit_reader); + } + + this.data.st_enable_flag = bit_reader.getBit(); + this.data.sao_enable_flag = bit_reader.getBit(); + this.data.alf_enable_flag = bit_reader.getBit(); + this.data.affine_enable_flag = bit_reader.getBit(); + this.data.smvd_enable_flag = bit_reader.getBit(); + this.data.ipcm_enable_flag = bit_reader.getBit(); + this.data.amvr_enable_flag = bit_reader.getBit(); + this.data.num_of_hmvp_cand = bit_reader.getBits(4); + this.data.umve_enable_flag = bit_reader.getBit(); + if (this.data.num_of_hmvp_cand !== 0 && this.data.amvr_enable_flag) + this.data.emvr_enable_flag = bit_reader.getBit(); + this.data.intra_pf_enable_flag = bit_reader.getBit(); + this.data.tscpm_enable_flag = bit_reader.getBit(); + bit_reader.skipBit(); // marker_bit + + this.data.dt_enable_flag = bit_reader.getBit(); + if (this.data.dt_enable_flag) this.data.log2_max_dt_size_minus4 = bit_reader.getBits(2); + this.data.pbt_enable_flag = bit_reader.getBit(); + + if (this.data.profile_id.get() === MAIN_10 || this.data.profile_id.get() === HIGH_10) { + this.data.pmc_enable_flag = bit_reader.getBit(); + this.data.iip_enable_flag = bit_reader.getBit(); + this.data.sawp_enable_flag = bit_reader.getBit(); + if (this.data.affine_enable_flag) this.data.asr_enable_flag = bit_reader.getBit(); + this.data.awp_enable_flag = bit_reader.getBit(); + this.data.etmvp_mvap_enable_flag = bit_reader.getBit(); + this.data.dmvr_enable_flag = bit_reader.getBit(); + this.data.bio_enable_flag = bit_reader.getBit(); + this.data.bgc_enable_flag = bit_reader.getBit(); + this.data.inter_pf_enable_flag = bit_reader.getBit(); + this.data.inter_pfc_enable_flag = bit_reader.getBit(); + this.data.obmc_enable_flag = bit_reader.getBit(); + + this.data.sbt_enable_flag = bit_reader.getBit(); + this.data.ist_enable_flag = bit_reader.getBit(); + + this.data.esao_enable_flag = bit_reader.getBit(); + this.data.ccsao_enable_flag = bit_reader.getBit(); + if (this.data.alf_enable_flag) this.data.ealf_enable_flag = bit_reader.getBit(); + this.data.ibc_enable_flag = bit_reader.getBit(); + bit_reader.skipBit(); // marker_bit + + this.data.isc_enable_flag = bit_reader.getBit(); + if (this.data.ibc_enable_flag || this.data.isc_enable_flag) + this.data.num_of_intra_hmvp_cand = bit_reader.getBits(4); + this.data.fimc_enable_flag = bit_reader.getBit(); + this.data.nn_tools_set_hook = bit_reader.getBits(8); + if (this.data.nn_tools_set_hook & 0x01) + this.data.num_of_nn_filter_minus1 = bit_reader.getUE(); + bit_reader.skipBit(); // marker_bit + } + if (this.data.low_delay === 0) this.data.output_reorder_delay = bit_reader.getBits(5); + this.data.cross_patch_loop_filter_enable_flag = bit_reader.getBit(); + this.data.ref_colocated_patch_flag = bit_reader.getBit(); + this.data.stable_patch_flag = bit_reader.getBit(); + if (this.data.stable_patch_flag) { + this.data.uniform_patch_flag = bit_reader.getBit(); + if (this.data.uniform_patch_flag) { + bit_reader.skipBit(); // marker_bit + this.data.patch_width_minus1 = bit_reader.getUE(); + this.data.patch_height_minus1 = bit_reader.getUE(); + } + } + bit_reader.skipBits(2); // reserved bits + } + toHTML() { + return super.toHTML(this.data); + } +} + +export class av3cBox extends Box { + static override readonly fourcc = 'av3c' as const; + box_name = 'AVS3ConfigurationBox' as const; + + configurationVersion: DescribedValue; + sequence_header_length?: number; + sequence_header?: AVS3SequenceHeader; + library_dependency_idc?: BinaryValue; + + parse(stream: MP4BoxStream) { + const bit_reader = new BitBuffer(); + this.configurationVersion = new DescribedValue(stream.readUint8(), AVS3Vconfiguration); + if (this.configurationVersion.get() === 1) { + this.sequence_header_length = stream.readUint16(); + for (let i = 0; i < this.sequence_header_length; i++) + bit_reader.appendUint8(stream.readUint8()); + + this.sequence_header = new AVS3SequenceHeader(bit_reader); + + // library_dependency_idc is in the AVS3DecoderConfigurationRecord + this.library_dependency_idc = new BinaryValue(stream.readUint8(), 2); + } + } +} diff --git a/src/boxes/avs-common.ts b/src/boxes/avs-common.ts new file mode 100644 index 000000000..80449f23f --- /dev/null +++ b/src/boxes/avs-common.ts @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025. Paul Higgs + * License: BSD-3-Clause (see LICENSE file) + */ + +export type DescriberFunction = (n: number) => string; + +export class DescribedValue { + private value: number; + private description: string; + + constructor(value: number, descriptionFn?: DescriberFunction) { + this.value = value; + this.description = descriptionFn ? descriptionFn(value) : null; + } + toString() { + return this.value + (this.description ? ' (' + this.description + ')' : ''); + } + get() { + return this.value; + } +} + +export class HexadecimalValue { + private value: number; + private description: string; + + constructor(value: number, descriptionFn?: DescriberFunction) { + this.value = value; + this.description = descriptionFn ? descriptionFn(value) : null; + } + toString() { + return '0x' + this.value.toString(16) + (this.description ? ' (' + this.description + ')' : ''); + } + get() { + return this.value; + } +} + +export class BinaryValue { + private value: number; + private bits: number; + private description: string; + + constructor(value: number, bits: number, descriptionFn?: DescriberFunction) { + this.value = value; + this.bits = bits; + this.description = descriptionFn ? descriptionFn(value) : null; + } + toString() { + let res = 'b'; + for (let i = this.bits; i > 0; i--) res += this.value & (1 << (i - 1)) ? '1' : '0'; + return res + (this.description ? ' (' + this.description + ')' : ''); + } + public get() { + return this.value; + } +} + +export class AVS3data { + toHTML(data): string { + let res = ''; + const props = Object.getOwnPropertyNames(data); + if (props) + props.forEach(function (val) { + let fmt_val = ''; + if (Array.isArray(data[val])) { + for (let i = 0; i < data[val].length; i++) { + const hex = data[val][i].toString(16); + fmt_val += hex.length === 1 ? '0' + hex : hex; + if (i % 4 === 3) fmt_val += ' '; + } + } else fmt_val = data[val]; + res += '' + val + '' + fmt_val + ''; + }); + return '' + res + '
'; + } +} diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index 3f4830acd..abb4133a4 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. Paul Higgs + * Copyright (c) 2025. Paul Higgs * License: BSD-3-Clause (see LICENSE file) */ @@ -7,23 +7,18 @@ import { Box } from '#/box'; import { MP4BoxStream } from '#/stream'; import { BitBuffer } from '#/BitBuffer'; -export type DescriberFunction = (n: number) => string; +import { DescribedValue, AVS3data } from './avs-common'; -export class DescribedValue { - private value: number; - private description: string; +// values for audio_codec_id +const HIGH_RATE_CODING = 0, + LOSSLESS_CODING = 1, + FULL_RATE_CODING = 2; - constructor(value: number, descriptionFn?: DescriberFunction) { - this.value = value; - this.description = descriptionFn ? descriptionFn(value) : null; - } - toString() { - return this.value + (this.description ? ' (' + this.description + ')' : ''); - } - get() { - return this.value; - } -} +// values for content_type +const CHANNEL_BASED = 0, + OBJECT_BASED = 1, + CHANNEL_AND_OBJECT = 2, + HOA = 3; function AVS3Acodec(codec_id: number) { const codecs = ['General High Rate', 'Lossless', 'General Full Rate']; @@ -90,26 +85,6 @@ function AVS3Acodingprofile(conding_profile: number) { return 'reserved'; } -export class AVS3data { - toHTML(data): string { - let res = ''; - const props = Object.getOwnPropertyNames(data); - if (props) - props.forEach(function (val) { - let fmt_val = ''; - if (Array.isArray(data[val])) { - for (let i = 0; i < data[val].length; i++) { - const hex = data[val][i].toString(16); - fmt_val += hex.length === 1 ? '0' + hex : hex; - if (i % 4 === 3) fmt_val += ' '; - } - } else fmt_val = data[val]; - res += '' + val + '' + fmt_val + ''; - }); - return '' + res + '
'; - } -} - interface GAconfig { sampling_frequency_index?: DescribedValue; nn_type?: DescribedValue; @@ -135,16 +110,16 @@ class AVS3GAConfig extends AVS3data { this.data.nn_type = new DescribedValue(bit_reader.getBits(3), AVS3Anntype); bit_reader.skipBits(1); this.data.content_type = bit_reader.getBits(4); - if (this.data.content_type === 0) { + if (this.data.content_type === CHANNEL_BASED) { this.data.channel_number_index = new DescribedValue( bit_reader.getBits(7), AVS3Achannel_number, ); bit_reader.skipBits(1); - } else if (this.data.content_type === 1) { + } else if (this.data.content_type === OBJECT_BASED) { this.data.number_objects = bit_reader.getBits(7); bit_reader.skipBits(1); - } else if (this.data.content_type === 2) { + } else if (this.data.content_type === CHANNEL_AND_OBJECT) { this.data.channel_number_index = new DescribedValue( bit_reader.getBits(7), AVS3Achannel_number, @@ -152,7 +127,7 @@ class AVS3GAConfig extends AVS3data { bit_reader.skipBits(1); this.data.number_objects = bit_reader.getBits(7); bit_reader.skipBits(1); - } else if (this.data.content_type === 3) { + } else if (this.data.content_type === HOA) { this.data.hoa_order = bit_reader.getBits(4); } this.data.total_bitrate = bit_reader.getUint16(); @@ -254,16 +229,20 @@ export class dca3Box extends Box { this.audio_codec_id = new DescribedValue(bit_reader.getBits(4), AVS3Acodec); switch (this.audio_codec_id.get()) { - case 2: + case FULL_RATE_CODING: this.Avs3AudioGAConfig = new AVS3GAConfig(bit_reader); break; - case 0: + case HIGH_RATE_CODING: this.Avs3AudioGHConfig = new AVS3GHConfig(bit_reader); break; - case 1: + case LOSSLESS_CODING: this.Avs3AudioLLConfig = new AVS3LLConfig(bit_reader); break; } bit_reader.byte_alignment(); } + + get_audio_codec_id_str() { + return (this.audio_codec_id.get() < 9 ? '0' : '') + this.audio_codec_id.get(); + } } diff --git a/src/boxes/sampleentries/sampleentry.ts b/src/boxes/sampleentries/sampleentry.ts index fef76dd58..b222b62d2 100644 --- a/src/boxes/sampleentries/sampleentry.ts +++ b/src/boxes/sampleentries/sampleentry.ts @@ -1,5 +1,7 @@ import { av1CBox } from '#/boxes/av1C'; import { avcCBox } from '#/boxes/avcC'; +import { av3cBox } from '#/boxes/av3c'; +import { dca3Box } from '#/boxes/dca3'; import { sinfBox } from '#/boxes/defaults'; import { esdsBox } from '#/boxes/esds'; import { hvcCBox } from '#/boxes/hvcC'; @@ -92,6 +94,26 @@ export class av01SampleEntry extends VisualSampleEntry { } } +export class avs3SampleEntry extends VisualSampleEntry { + av3c: av3cBox; + static override readonly fourcc = 'avs3' as const; + + getCodec(): string { + const baseCodec = super.getCodec(); + return ( + baseCodec + + '.' + + (this.av3c.sequence_header?.data?.profile_id + ? this.av3c.sequence_header.data?.profile_id.get().toString(16) + : 'XX') + + '.' + + (this.av3c.sequence_header?.data?.level_id + ? this.av3c.sequence_header.data?.level_id.get().toString(16) + : 'XX') + ); + } +} + export class dav1SampleEntry extends VisualSampleEntry { static override readonly fourcc = 'dav1' as const; } @@ -294,10 +316,6 @@ export class vp09SampleEntry extends vpcCSampleEntryBase { static override readonly fourcc = 'vp09' as const; } -export class avs3SampleEntry extends VisualSampleEntry { - static override readonly fourcc = 'avs3' as const; -} - export class j2kiSampleEntry extends VisualSampleEntry { static override readonly fourcc = 'j2ki' as const; } @@ -377,7 +395,12 @@ export class fLaCSampleEntry extends AudioSampleEntry { } export class av3aSampleEntry extends AudioSampleEntry { + dca3: dca3Box; static override readonly fourcc = 'av3a' as const; + getCodec() { + const baseCodec = super.getCodec(); + return baseCodec + '.' + this.dca3.get_audio_codec_id_str(); + } } export class a3asSampleEntry extends AudioSampleEntry { From f7c3a0260d47813bf8d95086f2296ec1e7b2f7f5 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Sun, 29 Jun 2025 15:03:20 +0900 Subject: [PATCH 07/18] fix: TypeScript oriented updates and improvements --- src/BitBuffer.ts | 67 ++++--- src/boxes/av3c.ts | 248 +++++++++++++------------ src/boxes/avs-common.ts | 16 +- src/boxes/dca3.ts | 14 +- src/boxes/sampleentries/sampleentry.ts | 30 ++- 5 files changed, 205 insertions(+), 170 deletions(-) diff --git a/src/BitBuffer.ts b/src/BitBuffer.ts index 09a036b4d..456a0f6d4 100644 --- a/src/BitBuffer.ts +++ b/src/BitBuffer.ts @@ -4,6 +4,7 @@ * * * reads bits and bytes from a buffer that may not contain aligned values + * TODO: add writing support */ import { Endianness } from '#/DataStream'; @@ -26,7 +27,6 @@ class State { export class BitBuffer { private endianness: Endianness; private _buffer: Array; - private _buffer_size: number; private _state: State; private _big_endian = true; // results are returned Big Endian @@ -38,16 +38,23 @@ export class BitBuffer { load(stream: Uint8Array): void { this._buffer = [...stream]; - this._buffer_size = stream.length; this._state.rbit = this._state.rbyte = 0; this._state.wbit = 0; - this._state.wbyte = this._state.end = this._buffer_size; + this._state.wbyte = this._state.end = this._buffer.length; this._state.read_error = this._state.write_error = false; } + appendUint8(byte: number): void { this._buffer.push(byte); - this._buffer_size += 1; - this._state.end = this._state.wbyte = this._buffer_size; + this._state.end = this._state.wbyte = this._buffer.length; + } + + extend(bits: number): void { + let count = bits; + while (count > 0) { + this._buffer.push(0); + count -= 8; + } } getBit(): number { @@ -140,16 +147,16 @@ export class BitBuffer { private _ByteSwap16 = function (x) { return (x << 8) | (x >> 8); }; - private _CondByteSwap16BE = function (val) { + private _CondByteSwap16BE = function (val: number) { return this._OSisLittleEndian() ? this._ByteSwap16(val) : val; }; - private _CondByteSwap16LE = function (val) { + private _CondByteSwap16LE = function (val: number) { return this._OSisLittleEndian() ? val : this._ByteSwap16(val); }; - private _GetUInt16BE = function (val) { + private _GetUInt16BE = function (val: number) { return this._CondByteSwap16BE(val); }; - private _GetUInt16LE = function (val) { + private _GetUInt16LE = function (val: number) { return this._CondByteSwap16LE(val); }; @@ -157,19 +164,19 @@ export class BitBuffer { return this._big_endian ? this._GetUInt24BE(this._rdb(3)) : this._GetUInt24LE(this._rdb(3)); } - private _ByteSwap24 = function (x) { + private _ByteSwap24 = function (x: number) { return ((x & 0xff0000) >> 16) | (x & 0xff00) | (x & (0xff << 16)); }; - private _CondByteSwap24BE = function (val) { + private _CondByteSwap24BE = function (val: number) { return this._OSisLittleEndian() ? this._ByteSwap24(val) : val; }; - private _CondByteSwap24LE = function (val) { + private _CondByteSwap24LE = function (val: number) { return this._OSisLittleEndian() ? val : this._ByteSwap24(val); }; - private _GetUInt24BE = function (val) { + private _GetUInt24BE = function (val: number) { return this._CondByteSwap24BE(val); }; - private _GetUInt24LE = function (val) { + private _GetUInt24LE = function (val: number) { return this._CondByteSwap24LE(val); }; @@ -177,19 +184,19 @@ export class BitBuffer { return this._big_endian ? this._GetUInt32BE(this._rdb(4)) : this._GetUInt32LE(this._rdb(4)); } - private _ByteSwap32(x) { + private _ByteSwap32(x: number) { return (x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | (x >> 24); } - private _CondByteSwap32BE(val) { + private _CondByteSwap32BE(val: number) { return this._OSisLittleEndian() ? this._ByteSwap32(val) : val; } - private _CondByteSwap32LE(val) { + private _CondByteSwap32LE(val: number) { return this._OSisLittleEndian() ? val : this._ByteSwap32(val); } - private _GetUInt32BE(val) { + private _GetUInt32BE(val: number) { return this._CondByteSwap32BE(val); } - private _GetUInt32LE(val) { + private _GetUInt32LE(val: number) { return this._CondByteSwap32LE(val); } @@ -282,11 +289,11 @@ export class BitBuffer { return this.getBits(zero_count + 1) - 1; } - byte_alignment() { + byte_alignment(): void { while (!this._state.read_error && this._state.rbit !== 0) this.skipBit(); } - private _OSisLittleEndian() { + private _OSisLittleEndian(): boolean { return this.endianness === Endianness.LITTLE_ENDIAN; } @@ -298,19 +305,29 @@ export class BitBuffer { return u8data[3] === 0xca ? Endianness.BIG_ENDIAN : Endianness.LITTLE_ENDIAN; } - currentReadByteOffset() { + currentReadByteOffset(): number { return this._state.rbyte; } - currentReadBitOffset() { + currentReadBitOffset(): number { return 8 * this._state.rbyte + this._state.rbit; } - currentWriteByteOffset() { + currentWriteByteOffset(): number { return this._state.wbyte; } - currentWriteBitOffset() { + currentWriteBitOffset(): number { return 8 * this._state.wbyte + this._state.wbit; } bitsRemaining() { return this.currentWriteBitOffset() - this.currentReadBitOffset(); } + writeBitsRemaining(): number { + return 8 * this._buffer.length - this.currentWriteBitOffset(); + } + /* + TODO - for near future implementation to support writing AVS3 related boxes that are not byte aligned + writeBit(bit: number): void { + if (this.writeBitsRemaining() < 1) + this.extend(1); + } + */ } diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index 1d165816b..b2bce8d9f 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -7,7 +7,13 @@ import { Box } from '#/box'; import { MP4BoxStream } from '#/stream'; import { BitBuffer } from '#/BitBuffer'; -import { DescribedValue, AVS3data, HexadecimalValue, BinaryValue } from './avs-common'; +import { + DescribedValue, + AVS3data, + HexadecimalValue, + BinaryValue, + BooleanValue, +} from './avs-common'; interface ReferencePicture { library_index_flag?: number; @@ -36,7 +42,7 @@ class ReferencePictureSet { get_reference_to_library_enable_flag(): number { return this.library_enable_flag_set ? this.reference_to_library_enable_flag : 0; } - push(pic) { + push(pic: ReferencePicture) { this.pics.push(pic); } toString() { @@ -188,23 +194,17 @@ const AVS3ratios = ['', '1.0', '4:3', '16:9', '2.21:1']; const AVS3Vconfiguration = (version: number) => (version !== 1 ? 'not supported' : ''); function AVS3profile(profile: number) { - const t = AVS3profiles.find(function (e) { - return e.profile === profile; - }); + const t = AVS3profiles.find(e => e.profile === profile); return t === undefined ? RESERVED : t.description; } function AVS3level(level: number) { - const t = AVS3levels.find(function (e) { - return e.level === level; - }); + const t = AVS3levels.find(e => e.level === level); return t === undefined ? RESERVED : t.description; } function AVS3precision(precision: number) { - const t = AVS3precisions.find(function (e) { - return e.precision === precision; - }); + const t = AVS3precisions.find(e => e.precision === precision); return t === undefined ? RESERVED : t.description; } @@ -224,11 +224,11 @@ interface SequenceHeaderElements { video_sequence_start_code?: HexadecimalValue; profile_id?: HexadecimalValue; level_id?: HexadecimalValue; - progressive_sequence?: number; - field_coded_sequence?: number; - library_stream_flag?: number; - library_picture_enable_flag?: number; - duplicate_sequence_number_flag?: number; + progressive_sequence?: BooleanValue; + field_coded_sequence?: BooleanValue; + library_stream_flag?: BooleanValue; + library_picture_enable_flag?: BooleanValue; + duplicate_sequence_number_flag?: BooleanValue; horizontal_size?: number; vertical_size?: number; chroma_format?: BinaryValue; @@ -239,11 +239,11 @@ interface SequenceHeaderElements { bit_rate_lower?: number; bit_rate_upper?: number; low_delay?: number; - temporal_id_enable_flag?: number; + temporal_id_enable_flag?: BooleanValue; max_dpb_minus1?: number; bbv_buffer_size?: number; - rpl1_index_exist_flag?: number; - rpl1_same_as_rpl0_flag?: number; + rpl1_index_exist_flag?: BooleanValue; + rpl1_same_as_rpl0_flag?: BooleanValue; num_ref_pic_list_set0?: number; rpl0?: ReferencePictureList; num_ref_pic_list_set1?: number; @@ -257,52 +257,52 @@ interface SequenceHeaderElements { log2_min_qt_size_minus2?: number; log2_max_bt_size_minus2?: number; log2_max_eqt_size_minus3?: number; - weight_quant_enable_flag?: number; - load_seq_weight_quant_data_flag?: number; + weight_quant_enable_flag?: BooleanValue; + load_seq_weight_quant_data_flag?: BooleanValue; weight_quant_matrix?: WeightQuantMatrix; - st_enable_flag?: number; - sao_enable_flag?: number; - alf_enable_flag?: number; - affine_enable_flag?: number; - smvd_enable_flag?: number; - ipcm_enable_flag?: number; - amvr_enable_flag?: number; + st_enable_flag?: BooleanValue; + sao_enable_flag?: BooleanValue; + alf_enable_flag?: BooleanValue; + affine_enable_flag?: BooleanValue; + smvd_enable_flag?: BooleanValue; + ipcm_enable_flag?: BooleanValue; + amvr_enable_flag?: BooleanValue; num_of_hmvp_cand?: number; - umve_enable_flag?: number; - emvr_enable_flag?: number; - intra_pf_enable_flag?: number; - tscpm_enable_flag?: number; - dt_enable_flag?: number; + umve_enable_flag?: BooleanValue; + emvr_enable_flag?: BooleanValue; + intra_pf_enable_flag?: BooleanValue; + tscpm_enable_flag?: BooleanValue; + dt_enable_flag?: BooleanValue; log2_max_dt_size_minus4?: number; - pbt_enable_flag?: number; - pmc_enable_flag?: number; - iip_enable_flag?: number; - sawp_enable_flag?: number; - asr_enable_flag?: number; - awp_enable_flag?: number; - etmvp_mvap_enable_flag?: number; - dmvr_enable_flag?: number; - bio_enable_flag?: number; - bgc_enable_flag?: number; - inter_pf_enable_flag?: number; - inter_pfc_enable_flag?: number; - obmc_enable_flag?: number; - sbt_enable_flag?: number; - ist_enable_flag?: number; - esao_enable_flag?: number; - ccsao_enable_flag?: number; - ealf_enable_flag?: number; - ibc_enable_flag?: number; - isc_enable_flag?: number; + pbt_enable_flag?: BooleanValue; + pmc_enable_flag?: BooleanValue; + iip_enable_flag?: BooleanValue; + sawp_enable_flag?: BooleanValue; + asr_enable_flag?: BooleanValue; + awp_enable_flag?: BooleanValue; + etmvp_mvap_enable_flag?: BooleanValue; + dmvr_enable_flag?: BooleanValue; + bio_enable_flag?: BooleanValue; + bgc_enable_flag?: BooleanValue; + inter_pf_enable_flag?: BooleanValue; + inter_pfc_enable_flag?: BooleanValue; + obmc_enable_flag?: BooleanValue; + sbt_enable_flag?: BooleanValue; + ist_enable_flag?: BooleanValue; + esao_enable_flag?: BooleanValue; + ccsao_enable_flag?: BooleanValue; + ealf_enable_flag?: BooleanValue; + ibc_enable_flag?: BooleanValue; + isc_enable_flag?: BooleanValue; num_of_intra_hmvp_cand?: number; - fimc_enable_flag?: number; + fimc_enable_flag?: BooleanValue; nn_tools_set_hook?: number; num_of_nn_filter_minus1?: number; output_reorder_delay?: number; - cross_patch_loop_filter_enable_flag?: number; - ref_colocated_patch_flag?: number; - stable_patch_flag?: number; - uniform_patch_flag?: number; + cross_patch_loop_filter_enable_flag?: BooleanValue; + ref_colocated_patch_flag?: BooleanValue; + stable_patch_flag?: BooleanValue; + uniform_patch_flag?: BooleanValue; patch_width_minus1?: number; patch_height_minus1?: number; } @@ -313,19 +313,19 @@ class AVS3SequenceHeader extends AVS3data { constructor(bit_reader: BitBuffer) { super(); this.data = {}; - this.load(bit_reader); + this.deserialise(bit_reader); } - load(bit_reader: BitBuffer) { + deserialise(bit_reader: BitBuffer) { this.data.video_sequence_start_code = new HexadecimalValue(bit_reader.getUint32()); this.data.profile_id = new HexadecimalValue(bit_reader.getUint8(), AVS3profile); this.data.level_id = new HexadecimalValue(bit_reader.getUint8(), AVS3level); - this.data.progressive_sequence = bit_reader.getBit(); - this.data.field_coded_sequence = bit_reader.getBit(); - this.data.library_stream_flag = bit_reader.getBit(); - if (!this.data.library_stream_flag) { - this.data.library_picture_enable_flag = bit_reader.getBit(); - if (this.data.library_picture_enable_flag) - this.data.duplicate_sequence_number_flag = bit_reader.getBit(); + this.data.progressive_sequence = new BooleanValue(bit_reader.getBit()); + this.data.field_coded_sequence = new BooleanValue(bit_reader.getBit()); + this.data.library_stream_flag = new BooleanValue(bit_reader.getBit()); + if (!this.data.library_stream_flag.get()) { + this.data.library_picture_enable_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.library_picture_enable_flag.get()) + this.data.duplicate_sequence_number_flag = new BooleanValue(bit_reader.getBit()); } bit_reader.skipBit(); // marker_bit @@ -349,21 +349,21 @@ class AVS3SequenceHeader extends AVS3data { this.data.bit_rate_upper = bit_reader.getBits(12); this.data.low_delay = bit_reader.getBit(); - this.data.temporal_id_enable_flag = bit_reader.getBit(); + this.data.temporal_id_enable_flag = new BooleanValue(bit_reader.getBit()); bit_reader.skipBit(); // marker_bit this.data.bbv_buffer_size = bit_reader.getBits(18); bit_reader.skipBit(); // marker_bit this.data.max_dpb_minus1 = bit_reader.getBits(4); - this.data.rpl1_index_exist_flag = bit_reader.getBit(); - this.data.rpl1_same_as_rpl0_flag = bit_reader.getBit(); + this.data.rpl1_index_exist_flag = new BooleanValue(bit_reader.getBit()); + this.data.rpl1_same_as_rpl0_flag = new BooleanValue(bit_reader.getBit()); bit_reader.skipBit(); // marker_bit const reference_picture_list = function ( list: number, rpls: number, - library_picture_enable_flag: number, + library_picture_enable_flag: boolean, ) { const this_set = new ReferencePictureSet(list, rpls); if (library_picture_enable_flag) @@ -387,13 +387,17 @@ class AVS3SequenceHeader extends AVS3data { this.data.num_ref_pic_list_set0 = bit_reader.getUE(); this.data.rpl0 = new ReferencePictureList(0); for (let j = 0; j < this.data.num_ref_pic_list_set0; j++) - this.data.rpl0.push(reference_picture_list(0, j, this.data.library_picture_enable_flag)); + this.data.rpl0.push( + reference_picture_list(0, j, this.data.library_picture_enable_flag.get()), + ); if (!this.data.rpl1_same_as_rpl0_flag) { this.data.num_ref_pic_list_set1 = bit_reader.getUE(); this.data.rpl1 = new ReferencePictureList(1); for (let j = 0; j < this.data.num_ref_pic_list_set1; j++) - this.data.rpl1.push(reference_picture_list(1, j, this.data.library_picture_enable_flag)); + this.data.rpl1.push( + reference_picture_list(1, j, this.data.library_picture_enable_flag.get()), + ); } this.data.num_ref_default_active_minus1_0 = bit_reader.getUE(); @@ -407,71 +411,73 @@ class AVS3SequenceHeader extends AVS3data { this.data.log2_max_eqt_size_minus3 = bit_reader.getBits(2); bit_reader.skipBit(); // marker_bit - this.data.weight_quant_enable_flag = bit_reader.getBit(); - if (this.data.weight_quant_enable_flag) { - this.data.load_seq_weight_quant_data_flag = bit_reader.getBit(); - if (this.data.load_seq_weight_quant_data_flag) + this.data.weight_quant_enable_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.weight_quant_enable_flag.get()) { + this.data.load_seq_weight_quant_data_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.load_seq_weight_quant_data_flag.get()) this.data.weight_quant_matrix = new WeightQuantMatrix(bit_reader); } - this.data.st_enable_flag = bit_reader.getBit(); - this.data.sao_enable_flag = bit_reader.getBit(); - this.data.alf_enable_flag = bit_reader.getBit(); - this.data.affine_enable_flag = bit_reader.getBit(); - this.data.smvd_enable_flag = bit_reader.getBit(); - this.data.ipcm_enable_flag = bit_reader.getBit(); - this.data.amvr_enable_flag = bit_reader.getBit(); + this.data.st_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.sao_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.alf_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.affine_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.smvd_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.ipcm_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.amvr_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.num_of_hmvp_cand = bit_reader.getBits(4); - this.data.umve_enable_flag = bit_reader.getBit(); - if (this.data.num_of_hmvp_cand !== 0 && this.data.amvr_enable_flag) - this.data.emvr_enable_flag = bit_reader.getBit(); - this.data.intra_pf_enable_flag = bit_reader.getBit(); - this.data.tscpm_enable_flag = bit_reader.getBit(); + this.data.umve_enable_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.num_of_hmvp_cand !== 0 && this.data.amvr_enable_flag.get()) + this.data.emvr_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.intra_pf_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.tscpm_enable_flag = new BooleanValue(bit_reader.getBit()); bit_reader.skipBit(); // marker_bit - this.data.dt_enable_flag = bit_reader.getBit(); - if (this.data.dt_enable_flag) this.data.log2_max_dt_size_minus4 = bit_reader.getBits(2); - this.data.pbt_enable_flag = bit_reader.getBit(); + this.data.dt_enable_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.dt_enable_flag.get()) this.data.log2_max_dt_size_minus4 = bit_reader.getBits(2); + this.data.pbt_enable_flag = new BooleanValue(bit_reader.getBit()); if (this.data.profile_id.get() === MAIN_10 || this.data.profile_id.get() === HIGH_10) { - this.data.pmc_enable_flag = bit_reader.getBit(); - this.data.iip_enable_flag = bit_reader.getBit(); - this.data.sawp_enable_flag = bit_reader.getBit(); - if (this.data.affine_enable_flag) this.data.asr_enable_flag = bit_reader.getBit(); - this.data.awp_enable_flag = bit_reader.getBit(); - this.data.etmvp_mvap_enable_flag = bit_reader.getBit(); - this.data.dmvr_enable_flag = bit_reader.getBit(); - this.data.bio_enable_flag = bit_reader.getBit(); - this.data.bgc_enable_flag = bit_reader.getBit(); - this.data.inter_pf_enable_flag = bit_reader.getBit(); - this.data.inter_pfc_enable_flag = bit_reader.getBit(); - this.data.obmc_enable_flag = bit_reader.getBit(); - - this.data.sbt_enable_flag = bit_reader.getBit(); - this.data.ist_enable_flag = bit_reader.getBit(); - - this.data.esao_enable_flag = bit_reader.getBit(); - this.data.ccsao_enable_flag = bit_reader.getBit(); - if (this.data.alf_enable_flag) this.data.ealf_enable_flag = bit_reader.getBit(); - this.data.ibc_enable_flag = bit_reader.getBit(); + this.data.pmc_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.iip_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.sawp_enable_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.affine_enable_flag.get()) + this.data.asr_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.awp_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.etmvp_mvap_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.dmvr_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.bio_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.bgc_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.inter_pf_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.inter_pfc_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.obmc_enable_flag = new BooleanValue(bit_reader.getBit()); + + this.data.sbt_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.ist_enable_flag = new BooleanValue(bit_reader.getBit()); + + this.data.esao_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.ccsao_enable_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.alf_enable_flag.get()) + this.data.ealf_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.ibc_enable_flag = new BooleanValue(bit_reader.getBit()); bit_reader.skipBit(); // marker_bit - this.data.isc_enable_flag = bit_reader.getBit(); - if (this.data.ibc_enable_flag || this.data.isc_enable_flag) + this.data.isc_enable_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.ibc_enable_flag.get() || this.data.isc_enable_flag.get()) this.data.num_of_intra_hmvp_cand = bit_reader.getBits(4); - this.data.fimc_enable_flag = bit_reader.getBit(); + this.data.fimc_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.nn_tools_set_hook = bit_reader.getBits(8); if (this.data.nn_tools_set_hook & 0x01) this.data.num_of_nn_filter_minus1 = bit_reader.getUE(); bit_reader.skipBit(); // marker_bit } if (this.data.low_delay === 0) this.data.output_reorder_delay = bit_reader.getBits(5); - this.data.cross_patch_loop_filter_enable_flag = bit_reader.getBit(); - this.data.ref_colocated_patch_flag = bit_reader.getBit(); - this.data.stable_patch_flag = bit_reader.getBit(); - if (this.data.stable_patch_flag) { - this.data.uniform_patch_flag = bit_reader.getBit(); - if (this.data.uniform_patch_flag) { + this.data.cross_patch_loop_filter_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.ref_colocated_patch_flag = new BooleanValue(bit_reader.getBit()); + this.data.stable_patch_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.stable_patch_flag.get()) { + this.data.uniform_patch_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.uniform_patch_flag.get()) { bit_reader.skipBit(); // marker_bit this.data.patch_width_minus1 = bit_reader.getUE(); this.data.patch_height_minus1 = bit_reader.getUE(); diff --git a/src/boxes/avs-common.ts b/src/boxes/avs-common.ts index 80449f23f..68a421444 100644 --- a/src/boxes/avs-common.ts +++ b/src/boxes/avs-common.ts @@ -57,8 +57,22 @@ export class BinaryValue { } } +export class BooleanValue { + private value: boolean; + + constructor(value: boolean | number) { + this.value = typeof value === 'number' ? value > 0 : value; + } + toString() { + return `${this.value ? 1 : 0} (${this.value ? 'true' : 'false'})`; + } + public get() { + return this.value; + } +} + export class AVS3data { - toHTML(data): string { + toHTML(data: object): string { let res = ''; const props = Object.getOwnPropertyNames(data); if (props) diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index abb4133a4..b2cec0323 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -100,9 +100,9 @@ class AVS3GAConfig extends AVS3data { constructor(bit_reader: BitBuffer) { super(); this.data = {}; - this.parse(bit_reader); + this.deserialise(bit_reader); } - parse(bit_reader: BitBuffer) { + deserialise(bit_reader: BitBuffer) { this.data.sampling_frequency_index = new DescribedValue( bit_reader.getBits(4), AVS3Asampling_frequency, @@ -154,9 +154,9 @@ class AVS3GHConfig extends AVS3data { constructor(bit_reader: BitBuffer) { super(); this.data = {}; - this.parse(bit_reader); + this.deserialise(bit_reader); } - parse(bit_reader: BitBuffer) { + deserialise(bit_reader: BitBuffer) { this.data.sampling_frequency_index = bit_reader.getBits(4); this.data.anc_data_index = bit_reader.getBit(); this.data.coding_profile = new DescribedValue(bit_reader.getBits(3), AVS3Acodingprofile); @@ -191,9 +191,9 @@ class AVS3LLConfig extends AVS3data { constructor(bit_reader: BitBuffer) { super(); this.data = {}; - this.parse(bit_reader); + this.deserialise(bit_reader); } - parse(bit_reader: BitBuffer) { + deserialise(bit_reader: BitBuffer) { this.data.sampling_frequency_index = bit_reader.getBits(4); if (this.data.sampling_frequency_index === 0xf) this.data.sampling_frequency = bit_reader.getUint24(); @@ -243,6 +243,6 @@ export class dca3Box extends Box { } get_audio_codec_id_str() { - return (this.audio_codec_id.get() < 9 ? '0' : '') + this.audio_codec_id.get(); + return this.audio_codec_id.get().toString(10).padStart(2, '0'); } } diff --git a/src/boxes/sampleentries/sampleentry.ts b/src/boxes/sampleentries/sampleentry.ts index b222b62d2..065774f38 100644 --- a/src/boxes/sampleentries/sampleentry.ts +++ b/src/boxes/sampleentries/sampleentry.ts @@ -95,22 +95,18 @@ export class av01SampleEntry extends VisualSampleEntry { } export class avs3SampleEntry extends VisualSampleEntry { - av3c: av3cBox; static override readonly fourcc = 'avs3' as const; + av3c: av3cBox; + getCodec(): string { - const baseCodec = super.getCodec(); - return ( - baseCodec + - '.' + - (this.av3c.sequence_header?.data?.profile_id - ? this.av3c.sequence_header.data?.profile_id.get().toString(16) - : 'XX') + - '.' + - (this.av3c.sequence_header?.data?.level_id - ? this.av3c.sequence_header.data?.level_id.get().toString(16) - : 'XX') - ); + const profile = this.av3c.sequence_header?.data?.profile_id + ? this.av3c.sequence_header.data?.profile_id.get().toString(16) + : 'XX'; + const level = this.av3c.sequence_header?.data?.level_id + ? this.av3c.sequence_header.data?.level_id.get().toString(16) + : 'XX'; + return `${super.getCodec()}.${profile}.${level}`; } } @@ -395,11 +391,13 @@ export class fLaCSampleEntry extends AudioSampleEntry { } export class av3aSampleEntry extends AudioSampleEntry { - dca3: dca3Box; + // AATF (AVS3 Audio Transport Format) audio sample entry static override readonly fourcc = 'av3a' as const; + + dca3: dca3Box; + getCodec() { - const baseCodec = super.getCodec(); - return baseCodec + '.' + this.dca3.get_audio_codec_id_str(); + return `${super.getCodec()}.${this.dca3.get_audio_codec_id_str()}`; } } From 1f56b505f7667f03d179ccf41e1ef69525895c95 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Mon, 11 Aug 2025 15:01:16 +0100 Subject: [PATCH 08/18] chore: leverage some more TypeScript features --- src/boxes/av3c.ts | 76 +++++++++++++------------- src/boxes/avs-common.ts | 56 +++++++++---------- src/boxes/sampleentries/sampleentry.ts | 4 +- 3 files changed, 68 insertions(+), 68 deletions(-) diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index b2bce8d9f..4a026570e 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -23,33 +23,33 @@ interface ReferencePicture { } class ReferencePictureSet { - list: number; - set: number; - pics: Array; - reference_to_library_enable_flag?: number; - private library_enable_flag_set: boolean; + private _list: number; + private _set: number; + private _pics: Array; + private _reference_to_library_enable_flag?: number; + private _library_enable_flag_set: boolean; constructor(list: number, set: number) { - this.list = list; - this.set = set; - this.pics = []; - this.library_enable_flag_set = false; + this._list = list; + this._set = set; + this._pics = []; + this._library_enable_flag_set = false; } set_reference_to_library_enable_flag(flag: number) { - this.library_enable_flag_set = true; - this.reference_to_library_enable_flag = flag; + this._library_enable_flag_set = true; + this._reference_to_library_enable_flag = flag; } get_reference_to_library_enable_flag(): number { - return this.library_enable_flag_set ? this.reference_to_library_enable_flag : 0; + return this._library_enable_flag_set ? this._reference_to_library_enable_flag : 0; } push(pic: ReferencePicture) { - this.pics.push(pic); + this._pics.push(pic); } toString() { let ret = '{'; - if (this.library_enable_flag_set) - ret += 'reference_to_library_enable_flag: ' + this.reference_to_library_enable_flag; - this.pics.forEach(e => { + if (this._library_enable_flag_set) + ret += 'reference_to_library_enable_flag: ' + this._reference_to_library_enable_flag; + this._pics.forEach(e => { ret += (ret.length > 3 ? ', ' : '') + JSON.stringify(e).replace(/"/g, ''); }); ret += '}'; @@ -58,20 +58,20 @@ class ReferencePictureSet { } class ReferencePictureList { - list?: number; - sets?: Array; + private _list?: number; + private _sets?: Array; constructor(list: number) { - this.list = list; - this.sets = []; + this._list = list; + this._sets = []; } push(set: ReferencePictureSet) { - this.sets.push(set); + this._sets.push(set); } toString() { - if (this.sets.length === 0) return '(empty)'; + if (this._sets.length === 0) return '(empty)'; const l: Array = []; - this.sets.forEach(set => { + this._sets.forEach(set => { l.push(set.toString()); }); return l.join(', '); @@ -322,9 +322,9 @@ class AVS3SequenceHeader extends AVS3data { this.data.progressive_sequence = new BooleanValue(bit_reader.getBit()); this.data.field_coded_sequence = new BooleanValue(bit_reader.getBit()); this.data.library_stream_flag = new BooleanValue(bit_reader.getBit()); - if (!this.data.library_stream_flag.get()) { + if (!this.data.library_stream_flag.value) { this.data.library_picture_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.library_picture_enable_flag.get()) + if (this.data.library_picture_enable_flag.value) this.data.duplicate_sequence_number_flag = new BooleanValue(bit_reader.getBit()); } bit_reader.skipBit(); // marker_bit @@ -336,7 +336,7 @@ class AVS3SequenceHeader extends AVS3data { this.data.chroma_format = new BinaryValue(bit_reader.getBits(2), 2, AVS3chroma); this.data.sample_precision = new BinaryValue(bit_reader.getBits(3), 3, AVS3precision); - if (this.data.profile_id.get() === MAIN_10 || this.data.profile_id.get() === HIGH_10) + if (this.data.profile_id.value === MAIN_10 || this.data.profile_id.value === HIGH_10) this.data.encoding_precision = new BinaryValue(bit_reader.getBits(3), 3, AVS3precision); bit_reader.skipBit(); // marker_bit @@ -388,7 +388,7 @@ class AVS3SequenceHeader extends AVS3data { this.data.rpl0 = new ReferencePictureList(0); for (let j = 0; j < this.data.num_ref_pic_list_set0; j++) this.data.rpl0.push( - reference_picture_list(0, j, this.data.library_picture_enable_flag.get()), + reference_picture_list(0, j, this.data.library_picture_enable_flag.value), ); if (!this.data.rpl1_same_as_rpl0_flag) { @@ -396,7 +396,7 @@ class AVS3SequenceHeader extends AVS3data { this.data.rpl1 = new ReferencePictureList(1); for (let j = 0; j < this.data.num_ref_pic_list_set1; j++) this.data.rpl1.push( - reference_picture_list(1, j, this.data.library_picture_enable_flag.get()), + reference_picture_list(1, j, this.data.library_picture_enable_flag.value), ); } @@ -412,9 +412,9 @@ class AVS3SequenceHeader extends AVS3data { bit_reader.skipBit(); // marker_bit this.data.weight_quant_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.weight_quant_enable_flag.get()) { + if (this.data.weight_quant_enable_flag.value) { this.data.load_seq_weight_quant_data_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.load_seq_weight_quant_data_flag.get()) + if (this.data.load_seq_weight_quant_data_flag.value) this.data.weight_quant_matrix = new WeightQuantMatrix(bit_reader); } @@ -427,21 +427,21 @@ class AVS3SequenceHeader extends AVS3data { this.data.amvr_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.num_of_hmvp_cand = bit_reader.getBits(4); this.data.umve_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.num_of_hmvp_cand !== 0 && this.data.amvr_enable_flag.get()) + if (this.data.num_of_hmvp_cand !== 0 && this.data.amvr_enable_flag.value) this.data.emvr_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.intra_pf_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.tscpm_enable_flag = new BooleanValue(bit_reader.getBit()); bit_reader.skipBit(); // marker_bit this.data.dt_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.dt_enable_flag.get()) this.data.log2_max_dt_size_minus4 = bit_reader.getBits(2); + if (this.data.dt_enable_flag.value) this.data.log2_max_dt_size_minus4 = bit_reader.getBits(2); this.data.pbt_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.profile_id.get() === MAIN_10 || this.data.profile_id.get() === HIGH_10) { + if (this.data.profile_id.value === MAIN_10 || this.data.profile_id.value === HIGH_10) { this.data.pmc_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.iip_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.sawp_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.affine_enable_flag.get()) + if (this.data.affine_enable_flag.value) this.data.asr_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.awp_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.etmvp_mvap_enable_flag = new BooleanValue(bit_reader.getBit()); @@ -457,13 +457,13 @@ class AVS3SequenceHeader extends AVS3data { this.data.esao_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.ccsao_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.alf_enable_flag.get()) + if (this.data.alf_enable_flag.value) this.data.ealf_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.ibc_enable_flag = new BooleanValue(bit_reader.getBit()); bit_reader.skipBit(); // marker_bit this.data.isc_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.ibc_enable_flag.get() || this.data.isc_enable_flag.get()) + if (this.data.ibc_enable_flag.value || this.data.isc_enable_flag.value) this.data.num_of_intra_hmvp_cand = bit_reader.getBits(4); this.data.fimc_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.nn_tools_set_hook = bit_reader.getBits(8); @@ -475,9 +475,9 @@ class AVS3SequenceHeader extends AVS3data { this.data.cross_patch_loop_filter_enable_flag = new BooleanValue(bit_reader.getBit()); this.data.ref_colocated_patch_flag = new BooleanValue(bit_reader.getBit()); this.data.stable_patch_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.stable_patch_flag.get()) { + if (this.data.stable_patch_flag.value) { this.data.uniform_patch_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.uniform_patch_flag.get()) { + if (this.data.uniform_patch_flag.value) { bit_reader.skipBit(); // marker_bit this.data.patch_width_minus1 = bit_reader.getUE(); this.data.patch_height_minus1 = bit_reader.getUE(); diff --git a/src/boxes/avs-common.ts b/src/boxes/avs-common.ts index de7bae1e3..9bbd25ca3 100644 --- a/src/boxes/avs-common.ts +++ b/src/boxes/avs-common.ts @@ -14,10 +14,7 @@ export class DescribedValue { this.description = descriptionFn ? descriptionFn(value) : undefined; } toString() { - return ( - this.value + - (this.description && this.description.length > 0 ? ' (' + this.description + ')' : '') - ); + return `${this.value}${this.description && this.description.length > 0 ? ' (' + this.description + ')' : ''}`; } get() { return this.value; @@ -25,52 +22,55 @@ export class DescribedValue { } export class HexadecimalValue { - private value: number; - private description: string; + private _value: number; + private _description: string; constructor(value: number, descriptionFn?: DescriberFunction) { - this.value = value; - this.description = descriptionFn ? descriptionFn(value) : undefined; + this._value = value; + this._description = descriptionFn ? descriptionFn(value) : undefined; } toString() { - return '0x' + this.value.toString(16) + (this.description ? ' (' + this.description + ')' : ''); + return `0x${this._value.toString(16)}${this._description && this._description.length > 0 ? ' (' + this._description + ')' : ''}`; } - get() { - return this.value; + get value(): number { + return this._value; } } export class BinaryValue { - private value: number; - private bits: number; - private description: string; + private _value: number; + private _bits: number; + private _description: string; constructor(value: number, bits: number, descriptionFn?: DescriberFunction) { - this.value = value; - this.bits = bits; - this.description = descriptionFn ? descriptionFn(value) : undefined; + this._value = value; + this._bits = bits; + this._description = descriptionFn ? descriptionFn(value) : undefined; } toString() { let res = 'b'; - for (let i = this.bits; i > 0; i--) res += this.value & (1 << (i - 1)) ? '1' : '0'; - return res + (this.description ? ' (' + this.description + ')' : ''); + for (let i = this._bits; i > 0; i--) res += this._value & (1 << (i - 1)) ? '1' : '0'; + return ( + res + + (this._description && this._description.length > 0 ? ' (' + this._description + ')' : '') + ); } - public get() { - return this.value; + get value(): number { + return this._value; } } export class BooleanValue { - private value: boolean; + private _value: boolean; constructor(value: boolean | number) { - this.value = typeof value === 'number' ? value > 0 : value; + this._value = typeof value === 'number' ? value > 0 : value; } toString() { - return `${this.value ? 1 : 0} (${this.value ? 'true' : 'false'})`; + return `${this._value ? 1 : 0} (${this._value ? 'true' : 'false'})`; } - public get() { - return this.value; + get value(): boolean { + return this._value; } } @@ -88,8 +88,8 @@ export class AVS3data { if (i % 4 === 3) fmt_val += ' '; } } else fmt_val = data[val]; - res += '' + val + '' + fmt_val + ''; + res += `${val}${fmt_val}`; }); - return '' + res + '
'; + return `${res}
`; } } diff --git a/src/boxes/sampleentries/sampleentry.ts b/src/boxes/sampleentries/sampleentry.ts index b5cc1aace..fa9cc0e85 100644 --- a/src/boxes/sampleentries/sampleentry.ts +++ b/src/boxes/sampleentries/sampleentry.ts @@ -113,10 +113,10 @@ export class avs3SampleEntry extends VisualSampleEntry { getCodec(): string { const profile = this.av3c.sequence_header?.data?.profile_id - ? this.av3c.sequence_header.data?.profile_id.get().toString(16) + ? this.av3c.sequence_header.data?.profile_id.value.toString(16) : 'XX'; const level = this.av3c.sequence_header?.data?.level_id - ? this.av3c.sequence_header.data?.level_id.get().toString(16) + ? this.av3c.sequence_header.data?.level_id.value.toString(16) : 'XX'; return `${super.getCodec()}.${profile}.${level}`; } From db889118f1060461e5dacd612514d9982afbc5a6 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Mon, 11 Aug 2025 16:38:45 +0100 Subject: [PATCH 09/18] chore: more Typescript aspects for AVS3 audio too --- src/boxes/avs-common.ts | 14 +++++++------- src/boxes/dca3.ts | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/boxes/avs-common.ts b/src/boxes/avs-common.ts index 9bbd25ca3..ac1025663 100644 --- a/src/boxes/avs-common.ts +++ b/src/boxes/avs-common.ts @@ -6,18 +6,18 @@ export type DescriberFunction = (n: number) => string; export class DescribedValue { - private value: number; - private description: string; + private _value: number; + private _description: string; constructor(value: number, descriptionFn?: DescriberFunction) { - this.value = value; - this.description = descriptionFn ? descriptionFn(value) : undefined; + this._value = value; + this._description = descriptionFn ? descriptionFn(value) : undefined; } toString() { - return `${this.value}${this.description && this.description.length > 0 ? ' (' + this.description + ')' : ''}`; + return `${this.value}${this._description && this._description.length > 0 ? ' (' + this._description + ')' : ''}`; } - get() { - return this.value; + get value(): number { + return this._value; } } diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index b2cec0323..1afa1c3e3 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -228,7 +228,7 @@ export class dca3Box extends Box { for (let i = 0; i < this.size - this.hdr_size; i++) bit_reader.appendUint8(stream.readUint8()); this.audio_codec_id = new DescribedValue(bit_reader.getBits(4), AVS3Acodec); - switch (this.audio_codec_id.get()) { + switch (this.audio_codec_id.value) { case FULL_RATE_CODING: this.Avs3AudioGAConfig = new AVS3GAConfig(bit_reader); break; @@ -243,6 +243,6 @@ export class dca3Box extends Box { } get_audio_codec_id_str() { - return this.audio_codec_id.get().toString(10).padStart(2, '0'); + return this.audio_codec_id.value.toString(10).padStart(2, '0'); } } From 6e658d58b1b8ad26a516ac2bd1a2d7bcd184ce1d Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Mon, 11 Aug 2025 16:49:55 +0100 Subject: [PATCH 10/18] chore: fix getter call --- src/boxes/av3c.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index 4a026570e..19a3efab8 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -502,7 +502,7 @@ export class av3cBox extends Box { parse(stream: MP4BoxStream) { const bit_reader = new BitBuffer(); this.configurationVersion = new DescribedValue(stream.readUint8(), AVS3Vconfiguration); - if (this.configurationVersion.get() === 1) { + if (this.configurationVersion.value === 1) { this.sequence_header_length = stream.readUint16(); for (let i = 0; i < this.sequence_header_length; i++) bit_reader.appendUint8(stream.readUint8()); From cda4d104cad7d1e2d5fe924ffdff9bb416327003 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Tue, 12 Aug 2025 06:12:21 +0100 Subject: [PATCH 11/18] fix: rely on projects prettier capabilities for indentation --- .vscode/settings.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2477f5be1..4c2e136ee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,4 @@ { - "editor.tabSize": 2, - "editor.insertSpaces": true, "eslint.useFlatConfig": true, "eslint.workingDirectories": [{ "mode": "auto" }], "eslint.options": { From aefb5bd2180a3de15a63801fed209b5497811c40 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Tue, 12 Aug 2025 08:57:39 +0100 Subject: [PATCH 12/18] fix: remove HTML based formatting of output value; not as pretty but in line with other box displays --- demo/boxHtmlTable.js | 19 +++++++++++++------ src/boxes/av3c.ts | 7 +++++-- src/boxes/avs-common.ts | 18 ++++++++++++++++++ src/boxes/dca3.ts | 12 ++++++------ src/boxes/sampleentries/sampleentry.ts | 1 + 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/demo/boxHtmlTable.js b/demo/boxHtmlTable.js index d57543ef8..156625afa 100644 --- a/demo/boxHtmlTable.js +++ b/demo/boxHtmlTable.js @@ -8,16 +8,23 @@ function generatePropertyValue(prop, value) { return groupsOf4.join(' '); } - // Check if we explicitly have a toHTML method - if (typeof value?.toHTML === 'function') { - return value.toHTML(); - } - // Check if we explicitly have a toString method if (typeof value?.toString === 'function') { const content = value.toString(); // if (value.hasOwnProperty('toString') || content.startsWith('<')) { - return content; + return content.replace( + /[&<>"'\-\n]/g, + m => + ({ + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '-': '‑', + '\n': '
', + })[m], + ); // } } diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index 19a3efab8..c02877830 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -485,8 +485,11 @@ class AVS3SequenceHeader extends AVS3data { } bit_reader.skipBits(2); // reserved bits } - toHTML() { - return super.toHTML(this.data); + //PH toHTML() { + //PH return super.toHTML(this.data); + //PH } + toString() { + return super.toString(this.data); } } diff --git a/src/boxes/avs-common.ts b/src/boxes/avs-common.ts index ac1025663..a630a3e4c 100644 --- a/src/boxes/avs-common.ts +++ b/src/boxes/avs-common.ts @@ -75,6 +75,7 @@ export class BooleanValue { } export class AVS3data { + /* not currently used to beautify output toHTML(data: object): string { let res = ''; const props = Object.getOwnPropertyNames(data); @@ -91,5 +92,22 @@ export class AVS3data { res += `${val}${fmt_val}`; }); return `${res}
`; + } */ + + toString(data: object): string { + let res = ''; + const props = Object.getOwnPropertyNames(data); + props.forEach(function (val) { + let fmt_val = ''; + if (Array.isArray(data[val])) { + for (let i = 0; i < data[val].length; i++) { + const hex = data[val][i].toString(16); + fmt_val += hex.length === 1 ? '0' + hex : hex; + if (i % 4 === 3) fmt_val += ' '; + } + } else fmt_val = data[val]; + res += `${val}: ${fmt_val}\n`; + }); + return res; } } diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index 1afa1c3e3..99fcafff4 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -133,8 +133,8 @@ class AVS3GAConfig extends AVS3data { this.data.total_bitrate = bit_reader.getUint16(); this.data.resolution = new DescribedValue(bit_reader.getBits(2), AVS3Aresolution); } - toHTML(): string { - return super.toHTML(this.data); + toString(): string { + return super.toString(this.data); } } @@ -172,8 +172,8 @@ class AVS3GHConfig extends AVS3data { this.data.addition_info.push(bit_reader.getUint8()); } } - toHTML(): string { - return super.toHTML(this.data); + toString(): string { + return super.toString(this.data); } } @@ -209,8 +209,8 @@ class AVS3LLConfig extends AVS3data { } bit_reader.skipBits(2); // reserved } - toHTML(): string { - return super.toHTML(this.data); + toString(): string { + return super.toString(this.data); } } diff --git a/src/boxes/sampleentries/sampleentry.ts b/src/boxes/sampleentries/sampleentry.ts index fa9cc0e85..7efef32a4 100644 --- a/src/boxes/sampleentries/sampleentry.ts +++ b/src/boxes/sampleentries/sampleentry.ts @@ -431,6 +431,7 @@ export class fLaCSampleEntry extends AudioSampleEntry { export class av3aSampleEntry extends AudioSampleEntry { // AATF (AVS3 Audio Transport Format) audio sample entry static override readonly fourcc = 'av3a' as const; + box_name = 'AVS3AudioSampleEntry' as const; dca3: dca3Box; From 165e94a532822886d5a5f60d6f4d08e9ba819ff2 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Tue, 12 Aug 2025 11:16:14 +0100 Subject: [PATCH 13/18] fix: ensure all objects are correctly formatted --- demo/boxHtmlTable.js | 30 +++++++++++++++--------------- src/boxes/av3c.ts | 3 --- src/boxes/avs-common.ts | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/demo/boxHtmlTable.js b/demo/boxHtmlTable.js index 156625afa..e811c4c2d 100644 --- a/demo/boxHtmlTable.js +++ b/demo/boxHtmlTable.js @@ -11,21 +11,21 @@ function generatePropertyValue(prop, value) { // Check if we explicitly have a toString method if (typeof value?.toString === 'function') { const content = value.toString(); - // if (value.hasOwnProperty('toString') || content.startsWith('<')) { - return content.replace( - /[&<>"'\-\n]/g, - m => - ({ - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', - '-': '‑', - '\n': '
', - })[m], - ); - // } + if (value.hasOwnProperty('toString') || content.startsWith('<')) { + return content.replace( + /[&<>"'\-\n]/g, + m => + ({ + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '-': '‑', + '\n': '
', + })[m], + ); + } } // Display arrays concisely diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index c02877830..575feaddb 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -485,9 +485,6 @@ class AVS3SequenceHeader extends AVS3data { } bit_reader.skipBits(2); // reserved bits } - //PH toHTML() { - //PH return super.toHTML(this.data); - //PH } toString() { return super.toString(this.data); } diff --git a/src/boxes/avs-common.ts b/src/boxes/avs-common.ts index a630a3e4c..35cd9e74f 100644 --- a/src/boxes/avs-common.ts +++ b/src/boxes/avs-common.ts @@ -13,6 +13,10 @@ export class DescribedValue { this._value = value; this._description = descriptionFn ? descriptionFn(value) : undefined; } + hasOwnProperty(name: string) { + // we need to 'trick' the check done in generatePropertyValue() og boxHTMLTable.js + return name === 'toString'; + } toString() { return `${this.value}${this._description && this._description.length > 0 ? ' (' + this._description + ')' : ''}`; } @@ -29,6 +33,10 @@ export class HexadecimalValue { this._value = value; this._description = descriptionFn ? descriptionFn(value) : undefined; } + hasOwnProperty(name: string) { + // we need to 'trick' the check done in generatePropertyValue() og boxHTMLTable.js + return name === 'toString'; + } toString() { return `0x${this._value.toString(16)}${this._description && this._description.length > 0 ? ' (' + this._description + ')' : ''}`; } @@ -47,6 +55,10 @@ export class BinaryValue { this._bits = bits; this._description = descriptionFn ? descriptionFn(value) : undefined; } + hasOwnProperty(name: string) { + // we need to 'trick' the check done in generatePropertyValue() og boxHTMLTable.js + return name === 'toString'; + } toString() { let res = 'b'; for (let i = this._bits; i > 0; i--) res += this._value & (1 << (i - 1)) ? '1' : '0'; @@ -66,6 +78,10 @@ export class BooleanValue { constructor(value: boolean | number) { this._value = typeof value === 'number' ? value > 0 : value; } + hasOwnProperty(name: string) { + // we need to 'trick' the check done in generatePropertyValue() og boxHTMLTable.js + return name === 'toString'; + } toString() { return `${this._value ? 1 : 0} (${this._value ? 'true' : 'false'})`; } @@ -94,6 +110,10 @@ export class AVS3data { return `${res}
`; } */ + hasOwnProperty(name: string) { + // we need to 'trick' the check done in generatePropertyValue() og boxHTMLTable.js + return name === 'toString'; + } toString(data: object): string { let res = ''; const props = Object.getOwnPropertyNames(data); From 81db94e9059364981b65de91d44376cc75057904 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Tue, 14 Apr 2026 14:14:04 +0100 Subject: [PATCH 14/18] chore: use MultiBifferStream rather than MP4BoxStream as input --- src/boxes/av3c.ts | 4 ++-- src/boxes/dca3.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index 575feaddb..e88e977e8 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -4,7 +4,7 @@ */ import { Box } from '#/box'; -import { MP4BoxStream } from '#/stream'; +import type { MultiBufferStream } from '#/buffer'; import { BitBuffer } from '#/BitBuffer'; import { @@ -499,7 +499,7 @@ export class av3cBox extends Box { sequence_header?: AVS3SequenceHeader; library_dependency_idc?: BinaryValue; - parse(stream: MP4BoxStream) { + parse(stream: MultiBufferStream) { const bit_reader = new BitBuffer(); this.configurationVersion = new DescribedValue(stream.readUint8(), AVS3Vconfiguration); if (this.configurationVersion.value === 1) { diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index 99fcafff4..362404334 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -4,7 +4,7 @@ */ import { Box } from '#/box'; -import { MP4BoxStream } from '#/stream'; +import type { MultiBufferStream } from '#/buffer'; import { BitBuffer } from '#/BitBuffer'; import { DescribedValue, AVS3data } from './avs-common'; @@ -223,7 +223,7 @@ export class dca3Box extends Box { private Avs3AudioGHConfig?: AVS3GHConfig; private Avs3AudioLLConfig?: AVS3LLConfig; - parse(stream: MP4BoxStream) { + parse(stream: MultiBufferStream) { const bit_reader = new BitBuffer(); for (let i = 0; i < this.size - this.hdr_size; i++) bit_reader.appendUint8(stream.readUint8()); this.audio_codec_id = new DescribedValue(bit_reader.getBits(4), AVS3Acodec); From c03e73d9dd5e1ffb34da85aa87f739b57adecc40 Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Wed, 15 Apr 2026 16:39:13 +0100 Subject: [PATCH 15/18] fix: use MultiBufferStream | DataStream type as parameter to BitStream constructor --- src/{BitBuffer.ts => BitStream.ts} | 58 +++++++++++------------------- src/boxes/av3c.ts | 13 ++++--- src/boxes/dca3.ts | 18 +++++----- 3 files changed, 36 insertions(+), 53 deletions(-) rename src/{BitBuffer.ts => BitStream.ts} (86%) diff --git a/src/BitBuffer.ts b/src/BitStream.ts similarity index 86% rename from src/BitBuffer.ts rename to src/BitStream.ts index 456a0f6d4..af130359b 100644 --- a/src/BitBuffer.ts +++ b/src/BitStream.ts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. Paul Higgs + * Copyright (c) 2026. Paul Higgs * License: BSD-3-Clause (see LICENSE file) * * @@ -7,7 +7,8 @@ * TODO: add writing support */ -import { Endianness } from '#/DataStream'; +import { DataStream, Endianness } from '#/DataStream'; +import type { MultiBufferStream } from './buffer'; class State { rbyte: number; @@ -19,33 +20,24 @@ class State { write_error: boolean; constructor() { - this.rbyte = this.rbit = this.rbyte = this.rbit = this.end - 0; + this.rbyte = this.rbit = this.wbyte = this.wbit = this.end = 0; this.read_error = this.write_error = false; } } -export class BitBuffer { - private endianness: Endianness; +export class BitStream { private _buffer: Array; + private _stream: MultiBufferStream | DataStream; private _state: State; private _big_endian = true; // results are returned Big Endian - constructor(stream?: Uint8Array, endianness?: Endianness) { + constructor(stream: MultiBufferStream | DataStream) { this._state = new State(); - this.load(stream ? stream : new Uint8Array([])); - this.endianness = endianness ? endianness : this._ENDIANNESS(); + this._stream = stream; } - load(stream: Uint8Array): void { - this._buffer = [...stream]; - this._state.rbit = this._state.rbyte = 0; - this._state.wbit = 0; - this._state.wbyte = this._state.end = this._buffer.length; - this._state.read_error = this._state.write_error = false; - } - - appendUint8(byte: number): void { - this._buffer.push(byte); + appendUint8(count = 1): void { + for (let i = 0; i < count; i++) this._buffer.push(this._stream.readUint8()); this._state.end = this._state.wbyte = this._buffer.length; } @@ -57,7 +49,7 @@ export class BitBuffer { } } - getBit(): number { + readBit(): number { //! Read the next bit and advance the read pointer. if (this._state.read_error || this.endOfRead()) { this._state.read_error = true; @@ -137,17 +129,17 @@ export class BitBuffer { return ff; // we should never get here!! } - getUint8() { + readUint8() { return this._rdb(1); } - getUint16() { + readUint16() { return this._big_endian ? this._GetUInt16BE(this._rdb(2)) : this._GetUInt16LE(this._rdb(2)); } - private _ByteSwap16 = function (x) { + private _ByteSwap16 = function (x: number): number { return (x << 8) | (x >> 8); }; - private _CondByteSwap16BE = function (val: number) { + private _CondByteSwap16BE = function (val: number): number { return this._OSisLittleEndian() ? this._ByteSwap16(val) : val; }; private _CondByteSwap16LE = function (val: number) { @@ -160,7 +152,7 @@ export class BitBuffer { return this._CondByteSwap16LE(val); }; - getUint24() { + readUint24() { return this._big_endian ? this._GetUInt24BE(this._rdb(3)) : this._GetUInt24LE(this._rdb(3)); } @@ -180,7 +172,7 @@ export class BitBuffer { return this._CondByteSwap24LE(val); }; - getUint32() { + readUint32() { return this._big_endian ? this._GetUInt32BE(this._rdb(4)) : this._GetUInt32LE(this._rdb(4)); } @@ -200,7 +192,7 @@ export class BitBuffer { return this._CondByteSwap32LE(val); } - getBits(bits: number): number { + readBits(bits: number): number { // No read if read error is already set or not enough bits to read. if ( this._state.read_error || @@ -278,7 +270,7 @@ export class BitBuffer { return this.skipBits(1); } - getUE(): number { + readUE(): number { // read in an unsigned Exp-Golomb code; if (this.getBit() === 1) return 0; let zero_count = 1; @@ -294,15 +286,7 @@ export class BitBuffer { } private _OSisLittleEndian(): boolean { - return this.endianness === Endianness.LITTLE_ENDIAN; - } - - private _ENDIANNESS(): Endianness { - const buf = new ArrayBuffer(4); - const u32data = new Uint32Array(buf); - const u8data = new Uint8Array(buf); - u32data[0] = 0xcafebabe; - return u8data[3] === 0xca ? Endianness.BIG_ENDIAN : Endianness.LITTLE_ENDIAN; + return this._stream.endianness === Endianness.LITTLE_ENDIAN; } currentReadByteOffset(): number { @@ -317,7 +301,7 @@ export class BitBuffer { currentWriteBitOffset(): number { return 8 * this._state.wbyte + this._state.wbit; } - bitsRemaining() { + bitsRemaining(): number { return this.currentWriteBitOffset() - this.currentReadBitOffset(); } writeBitsRemaining(): number { diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index e88e977e8..6cb4c8041 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -5,7 +5,7 @@ import { Box } from '#/box'; import type { MultiBufferStream } from '#/buffer'; -import { BitBuffer } from '#/BitBuffer'; +import { BitStream } from '#/BitStream'; import { DescribedValue, @@ -82,7 +82,7 @@ class WeightQuantMatrix { WeightQuantMatrix4x4: Array>; WeightQuantMatrix8x8: Array>; - constructor(reader: BitBuffer) { + constructor(reader: BitStream) { this.WeightQuantMatrix4x4 = []; this.WeightQuantMatrix8x8 = []; @@ -310,12 +310,12 @@ interface SequenceHeaderElements { class AVS3SequenceHeader extends AVS3data { data: SequenceHeaderElements; - constructor(bit_reader: BitBuffer) { + constructor(bit_reader: BitStream) { super(); this.data = {}; this.deserialise(bit_reader); } - deserialise(bit_reader: BitBuffer) { + deserialise(bit_reader: BitStream) { this.data.video_sequence_start_code = new HexadecimalValue(bit_reader.getUint32()); this.data.profile_id = new HexadecimalValue(bit_reader.getUint8(), AVS3profile); this.data.level_id = new HexadecimalValue(bit_reader.getUint8(), AVS3level); @@ -500,12 +500,11 @@ export class av3cBox extends Box { library_dependency_idc?: BinaryValue; parse(stream: MultiBufferStream) { - const bit_reader = new BitBuffer(); + const bit_reader = new BitStream(stream); this.configurationVersion = new DescribedValue(stream.readUint8(), AVS3Vconfiguration); if (this.configurationVersion.value === 1) { this.sequence_header_length = stream.readUint16(); - for (let i = 0; i < this.sequence_header_length; i++) - bit_reader.appendUint8(stream.readUint8()); + bit_reader.appendUint8(this.sequence_header_length); this.sequence_header = new AVS3SequenceHeader(bit_reader); diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index 362404334..ce19383dc 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -5,7 +5,7 @@ import { Box } from '#/box'; import type { MultiBufferStream } from '#/buffer'; -import { BitBuffer } from '#/BitBuffer'; +import { BitStream } from '#/BitStream'; import { DescribedValue, AVS3data } from './avs-common'; @@ -97,12 +97,12 @@ interface GAconfig { } class AVS3GAConfig extends AVS3data { data: GAconfig; - constructor(bit_reader: BitBuffer) { + constructor(bit_reader: BitStream) { super(); this.data = {}; this.deserialise(bit_reader); } - deserialise(bit_reader: BitBuffer) { + deserialise(bit_reader: BitStream) { this.data.sampling_frequency_index = new DescribedValue( bit_reader.getBits(4), AVS3Asampling_frequency, @@ -151,12 +151,12 @@ interface GHconfig { } class AVS3GHConfig extends AVS3data { data: GHconfig; - constructor(bit_reader: BitBuffer) { + constructor(bit_reader: BitStream) { super(); this.data = {}; this.deserialise(bit_reader); } - deserialise(bit_reader: BitBuffer) { + deserialise(bit_reader: BitStream) { this.data.sampling_frequency_index = bit_reader.getBits(4); this.data.anc_data_index = bit_reader.getBit(); this.data.coding_profile = new DescribedValue(bit_reader.getBits(3), AVS3Acodingprofile); @@ -188,12 +188,12 @@ interface LLconfig { } class AVS3LLConfig extends AVS3data { data: LLconfig; - constructor(bit_reader: BitBuffer) { + constructor(bit_reader: BitStream) { super(); this.data = {}; this.deserialise(bit_reader); } - deserialise(bit_reader: BitBuffer) { + deserialise(bit_reader: BitStream) { this.data.sampling_frequency_index = bit_reader.getBits(4); if (this.data.sampling_frequency_index === 0xf) this.data.sampling_frequency = bit_reader.getUint24(); @@ -224,8 +224,8 @@ export class dca3Box extends Box { private Avs3AudioLLConfig?: AVS3LLConfig; parse(stream: MultiBufferStream) { - const bit_reader = new BitBuffer(); - for (let i = 0; i < this.size - this.hdr_size; i++) bit_reader.appendUint8(stream.readUint8()); + const bit_reader = new BitStream(stream); + bit_reader.appendUint8(this.size - this.hdr_size); this.audio_codec_id = new DescribedValue(bit_reader.getBits(4), AVS3Acodec); switch (this.audio_codec_id.value) { From aa712b6c0391346890b11e22390a5ed3079fbedf Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Wed, 15 Apr 2026 17:27:16 +0100 Subject: [PATCH 16/18] chore: update accessor methods to match DataStream --- src/BitStream.ts | 148 +++++++++++++++++++------------------- src/boxes/av3c.ts | 180 +++++++++++++++++++++++----------------------- src/boxes/dca3.ts | 58 +++++++-------- 3 files changed, 192 insertions(+), 194 deletions(-) diff --git a/src/BitStream.ts b/src/BitStream.ts index af130359b..d8c959a24 100644 --- a/src/BitStream.ts +++ b/src/BitStream.ts @@ -26,102 +26,100 @@ class State { } export class BitStream { - private _buffer: Array; - private _stream: MultiBufferStream | DataStream; - private _state: State; - private _big_endian = true; // results are returned Big Endian + private buffer: Array; + private stream: MultiBufferStream | DataStream; + private state: State; + private big_endian = true; // results are returned Big Endian constructor(stream: MultiBufferStream | DataStream) { - this._state = new State(); - this._stream = stream; + this.state = new State(); + this.stream = stream; } appendUint8(count = 1): void { - for (let i = 0; i < count; i++) this._buffer.push(this._stream.readUint8()); - this._state.end = this._state.wbyte = this._buffer.length; + for (let i = 0; i < count; i++) this.buffer.push(this.stream.readUint8()); + this.state.end = this.state.wbyte = this.buffer.length; } extend(bits: number): void { let count = bits; while (count > 0) { - this._buffer.push(0); + this.buffer.push(0); count -= 8; } } readBit(): number { //! Read the next bit and advance the read pointer. - if (this._state.read_error || this.endOfRead()) { - this._state.read_error = true; + if (this.state.read_error || this.endOfRead()) { + this.state.read_error = true; return 0; } const bit: number = - (this._buffer[this._state.rbyte] >> - (this._big_endian ? 7 - this._state.rbit : this._state.rbit)) & + (this.buffer[this.state.rbyte] >> (this.big_endian ? 7 - this.state.rbit : this.state.rbit)) & 0x01; - if (++this._state.rbit > 7) { - this._state.rbyte++; - this._state.rbit = 0; + if (++this.state.rbit > 7) { + this.state.rbyte++; + this.state.rbit = 0; } return bit; } peekBit(): number { //! Read the next bit and but dont advance the read pointer. - if (this._state.read_error || this.endOfRead()) { - this._state.read_error = true; + if (this.state.read_error || this.endOfRead()) { + this.state.read_error = true; return 0; } const bit: number = - (this._buffer[this._state.rbyte] >> - (this._big_endian ? 7 - this._state.rbit : this._state.rbit)) & + (this.buffer[this.state.rbyte] >> (this.big_endian ? 7 - this.state.rbit : this.state.rbit)) & 0x01; return bit; } endOfRead(): boolean { - return this._state.rbyte === this._state.wbyte && this._state.rbit === this._state.wbit; + return this.state.rbyte === this.state.wbyte && this.state.rbit === this.state.wbit; } getBool(): boolean { - return this.getBit() !== 0; + return this.readBit() !== 0; } private _rdb(bytes: number): number { let i: number, res: number; // eslint-disable-next-line no-loss-of-precision const ff = 0xffffffffffffffff; - if (this._state.read_error) return ff; - if (this._state.rbit === 0) { + if (this.state.read_error) return ff; + if (this.state.rbit === 0) { // Read buffer is byte aligned. Most common case. - if (this._state.rbyte + bytes > this._state.wbyte) { + if (this.state.rbyte + bytes > this.state.wbyte) { // Not enough bytes to read. - this._state.read_error = true; + this.state.read_error = true; return ff; } else { - for (res = 0, i = 0; i < bytes; i++) res = (res << 8) + this._buffer[this._state.rbyte + i]; - this._state.rbyte += bytes; + for (res = 0, i = 0; i < bytes; i++) res = (res << 8) + this.buffer[this.state.rbyte + i]; + this.state.rbyte += bytes; return res; } } else { // Read buffer is not byte aligned, use an intermediate aligned buffer. if (this.currentReadBitOffset() + 8 * bytes > this.currentWriteBitOffset()) { // Not enough bytes to read. - this._state.read_error = true; + this.state.read_error = true; return ff; } else { for (res = 0, i = 0; i < bytes; i++) { - if (this._big_endian) + if (this.big_endian) res = (res << 8) + - ((this._buffer[this._state.rbyte] << this._state.rbit) | - (this._buffer[this._state.rbyte + 1] >> (8 - this._state.rbit))); + ((this.buffer[this.state.rbyte] << this.state.rbit) | + (this.buffer[this.state.rbyte + 1] >> (8 - this.state.rbit))); else res = (res << 8) + - ((this._buffer[this._state.rbyte] >> this._state.rbit) | - (this._buffer[this._state.rbyte + 1] << (8 - this._state.rbit))); - this._state.rbyte++; + ((this.buffer[this.state.rbyte] >> this.state.rbit) | + (this.buffer[this.state.rbyte + 1] << (8 - this.state.rbit))); + this.state.rbyte++; } return res; } @@ -134,16 +132,16 @@ export class BitStream { } readUint16() { - return this._big_endian ? this._GetUInt16BE(this._rdb(2)) : this._GetUInt16LE(this._rdb(2)); + return this.big_endian ? this._GetUInt16BE(this._rdb(2)) : this._GetUInt16LE(this._rdb(2)); } private _ByteSwap16 = function (x: number): number { return (x << 8) | (x >> 8); }; private _CondByteSwap16BE = function (val: number): number { - return this._OSisLittleEndian() ? this._ByteSwap16(val) : val; + return this.OSisLittleEndian() ? this._ByteSwap16(val) : val; }; private _CondByteSwap16LE = function (val: number) { - return this._OSisLittleEndian() ? val : this._ByteSwap16(val); + return this.OSisLittleEndian() ? val : this._ByteSwap16(val); }; private _GetUInt16BE = function (val: number) { return this._CondByteSwap16BE(val); @@ -153,17 +151,17 @@ export class BitStream { }; readUint24() { - return this._big_endian ? this._GetUInt24BE(this._rdb(3)) : this._GetUInt24LE(this._rdb(3)); + return this.big_endian ? this._GetUInt24BE(this._rdb(3)) : this._GetUInt24LE(this._rdb(3)); } private _ByteSwap24 = function (x: number) { return ((x & 0xff0000) >> 16) | (x & 0xff00) | (x & (0xff << 16)); }; private _CondByteSwap24BE = function (val: number) { - return this._OSisLittleEndian() ? this._ByteSwap24(val) : val; + return this.OSisLittleEndian() ? this._ByteSwap24(val) : val; }; private _CondByteSwap24LE = function (val: number) { - return this._OSisLittleEndian() ? val : this._ByteSwap24(val); + return this.OSisLittleEndian() ? val : this._ByteSwap24(val); }; private _GetUInt24BE = function (val: number) { return this._CondByteSwap24BE(val); @@ -173,17 +171,17 @@ export class BitStream { }; readUint32() { - return this._big_endian ? this._GetUInt32BE(this._rdb(4)) : this._GetUInt32LE(this._rdb(4)); + return this.big_endian ? this._GetUInt32BE(this._rdb(4)) : this._GetUInt32LE(this._rdb(4)); } private _ByteSwap32(x: number) { return (x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | (x >> 24); } private _CondByteSwap32BE(val: number) { - return this._OSisLittleEndian() ? this._ByteSwap32(val) : val; + return this.OSisLittleEndian() ? this._ByteSwap32(val) : val; } private _CondByteSwap32LE(val: number) { - return this._OSisLittleEndian() ? val : this._ByteSwap32(val); + return this.OSisLittleEndian() ? val : this._ByteSwap32(val); } private _GetUInt32BE(val: number) { return this._CondByteSwap32BE(val); @@ -195,29 +193,29 @@ export class BitStream { readBits(bits: number): number { // No read if read error is already set or not enough bits to read. if ( - this._state.read_error || + this.state.read_error || this.currentReadBitOffset() + bits > this.currentWriteBitOffset() ) { - this._state.read_error = true; + this.state.read_error = true; return 0; } let val = 0; - if (this._big_endian) { + if (this.big_endian) { // Read leading bits up to byte boundary - while (bits > 0 && this._state.rbit !== 0) { - val = (val << 1) | this.getBit(); + while (bits > 0 && this.state.rbit !== 0) { + val = (val << 1) | this.readBit(); --bits; } // Read complete bytes while (bits > 7) { - val = (val << 8) | this._buffer[this._state.rbyte++]; + val = (val << 8) | this.buffer[this.state.rbyte++]; bits -= 8; } // Read trailing bits while (bits > 0) { - val = (val << 1) | this.getBit(); + val = (val << 1) | this.readBit(); --bits; } } else { @@ -225,22 +223,22 @@ export class BitStream { let shift = 0; // Read leading bits up to byte boundary - while (bits > 0 && this._state.rbit !== 0) { - val |= this.getBit() << shift; + while (bits > 0 && this.state.rbit !== 0) { + val |= this.readBit() << shift; --bits; shift++; } // Read complete bytes while (bits > 7) { - val |= this._buffer[this._state.rbyte++] << shift; + val |= this.buffer[this.state.rbyte++] << shift; bits -= 8; shift += 8; } // Read trailing bits while (bits > 0) { - val |= this.getBit() << shift; + val |= this.readBit() << shift; --bits; shift++; } @@ -249,20 +247,20 @@ export class BitStream { } skipBits(bits: number): boolean { - if (this._state.read_error) { + if (this.state.read_error) { // Can't skip bits and bytes if read error is already set. return false; } - const rpos = 8 * this._state.rbyte + this._state.rbit + bits; - const wpos = 8 * this._state.wbyte + this._state.wbit; + const rpos = 8 * this.state.rbyte + this.state.rbit + bits; + const wpos = 8 * this.state.wbyte + this.state.wbit; if (rpos > wpos) { - this._state.rbyte = this._state.wbyte; - this._state.rbit = this._state.wbit; - this._state.read_error = true; + this.state.rbyte = this.state.wbyte; + this.state.rbit = this.state.wbit; + this.state.read_error = true; return false; } - this._state.rbyte = rpos >> 3; - this._state.rbit = rpos & 7; + this.state.rbyte = rpos >> 3; + this.state.rbit = rpos & 7; return true; } @@ -271,41 +269,41 @@ export class BitStream { } readUE(): number { - // read in an unsigned Exp-Golomb code; - if (this.getBit() === 1) return 0; + // read in an Unsigned Exp-Golomb code; + if (this.readBit() === 1) return 0; let zero_count = 1; while (this.peekBit() === 0) { - this.getBit(); + this.readBit(); zero_count++; } - return this.getBits(zero_count + 1) - 1; + return this.readBits(zero_count + 1) - 1; } byte_alignment(): void { - while (!this._state.read_error && this._state.rbit !== 0) this.skipBit(); + while (!this.state.read_error && this.state.rbit !== 0) this.skipBit(); } - private _OSisLittleEndian(): boolean { - return this._stream.endianness === Endianness.LITTLE_ENDIAN; + private OSisLittleEndian(): boolean { + return this.stream.endianness === Endianness.LITTLE_ENDIAN; } currentReadByteOffset(): number { - return this._state.rbyte; + return this.state.rbyte; } currentReadBitOffset(): number { - return 8 * this._state.rbyte + this._state.rbit; + return 8 * this.state.rbyte + this.state.rbit; } currentWriteByteOffset(): number { - return this._state.wbyte; + return this.state.wbyte; } currentWriteBitOffset(): number { - return 8 * this._state.wbyte + this._state.wbit; + return 8 * this.state.wbyte + this.state.wbit; } bitsRemaining(): number { return this.currentWriteBitOffset() - this.currentReadBitOffset(); } writeBitsRemaining(): number { - return 8 * this._buffer.length - this.currentWriteBitOffset(); + return 8 * this.buffer.length - this.currentWriteBitOffset(); } /* TODO - for near future implementation to support writing AVS3 related boxes that are not byte aligned diff --git a/src/boxes/av3c.ts b/src/boxes/av3c.ts index 6cb4c8041..8a0d69200 100644 --- a/src/boxes/av3c.ts +++ b/src/boxes/av3c.ts @@ -91,7 +91,7 @@ class WeightQuantMatrix { const WQMSize = 1 << (sizeId + 2); for (let i = 0; i < WQMSize; i++) { const iVal: Array = []; - for (let j = 0; j < WQMSize; j++) iVal.push(reader.getUE()); + for (let j = 0; j < WQMSize; j++) iVal.push(reader.readUE()); this_size.push(iVal); } if (sizeId === 0) this.WeightQuantMatrix4x4 = this_size; @@ -316,48 +316,48 @@ class AVS3SequenceHeader extends AVS3data { this.deserialise(bit_reader); } deserialise(bit_reader: BitStream) { - this.data.video_sequence_start_code = new HexadecimalValue(bit_reader.getUint32()); - this.data.profile_id = new HexadecimalValue(bit_reader.getUint8(), AVS3profile); - this.data.level_id = new HexadecimalValue(bit_reader.getUint8(), AVS3level); - this.data.progressive_sequence = new BooleanValue(bit_reader.getBit()); - this.data.field_coded_sequence = new BooleanValue(bit_reader.getBit()); - this.data.library_stream_flag = new BooleanValue(bit_reader.getBit()); + this.data.video_sequence_start_code = new HexadecimalValue(bit_reader.readUint32()); + this.data.profile_id = new HexadecimalValue(bit_reader.readUint8(), AVS3profile); + this.data.level_id = new HexadecimalValue(bit_reader.readUint8(), AVS3level); + this.data.progressive_sequence = new BooleanValue(bit_reader.readBit()); + this.data.field_coded_sequence = new BooleanValue(bit_reader.readBit()); + this.data.library_stream_flag = new BooleanValue(bit_reader.readBit()); if (!this.data.library_stream_flag.value) { - this.data.library_picture_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.library_picture_enable_flag = new BooleanValue(bit_reader.readBit()); if (this.data.library_picture_enable_flag.value) - this.data.duplicate_sequence_number_flag = new BooleanValue(bit_reader.getBit()); + this.data.duplicate_sequence_number_flag = new BooleanValue(bit_reader.readBit()); } bit_reader.skipBit(); // marker_bit - this.data.horizontal_size = bit_reader.getBits(14); + this.data.horizontal_size = bit_reader.readBits(14); bit_reader.skipBit(); // marker_bit - this.data.vertical_size = bit_reader.getBits(14); - this.data.chroma_format = new BinaryValue(bit_reader.getBits(2), 2, AVS3chroma); - this.data.sample_precision = new BinaryValue(bit_reader.getBits(3), 3, AVS3precision); + this.data.vertical_size = bit_reader.readBits(14); + this.data.chroma_format = new BinaryValue(bit_reader.readBits(2), 2, AVS3chroma); + this.data.sample_precision = new BinaryValue(bit_reader.readBits(3), 3, AVS3precision); if (this.data.profile_id.value === MAIN_10 || this.data.profile_id.value === HIGH_10) - this.data.encoding_precision = new BinaryValue(bit_reader.getBits(3), 3, AVS3precision); + this.data.encoding_precision = new BinaryValue(bit_reader.readBits(3), 3, AVS3precision); bit_reader.skipBit(); // marker_bit - this.data.aspect_ratio = new BinaryValue(bit_reader.getBits(4), 4, AVS3aspectratio); - this.data.frame_rate_code = new BinaryValue(bit_reader.getBits(4), 4, AVS3framerate); + this.data.aspect_ratio = new BinaryValue(bit_reader.readBits(4), 4, AVS3aspectratio); + this.data.frame_rate_code = new BinaryValue(bit_reader.readBits(4), 4, AVS3framerate); bit_reader.skipBit(); // marker_bit - this.data.bit_rate_lower = bit_reader.getBits(18); + this.data.bit_rate_lower = bit_reader.readBits(18); bit_reader.skipBit(); // marker_bit - this.data.bit_rate_upper = bit_reader.getBits(12); - this.data.low_delay = bit_reader.getBit(); - this.data.temporal_id_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.bit_rate_upper = bit_reader.readBits(12); + this.data.low_delay = bit_reader.readBit(); + this.data.temporal_id_enable_flag = new BooleanValue(bit_reader.readBit()); bit_reader.skipBit(); // marker_bit - this.data.bbv_buffer_size = bit_reader.getBits(18); + this.data.bbv_buffer_size = bit_reader.readBits(18); bit_reader.skipBit(); // marker_bit - this.data.max_dpb_minus1 = bit_reader.getBits(4); - this.data.rpl1_index_exist_flag = new BooleanValue(bit_reader.getBit()); - this.data.rpl1_same_as_rpl0_flag = new BooleanValue(bit_reader.getBit()); + this.data.max_dpb_minus1 = bit_reader.readBits(4); + this.data.rpl1_index_exist_flag = new BooleanValue(bit_reader.readBit()); + this.data.rpl1_same_as_rpl0_flag = new BooleanValue(bit_reader.readBit()); bit_reader.skipBit(); // marker_bit const reference_picture_list = function ( @@ -367,24 +367,24 @@ class AVS3SequenceHeader extends AVS3data { ) { const this_set = new ReferencePictureSet(list, rpls); if (library_picture_enable_flag) - this_set.set_reference_to_library_enable_flag(bit_reader.getBit()); - const num_of_ref_pic = bit_reader.getUE(); + this_set.set_reference_to_library_enable_flag(bit_reader.readBit()); + const num_of_ref_pic = bit_reader.readUE(); for (let i = 0; i < num_of_ref_pic; i++) { const this_pic: ReferencePicture = {}; let LibraryIndexFlag = 0; if (this_set.get_reference_to_library_enable_flag()) - LibraryIndexFlag = this_pic.library_index_flag = bit_reader.getBit(); - if (LibraryIndexFlag !== 0) this_pic.referenced_library_picture_index = bit_reader.getUE(); + LibraryIndexFlag = this_pic.library_index_flag = bit_reader.readBit(); + if (LibraryIndexFlag !== 0) this_pic.referenced_library_picture_index = bit_reader.readUE(); else { - this_pic.abs_delta_doi = bit_reader.getUE(); - if (this_pic.abs_delta_doi > 0) this_pic.sign_delta_doi = bit_reader.getBit(); + this_pic.abs_delta_doi = bit_reader.readUE(); + if (this_pic.abs_delta_doi > 0) this_pic.sign_delta_doi = bit_reader.readBit(); } this_set.push(this_pic); } return this_set; }; - this.data.num_ref_pic_list_set0 = bit_reader.getUE(); + this.data.num_ref_pic_list_set0 = bit_reader.readUE(); this.data.rpl0 = new ReferencePictureList(0); for (let j = 0; j < this.data.num_ref_pic_list_set0; j++) this.data.rpl0.push( @@ -392,7 +392,7 @@ class AVS3SequenceHeader extends AVS3data { ); if (!this.data.rpl1_same_as_rpl0_flag) { - this.data.num_ref_pic_list_set1 = bit_reader.getUE(); + this.data.num_ref_pic_list_set1 = bit_reader.readUE(); this.data.rpl1 = new ReferencePictureList(1); for (let j = 0; j < this.data.num_ref_pic_list_set1; j++) this.data.rpl1.push( @@ -400,87 +400,87 @@ class AVS3SequenceHeader extends AVS3data { ); } - this.data.num_ref_default_active_minus1_0 = bit_reader.getUE(); - this.data.num_ref_default_active_minus1_1 = bit_reader.getUE(); - this.data.log2_lcu_size_minus2 = bit_reader.getBits(3); - this.data.log2_min_cu_size_minus2 = bit_reader.getBits(2); - this.data.log2_max_part_ratio_minus2 = bit_reader.getBits(2); - this.data.max_split_times_minus6 = bit_reader.getBits(3); - this.data.log2_min_qt_size_minus2 = bit_reader.getBits(3); - this.data.log2_max_bt_size_minus2 = bit_reader.getBits(3); - this.data.log2_max_eqt_size_minus3 = bit_reader.getBits(2); + this.data.num_ref_default_active_minus1_0 = bit_reader.readUE(); + this.data.num_ref_default_active_minus1_1 = bit_reader.readUE(); + this.data.log2_lcu_size_minus2 = bit_reader.readBits(3); + this.data.log2_min_cu_size_minus2 = bit_reader.readBits(2); + this.data.log2_max_part_ratio_minus2 = bit_reader.readBits(2); + this.data.max_split_times_minus6 = bit_reader.readBits(3); + this.data.log2_min_qt_size_minus2 = bit_reader.readBits(3); + this.data.log2_max_bt_size_minus2 = bit_reader.readBits(3); + this.data.log2_max_eqt_size_minus3 = bit_reader.readBits(2); bit_reader.skipBit(); // marker_bit - this.data.weight_quant_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.weight_quant_enable_flag = new BooleanValue(bit_reader.readBit()); if (this.data.weight_quant_enable_flag.value) { - this.data.load_seq_weight_quant_data_flag = new BooleanValue(bit_reader.getBit()); + this.data.load_seq_weight_quant_data_flag = new BooleanValue(bit_reader.readBit()); if (this.data.load_seq_weight_quant_data_flag.value) this.data.weight_quant_matrix = new WeightQuantMatrix(bit_reader); } - this.data.st_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.sao_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.alf_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.affine_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.smvd_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.ipcm_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.amvr_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.num_of_hmvp_cand = bit_reader.getBits(4); - this.data.umve_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.st_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.sao_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.alf_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.affine_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.smvd_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.ipcm_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.amvr_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.num_of_hmvp_cand = bit_reader.readBits(4); + this.data.umve_enable_flag = new BooleanValue(bit_reader.readBit()); if (this.data.num_of_hmvp_cand !== 0 && this.data.amvr_enable_flag.value) - this.data.emvr_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.intra_pf_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.tscpm_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.emvr_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.intra_pf_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.tscpm_enable_flag = new BooleanValue(bit_reader.readBit()); bit_reader.skipBit(); // marker_bit - this.data.dt_enable_flag = new BooleanValue(bit_reader.getBit()); - if (this.data.dt_enable_flag.value) this.data.log2_max_dt_size_minus4 = bit_reader.getBits(2); - this.data.pbt_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.dt_enable_flag = new BooleanValue(bit_reader.readBit()); + if (this.data.dt_enable_flag.value) this.data.log2_max_dt_size_minus4 = bit_reader.readBits(2); + this.data.pbt_enable_flag = new BooleanValue(bit_reader.readBit()); if (this.data.profile_id.value === MAIN_10 || this.data.profile_id.value === HIGH_10) { - this.data.pmc_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.iip_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.sawp_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.pmc_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.iip_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.sawp_enable_flag = new BooleanValue(bit_reader.readBit()); if (this.data.affine_enable_flag.value) - this.data.asr_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.awp_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.etmvp_mvap_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.dmvr_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.bio_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.bgc_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.inter_pf_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.inter_pfc_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.obmc_enable_flag = new BooleanValue(bit_reader.getBit()); - - this.data.sbt_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.ist_enable_flag = new BooleanValue(bit_reader.getBit()); - - this.data.esao_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.ccsao_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.asr_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.awp_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.etmvp_mvap_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.dmvr_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.bio_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.bgc_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.inter_pf_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.inter_pfc_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.obmc_enable_flag = new BooleanValue(bit_reader.readBit()); + + this.data.sbt_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.ist_enable_flag = new BooleanValue(bit_reader.readBit()); + + this.data.esao_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.ccsao_enable_flag = new BooleanValue(bit_reader.readBit()); if (this.data.alf_enable_flag.value) - this.data.ealf_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.ibc_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.ealf_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.ibc_enable_flag = new BooleanValue(bit_reader.readBit()); bit_reader.skipBit(); // marker_bit - this.data.isc_enable_flag = new BooleanValue(bit_reader.getBit()); + this.data.isc_enable_flag = new BooleanValue(bit_reader.readBit()); if (this.data.ibc_enable_flag.value || this.data.isc_enable_flag.value) - this.data.num_of_intra_hmvp_cand = bit_reader.getBits(4); - this.data.fimc_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.nn_tools_set_hook = bit_reader.getBits(8); + this.data.num_of_intra_hmvp_cand = bit_reader.readBits(4); + this.data.fimc_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.nn_tools_set_hook = bit_reader.readBits(8); if (this.data.nn_tools_set_hook & 0x01) - this.data.num_of_nn_filter_minus1 = bit_reader.getUE(); + this.data.num_of_nn_filter_minus1 = bit_reader.readUE(); bit_reader.skipBit(); // marker_bit } - if (this.data.low_delay === 0) this.data.output_reorder_delay = bit_reader.getBits(5); - this.data.cross_patch_loop_filter_enable_flag = new BooleanValue(bit_reader.getBit()); - this.data.ref_colocated_patch_flag = new BooleanValue(bit_reader.getBit()); - this.data.stable_patch_flag = new BooleanValue(bit_reader.getBit()); + if (this.data.low_delay === 0) this.data.output_reorder_delay = bit_reader.readBits(5); + this.data.cross_patch_loop_filter_enable_flag = new BooleanValue(bit_reader.readBit()); + this.data.ref_colocated_patch_flag = new BooleanValue(bit_reader.readBit()); + this.data.stable_patch_flag = new BooleanValue(bit_reader.readBit()); if (this.data.stable_patch_flag.value) { - this.data.uniform_patch_flag = new BooleanValue(bit_reader.getBit()); + this.data.uniform_patch_flag = new BooleanValue(bit_reader.readBit()); if (this.data.uniform_patch_flag.value) { bit_reader.skipBit(); // marker_bit - this.data.patch_width_minus1 = bit_reader.getUE(); - this.data.patch_height_minus1 = bit_reader.getUE(); + this.data.patch_width_minus1 = bit_reader.readUE(); + this.data.patch_height_minus1 = bit_reader.readUE(); } } bit_reader.skipBits(2); // reserved bits diff --git a/src/boxes/dca3.ts b/src/boxes/dca3.ts index ce19383dc..68d2c2332 100644 --- a/src/boxes/dca3.ts +++ b/src/boxes/dca3.ts @@ -104,34 +104,34 @@ class AVS3GAConfig extends AVS3data { } deserialise(bit_reader: BitStream) { this.data.sampling_frequency_index = new DescribedValue( - bit_reader.getBits(4), + bit_reader.readBits(4), AVS3Asampling_frequency, ); - this.data.nn_type = new DescribedValue(bit_reader.getBits(3), AVS3Anntype); + this.data.nn_type = new DescribedValue(bit_reader.readBits(3), AVS3Anntype); bit_reader.skipBits(1); - this.data.content_type = bit_reader.getBits(4); + this.data.content_type = bit_reader.readBits(4); if (this.data.content_type === CHANNEL_BASED) { this.data.channel_number_index = new DescribedValue( - bit_reader.getBits(7), + bit_reader.readBits(7), AVS3Achannel_number, ); bit_reader.skipBits(1); } else if (this.data.content_type === OBJECT_BASED) { - this.data.number_objects = bit_reader.getBits(7); + this.data.number_objects = bit_reader.readBits(7); bit_reader.skipBits(1); } else if (this.data.content_type === CHANNEL_AND_OBJECT) { this.data.channel_number_index = new DescribedValue( - bit_reader.getBits(7), + bit_reader.readBits(7), AVS3Achannel_number, ); bit_reader.skipBits(1); - this.data.number_objects = bit_reader.getBits(7); + this.data.number_objects = bit_reader.readBits(7); bit_reader.skipBits(1); } else if (this.data.content_type === HOA) { - this.data.hoa_order = bit_reader.getBits(4); + this.data.hoa_order = bit_reader.readBits(4); } - this.data.total_bitrate = bit_reader.getUint16(); - this.data.resolution = new DescribedValue(bit_reader.getBits(2), AVS3Aresolution); + this.data.total_bitrate = bit_reader.readUint16(); + this.data.resolution = new DescribedValue(bit_reader.readBits(2), AVS3Aresolution); } toString(): string { return super.toString(this.data); @@ -157,19 +157,19 @@ class AVS3GHConfig extends AVS3data { this.deserialise(bit_reader); } deserialise(bit_reader: BitStream) { - this.data.sampling_frequency_index = bit_reader.getBits(4); - this.data.anc_data_index = bit_reader.getBit(); - this.data.coding_profile = new DescribedValue(bit_reader.getBits(3), AVS3Acodingprofile); - this.data.bitstream_type = bit_reader.getBits(1); - this.data.channel_number_index = bit_reader.getBits(7); - this.data.bitrate_index = bit_reader.getBits(4); - this.data.raw_frame_length = bit_reader.getUint16(); - this.data.resolution = new DescribedValue(bit_reader.getBits(2), AVS3Aresolution); - const addition_info_length = bit_reader.getUint16(); + this.data.sampling_frequency_index = bit_reader.readBits(4); + this.data.anc_data_index = bit_reader.readBit(); + this.data.coding_profile = new DescribedValue(bit_reader.readBits(3), AVS3Acodingprofile); + this.data.bitstream_type = bit_reader.readBits(1); + this.data.channel_number_index = bit_reader.readBits(7); + this.data.bitrate_index = bit_reader.readBits(4); + this.data.raw_frame_length = bit_reader.readUint16(); + this.data.resolution = new DescribedValue(bit_reader.readBits(2), AVS3Aresolution); + const addition_info_length = bit_reader.readUint16(); if (addition_info_length > 0) { this.data.addition_info = []; for (let i = 0; i < addition_info_length; i++) - this.data.addition_info.push(bit_reader.getUint8()); + this.data.addition_info.push(bit_reader.readUint8()); } } toString(): string { @@ -194,18 +194,18 @@ class AVS3LLConfig extends AVS3data { this.deserialise(bit_reader); } deserialise(bit_reader: BitStream) { - this.data.sampling_frequency_index = bit_reader.getBits(4); + this.data.sampling_frequency_index = bit_reader.readBits(4); if (this.data.sampling_frequency_index === 0xf) - this.data.sampling_frequency = bit_reader.getUint24(); - this.data.anc_data_index = bit_reader.getBit(); - this.data.coding_profile = new DescribedValue(bit_reader.getBits(3), AVS3Acodingprofile); - this.data.channel_number = bit_reader.getUint8(); - this.data.resolution = new DescribedValue(bit_reader.getBits(2), AVS3Aresolution); - const addition_info_length = bit_reader.getUint16(); + this.data.sampling_frequency = bit_reader.readUint24(); + this.data.anc_data_index = bit_reader.readBit(); + this.data.coding_profile = new DescribedValue(bit_reader.readBits(3), AVS3Acodingprofile); + this.data.channel_number = bit_reader.readUint8(); + this.data.resolution = new DescribedValue(bit_reader.readBits(2), AVS3Aresolution); + const addition_info_length = bit_reader.readUint16(); if (addition_info_length > 0) { this.data.addition_info = []; for (let i = 0; i < addition_info_length; i++) - this.data.addition_info.push(bit_reader.getUint8()); + this.data.addition_info.push(bit_reader.readUint8()); } bit_reader.skipBits(2); // reserved } @@ -226,7 +226,7 @@ export class dca3Box extends Box { parse(stream: MultiBufferStream) { const bit_reader = new BitStream(stream); bit_reader.appendUint8(this.size - this.hdr_size); - this.audio_codec_id = new DescribedValue(bit_reader.getBits(4), AVS3Acodec); + this.audio_codec_id = new DescribedValue(bit_reader.readBits(4), AVS3Acodec); switch (this.audio_codec_id.value) { case FULL_RATE_CODING: From 41402f856160a24d1a8f14ba62bad57be7129ffd Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Wed, 15 Apr 2026 17:59:46 +0100 Subject: [PATCH 17/18] chore: make the State class more meaningful --- src/BitStream.ts | 86 ++++++++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 35 deletions(-) diff --git a/src/BitStream.ts b/src/BitStream.ts index d8c959a24..8ebbdd3cf 100644 --- a/src/BitStream.ts +++ b/src/BitStream.ts @@ -23,6 +23,22 @@ class State { this.rbyte = this.rbit = this.wbyte = this.wbit = this.end = 0; this.read_error = this.write_error = false; } + + readBitOffset(): number { + return 8 * this.rbyte + this.rbit; + } + readByteOffset() { + return this.rbyte; + } + writeBitOffset(): number { + return 8 * this.wbyte + this.wbit; + } + writeByteOffset() { + return this.wbyte; + } + aligned() { + return this.read_error && this.rbit !== 0; + } } export class BitStream { @@ -85,7 +101,7 @@ export class BitStream { return this.readBit() !== 0; } - private _rdb(bytes: number): number { + private rdb = function (bytes: number): number { let i: number, res: number; // eslint-disable-next-line no-loss-of-precision const ff = 0xffffffffffffffff; @@ -125,70 +141,70 @@ export class BitStream { } } return ff; // we should never get here!! - } + }; readUint8() { - return this._rdb(1); + return this.rdb(1); } readUint16() { - return this.big_endian ? this._GetUInt16BE(this._rdb(2)) : this._GetUInt16LE(this._rdb(2)); + return this.big_endian ? this.GetUInt16BE(this.rdb(2)) : this.GetUInt16LE(this.rdb(2)); } - private _ByteSwap16 = function (x: number): number { + private ByteSwap16 = function (x: number): number { return (x << 8) | (x >> 8); }; - private _CondByteSwap16BE = function (val: number): number { + private CondByteSwap16BE = function (val: number): number { return this.OSisLittleEndian() ? this._ByteSwap16(val) : val; }; - private _CondByteSwap16LE = function (val: number) { + private CondByteSwap16LE = function (val: number) { return this.OSisLittleEndian() ? val : this._ByteSwap16(val); }; - private _GetUInt16BE = function (val: number) { + private GetUInt16BE = function (val: number) { return this._CondByteSwap16BE(val); }; - private _GetUInt16LE = function (val: number) { - return this._CondByteSwap16LE(val); + private GetUInt16LE = function (val: number) { + return this.CondByteSwap16LE(val); }; readUint24() { - return this.big_endian ? this._GetUInt24BE(this._rdb(3)) : this._GetUInt24LE(this._rdb(3)); + return this.big_endian ? this.GetUInt24BE(this.rdb(3)) : this.GetUInt24LE(this.rdb(3)); } - private _ByteSwap24 = function (x: number) { + private ByteSwap24 = function (x: number) { return ((x & 0xff0000) >> 16) | (x & 0xff00) | (x & (0xff << 16)); }; - private _CondByteSwap24BE = function (val: number) { - return this.OSisLittleEndian() ? this._ByteSwap24(val) : val; + private CondByteSwap24BE = function (val: number) { + return this.OSisLittleEndian() ? this.ByteSwap24(val) : val; }; - private _CondByteSwap24LE = function (val: number) { + private CondByteSwap24LE = function (val: number) { return this.OSisLittleEndian() ? val : this._ByteSwap24(val); }; - private _GetUInt24BE = function (val: number) { + private GetUInt24BE = function (val: number) { return this._CondByteSwap24BE(val); }; - private _GetUInt24LE = function (val: number) { + private GetUInt24LE = function (val: number) { return this._CondByteSwap24LE(val); }; readUint32() { - return this.big_endian ? this._GetUInt32BE(this._rdb(4)) : this._GetUInt32LE(this._rdb(4)); + return this.big_endian ? this.GetUInt32BE(this.rdb(4)) : this._GetUInt32LE(this.rdb(4)); } - private _ByteSwap32(x: number) { + private _ByteSwap32 = function (x: number) { return (x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | (x >> 24); - } - private _CondByteSwap32BE(val: number) { + }; + private CondByteSwap32BE = function (val: number) { return this.OSisLittleEndian() ? this._ByteSwap32(val) : val; - } - private _CondByteSwap32LE(val: number) { + }; + private CondByteSwap32LE = function (val: number) { return this.OSisLittleEndian() ? val : this._ByteSwap32(val); - } - private _GetUInt32BE(val: number) { - return this._CondByteSwap32BE(val); - } - private _GetUInt32LE(val: number) { - return this._CondByteSwap32LE(val); - } + }; + private GetUInt32BE = function (val: number) { + return this.CondByteSwap32BE(val); + }; + private _GetUInt32LE = function (val: number) { + return this.CondByteSwap32LE(val); + }; readBits(bits: number): number { // No read if read error is already set or not enough bits to read. @@ -280,7 +296,7 @@ export class BitStream { } byte_alignment(): void { - while (!this.state.read_error && this.state.rbit !== 0) this.skipBit(); + while (!this.state.aligned) this.skipBit(); } private OSisLittleEndian(): boolean { @@ -288,16 +304,16 @@ export class BitStream { } currentReadByteOffset(): number { - return this.state.rbyte; + return this.state.readByteOffset(); } currentReadBitOffset(): number { - return 8 * this.state.rbyte + this.state.rbit; + return this.state.readBitOffset(); } currentWriteByteOffset(): number { - return this.state.wbyte; + return this.state.writeByteOffset(); } currentWriteBitOffset(): number { - return 8 * this.state.wbyte + this.state.wbit; + return this.state.writeBitOffset(); } bitsRemaining(): number { return this.currentWriteBitOffset() - this.currentReadBitOffset(); From 436328aecf85cd242dfdfa2efd2a8ec22d156e6a Mon Sep 17 00:00:00 2001 From: Paul Higgs Date: Thu, 16 Apr 2026 06:59:58 +0100 Subject: [PATCH 18/18] chore: optimise private methods and align public methods with DataStream --- src/BitStream.ts | 78 +++++++++++++++++++++--------------------------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/src/BitStream.ts b/src/BitStream.ts index 8ebbdd3cf..eab260a42 100644 --- a/src/BitStream.ts +++ b/src/BitStream.ts @@ -45,11 +45,12 @@ export class BitStream { private buffer: Array; private stream: MultiBufferStream | DataStream; private state: State; - private big_endian = true; // results are returned Big Endian + private endianness: Endianness; - constructor(stream: MultiBufferStream | DataStream) { + constructor(stream: MultiBufferStream | DataStream, endianness?: Endianness) { this.state = new State(); this.stream = stream; + this.endianness = endianness ? endianness : Endianness.BIG_ENDIAN; } appendUint8(count = 1): void { @@ -72,7 +73,8 @@ export class BitStream { return 0; } const bit: number = - (this.buffer[this.state.rbyte] >> (this.big_endian ? 7 - this.state.rbit : this.state.rbit)) & + (this.buffer[this.state.rbyte] >> + (this.endianness === Endianness.BIG_ENDIAN ? 7 - this.state.rbit : this.state.rbit)) & 0x01; if (++this.state.rbit > 7) { this.state.rbyte++; @@ -88,7 +90,8 @@ export class BitStream { return 0; } const bit: number = - (this.buffer[this.state.rbyte] >> (this.big_endian ? 7 - this.state.rbit : this.state.rbit)) & + (this.buffer[this.state.rbyte] >> + (this.endianness === Endianness.BIG_ENDIAN ? 7 - this.state.rbit : this.state.rbit)) & 0x01; return bit; } @@ -125,7 +128,7 @@ export class BitStream { return ff; } else { for (res = 0, i = 0; i < bytes; i++) { - if (this.big_endian) + if (this.endianness === Endianness.BIG_ENDIAN) res = (res << 8) + ((this.buffer[this.state.rbyte] << this.state.rbit) | @@ -147,63 +150,50 @@ export class BitStream { return this.rdb(1); } - readUint16() { - return this.big_endian ? this.GetUInt16BE(this.rdb(2)) : this.GetUInt16LE(this.rdb(2)); + readUint16(endianness?: Endianness) { + return (endianness ?? this.endianness) === Endianness.BIG_ENDIAN + ? this.GetUInt16BE(this.rdb(2)) + : this.GetUInt16LE(this.rdb(2)); } - private ByteSwap16 = function (x: number): number { - return (x << 8) | (x >> 8); - }; - private CondByteSwap16BE = function (val: number): number { - return this.OSisLittleEndian() ? this._ByteSwap16(val) : val; - }; - private CondByteSwap16LE = function (val: number) { - return this.OSisLittleEndian() ? val : this._ByteSwap16(val); - }; private GetUInt16BE = function (val: number) { - return this._CondByteSwap16BE(val); + return this.OSisLittleEndian() ? this.ByteSwap16(val) : val; }; private GetUInt16LE = function (val: number) { - return this.CondByteSwap16LE(val); + return this.OSisLittleEndian() ? val : this.ByteSwap16(val); + }; + private ByteSwap16 = function (x: number): number { + return (x << 8) | (x >> 8); }; - readUint24() { - return this.big_endian ? this.GetUInt24BE(this.rdb(3)) : this.GetUInt24LE(this.rdb(3)); + readUint24(endianness?: Endianness) { + return (endianness ?? this.endianness) === Endianness.BIG_ENDIAN + ? this.GetUInt24BE(this.rdb(3)) + : this.GetUInt24LE(this.rdb(3)); } private ByteSwap24 = function (x: number) { return ((x & 0xff0000) >> 16) | (x & 0xff00) | (x & (0xff << 16)); }; - private CondByteSwap24BE = function (val: number) { - return this.OSisLittleEndian() ? this.ByteSwap24(val) : val; - }; - private CondByteSwap24LE = function (val: number) { - return this.OSisLittleEndian() ? val : this._ByteSwap24(val); - }; private GetUInt24BE = function (val: number) { - return this._CondByteSwap24BE(val); + return this.OSisLittleEndian() ? this.ByteSwap24(val) : val; }; private GetUInt24LE = function (val: number) { - return this._CondByteSwap24LE(val); + return this.OSisLittleEndian() ? val : this.ByteSwap24(val); }; - readUint32() { - return this.big_endian ? this.GetUInt32BE(this.rdb(4)) : this._GetUInt32LE(this.rdb(4)); + readUint32(endianness?: Endianness) { + return (endianness ?? this.endianness) === Endianness.BIG_ENDIAN + ? this.GetUInt32BE(this.rdb(4)) + : this.GetUInt32LE(this.rdb(4)); } - - private _ByteSwap32 = function (x: number) { + private ByteSwap32 = function (x: number) { return (x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | (x >> 24); }; - private CondByteSwap32BE = function (val: number) { - return this.OSisLittleEndian() ? this._ByteSwap32(val) : val; - }; - private CondByteSwap32LE = function (val: number) { - return this.OSisLittleEndian() ? val : this._ByteSwap32(val); - }; private GetUInt32BE = function (val: number) { - return this.CondByteSwap32BE(val); + return this.OSisLittleEndian() ? this.ByteSwap32(val) : val; }; - private _GetUInt32LE = function (val: number) { - return this.CondByteSwap32LE(val); + private GetUInt32LE = function (val: number) { + return this.OSisLittleEndian() ? val : this.ByteSwap32(val); }; readBits(bits: number): number { @@ -216,7 +206,7 @@ export class BitStream { return 0; } let val = 0; - if (this.big_endian) { + if (this.endianness === Endianness.BIG_ENDIAN) { // Read leading bits up to byte boundary while (bits > 0 && this.state.rbit !== 0) { val = (val << 1) | this.readBit(); @@ -299,9 +289,9 @@ export class BitStream { while (!this.state.aligned) this.skipBit(); } - private OSisLittleEndian(): boolean { + private OSisLittleEndian = function (): boolean { return this.stream.endianness === Endianness.LITTLE_ENDIAN; - } + }; currentReadByteOffset(): number { return this.state.readByteOffset();