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.

408 lines
16 KiB

'use strict';
var
QUnit = require('qunit'),
probe = require('../lib/mp4/probe'),
mp4Helpers = require('./utils/mp4-helpers'),
box = mp4Helpers.box,
// defined below
moovWithoutMdhd,
moovWithoutTkhd,
moofWithTfdt,
multiMoof,
multiTraf,
noTrunSamples,
v1boxes;
QUnit.module('MP4 Probe');
QUnit.test('reads the timescale from an mdhd', function(assert) {
// sampleMoov has a base timescale of 1000 with an override to 90kHz
// in the mdhd
assert.deepEqual(probe.timescale(new Uint8Array(mp4Helpers.sampleMoov)), {
1: 90e3,
2: 90e3
}, 'found the timescale');
});
QUnit.test('reads tracks', function(assert) {
var tracks = probe.tracks(new Uint8Array(mp4Helpers.sampleMoov));
assert.equal(tracks.length, 2, 'two tracks');
assert.equal(tracks[0].codec, 'avc1.4d400d', 'codec is correct');
assert.equal(tracks[0].id, 1, 'id is correct');
assert.equal(tracks[0].type, 'video', 'type is correct');
assert.equal(tracks[0].timescale, 90e3, 'timescale is correct');
assert.equal(tracks[1].codec, 'mp4a.40.2', 'codec is correct');
assert.equal(tracks[1].id, 2, 'id is correct');
assert.equal(tracks[1].type, 'audio', 'type is correct');
assert.equal(tracks[1].timescale, 90e3, 'timescale is correct');
});
QUnit.test('returns null if the tkhd is missing', function(assert) {
assert.equal(probe.timescale(new Uint8Array(moovWithoutTkhd)), null, 'indicated missing info');
});
QUnit.test('returns null if the mdhd is missing', function(assert) {
assert.equal(probe.timescale(new Uint8Array(moovWithoutMdhd)), null, 'indicated missing info');
});
QUnit.test('startTime reads the base decode time from a tfdt', function(assert) {
assert.equal(probe.startTime({
4: 2
}, new Uint8Array(moofWithTfdt)),
0x01020304 / 2,
'calculated base decode time');
});
QUnit.test('startTime returns the earliest base decode time', function(assert) {
assert.equal(probe.startTime({
4: 2,
6: 1
}, new Uint8Array(multiMoof)),
0x01020304 / 2,
'returned the earlier time');
});
QUnit.test('startTime parses 64-bit base decode times', function(assert) {
assert.equal(probe.startTime({
4: 3
}, new Uint8Array(v1boxes)),
0x0101020304 / 3,
'parsed a long value');
});
QUnit.test('compositionStartTime calculates composition time using composition time' +
'offset from first trun sample', function(assert) {
assert.equal(probe.compositionStartTime({
1: 6,
4: 3
}, new Uint8Array(moofWithTfdt)),
(0x01020304 + 10) / 3,
'calculated correct composition start time');
});
QUnit.test('compositionStartTime looks at only the first traf', function(assert) {
assert.equal(probe.compositionStartTime({
2: 6,
4: 3
}, new Uint8Array(multiTraf)),
(0x01020304 + 10) / 3,
'calculated composition start time from first traf');
});
QUnit.test('compositionStartTime uses default composition time offset of 0' +
'if no trun samples present', function(assert) {
assert.equal(probe.compositionStartTime({
2: 6,
4: 3
}, new Uint8Array(noTrunSamples)),
(0x01020304 + 0) / 3,
'calculated correct composition start time using default offset');
});
QUnit.test('getTimescaleFromMediaHeader gets timescale for version 0 mdhd', function(assert) {
var mdhd = new Uint8Array([
0x00, // version 0
0x00, 0x00, 0x00, // flags
// version 0 has 32 bit creation_time, modification_time, and duration
0x00, 0x00, 0x00, 0x02, // creation_time
0x00, 0x00, 0x00, 0x03, // modification_time
0x00, 0x00, 0x03, 0xe8, // timescale = 1000
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
0x15, 0xc7 // 'eng' language
]);
assert.equal(
probe.getTimescaleFromMediaHeader(mdhd),
1000,
'got timescale from version 0 mdhd'
);
});
QUnit.test('getTimescaleFromMediaHeader gets timescale for version 0 mdhd', function(assert) {
var mdhd = new Uint8Array([
0x01, // version 1
0x00, 0x00, 0x00, // flags
// version 1 has 64 bit creation_time, modification_time, and duration
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, // creation_time
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, // modification_time
0x00, 0x00, 0x03, 0xe8, // timescale = 1000
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
0x15, 0xc7 // 'eng' language
]);
assert.equal(
probe.getTimescaleFromMediaHeader(mdhd),
1000,
'got timescale from version 1 mdhd'
);
});
// ---------
// Test Data
// ---------
moovWithoutTkhd =
box('moov',
box('trak',
box('mdia',
box('mdhd',
0x00, // version 0
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x02, // creation_time
0x00, 0x00, 0x00, 0x03, // modification_time
0x00, 0x00, 0x03, 0xe8, // timescale = 1000
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
0x15, 0xc7, // 'eng' language
0x00, 0x00),
box('hdlr',
0x00, // version 1
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00, // pre_defined
mp4Helpers.typeBytes('vide'), // handler_type
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
mp4Helpers.typeBytes('one'), 0x00)))); // name
moovWithoutMdhd =
box('moov',
box('trak',
box('tkhd',
0x01, // version 1
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, // creation_time
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x03, // modification_time
0x00, 0x00, 0x00, 0x01, // track_ID
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00,
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
mp4Helpers.unityMatrix,
0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
box('mdia',
box('hdlr',
0x01, // version 1
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x00, // pre_defined
mp4Helpers.typeBytes('vide'), // handler_type
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
0x00, 0x00, 0x00, 0x00, // reserved
mp4Helpers.typeBytes('one'), 0x00)))); // name
moofWithTfdt =
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, 0x04, // track_ID = 4
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),
box('tfdt',
0x00, // version
0x00, 0x00, 0x00, // flags
0x01, 0x02, 0x03, 0x04), // baseMediaDecodeTime
box('trun',
0x00, // version
0x00, 0x0f, 0x01, // flags: dataOffsetPresent, sampleDurationPresent,
// sampleSizePresent, sampleFlagsPresent,
// sampleCompositionTimeOffsetsPresent
0x00, 0x00, 0x00, 0x02, // sample_count
0x00, 0x00, 0x00, 0x00, // data_offset, no first_sample_flags
// sample 1
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x0a, // signed sample_composition_time_offset = 10
// sample 2
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x14))); // signed sample_composition_time_offset = 20
noTrunSamples =
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, 0x04, // track_ID = 4
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),
box('tfdt',
0x00, // version
0x00, 0x00, 0x00, // flags
0x01, 0x02, 0x03, 0x04), // baseMediaDecodeTime
box('trun',
0x00, // version
0x00, 0x0f, 0x01, // flags: dataOffsetPresent, sampleDurationPresent,
// sampleSizePresent, sampleFlagsPresent,
// sampleCompositionTimeOffsetsPresent
0x00, 0x00, 0x00, 0x00, // sample_count
0x00, 0x00, 0x00, 0x00))); // data_offset, no first_sample_flags
multiTraf =
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, 0x04, // track_ID = 4
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),
box('tfdt',
0x00, // version
0x00, 0x00, 0x00, // flags
0x01, 0x02, 0x03, 0x04), // baseMediaDecodeTime
box('trun',
0x00, // version
0x00, 0x0f, 0x01, // flags: dataOffsetPresent, sampleDurationPresent,
// sampleSizePresent, sampleFlagsPresent,
// sampleCompositionTimeOffsetsPresent
0x00, 0x00, 0x00, 0x02, // sample_count
0x00, 0x00, 0x00, 0x00, // data_offset, no first_sample_flags
// sample 1
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x0a, // signed sample_composition_time_offset = 10
// sample 2
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x14)), // signed sample_composition_time_offset = 20
box('traf',
box('tfhd',
0x00, // version
0x00, 0x00, 0x3b, // flags
0x00, 0x00, 0x00, 0x02, // track_ID = 2
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),
box('tfdt',
0x00, // version
0x00, 0x00, 0x00, // flags
0x01, 0x02, 0x01, 0x02), // baseMediaDecodeTime
box('trun',
0x00, // version
0x00, 0x0f, 0x01, // flags: dataOffsetPresent, sampleDurationPresent,
// sampleSizePresent, sampleFlagsPresent,
// sampleCompositionTimeOffsetsPresent
0x00, 0x00, 0x00, 0x02, // sample_count
0x00, 0x00, 0x00, 0x00, // data_offset, no first_sample_flags
// sample 1
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x0b, // signed sample_composition_time_offset = 11
// sample 2
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x05))); // signed sample_composition_time_offset = 5
multiMoof = moofWithTfdt
.concat(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, 0x06, // track_ID = 6
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),
box('tfdt',
0x00, // version
0x00, 0x00, 0x00, // flags
0x01, 0x02, 0x03, 0x04), // baseMediaDecodeTime
box('trun',
0x00, // version
0x00, 0x0f, 0x01, // flags: dataOffsetPresent, sampleDurationPresent,
// sampleSizePresent, sampleFlagsPresent,
// sampleCompositionTimeOffsetsPresent
0x00, 0x00, 0x00, 0x02, // sample_count
0x00, 0x00, 0x00, 0x00, // data_offset, no first_sample_flags
// sample 1
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x14, // signed sample_composition_time_offset = 20
// sample 2
0x00, 0x00, 0x00, 0x0a, // sample_duration = 10
0x00, 0x00, 0x00, 0x0a, // sample_size = 10
0x00, 0x00, 0x00, 0x00, // sample_flags
0x00, 0x00, 0x00, 0x0a)))); // signed sample_composition_time_offset = 10
v1boxes =
box('moof',
box('mfhd',
0x01, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x04), // sequence_number
box('traf',
box('tfhd',
0x01, // version
0x00, 0x00, 0x3b, // flags
0x00, 0x00, 0x00, 0x04, // track_ID = 4
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),
box('tfdt',
0x01, // version
0x00, 0x00, 0x00, // flags
0x00, 0x00, 0x00, 0x01,
0x01, 0x02, 0x03, 0x04))); // baseMediaDecodeTime