From: Jer Noble Date: Fri, 7 Dec 2012 09:06:00 +0000 (-0800) Subject: Added tkhd, mdhd atoms. Added getAtomByType and getAtomsByType. X-Git-Url: http://id.pley.net/movie_parser.git/commitdiff_plain/48b43d25b544737831d2252f6af2a64c0861180b Added tkhd, mdhd atoms. Added getAtomByType and getAtomsByType. --- diff --git a/Atom.js b/Atom.js index 52ee673..c7507db 100644 --- 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 diff --git a/AtomTester.html b/AtomTester.html index 82d9be2..0419988 100644 --- a/AtomTester.html +++ b/AtomTester.html @@ -17,7 +17,7 @@ if (!atom) break; atoms.push(atom); - output.appendChild(atom.toDOMNode()); + output.appendChild(toDOMNode(atom)); offset += atom.size; } }; @@ -25,6 +25,38 @@ 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; }