]> 105106.c2e0p.group Git - movie_parser.git/blobdiff - Atom.js
Add new atom types: SampleSize, TrackExtends, OriginalFormat, SchemeType, and SchemeInfo.
[movie_parser.git] / Atom.js
diff --git a/Atom.js b/Atom.js
index 52ee673e5d14ff65b9fd1d3755aa7a1619f7e6b9..8a73803fd4bc2d5a0f21c0c50958f80f1f1f7370 100644 (file)
--- a/Atom.js
+++ b/Atom.js
-var Atom = function(buffer, offset) {
-    this.is64bit = false;
-    this.size = 0;
-    this.minimumSize;
-    this.type = '';
-    
+var Atom = function (buffer, offset) {
+    this.setDefaults();
     return this.parse(buffer, offset) ? this : null;
 };
 
-Atom.create = function(buffer, offset)
-{
+Atom.create = function (buffer, offset) {
     // 'offset' is optional.
-    if (arguments.length < 2)
+    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);
-    
+    var atom;
+
     switch (type) {
         case 'ftyp':
-            return new FtypAtom(buffer, offset);
+            return new FileTypeAtom(buffer, offset);
         case 'moov':
+            return new ContainerAtom(buffer, offset, 'Movie Atom');
+        case 'trak':
+            return new ContainerAtom(buffer, offset, 'Track Atom');
+        case 'mdia':
+            return new ContainerAtom(buffer, offset, 'Media Atom');
+        case 'minf':
+            return new ContainerAtom(buffer, offset, 'Media Info Atom');
+        case 'mvex':
+            return new ContainerAtom(buffer, offset, 'Movie Extends Atom');
+        case 'stbl':
             return new ContainerAtom(buffer, offset);
         case 'mvhd':
-            return new MvhdAtom(buffer, offset);
+            return new MovieHeaderAtom(buffer, offset);
+        case 'tkhd':
+            return new TrackHeaderAtom(buffer, offset);
+        case 'mdhd':
+            return new MediaHeaderAtom(buffer, offset);
+        case 'stss':
+            return new SyncSampleAtom(buffer, offset);
+        case 'stts':
+            return new TimeToSampleAtom(buffer, offset);
+        case 'stsz':
+            return new SampleSizeAtom(buffer, offset);
+        case 'trex':
+            return new TrackExtendsAtom(buffer, offset);
+        case 'sinf':
+            return new ContainerAtom(buffer, offset, 'Protection Scheme Info Atom');
+        case 'ipro':
+            return new ContainerAtom(buffer, offset, 'Item Protection Atom');
+        case 'frma':
+            return new OriginalFormatBox(buffer, offset);
+        case 'schm':
+            return new SchemeTypeBox(buffer, offset);
+        case 'schi':
+            return new SchemeInfoBox(Buffer, offset);
         default:
             return new Atom(buffer, offset);
     }
-}
+};
+
+Atom.prototype.super = function (object) {
+    return Object.getPrototypeOf(object.prototype);
+};
+
+Atom.prototype.setDefaults = function () {
+    Object.defineProperty(this, "is64bit", {
+            value: false,
+            writable: true,
+            enumerable: false,
+            configurable: true,
+        });
+    Object.defineProperty(this, "minimumSize", {
+            value: 8,
+            writable: true,
+            enumerable: false,
+            configurable: true,
+        });
+    Object.defineProperty(this, "parent", {
+            value: null,
+            writable: true,
+            enumerable: false,
+            configurable: true,
+        });
+    Object.defineProperty(this, "description", {
+            value: "Undifferentiated Atom",
+            writable: true,
+            enumerable: false,
+            configurable: true,
+        });
+    this.offset = 0;
+    this.size = 0;
+    this.type = '';
+};
 
