return this.parse(buffer, offset) ? this : null;
};
-Atom.create = function(buffer, offset)
-{
+Atom.create = function(buffer, offset) {
// 'offset' is optional.
if (arguments.length < 2)
offset = 0;
var typeArrayView = new Uint8Array(buffer, offset + 4, 4);
var type = String.fromCharCode.apply(null, typeArrayView);
-
+
switch (type) {
case 'ftyp':
- return new FtypAtom(buffer, offset);
+ return new FileTypeAtom(buffer, offset);
case 'moov':
case 'trak':
case 'mdia':
case 'stbl':
return new ContainerAtom(buffer, offset);
case 'mvhd':
- return new MvhdAtom(buffer, offset);
+ return new MovieHeaderAtom(buffer, offset);
case 'tkhd':
- return new TkhdAtom(buffer, offset);
+ return new TrackHeaderAtom(buffer, offset);
case 'mdhd':
- return new MdhdAtom(buffer, offset);
+ return new MediaHeaderAtom(buffer, offset);
+ case 'stss':
+ return new SyncSampleAtom(buffer, offset);
+ case 'stts':
+ return new TimeToSampleAtom(buffer, offset);
default:
return new Atom(buffer, offset);
}
}
-Atom.prototype.setDefaults = function()
-{
- Object.defineProperty(this, "is64bit", {
+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", {
+ Object.defineProperty(this, "minimumSize", {
value: 8,
writable: true,
enumerable: false,
configurable: true,
});
- Object.defineProperty(this, "parent", {
+ Object.defineProperty(this, "parent", {
value: null,
writable: true,
enumerable: false,
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;
+ if (buffer.byteLength - offset < this.minimumSize)
+ return 0;
var view = new DataView(buffer, offset, 4);
- offset += 4;
+ 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;
- // 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.getAtomByType = function(type) {
return result;
};
-var FtypAtom = function(buffer, offset) {
- return Atom.prototype.constructor.call(this, buffer, offset);
+var FileTypeAtom = function(buffer, offset) {
+ this.super(FileTypeAtom).constructor.call(this, buffer, offset);
}
-FtypAtom.prototype = Object.create(Atom.prototype);
+FileTypeAtom.prototype = Object.create(Atom.prototype);
-FtypAtom.prototype.setDefaults = function() {
- Atom.prototype.setDefaults.call(this);
+FileTypeAtom.prototype.setDefaults = function() {
+ this.super(FileTypeAtom).setDefaults.call(this);
this.minimumSize = 16;
this.brand = "";
this.version = 0;
this.compatible_brands = [];
}
-FtypAtom.prototype.parse = function(buffer, offset) {
- if (!Atom.prototype.parse.call(this, buffer, offset))
- return false;
+FileTypeAtom.prototype.parse = function(buffer, offset) {
+ var headerOffset = this.super(FileTypeAtom).parse.call(this, buffer, offset);
+ if (!headerOffset)
+ return 0;
- var begin = offset;
- var end = begin + this.size;
- offset += this.is64bit ? 16 : 8;
+ var view = new DataView(buffer, offset, this.size);
- 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) {
- return Atom.prototype.constructor.call(this, buffer, offset);
+ this.super(ContainerAtom).constructor.call(this, buffer, offset);
}
ContainerAtom.prototype = Object.create(Atom.prototype);
-ContainerAtom.prototype.setDefaults = function()
-{
- Atom.prototype.setDefaults.call(this);
+ContainerAtom.prototype.setDefaults = function() {
+ this.super(ContainerAtom).setDefaults.call(this);
this.childAtoms = [];
}
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;
+ var headerOffset = this.super(ContainerAtom).parse.call(this, buffer, offset);
+ if (!headerOffset)
+ return 0;
- while (offset < end) {
- var childAtom = Atom.create(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;
}
}
-var MvhdAtom = function(buffer, offset) {
- return Atom.prototype.constructor.call(this, buffer, offset);
+var VersionFlagsAtom = function(buffer, offset) {
+ this.super(VersionFlagsAtom).constructor.call(this, buffer, offset);
}
-MvhdAtom.prototype = Object.create(Atom.prototype);
+VersionFlagsAtom.prototype = Object.create(Atom.prototype);
-MvhdAtom.prototype.setDefaults = function() {
- Atom.prototype.setDefaults.call(this);
+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);
+ if (!headerOffset)
+ return 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;
+
+ 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.creationTime = 0;
this.modificationTime = 0;
this.timeScale = 0;
this.nextTrackID = 0;
}
-MvhdAtom.prototype.parse = function(buffer, offset) {
- if (!Atom.prototype.parse.call(this, buffer, offset))
- return false;
-
- offset += this.is64bit ? 16 : 8;
+MovieHeaderAtom.prototype.parse = function(buffer, offset) {
+ var headerOffset = this.super(MovieHeaderAtom).parse.call(this, buffer, offset);
+ if (!headerOffset)
+ return 0;
- var headerOffset = 0;
var view = new DataView(buffer, offset);
- this.version = view.getUint8(headerOffset);
- headerOffset += 4;
-
this.creationTime = 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.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;
}
-var TkhdAtom = function(buffer, offset) {
- return Atom.prototype.constructor.call(this, buffer, offset);
+var TrackHeaderAtom = function(buffer, offset) {
+ this.super(TrackHeaderAtom).constructor.call(this, buffer, offset);
}
-TkhdAtom.prototype = Object.create(Atom.prototype);
+TrackHeaderAtom.prototype = Object.create(Atom.prototype);
+
+TrackHeaderAtom.prototype.setDefaults = function() {
+ this.super(TrackHeaderAtom).setDefaults.call(this);
-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.height = 0;
}
-TkhdAtom.prototype.parse = function(buffer, offset)
-{
- if (!Atom.prototype.parse.call(this, buffer, offset))
- return false;
-
- offset += this.is64bit ? 16 : 8;
+TrackHeaderAtom.prototype.parse = function(buffer, offset) {
+ var headerOffset = this.super(TrackHeaderAtom).parse.call(this, buffer, offset);
+ if (!headerOffset)
+ return 0;
- 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.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);
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);
+var MediaHeaderAtom = function(buffer, offset) {
+ this.super(MediaHeaderAtom).constructor.call(this, buffer, offset);
}
-MdhdAtom.prototype = Object.create(Atom.prototype);
+MediaHeaderAtom.prototype = Object.create(VersionFlagsAtom.prototype);
+
+MediaHeaderAtom.prototype.setDefaults = function() {
+ this.super(MediaHeaderAtom).setDefaults.call(this);
-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.quality = 0;
}
-MdhdAtom.prototype.parse = function(buffer, offset)
-{
- if (!Atom.prototype.parse.call(this, buffer, offset))
- return false;
-
- offset += this.is64bit ? 16 : 8;
+MediaHeaderAtom.prototype.parse = function(buffer, offset) {
+ var headerOffset = this.super(MediaHeaderAtom).parse.call(this, buffer, offset);
+ if (!headerOffset)
+ return 0;
- 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.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.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);
+ if (!headerOffset)
+ return 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.entries = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ while (headerOffset < this.size) {
+ var sampleNumber = view.getUint32(headerOffset);
+ headerOffset += 4;
+ this.syncSamples.push(sampleNumber);
+ }
+}
+
+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.entries = 0;
+
+ Object.defineProperty(this, "timeToSamples", {
+ value: [],
+ writable: true,
+ enumerable: false,
+ configurable: true,
+ });
+}
+
+TimeToSampleAtom.prototype.parse = function(buffer, offset) {
+ var headerOffset = this.super(TimeToSampleAtom).parse.call(this, buffer, offset);
+ if (!headerOffset)
+ return 0;
+
+ var view = new DataView(buffer, offset);
+
+ this.entries = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ while (headerOffset < this.size) {
+ var sampleCount = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ var sampleDuration = view.getUint32(headerOffset);
+ headerOffset += 4;
+
+ this.timeToSamples.push([sampleCount, sampleDuration]);
+ }
}
\ No newline at end of file