isobmff-inspector
Version:
Simple ISOBMFF parser, compatible with JavaScript and Node.JS
1,412 lines (1,403 loc) • 157 kB
JavaScript
(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() :
typeof define === "function" && define.amd ? define(factory) :
(global = typeof globalThis !== "undefined" ? globalThis : global || self, global.inspectISOBMFF = factory());
})(this, function () {
"use strict";
"use strict";
var __inspectISOBMFFBundle = (() => {
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
var __typeError = (msg) => {
throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
var __await = function(promise, isYieldStar) {
this[0] = promise;
this[1] = isYieldStar;
};
var __asyncGenerator = (__this, __arguments, generator) => {
var resume = (k, v, yes, no) => {
try {
var x = generator[k](v), isAwait = (v = x.value) instanceof __await, done = x.done;
Promise.resolve(isAwait ? v[0] : v).then((y) => isAwait ? resume(k === "return" ? k : "next", v[1] ? { done: y.done, value: y.value } : y, yes, no) : yes({ value: y, done })).catch((e) => resume("throw", e, yes, no));
} catch (e) {
no(e);
}
}, method = (k, call, wait, clear) => it[k] = (x) => (call = new Promise((yes, no, run) => (run = () => resume(k, x, yes, no), q ? q.then(run) : run())), clear = () => q === wait && (q = 0), q = wait = call.then(clear, clear), call), q, it = {};
return generator = generator.apply(__this, __arguments), it[__knownSymbol("asyncIterator")] = () => it, method("next"), method("throw"), method("return"), it;
};
var __yieldStar = (value) => {
var obj = value[__knownSymbol("asyncIterator")], isAwait = false, method, it = {};
if (obj == null) {
obj = value[__knownSymbol("iterator")]();
method = (k) => it[k] = (x) => obj[k](x);
} else {
obj = obj.call(value);
method = (k) => it[k] = (v) => {
if (isAwait) {
isAwait = false;
if (k === "throw") throw v;
return v;
}
isAwait = true;
return {
done: false,
value: new __await(new Promise((resolve) => {
var x = obj[k](v);
if (!(x instanceof Object)) __typeError("Object expected");
resolve(x);
}), 1)
};
};
}
return it[__knownSymbol("iterator")] = () => it, method("next"), "throw" in obj ? method("throw") : it.throw = (x) => {
throw x;
}, "return" in obj && method("return"), it;
};
var __forAwait = (obj, it, method) => (it = obj[__knownSymbol("asyncIterator")]) ? it.call(obj) : (obj = obj[__knownSymbol("iterator")](), it = {}, method = (key, fn) => (fn = obj[key]) && (it[key] = (arg) => new Promise((yes, no, done) => (arg = fn.call(obj, arg), done = arg.done, Promise.resolve(arg.value).then((value) => yes({ value, done }), no)))), method("next"), method("return"), it);
// src/main.js
var main_exports = {};
__export(main_exports, {
default: () => main_default,
parse: () => parse,
parseBuffer: () => parseBuffer,
parseEvents: () => parseEvents
});
// src/utils/bytes.js
var textDecoder = new TextDecoder();
function parseBoxType(bytes, offset) {
return String.fromCharCode(
bytes[offset],
bytes[offset + 1],
bytes[offset + 2],
bytes[offset + 3]
);
}
function be2toi(bytes, off) {
return (bytes[0 + off] << 8) + bytes[1 + off];
}
function be3toi(bytes, off) {
return bytes[0 + off] * 65536 + bytes[1 + off] * 256 + bytes[2 + off];
}
function be4toi(bytes, off) {
return bytes[0 + off] * 16777216 + bytes[1 + off] * 65536 + bytes[2 + off] * 256 + bytes[3 + off];
}
function be5toi(bytes, off) {
return bytes[0 + off] * 4294967296 + bytes[1 + off] * 16777216 + bytes[2 + off] * 65536 + bytes[3 + off] * 256 + bytes[4 + off];
}
function be8toi(bytes, off) {
return (bytes[0 + off] * 16777216 + bytes[1 + off] * 65536 + bytes[2 + off] * 256 + bytes[3 + off]) * 4294967296 + bytes[4 + off] * 16777216 + bytes[5 + off] * 65536 + bytes[6 + off] * 256 + bytes[7 + off];
}
function bytesToHex(uint8arr, off, nbBytes) {
if (!uint8arr) {
return "";
}
const arr = uint8arr.slice(off, nbBytes + off);
let hexStr = "";
for (let i = 0; i < arr.length; i++) {
let hex = (arr[i] & 255).toString(16);
hex = hex.length === 1 ? `0${hex}` : hex;
hexStr += hex;
}
return hexStr.toUpperCase();
}
function utf8ToStr(uint8arr, off = 0, nbBytes) {
if (!uint8arr) {
return "";
}
if (nbBytes === void 0) {
if (off === 0) {
return textDecoder.decode(uint8arr);
}
return textDecoder.decode(uint8arr.slice(off));
}
const arr = uint8arr.slice(off, nbBytes + off);
return textDecoder.decode(arr);
}
function viewToUint8Array(view) {
return new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
}
function isBufferSource(value) {
return value instanceof ArrayBuffer || ArrayBuffer.isView(value);
}
function bufferSourceToUint8Array(arr) {
if (arr instanceof Uint8Array) {
return arr;
}
if (arr instanceof ArrayBuffer) {
return new Uint8Array(arr);
}
return viewToUint8Array(arr);
}
function byteChunkToUint8Array(chunk) {
if (chunk instanceof Uint8Array) {
return chunk;
}
if (chunk instanceof ArrayBuffer) {
return new Uint8Array(chunk);
}
if (ArrayBuffer.isView(chunk)) {
return viewToUint8Array(chunk);
}
throw new Error(
"Progressive ISOBMFF inputs must yield ArrayBuffer or TypedArray chunks."
);
}
function asyncByteIterable(iterable) {
return {
[Symbol.asyncIterator]() {
return __asyncGenerator(this, null, function* () {
try {
for (var iter = __forAwait(iterable), more, temp, error; more = !(temp = yield new __await(iter.next())).done; more = false) {
const chunk = temp.value;
yield byteChunkToUint8Array(chunk);
}
} catch (temp) {
error = [temp];
} finally {
try {
more && (temp = iter.return) && (yield new __await(temp.call(iter)));
} finally {
if (error)
throw error[0];
}
}
});
}
};
}
function getProgressiveSource(input) {
if (input === null || input === void 0) {
return void 0;
}
if (typeof input === "object" && "body" in input) {
const body = (
/** @type {{ body?: unknown }} */
input.body
);
if (body !== null && body !== void 0) {
return getProgressiveSource(body);
}
}
if (typeof input === "object" && "getReader" in input && typeof input.getReader === "function") {
return {
[Symbol.asyncIterator]() {
return __asyncGenerator(this, null, function* () {
const reader = (
/** @type {ReadableStream} */
input.getReader()
);
try {
while (true) {
const { done, value } = yield new __await(reader.read());
if (done) {
break;
}
yield byteChunkToUint8Array(value);
}
} finally {
reader.releaseLock();
}
});
}
};
}
if (typeof input === "object" && Symbol.asyncIterator in input && typeof input[Symbol.asyncIterator] === "function") {
return asyncByteIterable(
/** @type {AsyncIterable<unknown>} */
input
);
}
if (typeof input === "object" && Symbol.iterator in input && typeof input[Symbol.iterator] === "function") {
return asyncByteIterable(
/** @type {Iterable<unknown>} */
input
);
}
if (typeof input === "object" && "stream" in input && typeof input.stream === "function") {
return getProgressiveSource(input.stream());
}
if (typeof input === "object" && "arrayBuffer" in input && typeof input.arrayBuffer === "function") {
const arrayBuffer = (
/** @type {{ arrayBuffer: () => Promise<ArrayBuffer> }} */
input.arrayBuffer
);
return asyncByteIterable({
[Symbol.asyncIterator]() {
return __asyncGenerator(this, null, function* () {
yield yield new __await(arrayBuffer.call(input));
});
}
});
}
return void 0;
}
// src/utils/ProgressiveByteReader.js
var ProgressiveByteReader = class {
/**
* @param {AsyncIterator<Uint8Array>} iterator
*/
constructor(iterator) {
this._iterator = iterator;
this._buffers = [];
this._bufferedLength = 0;
this._done = false;
}
/**
* @param {number} nbBytes
* @returns {Promise<void>}
*/
async ensure(nbBytes) {
while (!this._done && this._bufferedLength < nbBytes) {
const next = await this._iterator.next();
if (next.done) {
this._done = true;
break;
}
if (next.value.length > 0) {
this._buffers.push(next.value);
this._bufferedLength += next.value.length;
}
}
}
/**
* @returns {number}
*/
getBufferedLength() {
return this._bufferedLength;
}
/**
* @returns {boolean}
*/
isDone() {
return this._done && this._bufferedLength === 0;
}
/**
* @param {number} nbBytes
* @returns {Uint8Array}
*/
takeAvailable(nbBytes) {
const size = Math.min(nbBytes, this._bufferedLength);
const result = new Uint8Array(size);
let resultOffset = 0;
while (resultOffset < size) {
const buffer = this._buffers[0];
const copiedLength = Math.min(buffer.length, size - resultOffset);
result.set(buffer.subarray(0, copiedLength), resultOffset);
resultOffset += copiedLength;
if (copiedLength === buffer.length) {
this._buffers.shift();
} else {
this._buffers[0] = buffer.subarray(copiedLength);
}
this._bufferedLength -= copiedLength;
}
return result;
}
/**
* @param {number} nbBytes
* @returns {Promise<Uint8Array>}
*/
async read(nbBytes) {
await this.ensure(nbBytes);
return this.takeAvailable(nbBytes);
}
/**
* @param {number} nbBytes
* @param {(chunk: Uint8Array) => void | Promise<void>} onChunk
* @returns {Promise<Uint8Array>}
*/
async readWithCallback(nbBytes, onChunk) {
return this._readConsumed(nbBytes, onChunk);
}
/**
* @param {number} nbBytes
* @returns {Promise<number>}
*/
async skip(nbBytes) {
return this._skipConsumed(nbBytes);
}
/**
* @param {number} nbBytes
* @param {(chunk: Uint8Array) => void | Promise<void>} onChunk
* @returns {Promise<number>}
*/
async skipWithCallback(nbBytes, onChunk) {
return this._skipConsumed(nbBytes, onChunk);
}
/**
* @returns {Promise<number>}
*/
async skipUntilEnd() {
return this._skipConsumed(void 0);
}
/**
* @param {(chunk: Uint8Array) => void | Promise<void>} onChunk
* @returns {Promise<number>}
*/
async skipUntilEndWithCallback(onChunk) {
return this._skipConsumed(void 0, onChunk);
}
/**
* @returns {Promise<Uint8Array>}
*/
async readUntilEnd() {
return this._readConsumed(void 0);
}
/**
* @param {(chunk: Uint8Array) => void | Promise<void>} onChunk
* @returns {Promise<Uint8Array>}
*/
async readUntilEndWithCallback(onChunk) {
return this._readConsumed(void 0, onChunk);
}
/**
* @param {number | undefined} nbBytes
* @param {((chunk: Uint8Array) => void | Promise<void>)=} onChunk
* @returns {Promise<number>}
*/
async _skipConsumed(nbBytes, onChunk) {
return this._consume(nbBytes, onChunk, false);
}
/**
* @param {number | undefined} nbBytes
* @param {((chunk: Uint8Array) => void | Promise<void>)=} onChunk
* @returns {Promise<Uint8Array>}
*/
async _readConsumed(nbBytes, onChunk) {
return this._consume(nbBytes, onChunk, true);
}
/**
* @param {number | undefined} nbBytes
* @param {((chunk: Uint8Array) => void | Promise<void>) | undefined} onChunk
* @param {boolean} collect
* @returns {Promise<any>}
*/
async _consume(nbBytes, onChunk, collect) {
let remaining = nbBytes;
let consumed = 0;
const chunks = [];
while (remaining === void 0 || remaining > 0) {
await this.ensure(1);
if (this._bufferedLength === 0) {
break;
}
const chunkLength = remaining === void 0 ? this._bufferedLength : Math.min(remaining, this._bufferedLength);
const chunk = this.takeAvailable(chunkLength);
consumed += chunk.length;
if (remaining !== void 0) {
remaining -= chunk.length;
}
if (collect) {
chunks.push(chunk);
}
await (onChunk == null ? void 0 : onChunk(chunk));
}
if (!collect) {
return consumed;
}
const result = new Uint8Array(consumed);
let offset = 0;
for (const chunk of chunks) {
result.set(chunk, offset);
offset += chunk.length;
}
return result;
}
};
// src/fields.js
var MAC_EPOCH_TO_UNIX_EPOCH_SECONDS = 2082844800;
function decodeFixedPoint(value, fractionalBits) {
return value / 2 ** fractionalBits;
}
function toSignedInt(value, bits) {
const maxUnsigned = 2 ** bits;
const signedBoundary = 2 ** (bits - 1);
return value >= signedBoundary ? value - maxUnsigned : value;
}
function decodeSignedFixedPoint(value, bits, fractionalBits) {
return decodeFixedPoint(toSignedInt(value, bits), fractionalBits);
}
function bytesField(value, offset, nbBytes) {
return { kind: "bytes", value: bytesToHex(value, offset, nbBytes) };
}
function fixedPointField(raw, bits, fractionalBits, format) {
return {
kind: "fixed-point",
value: decodeFixedPoint(raw, fractionalBits),
raw,
format,
signed: false,
bits
};
}
function signedFixedPointField(raw, bits, fractionalBits, format) {
return {
kind: "fixed-point",
value: decodeSignedFixedPoint(raw, bits, fractionalBits),
raw,
format,
signed: true,
bits
};
}
function bitsField(raw, totalBits, parts) {
var _a, _b;
let remainingBits = totalBits;
const fields = parts.map((part) => {
remainingBits -= part.bits;
const value = Math.floor(raw / 2 ** remainingBits) & 2 ** part.bits - 1;
return {
key: part.key,
value,
bits: part.bits,
shift: remainingBits,
mask: (2 ** part.bits - 1) * 2 ** remainingBits
};
});
return {
kind: "bits",
value: (_b = (_a = fields.find((field) => field.key === "value")) == null ? void 0 : _a.value) != null ? _b : raw,
raw,
bits: totalBits,
fields
};
}
function flagsField(raw, totalBits, flags) {
return {
kind: "flags",
value: raw,
raw,
bits: totalBits,
flags: Object.entries(flags).map(([key, mask]) => ({
key,
value: (raw & mask) !== 0,
mask
}))
};
}
function unixSecondsToIsoString(unixSeconds) {
if (typeof unixSeconds === "bigint") {
if (unixSeconds < BigInt(Number.MIN_SAFE_INTEGER) || unixSeconds > BigInt(Number.MAX_SAFE_INTEGER)) {
return null;
}
return unixSecondsToIsoString(Number(unixSeconds));
}
const unixMilliseconds = unixSeconds * 1e3;
if (!Number.isFinite(unixMilliseconds)) {
return null;
}
const date = new Date(unixMilliseconds);
return Number.isNaN(date.getTime()) ? null : date.toISOString();
}
function macDateField(value) {
const unixSeconds = typeof value === "bigint" ? value - BigInt(MAC_EPOCH_TO_UNIX_EPOCH_SECONDS) : value - MAC_EPOCH_TO_UNIX_EPOCH_SECONDS;
return {
kind: "date",
value,
date: unixSecondsToIsoString(unixSeconds),
epoch: "1904-01-01T00:00:00.000Z",
unit: "seconds"
};
}
function parsedBoxValue(key, value, meta) {
const metadata = typeof meta === "string" ? { description: meta } : meta != null ? meta : {};
const ret = __spreadValues({
key
}, normalizeField(value));
if (metadata.offset !== void 0) {
ret.offset = metadata.offset;
}
if (metadata.byteLength !== void 0) {
ret.byteLength = metadata.byteLength;
}
if (metadata.description !== void 0) {
ret.description = metadata.description;
}
return ret;
}
function structField(fields, layout) {
const ret = {
kind: "struct",
fields
};
if (layout !== void 0) {
ret.layout = layout;
}
return ret;
}
function isParsedField(value) {
return typeof value === "object" && value !== null && "kind" in value && typeof value.kind === "string";
}
function normalizeField(value) {
if (isParsedField(value)) {
return value;
}
if (typeof value === "number") {
return { kind: "number", value };
}
if (typeof value === "bigint") {
return { kind: "bigint", value };
}
if (typeof value === "string") {
return { kind: "string", value };
}
if (typeof value === "boolean") {
return { kind: "boolean", value };
}
if (Array.isArray(value)) {
return {
kind: "array",
items: value.map((item) => normalizeField(item))
};
}
if (value && typeof value === "object") {
if (value instanceof Uint8Array) {
return {
kind: "bytes",
value: bytesToHex(value, 0, value.byteLength)
};
}
return structField(
Object.entries(value).map(
([key, fieldValue]) => parsedBoxValue(key, fieldValue)
)
);
}
if (value === null) {
return { kind: "null", value: null };
}
throw new TypeError(`Unsupported parsed field value: ${typeof value}`);
}
// src/BoxReader.js
var _buffer, _baseOffset, _values, _issues, _currentOffset, _BoxReader_instances, ensureAvailable_fn, withSpan_fn, pushField_fn, bytesToInt_fn, bytesToUint64BigInt_fn, bytesToInt64BigInt_fn, readAsUtf8_fn, readFourCc_fn, parseNullTerminatedAscii_fn, parseNullTerminatedUtf8_fn;
var BoxReader = class {
/**
* @param {Uint8Array} buffer
* @param {number=} baseOffset
*/
constructor(buffer, baseOffset = 0) {
__privateAdd(this, _BoxReader_instances);
/** @type {Uint8Array} */
__privateAdd(this, _buffer);
/** @type {number} */
__privateAdd(this, _baseOffset);
/** @type {import("./types.js").ParsedBoxValue[]} */
__privateAdd(this, _values, []);
/** @type {import("./types.js").ParsedBoxIssue[]} */
__privateAdd(this, _issues, []);
/**
* Current byte position in #buffer, starting at 0 and ending at
* `#buffer.length`.
*
* Each read operation will advance this cursor in #buffer.
*/
__privateAdd(this, _currentOffset, 0);
__privateSet(this, _buffer, buffer);
__privateSet(this, _baseOffset, baseOffset);
}
/**
* Get the number of bytes that are not yet read.
* @returns {number}
*/
getRemainingLength() {
return Math.max(0, __privateGet(this, _buffer).length - __privateGet(this, _currentOffset));
}
/**
* If `true`, the current box is already fully parsed.
* @returns {boolean}
*/
isFinished() {
return __privateGet(this, _buffer).length <= __privateGet(this, _currentOffset);
}
/**
* Returns the total length of the current box in bytes.
* @returns {number}
*/
getTotalLength() {
return __privateGet(this, _buffer).length;
}
/**
* Returns the current byte position in the box payload.
* @returns {number}
*/
getCurrentOffset() {
return __privateGet(this, _currentOffset);
}
/**
* Read the next `nbBytes` bytes, convert it into the corresponding
* unsigned integer and store it as a field named `key` for the current box.
*
* Throws if less that `nbBytes` bytes remain in the current box.
*
* Throws if 8 bytes or more is read. If you need to read 8 bytes, use
* `fieldUint64` (which creates a bigint).
*
* @template {NumberKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {number}
*/
fieldUint(key, nbBytes, meta) {
const baseOffset = __privateGet(this, _currentOffset);
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, __privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes), __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
}
/**
* Read the next 8 bytes, convert it into the corresponding
* unsigned bigint and store it as a field named `key` for the current box.
*
* Throws if less that 8 bytes remain in the current box.
*
* @template {BigIntKeys<T>} K
* @param {K} key
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {bigint}
*/
fieldUint64(key, meta) {
const baseOffset = __privateGet(this, _currentOffset);
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, __privateMethod(this, _BoxReader_instances, bytesToUint64BigInt_fn).call(this), __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
}
/**
* Read the next 8 bytes, convert it into the corresponding
* **signed** bigint and store it as a field named `key` for the current box.
*
* Throws if less that 8 bytes remain in the current box.
*
* @template {BigIntKeys<T>} K
* @param {K} key
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {bigint}
*/
fieldInt64(key, meta) {
const baseOffset = __privateGet(this, _currentOffset);
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, __privateMethod(this, _BoxReader_instances, bytesToInt64BigInt_fn).call(this), __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
}
/**
* @template {NumberKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {number}
*/
fieldSignedInt(key, nbBytes, meta) {
const baseOffset = __privateGet(this, _currentOffset);
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, toSignedInt(__privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes), nbBytes * 8), __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
}
/**
* @template {BytesKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {Uint8Array}
*/
fieldBytes(key, nbBytes, meta) {
__privateMethod(this, _BoxReader_instances, ensureAvailable_fn).call(this, nbBytes);
const baseOffset = __privateGet(this, _currentOffset);
const value = __privateGet(this, _buffer).slice(baseOffset, baseOffset + nbBytes);
__privateSet(this, _currentOffset, __privateGet(this, _currentOffset) + nbBytes);
__privateGet(this, _values).push(
parsedBoxValue(
key,
bytesField(__privateGet(this, _buffer), baseOffset, nbBytes),
__privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)
)
);
return value;
}
/**
* @template {StringKeys<T>} K
* @param {K} key
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {string}
*/
fieldNullTerminatedAscii(key, meta) {
const baseOffset = __privateGet(this, _currentOffset);
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, __privateMethod(this, _BoxReader_instances, parseNullTerminatedAscii_fn).call(this), __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
}
/**
* @template {StringKeys<T>} K
* @param {K} key
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {string}
*/
fieldNullTerminatedUtf8(key, meta) {
const baseOffset = __privateGet(this, _currentOffset);
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, __privateMethod(this, _BoxReader_instances, parseNullTerminatedUtf8_fn).call(this), __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
}
/**
* Decode the next 4 bytes into a string if printable ASCII, or the
* corresponding 32 bit integer if not, and set it as a field named
* `key` on the current box.
*
* Throws if less than 4 are remaining in the buffer.
*
* @template {StringKeys<T>} K
* @param {K} key
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {string|number}
*/
fieldFourCc(key, meta) {
const baseOffset = __privateGet(this, _currentOffset);
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, __privateMethod(this, _BoxReader_instances, readFourCc_fn).call(this), __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
}
/**
* @template {FixedPointKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {number} fractionalBits
* @param {string} format
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {import("./types.js").ParsedFixedPointField}
*/
fieldFixedPoint(key, nbBytes, fractionalBits, format, meta) {
const baseOffset = __privateGet(this, _currentOffset);
const value = fixedPointField(
__privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes),
nbBytes * 8,
fractionalBits,
format
);
__privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, value, __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
return value;
}
/**
* @template {FixedPointKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {number} bits
* @param {number} fractionalBits
* @param {string} format
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {import("./types.js").ParsedFixedPointField}
*/
fieldSignedFixedPoint(key, nbBytes, bits, fractionalBits, format, meta) {
const baseOffset = __privateGet(this, _currentOffset);
const value = signedFixedPointField(
__privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes),
bits,
fractionalBits,
format
);
__privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, value, __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
return value;
}
/**
* @template {DateKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {import("./types.js").ParsedDateField}
*/
fieldMacDate(key, nbBytes, meta) {
const baseOffset = __privateGet(this, _currentOffset);
const raw = nbBytes === 8 ? __privateMethod(this, _BoxReader_instances, bytesToUint64BigInt_fn).call(this) : __privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes);
const value = macDateField(raw);
__privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, value, __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
return value;
}
/**
* @template {BitsKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {import("./types.js").ParsedBitsFieldPartDefinition[]} parts
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {number}
*/
fieldBits(key, nbBytes, parts, meta) {
const baseOffset = __privateGet(this, _currentOffset);
const value = bitsField(__privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes), nbBytes * 8, parts);
__privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, value, __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
return value.value;
}
/**
* @template {FlagsKeys<T>} K
* @param {K} key
* @param {number} nbBytes
* @param {Record<string, number>} flags
* @param {string|ParsedBoxFieldMetadata} [meta]
* @returns {number}
*/
fieldFlags(key, nbBytes, flags, meta) {
const baseOffset = __privateGet(this, _currentOffset);
const value = flagsField(__privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes), nbBytes * 8, flags);
__privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, value, __spreadValues({}, __privateMethod(this, _BoxReader_instances, withSpan_fn).call(this, baseOffset, meta)));
return value.value;
}
/**
* @template V
* @template {KeysForValue<T, V>} K
* @param {K} key
* @param {V} value
* @param {string | ParsedBoxFieldMetadata=} [meta]
* @returns {V}
*/
addField(key, value, meta) {
return __privateMethod(this, _BoxReader_instances, pushField_fn).call(this, key, value, typeof meta === "string" || (meta == null ? void 0 : meta.offset) === void 0 ? meta : __spreadProps(__spreadValues({}, meta), { offset: __privateGet(this, _baseOffset) + meta.offset }));
}
/**
* @param {"warning" | "error"} severity
* @param {string} message
* @returns {void}
*/
addIssue(severity, message) {
__privateGet(this, _issues).push({ severity, message });
}
/**
* Read the next `nbBytes` bytes and returns the corresponding
* unsigned integer.
*
* Throws if less that `nbBytes` bytes remain in the current box.
*
* Throws if 8 bytes or more is read. If you need to read 8 bytes, use
* `fieldUint64` (which creates a bigint).
* @param {number} nbBytes
* @returns {number}
*/
readUint(nbBytes) {
return __privateMethod(this, _BoxReader_instances, bytesToInt_fn).call(this, nbBytes);
}
/**
* Read the next 8 bytes and returns the corresponding bigint.
*
* Throws if less that 8 bytes remain in the current box.
*
* @returns {bigint}
*/
readUint64() {
return __privateMethod(this, _BoxReader_instances, bytesToUint64BigInt_fn).call(this);
}
/**
* Parse the next 4 bytes as a **signed** (two's complement) 64-bit integer
* into a bigint.
*
* Throws if less than 8 bytes are remaining in the buffer.
*
* @returns {bigint}
*/
readInt64() {
return __privateMethod(this, _BoxReader_instances, bytesToInt64BigInt_fn).call(this);
}
/**
* Read the next bytes and return it as an Uint8Array.
*
* Throws if less than `nbBytes` bytes are remaining in the buffer.
*
* @param {number} nbBytes
* @returns {Uint8Array}
*/
readBytes(nbBytes) {
__privateMethod(this, _BoxReader_instances, ensureAvailable_fn).call(this, nbBytes);
const res = __privateGet(this, _buffer).slice(
__privateGet(this, _currentOffset),
__privateGet(this, _currentOffset) + nbBytes
);
__privateSet(this, _currentOffset, __privateGet(this, _currentOffset) + nbBytes);
return res;
}
/**
* Decode the next `nbBytes` as UTF-8 text.
*
* Throws if less than `nbBytes` bytes are remaining in the buffer.
*
* @param {number} nbBytes
* @returns {string}
*/
readAsUtf8(nbBytes) {
return __privateMethod(this, _BoxReader_instances, readAsUtf8_fn).call(this, nbBytes);
}
/**
* Decode the next 4 bytes into a string if printable ASCII, or the
* corresponding 32 bit integer if not.
*
* Throws if less than 4 are remaining in the buffer.
*
* @returns {string|number}
*/
readFourCc() {
return __privateMethod(this, _BoxReader_instances, readFourCc_fn).call(this);
}
/** @returns {import("./types.js").ParsedBoxValue[]} */
getValues() {
return __privateGet(this, _values).slice();
}
/** @returns {import("./types.js").ParsedBoxIssue[]} */
getIssues() {
return __privateGet(this, _issues).slice();
}
};
_buffer = new WeakMap();
_baseOffset = new WeakMap();
_values = new WeakMap();
_issues = new WeakMap();
_currentOffset = new WeakMap();
_BoxReader_instances = new WeakSet();
// Now, private methods implementing the logic
/**
* @param {number} nbBytes
* @returns {void}
*/
ensureAvailable_fn = function(nbBytes) {
if (!Number.isInteger(nbBytes) || nbBytes < 0) {
throw new Error(`Cannot read an invalid byte length: ${nbBytes}.`);
}
const remaining = __privateGet(this, _buffer).length - __privateGet(this, _currentOffset);
if (remaining < nbBytes) {
throw new Error(
`Cannot read ${nbBytes} byte(s) at offset ${__privateGet(this, _currentOffset)}: only ${Math.max(0, remaining)} byte(s) remaining.`
);
}
};
/**
* @param {number} baseOffset
* @param {string | ParsedBoxFieldMetadata | undefined} meta
* @returns {ParsedBoxFieldMetadata}
*/
withSpan_fn = function(baseOffset, meta) {
var _a;
const normalized = typeof meta === "string" ? { description: meta } : meta != null ? meta : {};
return __spreadProps(__spreadValues({}, normalized), {
offset: normalized.offset === void 0 ? __privateGet(this, _baseOffset) + baseOffset : __privateGet(this, _baseOffset) + normalized.offset,
byteLength: (_a = normalized.byteLength) != null ? _a : __privateGet(this, _currentOffset) - baseOffset
});
};
/**
* @template V
* @template {KeysForValue<T, V>} K
* @param {K} key
* @param {V} value
* @param {string | ParsedBoxFieldMetadata=} [meta]
* @returns {V}
*/
pushField_fn = function(key, value, meta) {
__privateGet(this, _values).push(parsedBoxValue(key, value, meta));
return value;
};
/**
* Returns the N next bytes, as a single number.
*
* /!\ only work for now for 1, 2, 3, 4, 5 or 8 bytes.
*
* /!\ Depending on the size of the number, it may be larger than JS'
* limit.
*
* @param {number} nbBytes
* @returns {number}
*/
bytesToInt_fn = function(nbBytes) {
__privateMethod(this, _BoxReader_instances, ensureAvailable_fn).call(this, nbBytes);
let res;
switch (nbBytes) {
case 1:
res = __privateGet(this, _buffer)[__privateGet(this, _currentOffset)];
break;
case 2:
res = be2toi(__privateGet(this, _buffer), __privateGet(this, _currentOffset));
break;
case 3:
res = be3toi(__privateGet(this, _buffer), __privateGet(this, _currentOffset));
break;
case 4:
res = be4toi(__privateGet(this, _buffer), __privateGet(this, _currentOffset));
break;
case 5:
res = be5toi(__privateGet(this, _buffer), __privateGet(this, _currentOffset));
break;
default:
throw new Error("not implemented yet.");
}
__privateSet(this, _currentOffset, __privateGet(this, _currentOffset) + nbBytes);
return res;
};
/**
* Returns the next 8 bytes as an exact unsigned 64-bit bigint.
* @returns {bigint}
*/
bytesToUint64BigInt_fn = function() {
__privateMethod(this, _BoxReader_instances, ensureAvailable_fn).call(this, 8);
const hex = bytesToHex(__privateGet(this, _buffer), __privateGet(this, _currentOffset), 8);
const toBigint = hexToBigInt(hex);
__privateSet(this, _currentOffset, __privateGet(this, _currentOffset) + 8);
return toBigint;
};
/**
* Returns the next 8 bytes as an exact signed 64-bit bigint.
* @returns {bigint}
*/
bytesToInt64BigInt_fn = function() {
__privateMethod(this, _BoxReader_instances, ensureAvailable_fn).call(this, 8);
const hex = bytesToHex(__privateGet(this, _buffer), __privateGet(this, _currentOffset), 8);
const toBigInt = BigInt.asIntN(64, hexToBigInt(hex));
__privateSet(this, _currentOffset, __privateGet(this, _currentOffset) + 8);
return toBigInt;
};
/**
* Returns the N next bytes into a string.
* @param {number} nbBytes
* @returns {string}
*/
readAsUtf8_fn = function(nbBytes) {
__privateMethod(this, _BoxReader_instances, ensureAvailable_fn).call(this, nbBytes);
const res = utf8ToStr(__privateGet(this, _buffer), __privateGet(this, _currentOffset), nbBytes);
__privateSet(this, _currentOffset, __privateGet(this, _currentOffset) + nbBytes);
return res;
};
readFourCc_fn = function() {
__privateMethod(this, _BoxReader_instances, ensureAvailable_fn).call(this, 4);
let isPrintable = true;
for (let i = __privateGet(this, _currentOffset); i < __privateGet(this, _currentOffset) + 4; i++) {
const b = __privateGet(this, _buffer)[i];
if (b < 32 || b > 126) {
isPrintable = false;
break;
}
}
const res = isPrintable ? (
// Convert to string, same codes as UTF-16's lower byte
String.fromCharCode(
__privateGet(this, _buffer)[__privateGet(this, _currentOffset)],
__privateGet(this, _buffer)[__privateGet(this, _currentOffset) + 1],
__privateGet(this, _buffer)[__privateGet(this, _currentOffset) + 2],
__privateGet(this, _buffer)[__privateGet(this, _currentOffset) + 3]
)
) : (
// Fallback: return unsigned 32-bit number (big-endian)
be4toi(__privateGet(this, _buffer), __privateGet(this, _currentOffset))
);
__privateSet(this, _currentOffset, __privateGet(this, _currentOffset) + 4);
return res;
};
/**
* @returns {string}
*/
parseNullTerminatedAscii_fn = function() {
const bytes = [];
while (!this.isFinished()) {
const value = this.readUint(1);
if (value === 0) {
break;
}
bytes.push(value);
}
return String.fromCharCode.apply(null, bytes);
};
/**
* @returns {string}
*/
parseNullTerminatedUtf8_fn = function() {
const bytes = [];
while (!this.isFinished()) {
const value = this.readUint(1);
if (value === 0) {
break;
}
bytes.push(value);
}
return utf8ToStr(new Uint8Array(bytes));
};
function hexToBigInt(hex) {
return BigInt(`0x${hex}`);
}
// src/boxes/helpers.js
function createTrackReferenceTypeBox(name, description) {
return {
name,
description,
parser(reader) {
const track_IDs = [];
const trackIdsOffset = reader.getCurrentOffset();
while (reader.getRemainingLength() >= 4) {
track_IDs.push(reader.readUint(4));
}
reader.addField("track_IDs", track_IDs, {
offset: trackIdsOffset,
byteLength: reader.getCurrentOffset() - trackIdsOffset
});
if (!reader.isFinished()) {
reader.fieldBytes("trailing_bytes", reader.getRemainingLength());
}
}
};
}
function parseTransformationMatrix(r) {
return structField(
[
parsedBoxValue(
"a",
signedFixedPointField(r.readUint(4), 32, 16, "16.16")
),
parsedBoxValue(
"b",
signedFixedPointField(r.readUint(4), 32, 16, "16.16")
),
parsedBoxValue("u", signedFixedPointField(r.readUint(4), 32, 30, "2.30")),
parsedBoxValue(
"c",
signedFixedPointField(r.readUint(4), 32, 16, "16.16")
),
parsedBoxValue(
"d",
signedFixedPointField(r.readUint(4), 32, 16, "16.16")
),
parsedBoxValue("v", signedFixedPointField(r.readUint(4), 32, 30, "2.30")),
parsedBoxValue(
"x",
signedFixedPointField(r.readUint(4), 32, 16, "16.16")
),
parsedBoxValue(
"y",
signedFixedPointField(r.readUint(4), 32, 16, "16.16")
),
parsedBoxValue("w", signedFixedPointField(r.readUint(4), 32, 30, "2.30"))
],
"matrix-3x3"
);
}
function parsePascalAsciiString(r, length) {
const stringLength = Math.min(r.readUint(1), length - 1);
let value = "";
for (let i = 0; i < stringLength; i++) {
const byte = r.readUint(1);
if (byte < 32 || byte > 126) {
throw new Error(
`Non-printable ASCII character found: 0x${byte.toString(16).toUpperCase()}`
);
}
value += String.fromCharCode(byte);
}
const paddingLength = length - 1 - stringLength;
if (paddingLength > 0) {
r.readBytes(paddingLength);
}
return value;
}
function readVisualSampleEntry(reader) {
const reserved = [];
const reservedOffset = reader.getCurrentOffset();
for (let i = 0; i < 6; i++) {
reserved.push(reader.readUint(1));
}
reader.addField("reserved", reserved, {
offset: reservedOffset,
byteLength: reader.getCurrentOffset() - reservedOffset
});
reader.fieldUint("data_reference_index", 2);
reader.fieldUint("pre_defined", 2);
reader.fieldUint("reserved_1", 2);
const preDefined1Offset = reader.getCurrentOffset();
const preDefined1 = [
reader.readUint(4),
reader.readUint(4),
reader.readUint(4)
];
reader.addField("pre_defined_1", preDefined1, {
offset: preDefined1Offset,
byteLength: reader.getCurrentOffset() - preDefined1Offset
});
reader.fieldUint("width", 2);
reader.fieldUint("height", 2);
reader.fieldFixedPoint("horizresolution", 4, 16, "16.16");
reader.fieldFixedPoint("vertresolution", 4, 16, "16.16");
reader.fieldUint("reserved_2", 4);
reader.fieldUint("frame_count", 2);
const compressorNameOffset = reader.getCurrentOffset();
reader.addField("compressorname", parsePascalAsciiString(reader, 32), {
offset: compressorNameOffset,
byteLength: reader.getCurrentOffset() - compressorNameOffset
});
reader.fieldUint("depth", 2);
reader.fieldUint("pre_defined", 2);
}
function parseAudioSampleEntry(r) {
const reserved = [];
const reservedOffset = r.getCurrentOffset();
for (let i = 0; i < 6; i++) {
reserved.push(r.readUint(1));
}
r.addField("reserved", reserved, {
offset: reservedOffset,
byteLength: r.getCurrentOffset() - reservedOffset
});
r.fieldUint("data_reference_index", 2);
const version = r.fieldUint("version", 2);
r.fieldUint("revision_level", 2);
r.fieldUint("vendor", 4);
r.fieldUint("channelcount", 2);
r.fieldUint("samplesize", 2);
r.fieldUint("compression_id", 2);
r.fieldUint("packet_size", 2);
r.fieldFixedPoint("samplerate", 4, 16, "16.16");
if (version === 1) {
r.fieldUint("samples_per_packet", 4);
r.fieldUint("bytes_per_packet", 4);
r.fieldUint("bytes_per_frame", 4);
r.fieldUint("bytes_per_sample", 4);
} else if (version === 2) {
r.fieldUint("struct_size", 4);
r.fieldFixedPoint("sample_rate", 4, 16, "16.16");
r.fieldUint("channel_count", 4);
r.fieldUint("reserved_1", 4);
r.fieldUint("bits_per_channel", 4);
r.fieldUint("format_specific_flags", 4);
r.fieldUint("bytes_per_audio_packet", 4);
r.fieldUint("LPCM_frames_per_audio_packet", 4);
}
}
function parseDescriptorLength(r) {
let length = 0;
let size = 0;
while (size < 4) {
const currentByte = r.readUint(1);
size += 1;
length = length << 7 | currentByte & 127;
if ((currentByte & 128) === 0) {
return {
length,
size
};
}
}
throw new Error("invalid descriptor length");
}
function parseNestedDescriptors(r, size) {
const descriptors = [];
let remaining = size;
while (remaining > 0) {
const before = r.getRemainingLength();
const descriptor = parseDescriptor(r);
const consumed = before - r.getRemainingLength();
remaining -= consumed;
descriptors.push(descriptor);
}
if (remaining !== 0) {
throw new Error("descriptor size mismatch");
}
return descriptors;
}
function parseDescriptorPayload(r, tag, size) {
if (tag === 3) {
const es_id = r.readUint(2);
const flags = r.readUint(1);
const ret = {
es_id,
stream_dependence_flag: !!(flags & 128),
URL_flag: !!(flags & 64),
OCRstream_flag: !!(flags & 32),
stream_priority: flags & 31
};
let consumed = 3;
if (ret.stream_dependence_flag) {
ret.depends_on_es_id = r.readUint(2);
consumed += 2;
}
if (ret.URL_flag) {
const urlLength = r.readUint(1);
ret.URL_length = urlLength;
ret.URL_string = urlLength > 0 ? r.readAsUtf8(urlLength) : "";
consumed += 1 + urlLength;
}
if (ret.OCRstream_flag) {
ret.ocr_es_id = r.readUint(2);
consumed += 2;
}
if (size > consumed) {
ret.descriptors = parseNestedDescriptors(r, size - consumed);
}
return ret;
}
if (tag === 4) {
const objectTypeIndication = r.readUint(1);
const streamByte = r.readUint(1);
const ret = {
object_type_indication: objectTypeIndication,
stream_type: streamByte >> 2 & 63,
up_stream: !!(streamByte >> 1 & 1),
reserved: streamByte & 1,
buffer_size_db: r.readUint(3),
max_bitrate: r.readUint(4),
avg_bitrate: r.readUint(4)
};
if (size > 13) {
ret.descriptors = parseNestedDescriptors(r, size - 13);
}
return ret;
}
if (tag === 5) {
return {
decoder_specific_info: size > 0 ? r.readBytes(size) : ""
};
}
if (tag === 6) {
return {
predefined: r.readUint(1),
remaining_payload: size > 1 ? r.readBytes(size - 1) : ""
};
}
return {
data: size > 0 ? r.readBytes(size) : ""
};
}
function parseDescriptor(r) {
const tag = r.readUint(1);
const { length, size } = parseDescriptorLength(r);
return {
tag,
size: length,
header_size: size + 1,
payload: parseDescriptorPayload(r, tag, length)
};
}
// src/boxes/ac-3.js
var ac_3_default = {
name: "AC-3 Audio Sample Entry",
description: "Describes AC-3 audio samples and their dac3 decoder-specific box.",
container: true,
parser(reader) {
parseAudioSampleEntry(reader);
}
};
// src/boxes/av01.js
var av01_default = {
name: "AV1 Sample Entry",
description: "Describes AV1 video samples and carries child decoder configuration boxes such as av1C.",
container: true,
parser(reader) {
readVisualSampleEntry(read