-Atom.prototype.parse = function(buffer, offset)
-{
+Atom.prototype.parse = function (buffer, offset) {
     // 'offset' is optional.
-    if (arguments.length < 2)
+    if (typeof(offset) == 'undefined')
         offset = 0;
 
-    // Atoms are 8 bytes minimum.
-    if (buffer.byteLength - offset < 8)
-        return false;
+    this.offset = offset;
+
+    if (buffer.byteLength - offset < this.minimumSize)
+        throw 'Buffer not long enough';
 
     var view = new DataView(buffer, offset, 4);
-    offset += 4;
+    var headerOffset = 0;
+
     this.size = view.getUint32(0);
+    headerOffset += 4;
 
-    var typeArrayView = new Uint8Array(buffer, offset, 4);
-    offset += 4;
+    var typeArrayView = new Uint8Array(buffer, offset + headerOffset, 4);
     this.type = String.fromCharCode.apply(null, typeArrayView);
+    headerOffset += 4;
 
     if (this.size == 1) {
         this.is64bit = true;
         if (buffer.byteLength - offset < 8)
-            return false;
+            throw 'Malformed extended size field';
 
-        // NOTE: JavaScript can only represent up to 2^53 as precise integer. 
+        // 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 view = new DataView(buffer, offset + headerOffset, 8);
         var upper = view.getUint32(0);
         var lower = view.getUint32(4);
         this.size = (upper << 32)  + lower;
+        headerOffset += 8;
     }
-    
-    return true;
+
+    return headerOffset;
 };
 
-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);
+Atom.prototype.getAtomByType = function (type) {
+    if (typeof(this.childAtoms) == 'undefined')
+        return null;
+
+    // Bredth first
+    for (var index in this.childAtoms) {
+        if (this.childAtoms[index].type == type)
+            return this.childAtoms[index];
     }
-    return output;
+
+    var result = null;
+    for (var index in this.childAtoms) {
+        if (result = this.childAtoms[index].getAtomByType(type))
+            break;
+    }
+    return result;
+};
+
+Atom.prototype.getAtomsByType = function (type) {
+    if (typeof(this.childAtoms) == 'undefined')
+        return [];
+
+    var result = [];
+
+    // Bredth first
+    for (var index in this.childAtoms) {
+        if (this.childAtoms[index].type == type)
+            result.push(this.childAtoms[index]);
+    }
+
+    for (var index in this.childAtoms)
+        result = result.concat(this.childAtoms[index].getAtomsByType(type));
+
+    return result;
+};
+
+var FileTypeAtom = function (buffer, offset) {
+    this.super(FileTypeAtom).constructor.call(this, buffer, offset);
 }
 
