mediabunny
Version:
Pure TypeScript media toolkit for reading, writing, and converting media files, directly in the browser.
683 lines (682 loc) • 25.6 kB
JavaScript
/*!
* Copyright (c) 2026-present, Vanilagy and contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import { assert, assertNever, textDecoder, textEncoder } from '../misc.js';
import { readBytes, readF32Be, readF64Be, readU8 } from '../reader.js';
/** Wrapper around a number to be able to differentiate it in the writer. */
export class EBMLFloat32 {
constructor(value) {
this.value = value;
}
}
/** Wrapper around a number to be able to differentiate it in the writer. */
export class EBMLFloat64 {
constructor(value) {
this.value = value;
}
}
/** Wrapper around a number to be able to differentiate it in the writer. */
export class EBMLSignedInt {
constructor(value) {
this.value = value;
}
}
export class EBMLUnicodeString {
constructor(value) {
this.value = value;
}
}
/** Defines some of the EBML IDs used by Matroska files. */
export var EBMLId;
(function (EBMLId) {
EBMLId[EBMLId["EBML"] = 440786851] = "EBML";
EBMLId[EBMLId["EBMLVersion"] = 17030] = "EBMLVersion";
EBMLId[EBMLId["EBMLReadVersion"] = 17143] = "EBMLReadVersion";
EBMLId[EBMLId["EBMLMaxIDLength"] = 17138] = "EBMLMaxIDLength";
EBMLId[EBMLId["EBMLMaxSizeLength"] = 17139] = "EBMLMaxSizeLength";
EBMLId[EBMLId["DocType"] = 17026] = "DocType";
EBMLId[EBMLId["DocTypeVersion"] = 17031] = "DocTypeVersion";
EBMLId[EBMLId["DocTypeReadVersion"] = 17029] = "DocTypeReadVersion";
EBMLId[EBMLId["Void"] = 236] = "Void";
EBMLId[EBMLId["Segment"] = 408125543] = "Segment";
EBMLId[EBMLId["SeekHead"] = 290298740] = "SeekHead";
EBMLId[EBMLId["Seek"] = 19899] = "Seek";
EBMLId[EBMLId["SeekID"] = 21419] = "SeekID";
EBMLId[EBMLId["SeekPosition"] = 21420] = "SeekPosition";
EBMLId[EBMLId["Duration"] = 17545] = "Duration";
EBMLId[EBMLId["Info"] = 357149030] = "Info";
EBMLId[EBMLId["TimestampScale"] = 2807729] = "TimestampScale";
EBMLId[EBMLId["MuxingApp"] = 19840] = "MuxingApp";
EBMLId[EBMLId["WritingApp"] = 22337] = "WritingApp";
EBMLId[EBMLId["Tracks"] = 374648427] = "Tracks";
EBMLId[EBMLId["TrackEntry"] = 174] = "TrackEntry";
EBMLId[EBMLId["TrackNumber"] = 215] = "TrackNumber";
EBMLId[EBMLId["TrackUID"] = 29637] = "TrackUID";
EBMLId[EBMLId["TrackType"] = 131] = "TrackType";
EBMLId[EBMLId["FlagEnabled"] = 185] = "FlagEnabled";
EBMLId[EBMLId["FlagDefault"] = 136] = "FlagDefault";
EBMLId[EBMLId["FlagForced"] = 21930] = "FlagForced";
EBMLId[EBMLId["FlagOriginal"] = 21934] = "FlagOriginal";
EBMLId[EBMLId["FlagHearingImpaired"] = 21931] = "FlagHearingImpaired";
EBMLId[EBMLId["FlagVisualImpaired"] = 21932] = "FlagVisualImpaired";
EBMLId[EBMLId["FlagCommentary"] = 21935] = "FlagCommentary";
EBMLId[EBMLId["FlagLacing"] = 156] = "FlagLacing";
EBMLId[EBMLId["Name"] = 21358] = "Name";
EBMLId[EBMLId["Language"] = 2274716] = "Language";
EBMLId[EBMLId["LanguageBCP47"] = 2274717] = "LanguageBCP47";
EBMLId[EBMLId["CodecID"] = 134] = "CodecID";
EBMLId[EBMLId["CodecPrivate"] = 25506] = "CodecPrivate";
EBMLId[EBMLId["CodecDelay"] = 22186] = "CodecDelay";
EBMLId[EBMLId["SeekPreRoll"] = 22203] = "SeekPreRoll";
EBMLId[EBMLId["DefaultDuration"] = 2352003] = "DefaultDuration";
EBMLId[EBMLId["Video"] = 224] = "Video";
EBMLId[EBMLId["PixelWidth"] = 176] = "PixelWidth";
EBMLId[EBMLId["PixelHeight"] = 186] = "PixelHeight";
EBMLId[EBMLId["DisplayWidth"] = 21680] = "DisplayWidth";
EBMLId[EBMLId["DisplayHeight"] = 21690] = "DisplayHeight";
EBMLId[EBMLId["DisplayUnit"] = 21682] = "DisplayUnit";
EBMLId[EBMLId["AlphaMode"] = 21440] = "AlphaMode";
EBMLId[EBMLId["Audio"] = 225] = "Audio";
EBMLId[EBMLId["SamplingFrequency"] = 181] = "SamplingFrequency";
EBMLId[EBMLId["Channels"] = 159] = "Channels";
EBMLId[EBMLId["BitDepth"] = 25188] = "BitDepth";
EBMLId[EBMLId["SimpleBlock"] = 163] = "SimpleBlock";
EBMLId[EBMLId["BlockGroup"] = 160] = "BlockGroup";
EBMLId[EBMLId["Block"] = 161] = "Block";
EBMLId[EBMLId["BlockAdditions"] = 30113] = "BlockAdditions";
EBMLId[EBMLId["BlockMore"] = 166] = "BlockMore";
EBMLId[EBMLId["BlockAdditional"] = 165] = "BlockAdditional";
EBMLId[EBMLId["BlockAddID"] = 238] = "BlockAddID";
EBMLId[EBMLId["BlockDuration"] = 155] = "BlockDuration";
EBMLId[EBMLId["ReferenceBlock"] = 251] = "ReferenceBlock";
EBMLId[EBMLId["Cluster"] = 524531317] = "Cluster";
EBMLId[EBMLId["Timestamp"] = 231] = "Timestamp";
EBMLId[EBMLId["Cues"] = 475249515] = "Cues";
EBMLId[EBMLId["CuePoint"] = 187] = "CuePoint";
EBMLId[EBMLId["CueTime"] = 179] = "CueTime";
EBMLId[EBMLId["CueTrackPositions"] = 183] = "CueTrackPositions";
EBMLId[EBMLId["CueTrack"] = 247] = "CueTrack";
EBMLId[EBMLId["CueClusterPosition"] = 241] = "CueClusterPosition";
EBMLId[EBMLId["Colour"] = 21936] = "Colour";
EBMLId[EBMLId["MatrixCoefficients"] = 21937] = "MatrixCoefficients";
EBMLId[EBMLId["TransferCharacteristics"] = 21946] = "TransferCharacteristics";
EBMLId[EBMLId["Primaries"] = 21947] = "Primaries";
EBMLId[EBMLId["Range"] = 21945] = "Range";
EBMLId[EBMLId["Projection"] = 30320] = "Projection";
EBMLId[EBMLId["ProjectionType"] = 30321] = "ProjectionType";
EBMLId[EBMLId["ProjectionPoseRoll"] = 30325] = "ProjectionPoseRoll";
EBMLId[EBMLId["Attachments"] = 423732329] = "Attachments";
EBMLId[EBMLId["AttachedFile"] = 24999] = "AttachedFile";
EBMLId[EBMLId["FileDescription"] = 18046] = "FileDescription";
EBMLId[EBMLId["FileName"] = 18030] = "FileName";
EBMLId[EBMLId["FileMediaType"] = 18016] = "FileMediaType";
EBMLId[EBMLId["FileData"] = 18012] = "FileData";
EBMLId[EBMLId["FileUID"] = 18094] = "FileUID";
EBMLId[EBMLId["Chapters"] = 272869232] = "Chapters";
EBMLId[EBMLId["Tags"] = 307544935] = "Tags";
EBMLId[EBMLId["Tag"] = 29555] = "Tag";
EBMLId[EBMLId["Targets"] = 25536] = "Targets";
EBMLId[EBMLId["TargetTypeValue"] = 26826] = "TargetTypeValue";
EBMLId[EBMLId["TargetType"] = 25546] = "TargetType";
EBMLId[EBMLId["TagTrackUID"] = 25541] = "TagTrackUID";
EBMLId[EBMLId["TagEditionUID"] = 25545] = "TagEditionUID";
EBMLId[EBMLId["TagChapterUID"] = 25540] = "TagChapterUID";
EBMLId[EBMLId["TagAttachmentUID"] = 25542] = "TagAttachmentUID";
EBMLId[EBMLId["SimpleTag"] = 26568] = "SimpleTag";
EBMLId[EBMLId["TagName"] = 17827] = "TagName";
EBMLId[EBMLId["TagLanguage"] = 17530] = "TagLanguage";
EBMLId[EBMLId["TagString"] = 17543] = "TagString";
EBMLId[EBMLId["TagBinary"] = 17541] = "TagBinary";
EBMLId[EBMLId["ContentEncodings"] = 28032] = "ContentEncodings";
EBMLId[EBMLId["ContentEncoding"] = 25152] = "ContentEncoding";
EBMLId[EBMLId["ContentEncodingOrder"] = 20529] = "ContentEncodingOrder";
EBMLId[EBMLId["ContentEncodingScope"] = 20530] = "ContentEncodingScope";
EBMLId[EBMLId["ContentCompression"] = 20532] = "ContentCompression";
EBMLId[EBMLId["ContentCompAlgo"] = 16980] = "ContentCompAlgo";
EBMLId[EBMLId["ContentCompSettings"] = 16981] = "ContentCompSettings";
EBMLId[EBMLId["ContentEncryption"] = 20533] = "ContentEncryption";
})(EBMLId || (EBMLId = {}));
export const LEVEL_0_EBML_IDS = [
EBMLId.EBML,
EBMLId.Segment,
];
// All the stuff that can appear in a segment, basically
export const LEVEL_1_EBML_IDS = [
EBMLId.SeekHead,
EBMLId.Info,
EBMLId.Cluster,
EBMLId.Tracks,
EBMLId.Cues,
EBMLId.Attachments,
EBMLId.Chapters,
EBMLId.Tags,
];
export const LEVEL_0_AND_1_EBML_IDS = [
...LEVEL_0_EBML_IDS,
...LEVEL_1_EBML_IDS,
];
export const measureUnsignedInt = (value) => {
if (value < (1 << 8)) {
return 1;
}
else if (value < (1 << 16)) {
return 2;
}
else if (value < (1 << 24)) {
return 3;
}
else if (value < 2 ** 32) {
return 4;
}
else if (value < 2 ** 40) {
return 5;
}
else {
return 6;
}
};
export const measureUnsignedBigInt = (value) => {
if (value < (1n << 8n)) {
return 1;
}
else if (value < (1n << 16n)) {
return 2;
}
else if (value < (1n << 24n)) {
return 3;
}
else if (value < (1n << 32n)) {
return 4;
}
else if (value < (1n << 40n)) {
return 5;
}
else if (value < (1n << 48n)) {
return 6;
}
else if (value < (1n << 56n)) {
return 7;
}
else {
return 8;
}
};
export const measureSignedInt = (value) => {
if (value >= -(1 << 6) && value < (1 << 6)) {
return 1;
}
else if (value >= -(1 << 13) && value < (1 << 13)) {
return 2;
}
else if (value >= -(1 << 20) && value < (1 << 20)) {
return 3;
}
else if (value >= -(1 << 27) && value < (1 << 27)) {
return 4;
}
else if (value >= -(2 ** 34) && value < 2 ** 34) {
return 5;
}
else {
return 6;
}
};
export const measureVarInt = (value) => {
if (value < (1 << 7) - 1) {
/** Top bit is set, leaving 7 bits to hold the integer, but we can't store
* 127 because "all bits set to one" is a reserved value. Same thing for the
* other cases below:
*/
return 1;
}
else if (value < (1 << 14) - 1) {
return 2;
}
else if (value < (1 << 21) - 1) {
return 3;
}
else if (value < (1 << 28) - 1) {
return 4;
}
else if (value < 2 ** 35 - 1) {
return 5;
}
else if (value < 2 ** 42 - 1) {
return 6;
}
else {
throw new Error('EBML varint size not supported ' + value);
}
};
export class EBMLWriter {
constructor(writer) {
this.writer = writer;
this.helper = new Uint8Array(8);
this.helperView = new DataView(this.helper.buffer);
/**
* Stores the position from the start of the file to where EBML elements have been written. This is used to
* rewrite/edit elements that were already added before, and to measure sizes of things.
*/
this.offsets = new WeakMap();
/** Same as offsets, but stores position where the element's data starts (after ID and size fields). */
this.dataOffsets = new WeakMap();
}
writeByte(value) {
this.helperView.setUint8(0, value);
this.writer.write(this.helper.subarray(0, 1));
}
writeFloat32(value) {
this.helperView.setFloat32(0, value, false);
this.writer.write(this.helper.subarray(0, 4));
}
writeFloat64(value) {
this.helperView.setFloat64(0, value, false);
this.writer.write(this.helper);
}
writeUnsignedInt(value, width = measureUnsignedInt(value)) {
let pos = 0;
// Each case falls through:
switch (width) {
case 6:
// Need to use division to access >32 bits of floating point var
this.helperView.setUint8(pos++, (value / 2 ** 40) | 0);
// eslint-disable-next-line no-fallthrough
case 5:
this.helperView.setUint8(pos++, (value / 2 ** 32) | 0);
// eslint-disable-next-line no-fallthrough
case 4:
this.helperView.setUint8(pos++, value >> 24);
// eslint-disable-next-line no-fallthrough
case 3:
this.helperView.setUint8(pos++, value >> 16);
// eslint-disable-next-line no-fallthrough
case 2:
this.helperView.setUint8(pos++, value >> 8);
// eslint-disable-next-line no-fallthrough
case 1:
this.helperView.setUint8(pos++, value);
break;
default:
throw new Error('Bad unsigned int size ' + width);
}
this.writer.write(this.helper.subarray(0, pos));
}
writeUnsignedBigInt(value, width = measureUnsignedBigInt(value)) {
let pos = 0;
for (let i = width - 1; i >= 0; i--) {
this.helperView.setUint8(pos++, Number((value >> BigInt(i * 8)) & 0xffn));
}
this.writer.write(this.helper.subarray(0, pos));
}
writeSignedInt(value, width = measureSignedInt(value)) {
if (value < 0) {
// Two's complement stuff
value += 2 ** (width * 8);
}
this.writeUnsignedInt(value, width);
}
writeVarInt(value, width = measureVarInt(value)) {
let pos = 0;
switch (width) {
case 1:
this.helperView.setUint8(pos++, (1 << 7) | value);
break;
case 2:
this.helperView.setUint8(pos++, (1 << 6) | (value >> 8));
this.helperView.setUint8(pos++, value);
break;
case 3:
this.helperView.setUint8(pos++, (1 << 5) | (value >> 16));
this.helperView.setUint8(pos++, value >> 8);
this.helperView.setUint8(pos++, value);
break;
case 4:
this.helperView.setUint8(pos++, (1 << 4) | (value >> 24));
this.helperView.setUint8(pos++, value >> 16);
this.helperView.setUint8(pos++, value >> 8);
this.helperView.setUint8(pos++, value);
break;
case 5:
/**
* JavaScript converts its doubles to 32-bit integers for bitwise
* operations, so we need to do a division by 2^32 instead of a
* right-shift of 32 to retain those top 3 bits
*/
this.helperView.setUint8(pos++, (1 << 3) | ((value / 2 ** 32) & 0x7));
this.helperView.setUint8(pos++, value >> 24);
this.helperView.setUint8(pos++, value >> 16);
this.helperView.setUint8(pos++, value >> 8);
this.helperView.setUint8(pos++, value);
break;
case 6:
this.helperView.setUint8(pos++, (1 << 2) | ((value / 2 ** 40) & 0x3));
this.helperView.setUint8(pos++, (value / 2 ** 32) | 0);
this.helperView.setUint8(pos++, value >> 24);
this.helperView.setUint8(pos++, value >> 16);
this.helperView.setUint8(pos++, value >> 8);
this.helperView.setUint8(pos++, value);
break;
default:
throw new Error('Bad EBML varint size ' + width);
}
this.writer.write(this.helper.subarray(0, pos));
}
writeAsciiString(str) {
this.writer.write(new Uint8Array(str.split('').map(x => x.charCodeAt(0))));
}
writeEBML(data) {
if (data === null)
return;
if (data instanceof Uint8Array) {
this.writer.write(data);
}
else if (Array.isArray(data)) {
for (const elem of data) {
this.writeEBML(elem);
}
}
else {
this.offsets.set(data, this.writer.getPos());
this.writeUnsignedInt(data.id); // ID field
if (Array.isArray(data.data)) {
const sizePos = this.writer.getPos();
const sizeSize = data.size === -1 ? 1 : (data.size ?? 4);
if (data.size === -1) {
// Write the reserved all-one-bits marker for unknown/unbounded size.
this.writeByte(0xff);
}
else {
this.writer.seek(this.writer.getPos() + sizeSize);
}
const startPos = this.writer.getPos();
this.dataOffsets.set(data, startPos);
this.writeEBML(data.data);
if (data.size !== -1) {
const size = this.writer.getPos() - startPos;
const endPos = this.writer.getPos();
this.writer.seek(sizePos);
this.writeVarInt(size, sizeSize);
this.writer.seek(endPos);
}
}
else if (typeof data.data === 'number') {
const size = data.size ?? measureUnsignedInt(data.data);
this.writeVarInt(size);
this.writeUnsignedInt(data.data, size);
}
else if (typeof data.data === 'bigint') {
const size = data.size ?? measureUnsignedBigInt(data.data);
this.writeVarInt(size);
this.writeUnsignedBigInt(data.data, size);
}
else if (typeof data.data === 'string') {
this.writeVarInt(data.data.length);
this.writeAsciiString(data.data);
}
else if (data.data instanceof Uint8Array) {
this.writeVarInt(data.data.byteLength, data.size);
this.writer.write(data.data);
}
else if (data.data instanceof EBMLFloat32) {
this.writeVarInt(4);
this.writeFloat32(data.data.value);
}
else if (data.data instanceof EBMLFloat64) {
this.writeVarInt(8);
this.writeFloat64(data.data.value);
}
else if (data.data instanceof EBMLSignedInt) {
const size = data.size ?? measureSignedInt(data.data.value);
this.writeVarInt(size);
this.writeSignedInt(data.data.value, size);
}
else if (data.data instanceof EBMLUnicodeString) {
const bytes = textEncoder.encode(data.data.value);
this.writeVarInt(bytes.length);
this.writer.write(bytes);
}
else {
assertNever(data.data);
}
}
}
}
export const MAX_VAR_INT_SIZE = 8;
export const MIN_HEADER_SIZE = 2; // 1-byte ID and 1-byte size
export const MAX_HEADER_SIZE = 2 * MAX_VAR_INT_SIZE; // 8-byte ID and 8-byte size
export const readVarIntSize = (slice) => {
if (slice.remainingLength < 1) {
return null;
}
const firstByte = readU8(slice);
slice.skip(-1);
if (firstByte === 0) {
return null; // Invalid VINT
}
let width = 1;
let mask = 0x80;
while ((firstByte & mask) === 0) {
width++;
mask >>= 1;
}
// Check if we have enough bytes to read the full varint
if (slice.remainingLength < width) {
return null;
}
return width;
};
export const readVarInt = (slice) => {
if (slice.remainingLength < 1) {
return null;
}
// Read the first byte to determine the width of the variable-length integer
const firstByte = readU8(slice);
if (firstByte === 0) {
return null; // Invalid VINT
}
// Find the position of VINT_MARKER, which determines the width
let width = 1;
let mask = 1 << 7;
while ((firstByte & mask) === 0) {
width++;
mask >>= 1;
}
if (slice.remainingLength < width - 1) {
// Not enough bytes
return null;
}
// First byte's value needs the marker bit cleared
let value = firstByte & (mask - 1);
// Read remaining bytes
for (let i = 1; i < width; i++) {
value *= 1 << 8;
value += readU8(slice);
}
return value;
};
export const readUnsignedInt = (slice, width) => {
if (width < 1 || width > 8) {
throw new Error('Bad unsigned int size ' + width);
}
let value = 0;
// Read bytes from most significant to least significant
for (let i = 0; i < width; i++) {
value *= 1 << 8;
value += readU8(slice);
}
return value;
};
export const readUnsignedBigInt = (slice, width) => {
if (width < 1) {
throw new Error('Bad unsigned int size ' + width);
}
let value = 0n;
for (let i = 0; i < width; i++) {
value <<= 8n;
value += BigInt(readU8(slice));
}
return value;
};
export const readSignedInt = (slice, width) => {
let value = readUnsignedInt(slice, width);
// If the highest bit is set, convert from two's complement
if (value & (1 << (width * 8 - 1))) {
value -= 2 ** (width * 8);
}
return value;
};
export const readElementId = (slice) => {
const size = readVarIntSize(slice);
if (size === null) {
return null;
}
if (slice.remainingLength < size) {
return null; // It don't fit
}
const id = readUnsignedInt(slice, size);
return id;
};
/** Returns `undefined` to indicate the EBML undefined size. Returns `null` if the size couldn't be read. */
export const readElementSize = (slice) => {
// Need at least 1 byte to read the size
if (slice.remainingLength < 1) {
return null;
}
const firstByte = readU8(slice);
if (firstByte === 0xff) {
return undefined;
}
slice.skip(-1);
const size = readVarInt(slice);
if (size === null) {
return null;
}
// In some (livestreamed) files, this is the value of the size field. While this technically is just a very
// large number, it is intended to behave like the reserved size 0xFF, meaning the size is undefined. We
// catch the number here. Note that it cannot be perfectly represented as a double, but the comparison works
// nonetheless.
// eslint-disable-next-line no-loss-of-precision
if (size === 0x00ffffffffffffff) {
return undefined;
}
return size;
};
export const readElementHeader = (slice) => {
assert(slice.remainingLength >= MIN_HEADER_SIZE);
const id = readElementId(slice);
if (id === null) {
return null;
}
const size = readElementSize(slice);
if (size === null) {
return null;
}
return { id, size };
};
export const readAsciiString = (slice, length) => {
const bytes = readBytes(slice, length);
// Actual string length might be shorter due to null terminators
let strLength = 0;
while (strLength < length && bytes[strLength] !== 0) {
strLength += 1;
}
return String.fromCharCode(...bytes.subarray(0, strLength));
};
export const readUnicodeString = (slice, length) => {
const bytes = readBytes(slice, length);
// Actual string length might be shorter due to null terminators
let strLength = 0;
while (strLength < length && bytes[strLength] !== 0) {
strLength += 1;
}
return textDecoder.decode(bytes.subarray(0, strLength));
};
export const readFloat = (slice, width) => {
if (width === 0) {
return 0;
}
if (width !== 4 && width !== 8) {
throw new Error('Bad float size ' + width);
}
return width === 4 ? readF32Be(slice) : readF64Be(slice);
};
/** Returns the byte offset in the file of the next element with a matching ID. */
export const searchForNextElementId = async (reader, startPos, ids, until) => {
const idsSet = new Set(ids);
let currentPos = startPos;
while (until === null || currentPos < until) {
let slice = reader.requestSliceRange(currentPos, MIN_HEADER_SIZE, MAX_HEADER_SIZE);
if (slice instanceof Promise)
slice = await slice;
if (!slice)
break;
const elementHeader = readElementHeader(slice);
if (!elementHeader) {
break;
}
if (idsSet.has(elementHeader.id)) {
return { pos: currentPos, found: true };
}
assertDefinedSize(elementHeader.size);
currentPos = slice.filePos + elementHeader.size;
}
return { pos: (until !== null && until > currentPos) ? until : currentPos, found: false };
};
/** Searches for the next occurrence of an element ID using a naive byte-wise search. */
export const resync = async (reader, startPos, ids, until) => {
const CHUNK_SIZE = 2 ** 16; // So we don't need to grab thousands of slices
const idsSet = new Set(ids);
let currentPos = startPos;
while (currentPos < until) {
let slice = reader.requestSliceRange(currentPos, 0, Math.min(CHUNK_SIZE, until - currentPos));
if (slice instanceof Promise)
slice = await slice;
if (!slice)
break;
if (slice.length < MAX_VAR_INT_SIZE)
break;
for (let i = 0; i < slice.length - MAX_VAR_INT_SIZE; i++) {
slice.filePos = currentPos;
const elementId = readElementId(slice);
if (elementId !== null && idsSet.has(elementId)) {
return currentPos;
}
currentPos++;
}
}
return null;
};
export const CODEC_STRING_MAP = {
'avc': 'V_MPEG4/ISO/AVC',
'hevc': 'V_MPEGH/ISO/HEVC',
'vp8': 'V_VP8',
'vp9': 'V_VP9',
'av1': 'V_AV1',
'aac': 'A_AAC',
'mp3': 'A_MPEG/L3',
'opus': 'A_OPUS',
'vorbis': 'A_VORBIS',
'flac': 'A_FLAC',
'ac3': 'A_AC3',
'eac3': 'A_EAC3',
'pcm-u8': 'A_PCM/INT/LIT',
'pcm-s16': 'A_PCM/INT/LIT',
'pcm-s16be': 'A_PCM/INT/BIG',
'pcm-s24': 'A_PCM/INT/LIT',
'pcm-s24be': 'A_PCM/INT/BIG',
'pcm-s32': 'A_PCM/INT/LIT',
'pcm-s32be': 'A_PCM/INT/BIG',
'pcm-f32': 'A_PCM/FLOAT/IEEE',
'pcm-f64': 'A_PCM/FLOAT/IEEE',
'webvtt': 'S_TEXT/WEBVTT',
};
export function assertDefinedSize(size) {
if (size === undefined) {
throw new Error('Undefined element size is used in a place where it is not supported.');
}
}
;