-
Notifications
You must be signed in to change notification settings - Fork 387
Add AVS3 audio and video #474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
paulhiggs
wants to merge
22
commits into
gpac:main
Choose a base branch
from
paulhiggs:add-AVS
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 12 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
4232f9b
build: auto indent with 2 spaces in VS Code
paulhiggs a79b137
feat: allow HTML formatted elements
paulhiggs 5e521fa
feat: Bitstream reader supporting non-aligned values
paulhiggs 9477c35
feat: add parsing of AVS3 audio configuration
paulhiggs 62e9384
feat: export items to be reused in AVS3 Video parsing
paulhiggs eec8081
feat: add parsing for AVS3 video using some functions in common with …
paulhiggs 3709f90
Merge branch 'gpac:main' into add-AVS
paulhiggs f7c3a02
fix: TypeScript oriented updates and improvements
paulhiggs 6918c6e
chore: pull upstream developments and adapt AVS3 parsing accordingly
paulhiggs 1f56b50
chore: leverage some more TypeScript features
paulhiggs db88911
chore: more Typescript aspects for AVS3 audio too
paulhiggs 6e658d5
chore: fix getter call
paulhiggs cda4d10
fix: rely on projects prettier capabilities for indentation
paulhiggs aefb5bd
fix: remove HTML based formatting of output value; not as pretty but …
paulhiggs 165e94a
fix: ensure all objects are correctly formatted
paulhiggs 5dadbba
Merge branch 'gpac:main' into add-AVS
paulhiggs 54264b7
Merge branch 'gpac:main' into add-AVS
paulhiggs 81db94e
chore: use MultiBifferStream rather than MP4BoxStream as input
paulhiggs c03e73d
fix: use MultiBufferStream | DataStream type as parameter to BitStrea…
paulhiggs aa712b6
chore: update accessor methods to match DataStream
paulhiggs 41402f8
chore: make the State class more meaningful
paulhiggs 436328a
chore: optimise private methods and align public methods with DataStream
paulhiggs File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
paulhiggs marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,333 @@ | ||
| /* | ||
| * 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 | ||
| * TODO: add writing support | ||
| */ | ||
|
|
||
| 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<number>; | ||
| private _state: State; | ||
| private _big_endian = true; // results are returned Big Endian | ||
|
paulhiggs marked this conversation as resolved.
Outdated
|
||
|
|
||
| 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._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); | ||
| 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 { | ||
| //! 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: 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); | ||
| }; | ||
| private _GetUInt16LE = function (val: number) { | ||
| return this._CondByteSwap16LE(val); | ||
| }; | ||
|
|
||
| getUint24() { | ||
| 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; | ||
| }; | ||
| private _CondByteSwap24LE = function (val: number) { | ||
| return this._OSisLittleEndian() ? val : this._ByteSwap24(val); | ||
| }; | ||
| private _GetUInt24BE = function (val: number) { | ||
| return this._CondByteSwap24BE(val); | ||
| }; | ||
| private _GetUInt24LE = function (val: number) { | ||
| return this._CondByteSwap24LE(val); | ||
| }; | ||
|
|
||
| getUint32() { | ||
| 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; | ||
| } | ||
| private _CondByteSwap32LE(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); | ||
| } | ||
|
|
||
| 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(): void { | ||
| while (!this._state.read_error && this._state.rbit !== 0) this.skipBit(); | ||
| } | ||
|
|
||
| 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; | ||
| } | ||
|
|
||
| currentReadByteOffset(): number { | ||
| return this._state.rbyte; | ||
| } | ||
| currentReadBitOffset(): number { | ||
| return 8 * this._state.rbyte + this._state.rbit; | ||
| } | ||
| currentWriteByteOffset(): number { | ||
| return this._state.wbyte; | ||
| } | ||
| 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); | ||
| } | ||
| */ | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.