-var FtypAtom = function(buffer, offset) {
+FileTypeAtom.prototype = Object.create(Atom.prototype);
+
+FileTypeAtom.prototype.setDefaults = function () {
+    this.super(FileTypeAtom).setDefaults.call(this);
+    this.description = "File Type Atom";
     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);
+FileTypeAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(FileTypeAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset, this.size);
 
-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;
+    var brandArrayView = new Uint8Array(buffer, offset + headerOffset, 4);
     this.brand = String.fromCharCode.apply(null, brandArrayView);
+    headerOffset += 4;
 
-    var view = new DataView(buffer, offset, 4);
-    offset += 4;
-    this.version = view.getUint32(0);
+    this.version = view.getUint32(headerOffset);
+    headerOffset += 4;
 
-    while (offset <= end - 4) {
-        var brandArrayView = new Uint8Array(buffer, offset, 4);
-        offset += 4;
+    while (headerOffset < this.size - 4) {
+        var brandArrayView = new Uint8Array(buffer, offset + headerOffset, 4);
         this.compatible_brands.push(String.fromCharCode.apply(null, brandArrayView));
+        headerOffset += 4;
     }
-    
+
     return true;
 }
 
-var ContainerAtom = function(buffer, offset) {
-    this.childAtoms = [];
-
-    return Object.getPrototypeOf(ContainerAtom.prototype).constructor.call(this, buffer, offset);
+var ContainerAtom = function (buffer, offset, description) {
+    var atom = this.super(ContainerAtom).constructor.call(this, buffer, offset);
+    atom.description = description;
+    return;
 }
 
 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;
+ContainerAtom.prototype.setDefaults = function () {
+    this.super(ContainerAtom).setDefaults.call(this);
+    this.childAtoms = [];
+}
 
-    while (offset < end) {
-        var childAtom = Atom.create(buffer, offset);
+ContainerAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(ContainerAtom).parse.call(this, buffer, offset);
+    while (headerOffset < this.size) {
+        var childAtom = Atom.create(buffer, offset + headerOffset);
         if (!childAtom)
             break;
-        offset += childAtom.size;
+        headerOffset += childAtom.size;
         this.childAtoms.push(childAtom);
+        childAtom.parent = this;
     }
+       return headerOffset;
 }
 
-var MvhdAtom = function(buffer, offset) {
+var VersionFlagsAtom = function (buffer, offset) {
+    this.super(VersionFlagsAtom).constructor.call(this, buffer, offset);
+}
+
+VersionFlagsAtom.prototype = Object.create(Atom.prototype);
+
+VersionFlagsAtom.prototype.setDefaults = function () {
+    this.super(VersionFlagsAtom).setDefaults.call(this);
     this.version = 0;
+    this.flags = 0;
+}
+
+VersionFlagsAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(VersionFlagsAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.version = view.getUint8(headerOffset);
+    headerOffset += 1;
+
+    // 'flags' is a 3-byte field, so retrieve from one extra byte and concatenate
+    this.flags = (view.getUint8(headerOffset) << 8) + view.getUint16(headerOffset + 1);
+    headerOffset += 3;
+
+    return headerOffset;
+}
+
+var MovieHeaderAtom = function (buffer, offset) {
+    return this.super(MovieHeaderAtom).constructor.call(this, buffer, offset);
+}
+
+MovieHeaderAtom.prototype = Object.create(VersionFlagsAtom.prototype);
+
+MovieHeaderAtom.prototype.setDefaults = function () {
+    this.super(MovieHeaderAtom).setDefaults.call(this);
+    this.description = "Movie Header Atom";
     this.creationTime = 0;
     this.modificationTime = 0;
     this.timeScale = 0;
@@ -181,41 +275,29 @@ var MvhdAtom = function(buffer, offset) {
     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;
+MovieHeaderAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(MovieHeaderAtom).parse.call(this, buffer, offset);
     var view = new DataView(buffer, offset);
 
-    this.version = view.getUint8(headerOffset);
+    this.creationTime = new Date(view.getUint32(headerOffset)*1000 + Date.UTC(1904, 0, 1));
     headerOffset += 4;
 
-    this.creationTime = view.getUint32(headerOffset);
-    headerOffset += 4;
-
-    this.modificationTime = view.getUint32(headerOffset);
+    this.modificationTime = new Date(view.getUint32(headerOffset)*1000 + Date.UTC(1904, 0, 1));
     headerOffset += 4;
 
     this.timeScale = view.getUint32(headerOffset);
     headerOffset += 4;
-    
+
     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;
+    this.preferredVolume = view.getUint16(headerOffset) / (1 << 8);
+    headerOffset += 2;
 
     // Reserved
     // Ten bytes reserved for use by Apple. Set to 0.
@@ -224,48 +306,413 @@ MvhdAtom.prototype.parse = function(buffer, offset) {
     this.movieMatrix = new Array(3);
     // a, b, u:
     this.movieMatrix[0] = new Array(3);
-    this.movieMatrix[0][1] = view.getUint32(headerOffset) / (1 << 16);
+    this.movieMatrix[0][0] = view.getUint32(headerOffset) / (1 << 16);
     headerOffset += 4;
-    this.movieMatrix[0][2] = view.getUint32(headerOffset) / (1 << 16);
+    this.movieMatrix[0][1] = view.getUint32(headerOffset) / (1 << 16);
     headerOffset += 4;
-    this.movieMatrix[0][3] = view.getUint32(headerOffset) / (1 << 30);
+    this.movieMatrix[0][2] = 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);
+    this.movieMatrix[1][0] = view.getUint32(headerOffset) / (1 << 16);
     headerOffset += 4;
-    this.movieMatrix[1][2] = view.getUint32(headerOffset) / (1 << 16);
+    this.movieMatrix[1][1] = view.getUint32(headerOffset) / (1 << 16);
     headerOffset += 4;
-    this.movieMatrix[1][3] = view.getUint32(headerOffset) / (1 << 30);
+    this.movieMatrix[1][2] = 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);
+    this.movieMatrix[2][0] = view.getUint32(headerOffset) / (1 << 16);
     headerOffset += 4;
-    this.movieMatrix[2][2] = view.getUint32(headerOffset) / (1 << 16);
+    this.movieMatrix[2][1] = view.getUint32(headerOffset) / (1 << 16);
     headerOffset += 4;
-    this.movieMatrix[2][3] = view.getUint32(headerOffset) / (1 << 30);
+    this.movieMatrix[2][2] = 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
+}
+
+var TrackHeaderAtom = function (buffer, offset) {
+    this.super(TrackHeaderAtom).constructor.call(this, buffer, offset);
+}
+
+TrackHeaderAtom.prototype = Object.create(VersionFlagsAtom.prototype);
+
+TrackHeaderAtom.prototype.setDefaults = function () {
+    this.super(TrackHeaderAtom).setDefaults.call(this);
+
+    this.description = "Track Header Atom";
+    this.creationTime = 0;
+    this.modificationTime = 0;
+    this.trackID = 0;
+    this.duration = 0;
+    this.layer = 0;
+    this.alternateGroup = 0;
+    this.volume = 0.0;
+    this.trackMatrix = [];
+    this.width = 0;
+    this.height = 0;
+}
+
+TrackHeaderAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(TrackHeaderAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.creationTime = new Date(view.getUint32(headerOffset)*1000 + Date.UTC(1904, 0, 1));
+    headerOffset += 4;
+
+    this.modificationTime = new Date(view.getUint32(headerOffset)*1000 + Date.UTC(1904, 0, 1));
+    headerOffset += 4;
+
+    this.trackID = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    // Reserved
+    // A 32-bit integer that is reserved for use by Apple. Set this field to 0.
+    headerOffset += 4;
+
+    this.duration = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    // Reserved
+    // An 8-byte value that is reserved for use by Apple. Set this field to 0.
+    headerOffset += 8;
+
+    this.layer = view.getUint16(headerOffset);
+    headerOffset += 2;
+
+    this.alternateGroup = view.getUint16(headerOffset);
+    headerOffset += 2;
+
+    this.volume = view.getUint16(headerOffset) / (1 << 8);
+    headerOffset += 2;
+
+    // Reserved
+    // A 16-bit integer that is reserved for use by Apple. Set this field to 0.
+    headerOffset += 2;
+
+    this.trackMatrix = new Array(3);
+    // a, b, u:
+    this.trackMatrix[0] = new Array(3);
+    this.trackMatrix[0][0] = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+    this.trackMatrix[0][1] = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+    this.trackMatrix[0][2] = view.getUint32(headerOffset) / (1 << 30);
+    headerOffset += 4;
+
+    // c, d, v:
+    this.trackMatrix[1] = new Array(3);
+    this.trackMatrix[1][0] = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+    this.trackMatrix[1][1] = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+    this.trackMatrix[1][2] = view.getUint32(headerOffset) / (1 << 30);
+    headerOffset += 4;
+
+    // x, y, w:
+    this.trackMatrix[2] = new Array(3);
+    this.trackMatrix[2][0] = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+    this.trackMatrix[2][1] = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+    this.trackMatrix[2][2] = view.getUint32(headerOffset) / (1 << 30);
+    headerOffset += 4;
+
+    this.width = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+
+    this.height = view.getUint32(headerOffset) / (1 << 16);
+    headerOffset += 4;
+}
+
+var MediaHeaderAtom = function (buffer, offset) {
+    this.super(MediaHeaderAtom).constructor.call(this, buffer, offset);
+}
+
+MediaHeaderAtom.prototype = Object.create(VersionFlagsAtom.prototype);
+
+MediaHeaderAtom.prototype.setDefaults = function () {
+    this.super(MediaHeaderAtom).setDefaults.call(this);
+
+    this.description = "Media Header Atom";
+    this.creationTime = 0;
+    this.modificationTime = 0;
+    this.timeScale = 0;
+    this.duration = 0;
+    this.language = 0;
+    this.quality = 0;
+}
+
+MediaHeaderAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(MediaHeaderAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.creationTime = new Date(view.getUint32(headerOffset)*1000 + Date.UTC(1904, 0, 1));
+    headerOffset += 4;
+
+    this.modificationTime = new Date(view.getUint32(headerOffset)*1000 + Date.UTC(1904, 0, 1));
+    headerOffset += 4;
+
+    this.timeScale = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.duration = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.language = view.getUint16(headerOffset);
+    headerOffset += 2;
+
+    this.quality = view.getUint16(headerOffset);
+    headerOffset += 2;
+};
+
+var SyncSampleAtom = function (buffer, offset) {
+    this.super(SyncSampleAtom).constructor.call(this, buffer, offset);
+};
+
+SyncSampleAtom.prototype = Object.create(Atom.prototype);
+
+SyncSampleAtom.prototype.setDefaults = function () {
+    this.super(SyncSampleAtom).setDefaults.call(this);
+
+    this.description = "Sync Sample Atom";
+    this.version = 0;
+    this.flags = 0;
+    this.entries = 0;
+    this.syncSamples = [];
+};
+
+SyncSampleAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(SyncSampleAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.version = view.getUint8(headerOffset);
+    headerOffset += 1;
+
+    // 'flags' is a 3-byte field, so retrieve from one extra byte and concatenate
+    this.flags = (view.getUint8(headerOffset) << 8) + view.getUint16(headerOffset + 1);
+    headerOffset += 3;
+
+    this.entries = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.syncSamples = new Uint32Array(this.entries);
+    var i = 0;
+    while (headerOffset < this.size) {
+        var sampleNumber = view.getUint32(headerOffset);
+        headerOffset += 4;
+        this.syncSamples[i] = sampleNumber;
+        ++i;
+    }
+};
+
+var TimeToSampleAtom = function (buffer, offset) {
+    this.super(TimeToSampleAtom).constructor.call(this, buffer, offset);
+};
+
+TimeToSampleAtom.prototype = Object.create(VersionFlagsAtom.prototype);
+
+TimeToSampleAtom.prototype.setDefaults = function () {
+    this.super(TimeToSampleAtom).setDefaults.call(this);
+
+    this.description = "Time-to-Sample Atom";
+    this.entries = 0;
+    
+    Object.defineProperty(this, "timeToSamples", {
+        value: null,
+        writable: true,
+        enumerable: false,
+        configurable: true,
+    });
+}
+
+TimeToSampleAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(TimeToSampleAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.entries = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.timeToSamples = new Array(this.entries);
+    var i = 0;
+
+    while (headerOffset < this.size) {
+        var sampleCount = view.getUint32(headerOffset);
+        headerOffset += 4;
+
+        var sampleDuration = view.getUint32(headerOffset);
+        headerOffset += 4;
+
+        this.timeToSamples[i] = [sampleCount, sampleDuration];
+        ++i;
+    }
+}
+
+var SampleSizeAtom = function (buffer, offset) {
+    this.super(SampleSizeAtom).constructor.call(this, buffer, offset);
+}
+
+SampleSizeAtom.prototype = Object.create(VersionFlagsAtom.prototype);
+
+SampleSizeAtom.prototype.setDefaults = function () {
+    this.super(SampleSizeAtom).setDefaults.call(this);
+
+    this.description = "Sample Size Atom";
+    this.sampleSize = 0;
+    this.entries = 0;
+    
+    Object.defineProperty(this, "sampleSizes", {
+        value: null,
+        writable: true,
+        enumerable: false,
+        configurable: true,
+    });
+}
+
+SampleSizeAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(SampleSizeAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.sampleSize = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.entries = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.sampleSizes = new Uint32Array(this.entries);
+    var i = 0;
+
+    while (headerOffset < this.size) {
+        this.sampleSizes[i] = view.getUint32(headerOffset);
+        headerOffset += 4;
+        ++i;
+    }
+}
+
+var TrackExtendsAtom = function (buffer, offset) {
+    this.super(TrackExtendsAtom).constructor.call(this, buffer, offset);
+}
+
+TrackExtendsAtom.prototype = Object.create(VersionFlagsAtom.prototype);
+
+TrackExtendsAtom.prototype.setDefaults = function () {
+    this.super(TrackExtendsAtom).setDefaults.call(this);
+
+    this.description = "Track Extends Atom";
+    this.trackID = 0;
+    this.default_sample_description_index = 0;
+    this.default_sample_duration = 0;
+    this.default_sample_size = 0;
+    this.default_sample_flags = 0;
+}
+
+TrackExtendsAtom.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(TrackExtendsAtom).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.trackID = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.default_sample_description_index = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.default_sample_duration = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.default_sample_size = view.getUint32(headerOffset);
+    headerOffset += 4;
+
+    this.default_sample_flags = view.getUint32(headerOffset);
+    headerOffset += 4;
+}
+
+var OriginalFormatBox = function (buffer, offset) {
+    this.super(OriginalFormatBox).constructor.call(this, buffer, offset);
+}
+
+OriginalFormatBox.prototype = Object.create(Atom.prototype);
+
+OriginalFormatBox.prototype.setDefaults = function () {
+    this.super(OriginalFormatBox).setDefaults.call(this);
+
+    this.description = "Original Format Box";
+    this.dataFormat = 0;
+}
+
+OriginalFormatBox.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(OriginalFormatBox).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.dataFormat = view.getUint32(headerOffset);
+    headerOffset += 4;
+}
+
+var SchemeTypeBox = function (buffer, offset) {
+    this.super(SchemeTypeBox).constructor.call(this, buffer, offset);
+}
+
+SchemeTypeBox.prototype = Object.create(VersionFlagsAtom.prototype);
+
+SchemeTypeBox.prototype.setDefaults = function () {
+    this.super(SchemeTypeBox).setDefaults.call(this);
+
+    this.description = "Scheme Type Box";
+    this.schemeType = 0;
+    this.schemeVersion = 0;
+    this.schemeURL = 0;
+}
+
+SchemeTypeBox.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(SchemeTypeBox).parse.call(this, buffer, offset);
+    var view = new DataView(buffer, offset);
+
+    this.schemeType = view.getUint32(headerOffset);
+    headerOffset += 4;
+    this.schemeVersion = view.getUint32(headerOffset);
+    headerOffset += 4;
+    if (this.flags & 0x1) {
+        var array = new Uint8Array(buffer, headerOffset, this.size - headerOffset);
+        this.schemeURL = String.fromCharCode.apply(null, array);
+    }
+}
+
+var SchemeInfoBox = function (buffer, offset) {
+    this.super(SchemeInfoBox).constructor.call(this, buffer, offset);
+}
+
+SchemeInfoBox.prototype = Object.create(VersionFlagsAtom.prototype);
+
+SchemeInfoBox.prototype.setDefaults = function () {
+    this.super(SchemeInfoBox).setDefaults.call(this);
+
+    this.description = "Scheme Information Box";
+    this.schemeSpecificData = 0;
+}
+
+SchemeInfoBox.prototype.parse = function (buffer, offset) {
+    var headerOffset = this.super(SchemeInfoBox).parse.call(this, buffer, offset);
+    this.schemeSpecificData = buffer.slice(headerOffset, this.size - headerOffset);
+}
+
+