@svta/common-media-library
Version:
A common library for media playback in JavaScript
290 lines • 10.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.IsoView = void 0;
const CONTAINERS_js_1 = require("./CONTAINERS.js");
const DATA_js_1 = require("./fields/DATA.js");
const INT_js_1 = require("./fields/INT.js");
const STRING_js_1 = require("./fields/STRING.js");
const TEMPLATE_js_1 = require("./fields/TEMPLATE.js");
const UINT_js_1 = require("./fields/UINT.js");
const UTF8_js_1 = require("./fields/UTF8.js");
const readData_js_1 = require("./readers/readData.js");
const readInt_js_1 = require("./readers/readInt.js");
const readString_js_1 = require("./readers/readString.js");
const readTemplate_js_1 = require("./readers/readTemplate.js");
const readTerminatedString_js_1 = require("./readers/readTerminatedString.js");
const readUint_js_1 = require("./readers/readUint.js");
const readUtf8String_js_1 = require("./readers/readUtf8String.js");
const readUtf8TerminatedString_js_1 = require("./readers/readUtf8TerminatedString.js");
/**
* ISO BMFF data view. Similar to DataView, but with additional methods for reading ISO BMFF data.
* It implements the iterator protocol, so it can be used in a for...of loop.
*
* @group ISOBMFF
*
* @beta
*/
class IsoView {
/**
* Creates a new IsoView instance. Similar to DataView, but with additional
* methods for reading ISO BMFF data. It implements the iterator protocol,
* so it can be used in a for...of loop.
*
* @param raw - The raw data to view.
* @param config - The configuration for the IsoView.
*/
constructor(raw, config) {
this.truncated = false;
/**
* Creates a new IsoView instance with a slice of the current data view.
*
* @param size - The size of the slice.
* @returns A new IsoView instance.
*/
this.slice = (size) => {
const dataView = new DataView(this.dataView.buffer, this.offset, size);
this.offset += size;
return new IsoView(dataView, this.config);
};
this.read = (type, size = 0) => {
// TODO: Change all sizes from bits to bytes
const { dataView, offset } = this;
let result;
let cursor = size;
switch (type) {
case UINT_js_1.UINT:
result = (0, readUint_js_1.readUint)(dataView, offset, size);
break;
case INT_js_1.INT:
result = (0, readInt_js_1.readInt)(dataView, offset, size);
break;
case TEMPLATE_js_1.TEMPLATE:
result = (0, readTemplate_js_1.readTemplate)(dataView, offset, size);
break;
case STRING_js_1.STRING:
if (size === -1) {
result = (0, readTerminatedString_js_1.readTerminatedString)(dataView, offset);
cursor = result.length + 1;
}
else {
result = (0, readString_js_1.readString)(dataView, offset, size);
}
break;
case DATA_js_1.DATA:
result = (0, readData_js_1.readData)(dataView, offset, size);
cursor = result.length;
break;
case UTF8_js_1.UTF8:
if (size === -1) {
result = (0, readUtf8TerminatedString_js_1.readUtf8TerminatedString)(dataView, offset);
cursor = result.length + 1;
}
else {
result = (0, readUtf8String_js_1.readUtf8String)(dataView, offset);
}
break;
default:
result = -1;
}
this.offset += cursor;
return result;
};
/**
* Reads a unsigned integer from the data view.
*
* @param size - The size of the integer in bytes.
* @returns The unsigned integer.
*/
this.readUint = (size) => {
return this.read(UINT_js_1.UINT, size);
};
/**
* Reads a signed integer from the data view.
*
* @param size - The size of the integer in bytes.
* @returns The signed integer.
*/
this.readInt = (size) => {
return this.read(INT_js_1.INT, size);
};
/**
* Reads a string from the data view.
*
* @param size - The size of the string in bytes.
* @returns The string.
*/
this.readString = (size) => {
return this.read(STRING_js_1.STRING, size);
};
/**
* Reads a template from the data view.
*
* @param size - The size of the template in bytes.
* @returns The template.
*/
this.readTemplate = (size) => {
return this.read(TEMPLATE_js_1.TEMPLATE, size);
};
/**
* Reads a byte array from the data view.
*
* @param size - The size of the data in bytes.
* @returns The data.
*/
this.readData = (size) => {
return this.read(DATA_js_1.DATA, size);
};
/**
* Reads a UTF-8 string from the data view.
*
* @param size - The size of the string in bytes.
* @returns The UTF-8 string.
*/
this.readUtf8 = (size) => {
return this.read(UTF8_js_1.UTF8, size);
};
/**
* Reads a full box from the data view.
*
* @returns The full box.
*/
this.readFullBox = () => {
return {
version: this.readUint(1),
flags: this.readUint(3),
};
};
/**
* Reads an array of values from the data view.
*
* @param type - The type of the values.
* @param size - The size of the values in bytes.
* @param length - The number of values to read.
* @returns The array of values.
*/
this.readArray = (type, size, length) => {
const value = [];
for (let i = 0; i < length; i++) {
value.push(this.read(type, size));
}
return value;
};
/**
* Reads a raw box from the data view.
*
* @returns The box.
*/
this.readBox = () => {
const { dataView, offset } = this;
// read box size and type without advancing the cursor in case the box is truncated
let cursor = 0;
const box = {
size: (0, readUint_js_1.readUint)(dataView, offset, 4),
type: (0, readString_js_1.readString)(dataView, offset + 4, 4),
};
cursor += 8;
if (box.size === 1) {
box.largesize = (0, readUint_js_1.readUint)(dataView, offset + cursor, 8);
cursor += 8;
}
const actualSize = box.largesize || box.size;
if (this.cursor + actualSize > dataView.byteLength) {
this.truncated = true;
throw new Error('Truncated box');
}
this.offset += cursor;
if (box.type === 'uuid') {
box.usertype = this.readArray('uint', 1, 16);
}
const viewSize = box.size === 0 ? this.bytesRemaining : actualSize - cursor;
box.data = this.slice(viewSize);
return box;
};
/**
* Reads a number of boxes from the data view.
*
* @param length - The number of boxes to read.
* @returns The boxes.
*/
this.readBoxes = (length) => {
const result = [];
for (const box of this) {
result.push(box);
if (length > 0 && result.length >= length) {
break;
}
}
return result;
};
/**
* Reads a number of entries from the data view.
*
* @param length - The number of entries to read.
* @param map - The function to map the entries.
* @returns The entries.
*/
this.readEntries = (length, map) => {
const result = [];
for (let i = 0; i < length; i++) {
result.push(map());
}
return result;
};
this.dataView = (raw instanceof ArrayBuffer) ? new DataView(raw) : (raw instanceof Uint8Array) ? new DataView(raw.buffer, raw.byteOffset, raw.byteLength) : raw;
this.offset = this.dataView.byteOffset;
this.config = config || { recursive: false, parsers: {} };
}
/**
* The current byteoffset in the data view.
*/
get cursor() {
return this.offset - this.dataView.byteOffset;
}
/**
* Whether the end of the data view has been reached.
*/
get done() {
return this.cursor >= this.dataView.byteLength || this.truncated;
}
/**
* The number of bytes remaining in the data view.
*/
get bytesRemaining() {
return this.dataView.byteLength - this.cursor;
}
/**
* Iterates over the boxes in the data view.
*
* @returns A generator of boxes.
*/
*[Symbol.iterator]() {
const { parsers = {}, recursive = false } = this.config;
while (!this.done) {
try {
const { type, data, ...rest } = this.readBox();
const box = { type, ...rest };
const parser = parsers[type] || parsers[type.trim()]; // url and urn boxes have a trailing space in their type field
if (parser) {
Object.assign(box, parser(data, this.config));
}
box.view = data;
if (CONTAINERS_js_1.CONTAINERS.includes(type)) {
const boxes = [];
for (const child of data) {
if (recursive) {
yield child;
}
boxes.push(child);
}
box.boxes = boxes;
}
yield box;
}
catch (error) {
break;
}
}
}
}
exports.IsoView = IsoView;
//# sourceMappingURL=IsoView.js.map