@framekit/webm-writer-esm
Version:
WebM Encoder for Canvas Frames
200 lines (181 loc) • 5.53 kB
JavaScript
/* eslint no-fallthrough: "off" */
/**
* A tool for presenting an ArrayBuffer as a stream for writing some simple data types.
*
* By Nicholas Sherlock
*
*/
/*
* Create an ArrayBuffer of the given length and present it as a writable stream with methods
* for writing data in different formats.
*/
export function ArrayBufferDataStream(length) {
this.data = new Uint8Array(length);
this.pos = 0;
}
ArrayBufferDataStream.prototype.seek = function (toOffset) {
this.pos = toOffset;
};
ArrayBufferDataStream.prototype.writeBytes = function (arr) {
for (let i = 0; i < arr.length; i++) {
this.data[this.pos++] = arr[i];
}
};
ArrayBufferDataStream.prototype.writeByte = function (b) {
this.data[this.pos++] = b;
};
//Synonym:
ArrayBufferDataStream.prototype.writeU8 =
ArrayBufferDataStream.prototype.writeByte;
ArrayBufferDataStream.prototype.writeU16BE = function (u) {
this.data[this.pos++] = u >> 8;
this.data[this.pos++] = u;
};
ArrayBufferDataStream.prototype.writeDoubleBE = function (d) {
let bytes = new Uint8Array(new Float64Array([d]).buffer);
for (let i = bytes.length - 1; i >= 0; i--) {
this.writeByte(bytes[i]);
}
};
ArrayBufferDataStream.prototype.writeFloatBE = function (d) {
let bytes = new Uint8Array(new Float32Array([d]).buffer);
for (let i = bytes.length - 1; i >= 0; i--) {
this.writeByte(bytes[i]);
}
};
/**
* Write an ASCII string to the stream
*/
ArrayBufferDataStream.prototype.writeString = function (s) {
for (let i = 0; i < s.length; i++) {
this.data[this.pos++] = s.charCodeAt(i);
}
};
/**
* Write the given 32-bit integer to the stream as an EBML variable-length integer using the given byte width
* (use measureEBMLVarInt).
*
* No error checking is performed to ensure that the supplied width is correct for the integer.
*
* @param i Integer to be written
* @param width Number of bytes to write to the stream
*/
ArrayBufferDataStream.prototype.writeEBMLVarIntWidth = function (i, width) {
switch (width) {
case 1:
this.writeU8((1 << 7) | i);
break;
case 2:
this.writeU8((1 << 6) | (i >> 8));
this.writeU8(i);
break;
case 3:
this.writeU8((1 << 5) | (i >> 16));
this.writeU8(i >> 8);
this.writeU8(i);
break;
case 4:
this.writeU8((1 << 4) | (i >> 24));
this.writeU8(i >> 16);
this.writeU8(i >> 8);
this.writeU8(i);
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.writeU8((1 << 3) | ((i / 4294967296) & 0x7));
this.writeU8(i >> 24);
this.writeU8(i >> 16);
this.writeU8(i >> 8);
this.writeU8(i);
break;
default:
throw new Error('Bad EBML VINT size ' + width);
}
};
/**
* Return the number of bytes needed to encode the given integer as an EBML VINT.
*/
ArrayBufferDataStream.prototype.measureEBMLVarInt = function (val) {
if (val < (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 (val < (1 << 14) - 1) {
return 2;
} else if (val < (1 << 21) - 1) {
return 3;
} else if (val < (1 << 28) - 1) {
return 4;
} else if (val < 34359738367) {
// 2 ^ 35 - 1 (can address 32GB)
return 5;
} else {
throw new Error('EBML VINT size not supported ' + val);
}
};
ArrayBufferDataStream.prototype.writeEBMLVarInt = function (i) {
this.writeEBMLVarIntWidth(i, this.measureEBMLVarInt(i));
};
/**
* Write the given unsigned 32-bit integer to the stream in big-endian order using the given byte width.
* No error checking is performed to ensure that the supplied width is correct for the integer.
*
* Omit the width parameter to have it determined automatically for you.
*
* @param u Unsigned integer to be written
* @param width Number of bytes to write to the stream
*/
ArrayBufferDataStream.prototype.writeUnsignedIntBE = function (u, width) {
if (width === undefined) {
width = this.measureUnsignedInt(u);
}
// Each case falls through:
switch (width) {
case 5:
this.writeU8(Math.floor(u / 4294967296)); // Need to use division to access >32 bits of floating point var
case 4:
this.writeU8(u >> 24);
case 3:
this.writeU8(u >> 16);
case 2:
this.writeU8(u >> 8);
case 1:
this.writeU8(u);
break;
default:
throw new Error('Bad UINT size ' + width);
}
};
/**
* Return the number of bytes needed to hold the non-zero bits of the given unsigned integer.
*/
ArrayBufferDataStream.prototype.measureUnsignedInt = function (val) {
// Force to 32-bit unsigned integer
if (val < 1 << 8) {
return 1;
} else if (val < 1 << 16) {
return 2;
} else if (val < 1 << 24) {
return 3;
} else if (val < 4294967296) {
return 4;
} else {
return 5;
}
};
/**
* Return a view on the portion of the buffer from the beginning to the current seek position as a Uint8Array.
*/
ArrayBufferDataStream.prototype.getAsDataArray = function () {
if (this.pos < this.data.byteLength) {
return this.data.subarray(0, this.pos);
} else if (this.pos == this.data.byteLength) {
return this.data;
} else {
throw new Error('ArrayBufferDataStream\'s pos lies beyond end of buffer');
}
};