]> id.pley.net Git - movie_parser.git/commitdiff
Added tkhd, mdhd atoms. Added getAtomByType and getAtomsByType.
authorJer Noble <jer.noble@apple.com>
Fri, 7 Dec 2012 09:06:00 +0000 (01:06 -0800)
committerJer Noble <jer.noble@apple.com>
Fri, 7 Dec 2012 09:06:00 +0000 (01:06 -0800)
Atom.js
AtomTester.html

diff --git a/Atom.js b/Atom.js
index 52ee673e5d14ff65b9fd1d3755aa7a1619f7e6b9..c7507db2f734e9917fd041d47d459b83d76ed917 100644 (file)
--- a/Atom.js
+++ b/Atom.js
@@ -1,9 +1,5 @@
 var Atom = function(buffer, offset) {
-    this.is64bit = false;
-    this.size = 0;
-    this.minimumSize;
-    this.type = '';
-    
+    this.setDefaults();
     return this.parse(buffer, offset) ? this : null;
 };
 
@@ -23,14 +19,46 @@ Atom.create = function(buffer, offset)
         case 'ftyp':
             return new FtypAtom(buffer, offset);
         case 'moov':
+        case 'trak':
+        case 'mdia':
+        case 'minf':
+        case 'stbl':
             return new ContainerAtom(buffer, offset);
         case 'mvhd':
             return new MvhdAtom(buffer, offset);
+        case 'tkhd':
+            return new TkhdAtom(buffer, offset);
+        case 'mdhd':
+            return new MdhdAtom(buffer, offset);
         default:
             return new Atom(buffer, offset);
     }
 }
 
+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,
+        });
+    this.size = 0;
+    this.type = '';
+}
+
 Atom.prototype.parse = function(buffer, offset)
 {
     // 'offset' is optional.
@@ -66,57 +94,56 @@ Atom.prototype.parse = function(buffer, offset)
     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);
