23a2bf27 by David LaPalomento

Inspect and generate avcC, btrt, stsz, tkhd, and vmhd boxes

Add support for a number of new boxes in the generator and inspector. Bring some existing generated boxes more closely into line with a working init segment.
1 parent 7cf3d82d
......@@ -2,7 +2,8 @@
'use strict';
var box, dinf, ftyp, minf, moov, mvex, mvhd, trak, tkhd, mdia, mdhd, hdlr, stbl,
stsd, types, MAJOR_BRAND, MINOR_VERSION, VIDEO_HDLR, DREF, TREX, Uint8Array, DataView;
stsd, types, MAJOR_BRAND, MINOR_VERSION, VIDEO_HDLR, VMHD, DREF, STCO, STSC, STSZ, STTS, TREX,
Uint8Array, DataView;
Uint8Array = window.Uint8Array;
DataView = window.DataView;
......@@ -12,6 +13,8 @@ DataView = window.DataView;
var i;
types = {
avc1: [], // codingname
avcC: [],
btrt: [],
dinf: [],
dref: [],
ftyp: [],
......@@ -26,10 +29,12 @@ DataView = window.DataView;
stco: [],
stsc: [],
stsd: [],
stsz: [],
stts: [],
trak: [],
trex: [],
tkhd: []
tkhd: [],
vmhd: []
};
for (i in types) {
......@@ -65,7 +70,11 @@ DataView = window.DataView;
DREF = new Uint8Array([
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00 // entry_count
0x00, 0x00, 0x00, 0x01, // entry_count
0x00, 0x00, 0x00, 0x0c, // entry_size
0x75, 0x72, 0x6c, 0x20, // 'url' type
0x00, // version 0
0x00, 0x00, 0x01 // entry_flags
]);
TREX = new Uint8Array([
0x00, // version 0
......@@ -76,6 +85,27 @@ DataView = window.DataView;
0x00, 0x00, 0x00, 0x00, // default_sample_size
0x00, 0x01, 0x00, 0x01 // default_sample_flags
]);
STCO = new Uint8Array([
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00 // entry_count
]);
STSC = STCO;
STSZ = new Uint8Array([
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00, // sample_size
0x00, 0x00, 0x00, 0x00, // sample_count
]);
STTS = STCO;
VMHD = new Uint8Array([
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, // graphicsmode
0x00, 0x00,
0x00, 0x00,
0x00, 0x00 // opcolor
]);
})();
box = function(type) {
......@@ -120,7 +150,8 @@ mdhd = function(duration) {
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x02, // creation_time
0x00, 0x00, 0x00, 0x03, // modification_time
0x00, 0x00, 0x00, 0x3c, // timescale
0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
(duration & 0xFF000000) >> 24,
(duration & 0xFF0000) >> 16,
(duration & 0xFF00) >> 8,
......@@ -133,7 +164,7 @@ mdia = function(duration, width, height) {
return box(types.mdia, mdhd(duration), hdlr(), minf(width, height));
};
minf = function(width, height) {
return box(types.minf, dinf(), stbl(width, height));
return box(types.minf, box(types.vmhd, VMHD), dinf(), stbl(width, height));
};
moov = function(duration, width, height) {
return box(types.moov, mvhd(duration), trak(duration, width, height), mvex());
......@@ -148,7 +179,7 @@ mvhd = function(duration) {
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x01, // creation_time
0x00, 0x00, 0x00, 0x02, // modification_time
0x00, 0x00, 0x00, 0x01, // timescale, 1 "tick" per second
0x00, 0x01, 0x5f, 0x90, // timescale, 90,000 "ticks" per second
(duration & 0xFF000000) >> 24,
(duration & 0xFF0000) >> 16,
(duration & 0xFF00) >> 8,
......@@ -181,9 +212,10 @@ mvhd = function(duration) {
stbl = function(width, height) {
return box(types.stbl,
stsd(width, height),
box(types.stts),
box(types.stsc),
box(types.stco));
box(types.stts, STTS),
box(types.stsc, STSC),
box(types.stsz, STSZ),
box(types.stco, STCO));
};
stsd = function(width, height) {
......@@ -218,7 +250,26 @@ stsd = function(width, height) {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, // compressorname
0x00, 0x18, // depth = 24
0x11, 0x11]))); // pre_defined = -1
0x11, 0x11]), // pre_defined = -1
box(types.avcC, new Uint8Array([
0x01, // configurationVersion
0x4d, // AVCProfileIndication??
0x40, // profile_compatibility
0x20, // AVCLevelIndication
0xff, // lengthSizeMinusOne
0xe1, // numOfSequenceParameterSets
0x00, 0x0c, // sequenceParameterSetLength
0x67, 0x4d, 0x40, 0x20,
0x96, 0x52, 0x80, 0xa0,
0x0b, 0x76, 0x02, 0x05, // SPS
0x01, // numOfPictureParameterSets
0x00, 0x04, // pictureParameterSetLength
0x68, 0xef, 0x38, 0x80])), // "PPS"
box(types.btrt, new Uint8Array([
0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
0x00, 0x2d, 0xc6, 0xc0])) // avgBitrate
));
};
tkhd = function(duration, width, height) {
......@@ -248,14 +299,12 @@ tkhd = function(duration, width, height) {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x40, 0x00, 0x00, 0x00, // transformation: unity matrix
(width & 0xFF000000) >> 24,
(width & 0xFF0000) >> 16,
(width & 0xFF00) >> 8,
width & 0xFF, // width
(height & 0xFF000000) >> 24,
(height & 0xFF0000) >> 16,
width & 0xFF,
0x00, 0x00, // width
(height & 0xFF00) >> 8,
height & 0xFF // height
height & 0xFF,
0x00, 0x00 // height
]));
};
......@@ -269,7 +318,7 @@ window.videojs.mp4 = {
initSegment: function() {
var
fileType = ftyp(),
movie = moov(1, 1280, 720),
movie = moov(0xffffffff, 1280, 720),
result = new Uint8Array(fileType.byteLength + movie.byteLength);
result.set(fileType);
......
......@@ -78,29 +78,48 @@ test('generates a moov', function() {
minf = boxes[0].boxes[1].boxes[1].boxes[2];
equal(minf.type, 'minf', 'generate an minf type');
equal(minf.boxes.length, 2, 'generates two minf sub boxes');
equal(minf.boxes.length, 3, 'generates three minf sub boxes');
equal(minf.boxes[0].type, 'vmhd', 'generates a vmhd type');
deepEqual({
type: 'vmhd',
size: 20,
version: 0,
flags: new Uint8Array([0, 0, 0]),
graphicsmode: 0,
opcolor: new Uint16Array([0, 0, 0])
}, minf.boxes[0], 'generates a vhmd');
equal(minf.boxes[1].type, 'dinf', 'generates a dinf type');
deepEqual({
type: 'dinf',
size: 24,
size: 36,
boxes: [{
type: 'dref',
size: 16,
size: 28,
version: 0,
flags: new Uint8Array([0, 0, 0]),
dataReferences: []
dataReferences: [{
type: 'url ',
size: 12,
version: 0,
flags: new Uint8Array([0, 0, 1])
}]
}]
}, minf.boxes[0], 'generates a dinf');
}, minf.boxes[1], 'generates a dinf');
equal(minf.boxes[1].type, 'stbl', 'generates an stbl type');
equal(minf.boxes[2].type, 'stbl', 'generates an stbl type');
deepEqual({
type: 'stbl',
size: 134,
size: 233,
boxes: [{
type: 'stsd',
size: 102,
size: 157,
version: 0,
flags: new Uint8Array([0, 0, 0]),
sampleDescriptions: [{
type: 'avc1',
size: 141,
dataReferenceIndex: 1,
width: 600,
height: 300,
......@@ -108,23 +127,57 @@ test('generates a moov', function() {
vertresolution: 72,
frameCount: 1,
depth: 24,
size: 86,
type: 'avc1'
config: [{
type: 'avcC',
size: 35,
configurationVersion: 1,
avcProfileIndication: 0x4d,
profileCompatibility: 0x40,
avcLevelIndication: 0x20,
lengthSizeMinusOne: 3,
sps: [new Uint8Array([
0x67, 0x4d, 0x40, 0x20,
0x96, 0x52, 0x80, 0xa0,
0x0b, 0x76, 0x02, 0x05
])],
pps: [new Uint8Array([
0x68, 0xef, 0x38, 0x80
])]
}, {
type: 'btrt',
size: 20,
bufferSizeDB: 1875072,
maxBitrate: 3000000,
avgBitrate: 3000000
}]
}]
}, {
type: 'stts',
size: 8,
size: 16,
version: 0,
flags: new Uint8Array([0, 0, 0]),
timeToSamples: []
}, {
type: 'stsc',
size: 8,
size: 16,
version: 0,
flags: new Uint8Array([0, 0, 0]),
sampleToChunks: []
}, {
type: 'stsz',
version: 0,
size: 20,
flags: new Uint8Array([0, 0, 0]),
sampleSize: 0,
entries: []
}, {
type: 'stco',
size: 8,
size: 16,
version: 0,
flags: new Uint8Array([0, 0, 0]),
chunkOffsets: []
}]
}, minf.boxes[1], 'generates a stbl');
}, minf.boxes[2], 'generates a stbl');
mvex = boxes[0].boxes[2];
......@@ -160,6 +213,7 @@ test('generates an initialization segment', function() {
equal(init.length, 2, 'generated two boxes');
equal(init[0].type, 'ftyp', 'generated a ftyp box');
equal(init[1].type, 'moov', 'generated a moov box');
equal(init[1].boxes[0].duration, 0xffffffff, 'wrote a maximum duration');
});
......
......@@ -105,8 +105,8 @@ var
0x00, 0x00, // non-audio track volume
0x00, 0x00, // reserved
unityMatrix,
0x00, 0x00, 0x01, 0x2c, // 300 = 0x12c width
0x00, 0x00, 0x00, 0x96), // 150 = 0x96 height
0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed point
0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed point
mdhd0 = box('mdhd',
0x00, // version 0
0x00, 0x00, 0x00, // flags
......@@ -201,8 +201,8 @@ test('can parse a version 0 mvhd', function() {
type: 'mvhd',
version: 0,
flags: new Uint8Array([0, 0, 0]),
creationTime: 1,
modificationTime: 2,
creationTime: new Date(1000 - 2082844800000),
modificationTime: new Date(2000 - 2082844800000),
timescale: 60,
duration: 600,
rate: 1,
......@@ -218,8 +218,8 @@ test('can parse a version 0 tkhd', function() {
type: 'tkhd',
version: 0,
flags: new Uint8Array([0, 0, 0]),
creationTime: 2,
modificationTime: 3,
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
size: 92,
trackId: 1,
duration: 600,
......@@ -237,8 +237,8 @@ test('can parse a version 0 mdhd', function() {
type: 'mdhd',
version: 0,
flags: new Uint8Array([0, 0, 0]),
creationTime: 2,
modificationTime: 3,
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
size: 32,
timescale: 60,
duration: 600,
......@@ -291,8 +291,8 @@ test('can parse a moov', function() {
0x00, 0x00, // non-audio track volume
0x00, 0x00, // reserved
unityMatrix,
0x00, 0x00, 0x01, 0x2c, // 300 = 0x12c width
0x00, 0x00, 0x00, 0x96), // 150 = 0x96 height
0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
box('mdia',
box('mdhd',
0x01, // version 1
......@@ -320,7 +320,10 @@ test('can parse a moov', function() {
box('dref',
0x01, // version 1
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00)), // entry_count
0x00, 0x00, 0x00, 0x01, // entry_count
box('url ',
0x00, // version
0x00, 0x00, 0x01))), // flags
box('stbl',
box('stsd',
0x01, // version 1
......@@ -329,25 +332,32 @@ test('can parse a moov', function() {
box('stts',
0x01, // version 1
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00), // entry_count
0x00, 0x00, 0x00, 0x01, // entry_count
0x00, 0x00, 0x00, 0x01, // sample_count
0x00, 0x00, 0x00, 0x01), // sample_delta
box('stsc',
0x01, // version 1
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00), // entry_count
0x00, 0x00, 0x00, 0x01, // entry_count
0x00, 0x00, 0x00, 0x02, // first_chunk
0x00, 0x00, 0x00, 0x03, // samples_per_chunk
0x00, 0x00, 0x00, 0x01), // sample_description_index
box('stco',
0x01, // version 1
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00)))))); // entry_count;
0x00, 0x00, 0x00, 0x01, // entry_count
0x00, 0x00, 0x00, 0x01)))))); // chunk_offset
deepEqual(videojs.inspectMp4(new Uint8Array(data)), [{
size: 433,
type: 'moov',
size: 469,
boxes: [{
type: 'mvhd',
version: 1,
flags: new Uint8Array([0, 0, 0]),
creationTime: 1,
modificationTime: 2,
creationTime: new Date(1000 - 2082844800000),
modificationTime: new Date(2000 - 2082844800000),
timescale: 60,
duration: 600,
rate: 1,
......@@ -357,13 +367,13 @@ test('can parse a moov', function() {
nextTrackId: 2
}, {
type: 'trak',
size: 305,
size: 341,
boxes: [{
type: 'tkhd',
flags: new Uint8Array([0, 0, 0]),
version: 1,
creationTime: 2,
modificationTime: 3,
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
size: 104,
trackId: 1,
duration: 600,
......@@ -375,13 +385,13 @@ test('can parse a moov', function() {
height: 150
}, {
type: 'mdia',
size: 193,
size: 229,
boxes: [{
type: 'mdhd',
version: 1,
flags: new Uint8Array([0, 0, 0]),
creationTime: 2,
modificationTime: 3,
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
timescale: 60,
duration: 600,
language: 'eng',
......@@ -395,20 +405,25 @@ test('can parse a moov', function() {
size: 37
}, {
type: 'minf',
size: 104,
size: 140,
boxes: [{
type: 'dinf',
size: 24,
size: 36,
boxes: [{
type: 'dref',
size: 28,
version: 1,
flags: new Uint8Array([0, 0, 0]),
dataReferences: [],
size: 16
dataReferences: [{
type: 'url ',
size: 12,
version: 0,
flags: new Uint8Array([0, 0, 1])
}],
}]
}, {
type: 'stbl',
size: 72,
size: 96,
boxes: [{
type: 'stsd',
size: 16,
......@@ -417,16 +432,29 @@ test('can parse a moov', function() {
sampleDescriptions: [],
}, {
type: 'stts',
timeToSamples: [],
size: 16
size: 24,
version: 1,
flags: new Uint8Array([0, 0, 0]),
timeToSamples: [{
sampleCount: 1,
sampleDelta: 1
}]
}, {
type: 'stsc',
sampleToChunks: [],
size: 16
version: 1,
flags: new Uint8Array([0, 0, 0]),
sampleToChunks: [{
firstChunk: 2,
samplesPerChunk: 3,
sampleDescriptionIndex: 1
}],
size: 28
}, {
type: 'stco',
chunkOffsets: [],
size: 16
size: 20,
version: 1,
flags: new Uint8Array([0, 0, 0]),
chunkOffsets: [1]
}]
}]
}]
......@@ -498,23 +526,99 @@ test('can parse a video stsd', function() {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, // compressorname
0x00, 0x18, // depth = 24
0x11, 0x11)); // pre_defined
0x11, 0x11, // pre_defined
box('avcC',
0x01, // configurationVersion
0x00, // AVCProfileIndication??
0x00, // profile_compatibility
0x00, // AVCLevelIndication
0x1c, // lengthSizeMinusOne
0xe1, // numOfSequenceParameterSets
0x00, 0x01, // sequenceParameterSetLength
0x00, // "SPS"
0x02, // numOfPictureParameterSets
0x00, 0x02, // pictureParameterSetLength
0x01, 0x02, // "PPS"
0x00, 0x01, // pictureParameterSetLength
0xff), // "PPS"
box('btrt',
0x00, 0x00, 0x00, 0x00, // bufferSizeDB
0x00, 0x00, 0x00, 0x01, // maxBitrate
0x00, 0x00, 0x00, 0x01))); // avgBitrate
deepEqual(videojs.inspectMp4(new Uint8Array(data)), [{
type: 'stsd',
size: 102,
size: 147,
version: 0,
flags: new Uint8Array([0, 0, 0]),
sampleDescriptions: [{
type: 'avc1',
size: 86,
size: 131,
dataReferenceIndex: 1,
width: 300,
height: 150,
horizresolution: 72,
vertresolution: 72,
frameCount: 1,
depth: 24
depth: 24,
config: [{
type: 'avcC',
size: 25,
configurationVersion: 1,
avcProfileIndication: 0,
profileCompatibility: 0,
avcLevelIndication: 0,
lengthSizeMinusOne: 0,
sps: [new Uint8Array(1)],
pps: [new Uint8Array([1, 2]),
new Uint8Array([0xff])]
}, {
type: 'btrt',
size: 20,
bufferSizeDB: 0,
maxBitrate: 1,
avgBitrate: 1
}]
}]
}]);
});
test('can parse a vmhd', function() {
var data = box('vmhd',
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, // graphicsmode
0x00, 0x00,
0x00, 0x00,
0x00, 0x00); // opcolor
deepEqual(videojs.inspectMp4(new Uint8Array(data)),
[{
type: 'vmhd',
size: 20,
version: 0,
flags: new Uint8Array([0, 0, 0]),
graphicsmode: 0,
opcolor: new Uint16Array([0, 0, 0])
}]);
});
test('can parse an stsz', function() {
var data = box('stsz',
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00, // sample_size
0x00, 0x00, 0x00, 0x03, // sample_count
0x00, 0x00, 0x00, 0x01, // entry_size
0x00, 0x00, 0x00, 0x02, // entry_size
0x00, 0x00, 0x00, 0x03); // entry_size
deepEqual(videojs.inspectMp4(new Uint8Array(data)),
[{
type: 'stsz',
size: 32,
version: 0,
flags: new Uint8Array([0, 0, 0]),
sampleSize: 0,
entries: [1, 2, 3]
}]);
});
......
......@@ -16,6 +16,9 @@ var
result += String.fromCharCode(buffer[3]);
return result;
},
parseMp4Date = function(seconds) {
return new Date(seconds * 1000 - 2082844800000);
},
// registry of handlers for individual mp4 box types
parse = {
......@@ -31,7 +34,53 @@ var
horizresolution: view.getUint16(28) + (view.getUint16(30) / 16),
vertresolution: view.getUint16(32) + (view.getUint16(34) / 16),
frameCount: view.getUint16(40),
depth: view.getUint16(74)
depth: view.getUint16(74),
config: videojs.inspectMp4(data.subarray(78, data.byteLength))
};
},
avcC: function(data) {
var
view = new DataView(data.buffer, data.byteOffset, data.byteLength),
result = {
configurationVersion: data[0],
avcProfileIndication: data[1],
profileCompatibility: data[2],
avcLevelIndication: data[3],
lengthSizeMinusOne: data[4] & 0x03,
sps: [],
pps: []
},
numOfSequenceParameterSets = data[5] & 0x1f,
numOfPictureParameterSets,
nalSize,
offset,
i;
// iterate past any SPSs
offset = 6;
for (i = 0; i < numOfSequenceParameterSets; i++) {
nalSize = view.getUint16(offset);
offset += 2;
result.sps.push(data.subarray(offset, offset + nalSize));
offset += nalSize;
}
// iterate past any PPSs
numOfPictureParameterSets = data[offset];
offset++;
for (i = 0; i < numOfPictureParameterSets; i++) {
nalSize = view.getUint16(offset);
offset += 2;
result.pps.push(data.subarray(offset, offset + nalSize));
offset += nalSize;
}
return result;
},
btrt: function(data) {
var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
return {
bufferSizeDB: view.getUint32(0),
maxBitrate: view.getUint32(4),
avgBitrate: view.getUint32(8)
};
},
ftyp: function(data) {
......@@ -58,7 +107,7 @@ var
return {
version: data[0],
flags: new Uint8Array(data.subarray(1, 4)),
dataReferences: []
dataReferences: videojs.inspectMp4(data.subarray(8))
};
},
hdlr: function(data) {
......@@ -105,17 +154,17 @@ var
};
if (result.version === 1) {
i += 4;
result.creationTime = view.getUint32(i); // truncating top 4 bytes
result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
i += 8;
result.modificationTime = view.getUint32(i); // truncating top 4 bytes
result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
i += 4;
result.timescale = view.getUint32(i);
i += 8;
result.duration = view.getUint32(i); // truncating top 4 bytes
} else {
result.creationTime = view.getUint32(i);
result.creationTime = parseMp4Date(view.getUint32(i));
i += 4;
result.modificationTime = view.getUint32(i);
result.modificationTime = parseMp4Date(view.getUint32(i));
i += 4;
result.timescale = view.getUint32(i);
i += 4;
......@@ -162,17 +211,17 @@ var
if (result.version === 1) {
i += 4;
result.creationTime = view.getUint32(i); // truncating top 4 bytes
result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
i += 8;
result.modificationTime = view.getUint32(i); // truncating top 4 bytes
result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
i += 4;
result.timescale = view.getUint32(i);
i += 8
result.duration = view.getUint32(i); // truncating top 4 bytes
} else {
result.creationTime = view.getUint32(i);
result.creationTime = parseMp4Date(view.getUint32(i));
i += 4;
result.modificationTime = view.getUint32(i);
result.modificationTime = parseMp4Date(view.getUint32(i));
i += 4;
result.timescale = view.getUint32(i);
i += 4;
......@@ -230,14 +279,38 @@ var
};
},
stco: function(data) {
return {
var
view = new DataView(data.buffer, data.byteOffset, data.byteLength),
result = {
version: data[0],
flags: new Uint8Array(data.subarray(1, 4)),
chunkOffsets: []
};
},
entryCount = view.getUint32(4),
i;
for (i = 8; entryCount; i += 4, entryCount--) {
result.chunkOffsets.push(view.getUint32(i));
}
return result;
},
stsc: function(data) {
return {
var
view = new DataView(data.buffer, data.byteOffset, data.byteLength),
entryCount = view.getUint32(4),
result = {
version: data[0],
flags: new Uint8Array(data.subarray(1, 4)),
sampleToChunks: []
};
},
i;
for (i = 8; entryCount; i += 12, entryCount--) {
result.sampleToChunks.push({
firstChunk: view.getUint32(i),
samplesPerChunk: view.getUint32(i + 4),
sampleDescriptionIndex: view.getUint32(i + 8)
});
}
return result;
},
stsd: function(data) {
return {
......@@ -246,10 +319,39 @@ var
sampleDescriptions: videojs.inspectMp4(data.subarray(8))
};
},
stsz: function(data) {
var
view = new DataView(data.buffer, data.byteOffset, data.byteLength),
result = {
version: data[0],
flags: new Uint8Array(data.subarray(1, 4)),
sampleSize: view.getUint32(4),
entries: []
},
i;
for (i = 12; i < data.byteLength; i += 4) {
result.entries.push(view.getUint32(i));
}
return result;
},
stts: function(data) {
return {
var
view = new DataView(data.buffer, data.byteOffset, data.byteLength),
result = {
version: data[0],
flags: new Uint8Array(data.subarray(1, 4)),
timeToSamples: []
};
},
entryCount = view.getUint32(4),
i;
for (i = 8; entryCount; i += 8, entryCount--) {
result.timeToSamples.push({
sampleCount: view.getUint32(i),
sampleDelta: view.getUint32(i + 4)
});
}
return result;
},
tkhd: function(data) {
var
......@@ -261,18 +363,18 @@ var
};
if (result.version === 1) {
i += 4;
result.creationTime = view.getUint32(i); // truncating top 4 bytes
result.creationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
i += 8;
result.modificationTime = view.getUint32(i); // truncating top 4 bytes
result.modificationTime = parseMp4Date(view.getUint32(i)); // truncating top 4 bytes
i += 4;
result.trackId = view.getUint32(i);
i += 4;
i += 8;
result.duration = view.getUint32(i); // truncating top 4 bytes
} else {
result.creationTime = view.getUint32(i);
result.creationTime = parseMp4Date(view.getUint32(i));
i += 4;
result.modificationTime = view.getUint32(i);
result.modificationTime = parseMp4Date(view.getUint32(i));
i += 4;
result.trackId = view.getUint32(i);
i += 4;
......@@ -291,10 +393,27 @@ var
i += 2;
result.matrix = new Uint32Array(data.subarray(i, i + (9 * 4)));
i += 9 * 4;
result.width = view.getUint32(i);
result.width = view.getUint16(i) + (view.getUint16(i + 2) / 16);
i += 4;
result.height = view.getUint32(i);
result.height = view.getUint16(i) + (view.getUint16(i + 2) / 16);
return result;
},
'url ': function(data) {
return {
version: data[0],
flags: new Uint8Array(data.subarray(1, 4))
};
},
vmhd: function(data) {
var view = new DataView(data.buffer, data.byteOffset, data.byteLength);
return {
version: data[0],
flags: new Uint8Array(data.subarray(1, 4)),
graphicsmode: view.getUint16(4),
opcolor: new Uint16Array([view.getUint16(6),
view.getUint16(8),
view.getUint16(10)])
};
}
};
......
......@@ -140,32 +140,13 @@
// write out the result
hex += '<pre>';
// hex += videojs.Hls.utils.hexDump(transmuxer.mp4);
hex += videojs.Hls.utils.hexDump(videojs.mp4.initSegment());
hex += '</pre>';
vjsOutput.innerHTML = hex;
// XXX media source testing
vjsBoxes.innerHTML = JSON.stringify(videojs.inspectMp4(videojs.mp4.initSegment()), null, ' ');
var video = document.createElement('video');
var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', function() {
var buffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d0020,mp4a.40.2');
buffer.addEventListener('updatestart', console.log.bind(console));
buffer.addEventListener('updateend', console.log.bind(console));
buffer.addEventListener('error', console.log.bind(console));
buffer.appendBuffer(videojs.mp4.initSegment());
console.log('done', mediaSource, buffer, video.error);
window.vjsMediaSource = mediaSource;
window.vjsSourceBuffer = buffer;
window.vjsVideo = video;
});
mediaSource.addEventListener('error', console.log.bind(console));
mediaSource.addEventListener('opened', console.log.bind(console));
mediaSource.addEventListener('closed', console.log.bind(console));
mediaSource.addEventListener('sourceended', console.log.bind(console));
video.src = URL.createObjectURL(mediaSource);
});
reader.readAsArrayBuffer(this.files[0]);
}, false);
......@@ -184,6 +165,28 @@
hex += videojs.Hls.utils.hexDump(bytes);
hex += '</pre>';
workingOutput.innerHTML = hex;
// XXX Media Sources Testing
var video = document.createElement('video');
var mediaSource = new MediaSource();
mediaSource.addEventListener('sourceopen', function() {
var buffer = mediaSource.addSourceBuffer('video/mp4;codecs=avc1.4d0020,mp4a.40.2');
buffer.addEventListener('updatestart', console.log.bind(console));
buffer.addEventListener('updateend', console.log.bind(console));
buffer.addEventListener('error', console.log.bind(console));
buffer.appendBuffer(bytes);
console.log('done');
window.vjsMediaSource = mediaSource;
window.vjsSourceBuffer = buffer;
window.vjsVideo = video;
});
mediaSource.addEventListener('error', console.log.bind(console));
mediaSource.addEventListener('opened', console.log.bind(console));
mediaSource.addEventListener('closed', console.log.bind(console));
mediaSource.addEventListener('sourceended', console.log.bind(console));
video.src = URL.createObjectURL(mediaSource);
video.addEventListener('error', console.log.bind(console));
});
reader.readAsArrayBuffer(this.files[0]);
}, false);
......