You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1100 lines
35 KiB

'use strict';
/*
======== A Handy Little QUnit Reference ========
http://api.qunitjs.com/
Test methods:
module(name, {[setup][ ,teardown]})
QUnit.test(name, callback)
expect(numberOfAssertions)
stop(increment)
start(decrement)
Test assertions:
assert.ok(value, [message])
assert.equal(actual, expected, [message])
notEqual(actual, expected, [message])
assert.deepEqual(actual, expected, [message])
notDeepEqual(actual, expected, [message])
assert.strictEqual(actual, expected, [message])
notStrictEqual(actual, expected, [message])
throws(block, [expected], [message])
*/
var
mp4 = require('../lib/mp4'),
mp4Helpers = require('./utils/mp4-helpers'),
QUnit = require('qunit'),
typeBytes = mp4Helpers.typeBytes,
box = mp4Helpers.box,
unityMatrix = mp4Helpers.unityMatrix,
mvhd0 = box('mvhd',
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x01, // creation_time
0x00, 0x00, 0x00, 0x02, // modification_time
0x00, 0x00, 0x00, 0x3c, // timescale
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
0x00, 0x01, 0x00, 0x00, // 1.0 rate
0x01, 0x00, // 1.0 volume
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
unityMatrix,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // pre_defined
0x00, 0x00, 0x00, 0x02),
tkhd0 = box('tkhd',
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x02, // creation_time
0x00, 0x00, 0x00, 0x03, // modification_time
0x00, 0x00, 0x00, 0x01, // track_ID
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, // layer
0x00, 0x00, // alternate_group
0x00, 0x00, // non-audio track volume
0x00, 0x00, // reserved
unityMatrix,
0x01, 0x2c, 0x80, 0x00, // 300.5 in 16.16 fixed point
0x00, 0x96, 0x80, 0x00), // 150.5 in 16.16 fixed point
mdhd0 = box('mdhd',
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x02, // creation_time
0x00, 0x00, 0x00, 0x03, // modification_time
0x00, 0x00, 0x00, 0x3c, // timescale
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
0x15, 0xc7, // 'eng' language
0x00, 0x00);
QUnit.module('MP4 Inspector');
QUnit.test('produces an empty array for empty input', function(assert) {
assert.strictEqual(mp4.tools.inspect(new Uint8Array([])).length, 0, 'returned an empty array');
});
QUnit.test('can parse a Box', function(assert) {
var box = new Uint8Array([
0x00, 0x00, 0x00, 0x00, // size 0
0x00, 0x00, 0x00, 0x00 // boxtype 0
]);
assert.deepEqual(mp4.tools.inspect(box), [{
type: '\u0000\u0000\u0000\u0000',
size: 0,
data: box.subarray(box.byteLength)
}], 'parsed a Box');
});
QUnit.test('can parse an ftyp', function(assert) {
assert.deepEqual(mp4.tools.inspect(new Uint8Array(box('ftyp',
0x61, 0x76, 0x63, 0x31, // major brand
0x00, 0x00, 0x00, 0x02, // minor version
98, 111, 111, 112, // compatible brands
98, 101, 101, 112 // compatible brands
))), [{
type: 'ftyp',
size: 4 * 6,
majorBrand: 'avc1',
minorVersion: 2,
compatibleBrands: ['boop', 'beep']
}], 'parsed an ftyp');
});
QUnit.test('can parse a pdin', function(assert) {
assert.deepEqual(mp4.tools.inspect(new Uint8Array(box('pdin',
0x01, // version 1
0x01, 0x02, 0x03, // flags
0x00, 0x00, 0x04, 0x00, // 1024 = 0x400 bytes/second rate
0x00, 0x00, 0x00, 0x01 // initial delay
))), [{
size: 20,
type: 'pdin',
version: 1,
flags: new Uint8Array([1, 2, 3]),
rate: 1024,
initialDelay: 1
}], 'parsed a pdin');
});
QUnit.test('can parse an mdat', function(assert) {
var mdat = new Uint8Array(box('mdat',
0, 0, 0, 4, // length
0x01, 0x02, 0x03, 0x04 // data
));
assert.deepEqual(mp4.tools.inspect(mdat), [{
size: 16,
type: 'mdat',
nals: [
'slice_layer_without_partitioning_rbsp'
],
byteLength: 8
}], 'parsed an mdat');
});
QUnit.test('can parse a free or skip', function(assert) {
var
free = new Uint8Array(box('free',
0x01, 0x02, 0x03, 0x04)), // data
skip = new Uint8Array(box('skip',
0x01, 0x02, 0x03, 0x04)); // data
assert.deepEqual(mp4.tools.inspect(free), [{
size: 12,
type: 'free',
data: free.subarray(free.byteLength - 4)
}], 'parsed a free');
assert.deepEqual(mp4.tools.inspect(skip), [{
size: 12,
type: 'skip',
data: skip.subarray(skip.byteLength - 4)
}], 'parsed a skip');
});
QUnit.test('can parse a version 0 mvhd', function(assert) {
assert.deepEqual(mp4.tools.inspect(new Uint8Array(mvhd0)), [{
type: 'mvhd',
version: 0,
flags: new Uint8Array([0, 0, 0]),
creationTime: new Date(1000 - 2082844800000),
modificationTime: new Date(2000 - 2082844800000),
timescale: 60,
duration: 600,
rate: 1,
volume: 1,
matrix: new Uint32Array(unityMatrix),
size: 108,
nextTrackId: 2
}]);
});
QUnit.test('can parse a version 0 tkhd', function(assert) {
assert.deepEqual(mp4.tools.inspect(new Uint8Array(tkhd0)), [{
type: 'tkhd',
version: 0,
flags: new Uint8Array([0, 0, 0]),
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
size: 92,
trackId: 1,
duration: 600,
layer: 0,
alternateGroup: 0,
volume: 0,
matrix: new Uint32Array(unityMatrix),
width: 300.5,
height: 150.5
}]);
});
QUnit.test('can parse a version 0 mdhd', function(assert) {
assert.deepEqual(mp4.tools.inspect(new Uint8Array(mdhd0)), [{
type: 'mdhd',
version: 0,
flags: new Uint8Array([0, 0, 0]),
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
size: 32,
timescale: 60,
duration: 600,
language: 'eng'
}]);
});
QUnit.test('can parse a moov', function(assert) {
var data = mp4Helpers.sampleMoov;
QUnit.dump.maxDepth = 100;
var result = QUnit.dump.parse(mp4.tools.inspect(new Uint8Array(data)));
var expected = QUnit.dump.parse([{
type: 'moov',
size: 1061,
boxes: [{
type: 'mvhd',
version: 1,
flags: new Uint8Array([0, 0, 0]),
creationTime: new Date(1000 - 2082844800000),
modificationTime: new Date(2000 - 2082844800000),
timescale: 1000,
duration: 600,
rate: 1,
size: 120,
volume: 1,
matrix: new Uint32Array(unityMatrix),
nextTrackId: 2
}, {
type: 'trak',
size: 475,
boxes: [{
type: 'tkhd',
flags: new Uint8Array([0, 0, 0]),
version: 1,
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
size: 104,
trackId: 1,
duration: 600,
layer: 0,
alternateGroup: 0,
volume: 0,
matrix: new Uint32Array(unityMatrix),
width: 300,
height: 150
}, {
type: 'edts',
size: 36,
boxes: [{
type: 'elst',
size: 28,
version: 0,
flags: new Uint8Array([0, 0, 0]),
edits: [{
segmentDuration: 0,
mediaTime: 1024,
mediaRate: 1.5
}]
}]
}, {
type: 'mdia',
size: 327,
boxes: [{
type: 'mdhd',
version: 1,
flags: new Uint8Array([0, 0, 0]),
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
timescale: 90e3,
duration: 600,
language: 'eng',
size: 44
}, {
type: 'hdlr',
version: 1,
flags: new Uint8Array([0, 0, 0]),
handlerType: 'vide',
name: 'one',
size: 37
}, {
type: 'minf',
size: 238,
boxes: [{
type: 'dinf',
size: 36,
boxes: [{
type: 'dref',
size: 28,
version: 1,
flags: new Uint8Array([0, 0, 0]),
dataReferences: [{
type: 'url ',
size: 12,
version: 0,
flags: new Uint8Array([0, 0, 1])
}]
}]
}, {
type: 'stbl',
size: 194,
boxes: [{
type: 'stsd',
size: 114,
version: 1,
flags: new Uint8Array([0, 0, 0]),
sampleDescriptions: [{
config: [
{
avcLevelIndication: 13,
avcProfileIndication: 77,
configurationVersion: 0,
lengthSizeMinusOne: 0,
pps: [],
profileCompatibility: 64,
size: 0,
sps: [],
type: 'avcC'
}
],
dataReferenceIndex: 0,
depth: 0,
frameCount: 0,
height: 0,
horizresolution: 0,
size: 98,
type: 'avc1',
vertresolution: 0,
width: 0
}
]
}, {
type: 'stts',
size: 24,
version: 1,
flags: new Uint8Array([0, 0, 0]),
timeToSamples: [{
sampleCount: 1,
sampleDelta: 1
}]
}, {
type: 'stsc',
version: 1,
flags: new Uint8Array([0, 0, 0]),
sampleToChunks: [{
firstChunk: 2,
samplesPerChunk: 3,
sampleDescriptionIndex: 1
}],
size: 28
}, {
type: 'stco',
size: 20,
version: 1,
flags: new Uint8Array([0, 0, 0]),
chunkOffsets: [1]
}]
}]
}]
}]
}, {
type: 'trak',
size: 458,
boxes: [{
type: 'tkhd',
flags: new Uint8Array([0, 0, 0]),
version: 1,
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
size: 104,
trackId: 2,
duration: 600,
layer: 0,
alternateGroup: 0,
volume: 0,
matrix: new Uint32Array(unityMatrix),
width: 300,
height: 150
}, {
type: 'edts',
size: 44,
boxes: [{
type: 'elst',
size: 36,
version: 1,
flags: new Uint8Array([0, 0, 0]),
edits: [{
segmentDuration: 0,
mediaTime: 1152921504606847000,
mediaRate: 1.5
}]
}]
}, {
type: 'mdia',
size: 302,
boxes: [{
type: 'mdhd',
version: 1,
flags: new Uint8Array([0, 0, 0]),
creationTime: new Date(2000 - 2082844800000),
modificationTime: new Date(3000 - 2082844800000),
timescale: 90e3,
duration: 600,
language: 'eng',
size: 44
}, {
type: 'hdlr',
version: 1,
flags: new Uint8Array([0, 0, 0]),
handlerType: 'soun',
name: 'one',
size: 37
}, {
type: 'minf',
size: 213,
boxes: [{
type: 'dinf',
size: 36,
boxes: [{
type: 'dref',
size: 28,
version: 1,
flags: new Uint8Array([0, 0, 0]),
dataReferences: [{
type: 'url ',
size: 12,
version: 0,
flags: new Uint8Array([0, 0, 1])
}]
}]
}, {
type: 'stbl',
size: 169,
boxes: [{
type: 'stsd',
size: 89,
version: 1,
flags: new Uint8Array([0, 0, 0]),
sampleDescriptions: [{
channelcount: 0,
dataReferenceIndex: 0,
samplerate: 0,
samplesize: 0,
size: 73,
streamDescriptor: {
decoderConfig: {
avgBitrate: 0,
bufferSize: 0,
decoderConfigDescriptor: {
audioObjectType: 0,
channelConfiguration: 0,
length: 0,
samplingFrequencyIndex: 0,
tag: 0
},
maxBitrate: 0,
objectProfileIndication: 64,
streamType: 2
},
esId: 0,
flags: {
0: 0,
1: 0,
2: 0
},
size: 0,
streamPriority: 0,
type: 'esds',
version: 0
},
type: 'mp4a'
}]
}, {
type: 'stts',
size: 24,
version: 1,
flags: new Uint8Array([0, 0, 0]),
timeToSamples: [{
sampleCount: 1,
sampleDelta: 1
}]
}, {
type: 'stsc',
version: 1,
flags: new Uint8Array([0, 0, 0]),
sampleToChunks: [{
firstChunk: 2,
samplesPerChunk: 3,
sampleDescriptionIndex: 1
}],
size: 28
}, {
type: 'stco',
size: 20,
version: 1,
flags: new Uint8Array([0, 0, 0]),
chunkOffsets: [1]
}]
}]
}]
}]
}]
}]);
assert.equal(result, expected, 'can parse moov');
});
QUnit.test('can parse an mvex', function(assert) {
var mvex =
box('mvex',
box('trex',
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x01, // track_ID
0x00, 0x00, 0x00, 0x01, // default_sample_description_index
0x00, 0x00, 0x00, 0x02, // default_sample_duration
0x00, 0x00, 0x00, 0x03, // default_sample_size
0x00, 0x61, 0x00, 0x01)); // default_sample_flags
assert.deepEqual(mp4.tools.inspect(new Uint8Array(mvex)), [{
type: 'mvex',
size: 40,
boxes: [{
type: 'trex',
size: 32,
version: 0,
flags: new Uint8Array([0, 0, 0]),
trackId: 1,
defaultSampleDescriptionIndex: 1,
defaultSampleDuration: 2,
defaultSampleSize: 3,
sampleDependsOn: 0,
sampleIsDependedOn: 1,
sampleHasRedundancy: 2,
samplePaddingValue: 0,
sampleIsDifferenceSample: true,
sampleDegradationPriority: 1
}]
}], 'parsed an mvex');
});
QUnit.test('can parse a video stsd', function(assert) {
var data = box('stsd',
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x01,
box('avc1',
0x00, 0x00, 0x00,
0x00, 0x00, 0x00, // reserved
0x00, 0x01, // data_reference_index
0x00, 0x00, // pre_defined
0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // pre_defined
0x01, 0x2c, // width = 300
0x00, 0x96, // height = 150
0x00, 0x48, 0x00, 0x00, // horizresolution
0x00, 0x48, 0x00, 0x00, // vertresolution
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x01, // frame_count
0x04,
typeBytes('avc1'),
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, // compressorname
0x00, 0x18, // depth = 24
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
box('pasp',
0x00, 0x00, 0x00, 0x01, // hSpacing
0x00, 0x00, 0x00, 0x01))); // vSpacing
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)), [{
type: 'stsd',
size: 163,
version: 0,
flags: new Uint8Array([0, 0, 0]),
sampleDescriptions: [{
type: 'avc1',
size: 147,
dataReferenceIndex: 1,
width: 300,
height: 150,
horizresolution: 72,
vertresolution: 72,
frameCount: 1,
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
}, {
type: 'pasp',
size: 16,
data: new Uint8Array([0, 0, 0, 1, 0, 0, 0, 1])
}]
}]
}]);
});
QUnit.test('can parse an audio stsd', function(assert) {
var data = box('stsd',
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x01, // entry_count
box('mp4a',
0x00, 0x00, 0x00,
0x00, 0x00, 0x00, // reserved
0x00, 0x01, // data_reference_index
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x02, // channelcount
0x00, 0x10, // samplesize
0x00, 0x00, // pre_defined
0x00, 0x00, // reserved
0xbb, 0x80, 0x00, 0x00, // samplerate, fixed-point 16.16
box('esds',
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x03, // tag, ES_DescrTag
0x00, // length
0x00, 0x01, // ES_ID
0x00, // streamDependenceFlag, URL_Flag, reserved, streamPriority
// DecoderConfigDescriptor
0x04, // tag, DecoderConfigDescrTag
0x0d, // length
0x40, // objectProfileIndication, AAC Main
0x15, // streamType, AudioStream. upstream, reserved
0x00, 0x00, 0xff, // bufferSizeDB
0x00, 0x00, 0x00, 0xff, // maxBitrate
0x00, 0x00, 0x00, 0xaa, // avgBitrate
// DecoderSpecificInfo
0x05, // tag, DecoderSpecificInfoTag
0x02, // length
// audioObjectType, samplingFrequencyIndex, channelConfiguration
0x11, 0x90,
// GASpecificConfig
0x06, 0x01, 0x02)));
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)), [{
version: 0,
flags: new Uint8Array([0, 0, 0]),
type: 'stsd',
size: 91,
sampleDescriptions: [{
type: 'mp4a',
dataReferenceIndex: 1,
channelcount: 2,
samplesize: 16,
samplerate: 48000,
size: 75,
streamDescriptor: {
type: 'esds',
version: 0,
size: 39,
flags: new Uint8Array([0, 0, 0]),
esId: 1,
streamPriority: 0,
decoderConfig: {
objectProfileIndication: 0x40,
streamType: 0x05,
bufferSize: 0xff,
maxBitrate: 0xff,
avgBitrate: 0xaa,
decoderConfigDescriptor: {
tag: 5,
length: 2,
audioObjectType: 2,
samplingFrequencyIndex: 3,
channelConfiguration: 2
}
}
}
}]
}], 'parsed an audio stsd');
});
QUnit.test('can parse an styp', function(assert) {
assert.deepEqual(mp4.tools.inspect(new Uint8Array(box('styp',
0x61, 0x76, 0x63, 0x31, // major brand
0x00, 0x00, 0x00, 0x02, // minor version
98, 111, 111, 112 // compatible brands
))), [{
type: 'styp',
size: 4 * 5,
majorBrand: 'avc1',
minorVersion: 2,
compatibleBrands: ['boop']
}], 'parsed an styp');
});
QUnit.test('can parse a vmhd', function(assert) {
var data = box('vmhd',
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, // graphicsmode
0x00, 0x00,
0x00, 0x00,
0x00, 0x00); // opcolor
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'vmhd',
size: 20,
version: 0,
flags: new Uint8Array([0, 0, 0]),
graphicsmode: 0,
opcolor: new Uint16Array([0, 0, 0])
}]);
});
QUnit.test('can parse an stsz', function(assert) {
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
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'stsz',
size: 32,
version: 0,
flags: new Uint8Array([0, 0, 0]),
sampleSize: 0,
entries: [1, 2, 3]
}]);
});
QUnit.test('can parse a moof', function(assert) {
var data = box('moof',
box('mfhd',
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x04), // sequence_number
box('traf',
box('tfhd',
0x00, // version
0x00, 0x00, 0x3b, // flags
0x00, 0x00, 0x00, 0x01, // track_ID
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, // base_data_offset
0x00, 0x00, 0x00, 0x02, // sample_description_index
0x00, 0x00, 0x00, 0x03, // default_sample_duration,
0x00, 0x00, 0x00, 0x04, // default_sample_size
0x00, 0x00, 0x00, 0x05))); // default_sample_flags
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'moof',
size: 72,
boxes: [{
type: 'mfhd',
size: 16,
version: 0,
flags: new Uint8Array([0, 0, 0]),
sequenceNumber: 4
},
{
type: 'traf',
size: 48,
boxes: [{
type: 'tfhd',
version: 0,
size: 40,
flags: new Uint8Array([0x00, 0, 0x3b]),
trackId: 1,
baseDataOffset: 1,
sampleDescriptionIndex: 2,
defaultSampleDuration: 3,
defaultSampleSize: 4,
defaultSampleFlags: 5
}]
}]
}]);
});
QUnit.test('can parse a trun', function(assert) {
var data = box('trun',
0x00, // version
0x00, 0x0b, 0x05, // flags
0x00, 0x00, 0x00, 0x02, // sample_count
0x00, 0x00, 0x00, 0x01, // data_offset
// first_sample_flags
// r:0000 il:10 sdo:01 sido:10 shr:01 spv:111 snss:1
// dp:1111 1110 1101 1100
0x09, 0x9f, 0xfe, 0xdc,
0x00, 0x00, 0x00, 0x09, // sample_duration
0x00, 0x00, 0x00, 0xff, // sample_size
0x00, 0x00, 0x00, 0x00, // sample_composition_time_offset
0x00, 0x00, 0x00, 0x08, // sample_duration
0x00, 0x00, 0x00, 0xfe, // sample_size
0x00, 0x00, 0x00, 0x00); // sample_composition_time_offset
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'trun',
version: 0,
size: 48,
flags: new Uint8Array([0, 0x0b, 0x05]),
dataOffset: 1,
samples: [{
duration: 9,
size: 0xff,
flags: {
isLeading: 2,
dependsOn: 1,
isDependedOn: 2,
hasRedundancy: 1,
paddingValue: 7,
isNonSyncSample: 1,
degradationPriority: 0xfedc
},
compositionTimeOffset: 0
}, {
duration: 8,
size: 0xfe,
compositionTimeOffset: 0
}]
}]);
});
QUnit.test('can parse a trun with per-sample flags', function(assert) {
var data = box('trun',
0x00, // version
0x00, 0x0f, 0x00, // flags
0x00, 0x00, 0x00, 0x01, // sample_count
0x00, 0x00, 0x00, 0x09, // sample_duration
0x00, 0x00, 0x00, 0xff, // sample_size
// sample_flags
// r:0000 il:00 sdo:01, sido:11 shr:00 spv:010 snss:0
// dp: 0001 0010 0011 0100
0x01, 0xc4, 0x12, 0x34,
0x00, 0x00, 0x00, 0x00); // sample_composition_time_offset
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'trun',
version: 0,
size: 32,
flags: new Uint8Array([0, 0x0f, 0x00]),
samples: [{
duration: 9,
size: 0xff,
flags: {
isLeading: 0,
dependsOn: 1,
isDependedOn: 3,
hasRedundancy: 0,
paddingValue: 2,
isNonSyncSample: 0,
degradationPriority: 0x1234
},
compositionTimeOffset: 0
}]
}]);
});
QUnit.test('can parse an sdtp', function(assert) {
var data = box('sdtp',
0x00, // version
0x00, 0x00, 0x00, // flags
// reserved + sample_depends_on +
// sample_is_dependend_on + sample_has_redundancy
0x15,
// reserved + sample_depends_on +
// sample_is_dependend_on + sample_has_redundancy
0x27);
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)), [{
type: 'sdtp',
version: 0,
flags: new Uint8Array([0, 0, 0]),
size: 14,
samples: [{
dependsOn: 1,
isDependedOn: 1,
hasRedundancy: 1
}, {
dependsOn: 2,
isDependedOn: 1,
hasRedundancy: 3
}]
}]);
});
QUnit.test('can parse a sidx', function(assert) {
var data = box('sidx',
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x02, // reference_ID
0x00, 0x00, 0x00, 0x01, // timescale
0x01, 0x02, 0x03, 0x04, // earliest_presentation_time
0x00, 0x00, 0x00, 0x00, // first_offset
0x00, 0x00, // reserved
0x00, 0x02, // reference_count
// first reference
0x80, 0x00, 0x00, 0x07, // reference_type(1) + referenced_size(31)
0x00, 0x00, 0x00, 0x08, // subsegment_duration
0x80, 0x00, 0x00, 0x09, // starts_with_SAP(1) + SAP_type(3) + SAP_delta_time(28)
// second reference
0x00, 0x00, 0x00, 0x03, // reference_type(1) + referenced_size(31)
0x00, 0x00, 0x00, 0x04, // subsegment_duration
0x10, 0x00, 0x00, 0x05 // starts_with_SAP(1) + SAP_type(3) + SAP_delta_time(28)
);
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'sidx',
version: 0,
flags: new Uint8Array([0, 0x00, 0x00]),
timescale: 1,
earliestPresentationTime: 0x01020304,
firstOffset: 0,
referenceId: 2,
size: 56,
references: [{
referenceType: 1,
referencedSize: 7,
subsegmentDuration: 8,
startsWithSap: true,
sapType: 0,
sapDeltaTime: 9
}, {
referenceType: 0,
referencedSize: 3,
subsegmentDuration: 4,
startsWithSap: false,
sapType: 1,
sapDeltaTime: 5
}]
}]);
});
QUnit.test('can parse a version 1 sidx', function(assert) {
var data = box('sidx',
0x01, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x02, // reference_ID
0x00, 0x00, 0x00, 0x01, // timescale
0x00, 0x00, 0x00, 0x00, // earliest_presentation_time
0x01, 0x02, 0x03, 0x04,
0x00, 0x00, 0x00, 0x00, // first_offset
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, // reserved
0x00, 0x02, // reference_count
// first reference
0x80, 0x00, 0x00, 0x07, // reference_type(1) + referenced_size(31)
0x00, 0x00, 0x00, 0x08, // subsegment_duration
0x80, 0x00, 0x00, 0x09, // starts_with_SAP(1) + SAP_type(3) + SAP_delta_time(28)
// second reference
0x00, 0x00, 0x00, 0x03, // reference_type(1) + referenced_size(31)
0x00, 0x00, 0x00, 0x04, // subsegment_duration
0x10, 0x00, 0x00, 0x05 // starts_with_SAP(1) + SAP_type(3) + SAP_delta_time(28)
);
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'sidx',
version: 1,
flags: new Uint8Array([0, 0x00, 0x00]),
timescale: 1,
earliestPresentationTime: 0x01020304,
firstOffset: 0,
referenceId: 2,
size: 64,
references: [{
referenceType: 1,
referencedSize: 7,
subsegmentDuration: 8,
startsWithSap: true,
sapType: 0,
sapDeltaTime: 9
}, {
referenceType: 0,
referencedSize: 3,
subsegmentDuration: 4,
startsWithSap: false,
sapType: 1,
sapDeltaTime: 5
}]
}]);
});
QUnit.test('can parse an smhd', function(assert) {
var data = box('smhd',
0x00, // version
0x00, 0x00, 0x00, // flags
0x00, 0xff, // balance, fixed-point 8.8
0x00, 0x00); // reserved
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'smhd',
size: 16,
version: 0,
flags: new Uint8Array([0, 0, 0]),
balance: 0xff / Math.pow(2, 8)
}],
'parsed an smhd');
});
QUnit.test('can parse a version 0 tfdt', function(assert) {
var data = box('tfdt',
0x00, // version
0x00, 0x00, 0x00, // flags
0x01, 0x02, 0x03, 0x04); // baseMediaDecodeTime
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'tfdt',
version: 0,
size: 16,
flags: new Uint8Array([0, 0, 0]),
baseMediaDecodeTime: 0x01020304
}]);
});
QUnit.test('can parse a version 1 tfdt and return an unsigned integer value', function(assert) {
var data = box('tfdt',
0x01, // version
0x00, 0x00, 0x00, // flags
0x81, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08); // baseMediaDecodeTime
assert.deepEqual(mp4.tools.inspect(new Uint8Array(data)),
[{
type: 'tfdt',
version: 1,
size: 20,
flags: new Uint8Array([0, 0, 0]),
baseMediaDecodeTime: 0x8102030405060708
}]);
});
QUnit.test('can parse a series of boxes', function(assert) {
var ftyp = [
0x00, 0x00, 0x00, 0x18 // size 4 * 6 = 24
].concat(typeBytes('ftyp')).concat([
0x69, 0x73, 0x6f, 0x6d, // major brand
0x00, 0x00, 0x00, 0x02, // minor version
98, 101, 101, 112, // compatible brands
98, 111, 111, 112 // compatible brands
]);
assert.deepEqual(mp4.tools.inspect(new Uint8Array(ftyp.concat(ftyp))),
[{
type: 'ftyp',
size: 4 * 6,
majorBrand: 'isom',
minorVersion: 2,
compatibleBrands: ['beep', 'boop']
}, {
type: 'ftyp',
size: 4 * 6,
majorBrand: 'isom',
minorVersion: 2,
compatibleBrands: ['beep', 'boop']
}],
'parsed two boxes in series');
});