mp4box
Version:
JavaScript version of GPAC's MP4Box tool
274 lines (250 loc) • 9.11 kB
JavaScript
/**
Reads a struct of data from the DataStream. The struct is defined as
a flat array of [name, type]-pairs. See the example below:
ds.readStruct([
'headerTag', 'uint32', // Uint32 in DataStream endianness.
'headerTag2', 'uint32be', // Big-endian Uint32.
'headerTag3', 'uint32le', // Little-endian Uint32.
'array', ['[]', 'uint32', 16], // Uint32Array of length 16.
'array2Length', 'uint32',
'array2', ['[]', 'uint32', 'array2Length'] // Uint32Array of length array2Length
]);
The possible values for the type are as follows:
// Number types
// Unsuffixed number types use DataStream endianness.
// To explicitly specify endianness, suffix the type with
// 'le' for little-endian or 'be' for big-endian,
// e.g. 'int32be' for big-endian int32.
'uint8' -- 8-bit unsigned int
'uint16' -- 16-bit unsigned int
'uint32' -- 32-bit unsigned int
'int8' -- 8-bit int
'int16' -- 16-bit int
'int32' -- 32-bit int
'float32' -- 32-bit float
'float64' -- 64-bit float
// String types
'cstring' -- ASCII string terminated by a zero byte.
'string:N' -- ASCII string of length N.
'string,CHARSET:N' -- String of byteLength N encoded with given CHARSET.
'u16string:N' -- UCS-2 string of length N in DataStream endianness.
'u16stringle:N' -- UCS-2 string of length N in little-endian.
'u16stringbe:N' -- UCS-2 string of length N in big-endian.
// Complex types
[name, type, name_2, type_2, ..., name_N, type_N] -- Struct
function(dataStream, struct) {} -- Callback function to read and return data.
{get: function(dataStream, struct) {},
set: function(dataStream, struct) {}}
-- Getter/setter functions to read and return data, handy for using the same
struct definition for reading and writing structs.
['[]', type, length] -- Array of given type and length. The length can be either
a number, a string that references a previously-read
field, or a callback function(struct, dataStream, type){}.
If length is '*', reads in as many elements as it can.
@param {Object} structDefinition Struct definition object.
@return {Object} The read struct. Null if failed to read struct.
*/
DataStream.prototype.readStruct = function(structDefinition) {
var struct = {}, t, v, n;
var p = this.position;
for (var i=0; i<structDefinition.length; i+=2) {
t = structDefinition[i+1];
v = this.readType(t, struct);
if (v == null) {
if (this.failurePosition === 0) {
this.failurePosition = this.position;
}
this.position = p;
return null;
}
struct[structDefinition[i]] = v;
}
return struct;
};
/**
Read UCS-2 string of desired length and endianness from the DataStream.
@param {number} length The length of the string to read.
@param {boolean} endianness The endianness of the string data in the DataStream.
@return {string} The read string.
*/
DataStream.prototype.readUCS2String = function(length, endianness) {
return String.fromCharCode.apply(null, this.readUint16Array(length, endianness));
};
/**
Reads an object of type t from the DataStream, passing struct as the thus-far
read struct to possible callbacks that refer to it. Used by readStruct for
reading in the values, so the type is one of the readStruct types.
@param {Object} t Type of the object to read.
@param {?Object} struct Struct to refer to when resolving length references
and for calling callbacks.
@return {?Object} Returns the object on successful read, null on unsuccessful.
*/
DataStream.prototype.readType = function(t, struct) {
if (typeof t == "function") {
return t(this, struct);
} else if (typeof t == "object" && !(t instanceof Array)) {
return t.get(this, struct);
} else if (t instanceof Array && t.length != 3) {
return this.readStruct(t, struct);
}
var v = null;
var lengthOverride = null;
var charset = "ASCII";
var pos = this.position;
var tp;
var i;
var u;
if (typeof t == 'string' && /:/.test(t)) {
tp = t.split(":");
t = tp[0];
lengthOverride = parseInt(tp[1]);
}
if (typeof t == 'string' && /,/.test(t)) {
tp = t.split(",");
t = tp[0];
charset = parseInt(tp[1]);
}
switch(t) {
case 'uint8':
v = this.readUint8(); break;
case 'int8':
v = this.readInt8(); break;
case 'uint16':
v = this.readUint16(this.endianness); break;
case 'int16':
v = this.readInt16(this.endianness); break;
case 'uint32':
v = this.readUint32(this.endianness); break;
case 'int32':
v = this.readInt32(this.endianness); break;
case 'float32':
v = this.readFloat32(this.endianness); break;
case 'float64':
v = this.readFloat64(this.endianness); break;
case 'uint16be':
v = this.readUint16(DataStream.BIG_ENDIAN); break;
case 'int16be':
v = this.readInt16(DataStream.BIG_ENDIAN); break;
case 'uint32be':
v = this.readUint32(DataStream.BIG_ENDIAN); break;
case 'int32be':
v = this.readInt32(DataStream.BIG_ENDIAN); break;
case 'float32be':
v = this.readFloat32(DataStream.BIG_ENDIAN); break;
case 'float64be':
v = this.readFloat64(DataStream.BIG_ENDIAN); break;
case 'uint16le':
v = this.readUint16(DataStream.LITTLE_ENDIAN); break;
case 'int16le':
v = this.readInt16(DataStream.LITTLE_ENDIAN); break;
case 'uint32le':
v = this.readUint32(DataStream.LITTLE_ENDIAN); break;
case 'int32le':
v = this.readInt32(DataStream.LITTLE_ENDIAN); break;
case 'float32le':
v = this.readFloat32(DataStream.LITTLE_ENDIAN); break;
case 'float64le':
v = this.readFloat64(DataStream.LITTLE_ENDIAN); break;
case 'cstring':
v = this.readCString(lengthOverride); break;
case 'string':
v = this.readString(lengthOverride, charset); break;
case 'u16string':
v = this.readUCS2String(lengthOverride, this.endianness); break;
case 'u16stringle':
v = this.readUCS2String(lengthOverride, DataStream.LITTLE_ENDIAN); break;
case 'u16stringbe':
v = this.readUCS2String(lengthOverride, DataStream.BIG_ENDIAN); break;
default:
if (t.length == 3) {
var ta = t[1];
var len = t[2];
var length = 0;
if (typeof len == 'function') {
length = len(struct, this, t);
} else if (typeof len == 'string' && struct[len] != null) {
length = parseInt(struct[len]);
} else {
length = parseInt(len);
}
if (typeof ta == "string") {
var tap = ta.replace(/(le|be)$/, '');
var endianness = null;
if (/le$/.test(ta)) {
endianness = DataStream.LITTLE_ENDIAN;
} else if (/be$/.test(ta)) {
endianness = DataStream.BIG_ENDIAN;
}
if (len == '*') {
length = null;
}
switch(tap) {
case 'uint8':
v = this.readUint8Array(length); break;
case 'uint16':
v = this.readUint16Array(length, endianness); break;
case 'uint32':
v = this.readUint32Array(length, endianness); break;
case 'int8':
v = this.readInt8Array(length); break;
case 'int16':
v = this.readInt16Array(length, endianness); break;
case 'int32':
v = this.readInt32Array(length, endianness); break;
case 'float32':
v = this.readFloat32Array(length, endianness); break;
case 'float64':
v = this.readFloat64Array(length, endianness); break;
case 'cstring':
case 'utf16string':
case 'string':
if (length == null) {
v = [];
while (!this.isEof()) {
u = this.readType(ta, struct);
if (u == null) break;
v.push(u);
}
} else {
v = new Array(length);
for (i=0; i<length; i++) {
v[i] = this.readType(ta, struct);
}
}
break;
}
} else {
if (len == '*') {
v = [];
var tmp_buffer = this.buffer;
while (true) {
var p = this.position;
try {
var o = this.readType(ta, struct);
if (o == null) {
this.position = p;
break;
}
v.push(o);
} catch(e) {
this.position = p;
break;
}
}
} else {
v = new Array(length);
for (i=0; i<length; i++) {
u = this.readType(ta, struct);
if (u == null) return null;
v[i] = u;
}
}
}
break;
}
}
if (lengthOverride != null) {
this.position = pos + lengthOverride;
}
return v;
};