var Atom = function(buffer, offset) {
+ this.is64bit = false;
this.size = 0;
+ this.minimumSize;
this.type = '';
- this.childAtoms = [];
- return this.parse(buffer, offset)
+ return this.parse(buffer, offset) ? this : null;
};
+Atom.create = function(buffer, offset)
+{
+ // 'offset' is optional.
+ if (arguments.length < 2)
+ offset = 0;
+
+ if (buffer.byteLength - offset < this.minimumSize)
+ return null;
+
+ var typeArrayView = new Uint8Array(buffer, offset + 4, 4);
+ var type = String.fromCharCode.apply(null, typeArrayView);
+
+ switch (type) {
+ case 'ftyp':
+ return new FtypAtom(buffer, offset);
+ case 'moov':
+ return new ContainerAtom(buffer, offset);
+ case 'mvhd':
+ return new MvhdAtom(buffer, offset);
+ default:
+ return new Atom(buffer, offset);
+ }
+}
+
Atom.prototype.parse = function(buffer, offset)
{
// 'offset' is optional.
// Atoms are 8 bytes minimum.
if (buffer.byteLength - offset < 8)
- return null;
+ return false;
var view = new DataView(buffer, offset, 4);
+ offset += 4;
this.size = view.getUint32(0);
- var typeArrayView = new Uint8Array(buffer, offset + 4, 4);
+ var typeArrayView = new Uint8Array(buffer, offset, 4);
+ offset += 4;
this.type = String.fromCharCode.apply(null, typeArrayView);
+
+ if (this.size == 1) {
+ this.is64bit = true;
+ if (buffer.byteLength - offset < 8)
+ return false;
+
+ // NOTE: JavaScript can only represent up to 2^53 as precise integer.
+ // This calculation may result in incorrect values.
+ var view = new DataView(buffer, offset, 8);
+ offset += 8;
+ var upper = view.getUint32(0);
+ var lower = view.getUint32(4);
+ this.size = (upper << 32) + lower;
+ }
+
+ return true;
+};
+
+Atom.prototype.toDOMNode = function()
+{
+ var output = document.createElement('dl');
+
+ for (property in this) {
+ var value = this[property];
+ if (typeof(value) == 'function')
+ continue;
+ var div = document.createElement('div');
+ var dt = document.createElement('dt');
+ dt.innerText = property;
+
+ var dd = document.createElement('dd');
+ if (value instanceof Atom)
+ dd.appendChild(value.toDOMNode());
+ else if (value instanceof Array) {
+ var ol = document.createElement('ol');
+ for (index in value) {
+ var li = document.createElement('li');
+ li.value = index;
+ if (value[index] instanceof Atom)
+ li.appendChild(value[index].toDOMNode())
+ else
+ li.innerText = value[index];
+ ol.appendChild(li);
+ }
+ dd.appendChild(ol);
+ }
+ else if (typeof(value) == "string")
+ dd.innerText = '"' + value + '"';
+ else
+ dd.innerText = value;
+
+ div.appendChild(dt);
+ div.appendChild(dd);
+ output.appendChild(div);
+ }
+ return output;
+}
+
+var FtypAtom = function(buffer, offset) {
+ this.minimumSize = 16;
+ this.brand = "";
+ this.version = 0;
+ this.compatible_brands = [];
+
+ return Object.getPrototypeOf(FtypAtom.prototype).constructor.call(this, buffer, offset)
+}
+
+FtypAtom.prototype = Object.create(Atom.prototype);
+
+FtypAtom.prototype.parse = function(buffer, offset) {
+ if (!Atom.prototype.parse.call(this, buffer, offset))
+ return false;
+
+ var begin = offset;
+ var end = begin + this.size;
+ offset += this.is64bit ? 16 : 8;
+
+ var brandArrayView = new Uint8Array(buffer, offset, 4);
+ offset += 4;
+ this.brand = String.fromCharCode.apply(null, brandArrayView);
+
+ var view = new DataView(buffer, offset, 4);
+ offset += 4;
+ this.version = view.getUint32(0);
+
+ while (offset <= end - 4) {
+ var brandArrayView = new Uint8Array(buffer, offset, 4);
+ offset += 4;
+ this.compatible_brands.push(String.fromCharCode.apply(null, brandArrayView));
+ }
+
+ return true;
+}
+
+var ContainerAtom = function(buffer, offset) {
+ this.childAtoms = [];
+
+ return Object.getPrototypeOf(ContainerAtom.prototype).constructor.call(this, buffer, offset);
+}
+
+ContainerAtom.prototype = Object.create(Atom.prototype);
+
+ContainerAtom.prototype.parse = function(buffer, offset) {
+ if (!Atom.prototype.parse.call(this, buffer, offset))
+ return false;
+
+ var begin = offset;
+ var end = begin + this.size;
+ offset += this.is64bit ? 16 : 8;
+
+ while (offset < end) {
+ var childAtom = Atom.create(buffer, offset);
+ if (!childAtom)
+ break;
+ offset += childAtom.size;
+ this.childAtoms.push(childAtom);
+ }
+}
+
+var MvhdAtom = function(buffer, offset) {
+ this.version = 0;
+ this.creationTime = 0;
+ this.modificationTime = 0;
+ this.timeScale = 0;
+ this.duration = 0;
+ this.preferredRate = 0.0;
+ this.preferredVolume = 0.0;
+ this.movieMatrix = [[]];
+ this.previewTime = 0;
+ this.posterTime = 0;
+ this.selectionTime = 0;
+ this.selectionDuration = 0;
+ this.nextTrackID = 0;
+
+ return Object.getPrototypeOf(MvhdAtom.prototype).constructor.call(this, buffer, offset);
+}
+
+MvhdAtom.prototype = Object.create(Atom.prototype);
+
+MvhdAtom.prototype.parse = function(buffer, offset) {
+ if (!Atom.prototype.parse.call(this, buffer, offset))
+ return false;
+
+ offset += this.is64bit ? 16 : 8;
+
+ var headerOffset = 0;
+ var view = new DataView(buffer, offset);
+
+ this.version = view.getUint8(headerOffset);
+ headerOffset += 4;
+
+ this.creationTime = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.modificationTime = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.timeScale = view.getUint32(headerOffset);
+ headerOffset += 4;
- return this;
-};
\ No newline at end of file
+ this.duration = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.preferredRate = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+
+ this.preferredVolume = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+
+ // Reserved
+ // Ten bytes reserved for use by Apple. Set to 0.
+ headerOffset += 10;
+
+ this.movieMatrix = new Array(3);
+ // a, b, u:
+ this.movieMatrix[0] = new Array(3);
+ this.movieMatrix[0][1] = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+ this.movieMatrix[0][2] = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+ this.movieMatrix[0][3] = view.getUint32(headerOffset) / (1 << 30);
+ headerOffset += 4;
+
+ // c, d, v:
+ this.movieMatrix[1] = new Array(3);
+ this.movieMatrix[1][1] = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+ this.movieMatrix[1][2] = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+ this.movieMatrix[1][3] = view.getUint32(headerOffset) / (1 << 30);
+ headerOffset += 4;
+
+ // x, y, w:
+ this.movieMatrix[2] = new Array(3);
+ this.movieMatrix[2][1] = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+ this.movieMatrix[2][2] = view.getUint32(headerOffset) / (1 << 16);
+ headerOffset += 4;
+ this.movieMatrix[2][3] = view.getUint32(headerOffset) / (1 << 30);
+ headerOffset += 4;
+
+ this.previewTime = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.previewDuration = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.posterTime = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.selectionTime = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.selectionDuration = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.nextTrackID = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ return true;
+}
\ No newline at end of file