+Atom.prototype.getAtomByType = function(type) {
+    if (typeof(this.childAtoms) == 'undefined')
+        return null;
+
+    // Bredth first
+    for (index in this.childAtoms) {
+        if (this.childAtoms[index].type == type)
+            return this.childAtoms[index];
     }
-    return output;
-}
+
+    var result = null;
+    for (index in this.childAtoms) {
+        if (result = this.childAtoms[index].getAtomsByType(type))
+            break;
+    }
+    return result;
+};
+
+Atom.prototype.getAtomsByType = function(type) {
+    if (typeof(this.childAtoms) == 'undefined')
+        return [];
+
+    var result = [];
+
+    // Bredth first
+    for (index in this.childAtoms) {
+        if (this.childAtoms[index].type == type)
+            result.push(this.childAtoms[index]);
+    }
+
+    for (index in this.childAtoms)
+        result = result.concat(this.childAtoms[index].getAtomsByType(type));
+
+    return result;
+};
 
 var FtypAtom = function(buffer, offset) {
+    return Atom.prototype.constructor.call(this, buffer, offset);
+}
+
+FtypAtom.prototype = Object.create(Atom.prototype);
+
+FtypAtom.prototype.setDefaults = function() {
+    Atom.prototype.setDefaults.call(this);
     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;
@@ -143,13 +170,17 @@ FtypAtom.prototype.parse = function(buffer, offset) {
 }
 
 var ContainerAtom = function(buffer, offset) {
-    this.childAtoms = [];
-
-    return Object.getPrototypeOf(ContainerAtom.prototype).constructor.call(this, buffer, offset);
+    return Atom.prototype.constructor.call(this, buffer, offset);
 }
 
 ContainerAtom.prototype = Object.create(Atom.prototype);
 
+ContainerAtom.prototype.setDefaults = function()
+{
+    Atom.prototype.setDefaults.call(this);
+    this.childAtoms = [];
+}
+
 ContainerAtom.prototype.parse = function(buffer, offset) {
     if (!Atom.prototype.parse.call(this, buffer, offset))
         return false;
@@ -164,10 +195,18 @@ ContainerAtom.prototype.parse = function(buffer, offset) {
             break;
         offset += childAtom.size;
         this.childAtoms.push(childAtom);
+        childAtom.parent = this;
     }
 }
 
 var MvhdAtom = function(buffer, offset) {
+    return Atom.prototype.constructor.call(this, buffer, offset);
+}
+
+MvhdAtom.prototype = Object.create(Atom.prototype);
+
+MvhdAtom.prototype.setDefaults = function() {
+    Atom.prototype.setDefaults.call(this);
     this.version = 0;
     this.creationTime = 0;
     this.modificationTime = 0;
@@ -181,12 +220,8 @@ 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;
@@ -199,10 +234,10 @@ MvhdAtom.prototype.parse = function(buffer, offset) {
     this.version = view.getUint8(headerOffset);
     headerOffset += 4;
 
-    this.creationTime = view.getUint32(headerOffset);
+    this.creationTime = new Date(view.getUint32(headerOffset)*1000 + Date.UTC(1904, 0, 1));
     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);
@@ -214,8 +249,8 @@ MvhdAtom.prototype.parse = function(buffer, offset) {
     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,29 +259,29 @@ 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);
@@ -268,4 +303,167 @@ MvhdAtom.prototype.parse = function(buffer, offset) {
     headerOffset += 4;
 
     return true;
+}
+
+var TkhdAtom = function(buffer, offset) {
+    return Atom.prototype.constructor.call(this, buffer, offset);
+}
+
+TkhdAtom.prototype = Object.create(Atom.prototype);
+
+TkhdAtom.prototype.setDefaults = function() {
+    Atom.prototype.setDefaults.call(this);
+    
+    this.version = 0;
+    this.flags = 0;
+    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;
+}
+
+TkhdAtom.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 += 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.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 MdhdAtom = function(buffer, offset) {
+    return Atom.prototype.constructor.call(this, buffer, offset);
+}
+
+MdhdAtom.prototype = Object.create(Atom.prototype);
+
+MdhdAtom.prototype.setDefaults = function() {
+    Atom.prototype.setDefaults.call(this);
+    
+    this.version = 0;
+    this.flags = 0;
+    this.creationTime = 0;
+    this.modificationTime = 0;
+    this.timeScale = 0;
+    this.duration = 0;
+    this.language = 0;
+    this.quality = 0;
+}
+
+MdhdAtom.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 += 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.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;
 }
\ No newline at end of file
index 82d9be2727d03348c6799746eb4012bf8201a5c8..04199880eaf42f8b2120f540b5b397d14016d519 100644 (file)
@@ -17,7 +17,7 @@
                         if (!atom)
                             break;
                         atoms.push(atom);
-                        output.appendChild(atom.toDOMNode());
+                        output.appendChild(toDOMNode(atom));
                         offset += atom.size;
                     }
                 };
             reader.readAsArrayBuffer(file);
         }
         
+        function toDOMNode(object)
+        {
+            var output = document.createElement('dl');
+
+            for (property in object) {
+                var value = object[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(toDOMNode(value));
+                else if (value instanceof Array) {
+                    dd.appendChild(toDOMNode(value));
+                }
+                else if (typeof(value) == "string")
+                    dd.innerText = '"' + value + '"';
+                else if (value instanceof Date)
+                    dd.innerText = value.toLocaleString();
+                else
+                    dd.innerText = value;
+
+                div.appendChild(dt);
+                div.appendChild(dd);
+                output.appendChild(div);
+            }
+            return output;
+        }
+        
         function setup() {
             document.getElementById('file').addEventListener('change', onFileSelect, false);
         }
@@ -34,7 +66,7 @@
         dt, dd { display: inline; }
         dt:after { content:": " }
         dd:after { content:"\A"; white-space:pre; }
-        dd ol { display: inline-block; margin: 0; vertical-align: top }
+        dd dl { margin: 0; vertical-align: top }
         
         /* make the output display as a table */
         dl { display: table; }