mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-05 19:42:38 +02:00
Also check the lang in case insensitive
This commit is contained in:
parent
33e7f7384e
commit
2a9630258f
22658 changed files with 3562773 additions and 3562767 deletions
10
node_modules/mux.js/test/.eslintrc.json
generated
vendored
10
node_modules/mux.js/test/.eslintrc.json
generated
vendored
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"env": {
|
||||
"qunit": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"env": {
|
||||
"qunit": true
|
||||
}
|
||||
}
|
||||
|
|
578
node_modules/mux.js/test/aac-stream.test.js
generated
vendored
578
node_modules/mux.js/test/aac-stream.test.js
generated
vendored
|
@ -1,289 +1,289 @@
|
|||
'use strict';
|
||||
|
||||
var
|
||||
aacStream,
|
||||
AacStream = require('../lib/aac'),
|
||||
QUnit = require('qunit'),
|
||||
utils = require('./utils'),
|
||||
createId3Header,
|
||||
createId3FrameHeader,
|
||||
createAdtsHeader;
|
||||
|
||||
createId3Header = function(tagSize) {
|
||||
var header = [];
|
||||
|
||||
header[0] = 'I'.charCodeAt(0);
|
||||
header[1] = 'D'.charCodeAt(0);
|
||||
header[2] = '3'.charCodeAt(0);
|
||||
// 2 version bytes, ID3v2.4.0 (major 4, revision 0)
|
||||
header[3] = 4;
|
||||
header[4] = 0;
|
||||
// unsynchronization, extended header, experimental indicator, footer present flags
|
||||
header[5] = 0;
|
||||
// "The ID3v2 tag size is the sum of the byte length of the extended
|
||||
// header, the padding and the frames after unsynchronisation. If a
|
||||
// footer is present this equals to ('total size' - 20) bytes, otherwise
|
||||
// ('total size' - 10) bytes."
|
||||
// http://id3.org/id3v2.4.0-structure
|
||||
header[6] = 0;
|
||||
header[7] = 0;
|
||||
header[8] = 0;
|
||||
header[9] = tagSize;
|
||||
|
||||
return header;
|
||||
};
|
||||
|
||||
createId3FrameHeader = function() {
|
||||
var header = [];
|
||||
|
||||
// four byte frame ID, XYZ are experimental
|
||||
header[0] = 'X'.charCodeAt(0);
|
||||
header[1] = 'Y'.charCodeAt(0);
|
||||
header[2] = 'Z'.charCodeAt(0);
|
||||
header[3] = '0'.charCodeAt(0);
|
||||
// four byte sync safe integer size (excluding frame header)
|
||||
header[4] = 0;
|
||||
header[5] = 0;
|
||||
header[6] = 0;
|
||||
header[7] = 10;
|
||||
// two bytes for flags
|
||||
header[8] = 0;
|
||||
header[9] = 0;
|
||||
|
||||
return header;
|
||||
};
|
||||
|
||||
createAdtsHeader = function(frameLength) {
|
||||
// Header consists of 7 or 9 bytes (without or with CRC).
|
||||
// see: https://wiki.multimedia.cx/index.php/ADTS
|
||||
return utils.binaryStringToArrayOfBytes(''.concat(
|
||||
// 12 bits for syncword (0xFFF)
|
||||
'111111111111',
|
||||
// 1 bit MPEG version
|
||||
'0',
|
||||
// 2 bit layer (always 0)
|
||||
'00',
|
||||
// 1 bit protection absent (1 for no CRC)
|
||||
'1',
|
||||
// 2 bit profile
|
||||
'10',
|
||||
// 4 bit sampling frequency index
|
||||
'0110',
|
||||
// 1 bit private bit
|
||||
'0',
|
||||
// 3 bit channel config
|
||||
'100',
|
||||
// 2 bit (ignore)
|
||||
'00',
|
||||
// 2 bit (copright bits)
|
||||
'00',
|
||||
// 13 bit frame length (includes header length)
|
||||
utils.leftPad(frameLength.toString(2), 13),
|
||||
// 11 bit buffer fullness
|
||||
'11111111111',
|
||||
// 2 bit number of AAC frames minus 1
|
||||
'00'
|
||||
// 16 bit CRC (if present)
|
||||
));
|
||||
};
|
||||
|
||||
QUnit.module('AAC Stream', {
|
||||
beforeEach: function() {
|
||||
aacStream = new AacStream();
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('parses ID3 tag', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
frameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(id3Tag));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frames');
|
||||
assert.equal(id3Count, 1, 'one id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('parses two ID3 tags in sequence', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
frameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(id3Tag.concat(id3Tag)));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frames');
|
||||
assert.equal(id3Count, 2, 'two id3 chunks');
|
||||
});
|
||||
|
||||
QUnit.test('does not parse second ID3 tag if it\'s incomplete', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
frameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(id3Tag.concat(id3Tag.slice(0, id3Tag.length - 1))));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frames');
|
||||
assert.equal(id3Count, 1, 'one id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('handles misaligned adts header', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
// fake adts frame
|
||||
adtsFrame = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
packetStream = createAdtsHeader(adtsFrame.length).concat(adtsFrame);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
// misalign by two bytes specific to a bug related to detecting sync bytes
|
||||
// (where we were only properly checking the second byte)
|
||||
aacStream.push(new Uint8Array([0x01, 0xf0].concat(packetStream)));
|
||||
|
||||
assert.equal(adtsCount, 1, 'one adts frames');
|
||||
assert.equal(id3Count, 0, 'no id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('handles incomplete adts frame after id3 frame', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
id3FrameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(id3FrameHeader.length).concat(id3FrameHeader),
|
||||
// in this case:
|
||||
// id3 tag = 20 bytes
|
||||
// adts header = 7 bytes
|
||||
// total = 27 bytes
|
||||
// report the ADTS frame size as 20 bytes
|
||||
adtsHeader = createAdtsHeader(20),
|
||||
// no adts frame, stream was cut off
|
||||
packetStream = id3Tag.concat(adtsHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(packetStream));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frame');
|
||||
assert.equal(id3Count, 1, 'one id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('emits data after receiving push', function(assert) {
|
||||
var
|
||||
array = new Uint8Array(109),
|
||||
count = 0;
|
||||
|
||||
array[0] = 255;
|
||||
array[1] = 241;
|
||||
array[2] = 92;
|
||||
array[3] = 128;
|
||||
array[4] = 13;
|
||||
array[5] = 191;
|
||||
array[6] = 252;
|
||||
array[7] = 33;
|
||||
array[8] = 32;
|
||||
array[9] = 3;
|
||||
array[10] = 64;
|
||||
array[11] = 104;
|
||||
array[12] = 27;
|
||||
array[13] = 212;
|
||||
aacStream.setTimestamp(90);
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.pts === 90 && frame.dts === 90) {
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
aacStream.push(array);
|
||||
assert.equal(count, 1);
|
||||
});
|
||||
|
||||
QUnit.test('continues parsing after corrupted stream', function(assert) {
|
||||
var
|
||||
array = new Uint8Array(10000),
|
||||
adtsCount = 0,
|
||||
id3Count = 0;
|
||||
|
||||
// an ID3 frame
|
||||
array[0] = 73;
|
||||
array[1] = 68;
|
||||
array[2] = 51;
|
||||
array[3] = 4;
|
||||
array[4] = 0;
|
||||
array[5] = 0;
|
||||
array[6] = 0;
|
||||
array[7] = 0;
|
||||
array[8] = 0;
|
||||
array[9] = 63;
|
||||
array[10] = 80;
|
||||
array[11] = 82;
|
||||
array[12] = 73;
|
||||
array[13] = 86;
|
||||
|
||||
// an atds frame
|
||||
array[1020] = 255;
|
||||
array[1021] = 241;
|
||||
array[1022] = 92;
|
||||
array[1023] = 128;
|
||||
array[1024] = 13;
|
||||
array[1025] = 191;
|
||||
array[1026] = 252;
|
||||
array[1027] = 33;
|
||||
array[1028] = 32;
|
||||
array[1029] = 3;
|
||||
array[1030] = 64;
|
||||
array[1031] = 104;
|
||||
array[1032] = 27;
|
||||
array[1033] = 212;
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
aacStream.push(array);
|
||||
assert.equal(adtsCount, 1);
|
||||
assert.equal(id3Count, 1);
|
||||
});
|
||||
'use strict';
|
||||
|
||||
var
|
||||
aacStream,
|
||||
AacStream = require('../lib/aac'),
|
||||
QUnit = require('qunit'),
|
||||
utils = require('./utils'),
|
||||
createId3Header,
|
||||
createId3FrameHeader,
|
||||
createAdtsHeader;
|
||||
|
||||
createId3Header = function(tagSize) {
|
||||
var header = [];
|
||||
|
||||
header[0] = 'I'.charCodeAt(0);
|
||||
header[1] = 'D'.charCodeAt(0);
|
||||
header[2] = '3'.charCodeAt(0);
|
||||
// 2 version bytes, ID3v2.4.0 (major 4, revision 0)
|
||||
header[3] = 4;
|
||||
header[4] = 0;
|
||||
// unsynchronization, extended header, experimental indicator, footer present flags
|
||||
header[5] = 0;
|
||||
// "The ID3v2 tag size is the sum of the byte length of the extended
|
||||
// header, the padding and the frames after unsynchronisation. If a
|
||||
// footer is present this equals to ('total size' - 20) bytes, otherwise
|
||||
// ('total size' - 10) bytes."
|
||||
// http://id3.org/id3v2.4.0-structure
|
||||
header[6] = 0;
|
||||
header[7] = 0;
|
||||
header[8] = 0;
|
||||
header[9] = tagSize;
|
||||
|
||||
return header;
|
||||
};
|
||||
|
||||
createId3FrameHeader = function() {
|
||||
var header = [];
|
||||
|
||||
// four byte frame ID, XYZ are experimental
|
||||
header[0] = 'X'.charCodeAt(0);
|
||||
header[1] = 'Y'.charCodeAt(0);
|
||||
header[2] = 'Z'.charCodeAt(0);
|
||||
header[3] = '0'.charCodeAt(0);
|
||||
// four byte sync safe integer size (excluding frame header)
|
||||
header[4] = 0;
|
||||
header[5] = 0;
|
||||
header[6] = 0;
|
||||
header[7] = 10;
|
||||
// two bytes for flags
|
||||
header[8] = 0;
|
||||
header[9] = 0;
|
||||
|
||||
return header;
|
||||
};
|
||||
|
||||
createAdtsHeader = function(frameLength) {
|
||||
// Header consists of 7 or 9 bytes (without or with CRC).
|
||||
// see: https://wiki.multimedia.cx/index.php/ADTS
|
||||
return utils.binaryStringToArrayOfBytes(''.concat(
|
||||
// 12 bits for syncword (0xFFF)
|
||||
'111111111111',
|
||||
// 1 bit MPEG version
|
||||
'0',
|
||||
// 2 bit layer (always 0)
|
||||
'00',
|
||||
// 1 bit protection absent (1 for no CRC)
|
||||
'1',
|
||||
// 2 bit profile
|
||||
'10',
|
||||
// 4 bit sampling frequency index
|
||||
'0110',
|
||||
// 1 bit private bit
|
||||
'0',
|
||||
// 3 bit channel config
|
||||
'100',
|
||||
// 2 bit (ignore)
|
||||
'00',
|
||||
// 2 bit (copright bits)
|
||||
'00',
|
||||
// 13 bit frame length (includes header length)
|
||||
utils.leftPad(frameLength.toString(2), 13),
|
||||
// 11 bit buffer fullness
|
||||
'11111111111',
|
||||
// 2 bit number of AAC frames minus 1
|
||||
'00'
|
||||
// 16 bit CRC (if present)
|
||||
));
|
||||
};
|
||||
|
||||
QUnit.module('AAC Stream', {
|
||||
beforeEach: function() {
|
||||
aacStream = new AacStream();
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('parses ID3 tag', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
frameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(id3Tag));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frames');
|
||||
assert.equal(id3Count, 1, 'one id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('parses two ID3 tags in sequence', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
frameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(id3Tag.concat(id3Tag)));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frames');
|
||||
assert.equal(id3Count, 2, 'two id3 chunks');
|
||||
});
|
||||
|
||||
QUnit.test('does not parse second ID3 tag if it\'s incomplete', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
frameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(frameHeader.length).concat(frameHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(id3Tag.concat(id3Tag.slice(0, id3Tag.length - 1))));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frames');
|
||||
assert.equal(id3Count, 1, 'one id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('handles misaligned adts header', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
// fake adts frame
|
||||
adtsFrame = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
packetStream = createAdtsHeader(adtsFrame.length).concat(adtsFrame);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
// misalign by two bytes specific to a bug related to detecting sync bytes
|
||||
// (where we were only properly checking the second byte)
|
||||
aacStream.push(new Uint8Array([0x01, 0xf0].concat(packetStream)));
|
||||
|
||||
assert.equal(adtsCount, 1, 'one adts frames');
|
||||
assert.equal(id3Count, 0, 'no id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('handles incomplete adts frame after id3 frame', function(assert) {
|
||||
var
|
||||
id3Count = 0,
|
||||
adtsCount = 0,
|
||||
id3FrameHeader = createId3FrameHeader(),
|
||||
id3Tag = createId3Header(id3FrameHeader.length).concat(id3FrameHeader),
|
||||
// in this case:
|
||||
// id3 tag = 20 bytes
|
||||
// adts header = 7 bytes
|
||||
// total = 27 bytes
|
||||
// report the ADTS frame size as 20 bytes
|
||||
adtsHeader = createAdtsHeader(20),
|
||||
// no adts frame, stream was cut off
|
||||
packetStream = id3Tag.concat(adtsHeader);
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
|
||||
aacStream.push(new Uint8Array(packetStream));
|
||||
|
||||
assert.equal(adtsCount, 0, 'no adts frame');
|
||||
assert.equal(id3Count, 1, 'one id3 chunk');
|
||||
});
|
||||
|
||||
QUnit.test('emits data after receiving push', function(assert) {
|
||||
var
|
||||
array = new Uint8Array(109),
|
||||
count = 0;
|
||||
|
||||
array[0] = 255;
|
||||
array[1] = 241;
|
||||
array[2] = 92;
|
||||
array[3] = 128;
|
||||
array[4] = 13;
|
||||
array[5] = 191;
|
||||
array[6] = 252;
|
||||
array[7] = 33;
|
||||
array[8] = 32;
|
||||
array[9] = 3;
|
||||
array[10] = 64;
|
||||
array[11] = 104;
|
||||
array[12] = 27;
|
||||
array[13] = 212;
|
||||
aacStream.setTimestamp(90);
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.pts === 90 && frame.dts === 90) {
|
||||
count += 1;
|
||||
}
|
||||
});
|
||||
aacStream.push(array);
|
||||
assert.equal(count, 1);
|
||||
});
|
||||
|
||||
QUnit.test('continues parsing after corrupted stream', function(assert) {
|
||||
var
|
||||
array = new Uint8Array(10000),
|
||||
adtsCount = 0,
|
||||
id3Count = 0;
|
||||
|
||||
// an ID3 frame
|
||||
array[0] = 73;
|
||||
array[1] = 68;
|
||||
array[2] = 51;
|
||||
array[3] = 4;
|
||||
array[4] = 0;
|
||||
array[5] = 0;
|
||||
array[6] = 0;
|
||||
array[7] = 0;
|
||||
array[8] = 0;
|
||||
array[9] = 63;
|
||||
array[10] = 80;
|
||||
array[11] = 82;
|
||||
array[12] = 73;
|
||||
array[13] = 86;
|
||||
|
||||
// an atds frame
|
||||
array[1020] = 255;
|
||||
array[1021] = 241;
|
||||
array[1022] = 92;
|
||||
array[1023] = 128;
|
||||
array[1024] = 13;
|
||||
array[1025] = 191;
|
||||
array[1026] = 252;
|
||||
array[1027] = 33;
|
||||
array[1028] = 32;
|
||||
array[1029] = 3;
|
||||
array[1030] = 64;
|
||||
array[1031] = 104;
|
||||
array[1032] = 27;
|
||||
array[1033] = 212;
|
||||
|
||||
aacStream.on('data', function(frame) {
|
||||
if (frame.type === 'timed-metadata') {
|
||||
id3Count += 1;
|
||||
} else if (frame.type === 'audio') {
|
||||
adtsCount += 1;
|
||||
}
|
||||
});
|
||||
aacStream.push(array);
|
||||
assert.equal(adtsCount, 1);
|
||||
assert.equal(id3Count, 1);
|
||||
});
|
||||
|
|
178
node_modules/mux.js/test/aac-utils.test.js
generated
vendored
178
node_modules/mux.js/test/aac-utils.test.js
generated
vendored
|
@ -1,89 +1,89 @@
|
|||
'use strict';
|
||||
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
utils = require('../lib/aac/utils.js'),
|
||||
testSegment = segments['test-aac-segment.aac']();
|
||||
|
||||
var id3TagOffset = 0;
|
||||
var audioFrameOffset = 73;
|
||||
|
||||
|
||||
QUnit.module('AAC Utils');
|
||||
|
||||
QUnit.test('correctly determines aac data', function(assert) {
|
||||
assert.ok(utils.isLikelyAacData(testSegment), 'test segment is aac');
|
||||
|
||||
|
||||
var id3Offset = utils.parseId3TagSize(testSegment, 0);
|
||||
var id3 = Array.prototype.slice.call(testSegment, 0, id3Offset);
|
||||
var segmentOnly = testSegment.subarray(id3Offset);
|
||||
var multipleId3 = new Uint8Array([]
|
||||
.concat(id3)
|
||||
.concat(id3)
|
||||
.concat(id3)
|
||||
.concat(id3)
|
||||
.concat(Array.prototype.slice.call(segmentOnly))
|
||||
);
|
||||
|
||||
assert.ok(utils.isLikelyAacData(segmentOnly), 'test segment is aac without id3');
|
||||
assert.notOk(utils.isLikelyAacData(testSegment.subarray(id3Offset + 25)), 'non aac data not recognized');
|
||||
assert.notOk(utils.isLikelyAacData(testSegment.subarray(0, 5)), 'not enough aac data is not recognized');
|
||||
assert.ok(utils.isLikelyAacData(multipleId3), 'test segment with multilpe id3');
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('correctly parses aac packet type', function(assert) {
|
||||
assert.equal(utils.parseType(testSegment, id3TagOffset), 'timed-metadata',
|
||||
'parsed timed-metadata type');
|
||||
assert.equal(utils.parseType(testSegment, 1), null,
|
||||
'parsed unknown type');
|
||||
assert.equal(utils.parseType(testSegment, audioFrameOffset), 'audio',
|
||||
'parsed audio type');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses ID3 tag size', function(assert) {
|
||||
assert.equal(utils.parseId3TagSize(testSegment, id3TagOffset), 73,
|
||||
'correct id3 tag size');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses timestamp from ID3 metadata', function(assert) {
|
||||
var frameSize = utils.parseId3TagSize(testSegment, id3TagOffset);
|
||||
var frame = testSegment.subarray(id3TagOffset, id3TagOffset + frameSize);
|
||||
|
||||
assert.equal(utils.parseAacTimestamp(frame), 895690, 'correct aac timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses adts frame size', function(assert) {
|
||||
assert.equal(utils.parseAdtsSize(testSegment, audioFrameOffset), 13,
|
||||
'correct adts frame size');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses packet sample rate', function(assert) {
|
||||
var frameSize = utils.parseAdtsSize(testSegment, audioFrameOffset);
|
||||
var frame = testSegment.subarray(audioFrameOffset, audioFrameOffset + frameSize);
|
||||
|
||||
assert.equal(utils.parseSampleRate(frame), 44100, 'correct sample rate');
|
||||
});
|
||||
|
||||
QUnit.test('parses correct ID3 tag size', function(assert) {
|
||||
var packetStream = new Uint8Array(10);
|
||||
|
||||
packetStream[9] = 63;
|
||||
|
||||
assert.equal(utils.parseId3TagSize(packetStream, 0),
|
||||
73,
|
||||
'correctly parsed a header without a footer');
|
||||
});
|
||||
|
||||
QUnit.test('parses correct ADTS Frame size', function(assert) {
|
||||
var packetStream = new Uint8Array(6);
|
||||
|
||||
packetStream[3] = 128;
|
||||
packetStream[4] = 29;
|
||||
packetStream[5] = 255;
|
||||
|
||||
assert.equal(utils.parseAdtsSize(packetStream, 0), 239, 'correctly parsed framesize');
|
||||
});
|
||||
'use strict';
|
||||
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
utils = require('../lib/aac/utils.js'),
|
||||
testSegment = segments['test-aac-segment.aac']();
|
||||
|
||||
var id3TagOffset = 0;
|
||||
var audioFrameOffset = 73;
|
||||
|
||||
|
||||
QUnit.module('AAC Utils');
|
||||
|
||||
QUnit.test('correctly determines aac data', function(assert) {
|
||||
assert.ok(utils.isLikelyAacData(testSegment), 'test segment is aac');
|
||||
|
||||
|
||||
var id3Offset = utils.parseId3TagSize(testSegment, 0);
|
||||
var id3 = Array.prototype.slice.call(testSegment, 0, id3Offset);
|
||||
var segmentOnly = testSegment.subarray(id3Offset);
|
||||
var multipleId3 = new Uint8Array([]
|
||||
.concat(id3)
|
||||
.concat(id3)
|
||||
.concat(id3)
|
||||
.concat(id3)
|
||||
.concat(Array.prototype.slice.call(segmentOnly))
|
||||
);
|
||||
|
||||
assert.ok(utils.isLikelyAacData(segmentOnly), 'test segment is aac without id3');
|
||||
assert.notOk(utils.isLikelyAacData(testSegment.subarray(id3Offset + 25)), 'non aac data not recognized');
|
||||
assert.notOk(utils.isLikelyAacData(testSegment.subarray(0, 5)), 'not enough aac data is not recognized');
|
||||
assert.ok(utils.isLikelyAacData(multipleId3), 'test segment with multilpe id3');
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('correctly parses aac packet type', function(assert) {
|
||||
assert.equal(utils.parseType(testSegment, id3TagOffset), 'timed-metadata',
|
||||
'parsed timed-metadata type');
|
||||
assert.equal(utils.parseType(testSegment, 1), null,
|
||||
'parsed unknown type');
|
||||
assert.equal(utils.parseType(testSegment, audioFrameOffset), 'audio',
|
||||
'parsed audio type');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses ID3 tag size', function(assert) {
|
||||
assert.equal(utils.parseId3TagSize(testSegment, id3TagOffset), 73,
|
||||
'correct id3 tag size');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses timestamp from ID3 metadata', function(assert) {
|
||||
var frameSize = utils.parseId3TagSize(testSegment, id3TagOffset);
|
||||
var frame = testSegment.subarray(id3TagOffset, id3TagOffset + frameSize);
|
||||
|
||||
assert.equal(utils.parseAacTimestamp(frame), 895690, 'correct aac timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses adts frame size', function(assert) {
|
||||
assert.equal(utils.parseAdtsSize(testSegment, audioFrameOffset), 13,
|
||||
'correct adts frame size');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses packet sample rate', function(assert) {
|
||||
var frameSize = utils.parseAdtsSize(testSegment, audioFrameOffset);
|
||||
var frame = testSegment.subarray(audioFrameOffset, audioFrameOffset + frameSize);
|
||||
|
||||
assert.equal(utils.parseSampleRate(frame), 44100, 'correct sample rate');
|
||||
});
|
||||
|
||||
QUnit.test('parses correct ID3 tag size', function(assert) {
|
||||
var packetStream = new Uint8Array(10);
|
||||
|
||||
packetStream[9] = 63;
|
||||
|
||||
assert.equal(utils.parseId3TagSize(packetStream, 0),
|
||||
73,
|
||||
'correctly parsed a header without a footer');
|
||||
});
|
||||
|
||||
QUnit.test('parses correct ADTS Frame size', function(assert) {
|
||||
var packetStream = new Uint8Array(6);
|
||||
|
||||
packetStream[3] = 128;
|
||||
packetStream[4] = 29;
|
||||
packetStream[5] = 255;
|
||||
|
||||
assert.equal(utils.parseAdtsSize(packetStream, 0), 239, 'correctly parsed framesize');
|
||||
});
|
||||
|
|
34
node_modules/mux.js/test/base64-to-uint8-array.js
generated
vendored
34
node_modules/mux.js/test/base64-to-uint8-array.js
generated
vendored
|
@ -1,17 +1,17 @@
|
|||
var window = require('global/window');
|
||||
// TODO: use vhs-utils here
|
||||
|
||||
var atob = (s) => window.atob ? window.atob(s) : Buffer.from(s, 'base64').toString('binary');
|
||||
|
||||
var base64ToUint8Array = function(base64) {
|
||||
var decoded = atob(base64);
|
||||
var uint8Array = new Uint8Array(new ArrayBuffer(decoded.length));
|
||||
|
||||
for (var i = 0; i < decoded.length; i++) {
|
||||
uint8Array[i] = decoded.charCodeAt(i);
|
||||
}
|
||||
|
||||
return uint8Array;
|
||||
};
|
||||
|
||||
module.exports = base64ToUint8Array;
|
||||
var window = require('global/window');
|
||||
// TODO: use vhs-utils here
|
||||
|
||||
var atob = (s) => window.atob ? window.atob(s) : Buffer.from(s, 'base64').toString('binary');
|
||||
|
||||
var base64ToUint8Array = function(base64) {
|
||||
var decoded = atob(base64);
|
||||
var uint8Array = new Uint8Array(new ArrayBuffer(decoded.length));
|
||||
|
||||
for (var i = 0; i < decoded.length; i++) {
|
||||
uint8Array[i] = decoded.charCodeAt(i);
|
||||
}
|
||||
|
||||
return uint8Array;
|
||||
};
|
||||
|
||||
module.exports = base64ToUint8Array;
|
||||
|
|
542
node_modules/mux.js/test/caption-parser.test.js
generated
vendored
542
node_modules/mux.js/test/caption-parser.test.js
generated
vendored
|
@ -1,271 +1,271 @@
|
|||
'use strict';
|
||||
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var probe = require('../lib/mp4/probe');
|
||||
var CaptionParser = require('../lib/mp4').CaptionParser;
|
||||
var captionParser;
|
||||
|
||||
var dashInit = segments['dash-608-captions-init.mp4']();
|
||||
// This file includes 2 segments data to force a flush
|
||||
// of the first caption. The second caption is at 200s
|
||||
var dashSegment = segments['dash-608-captions-seg.m4s']();
|
||||
var malformedSei = segments['malformed-sei.m4s']();
|
||||
var malformedSeiInit = segments['malformed-sei-init.mp4']();
|
||||
|
||||
var mp4Helpers = require('./utils/mp4-helpers');
|
||||
var box = mp4Helpers.box;
|
||||
var seiNalUnitGenerator = require('./utils/sei-nal-unit-generator');
|
||||
var makeMdatFromCaptionPackets = seiNalUnitGenerator.makeMdatFromCaptionPackets;
|
||||
var characters = seiNalUnitGenerator.characters;
|
||||
|
||||
var packets0;
|
||||
var version0Moof;
|
||||
var version0Segment;
|
||||
|
||||
var packets1;
|
||||
var version1Moof;
|
||||
var version1Segment;
|
||||
|
||||
QUnit.module('MP4 Caption Parser', {
|
||||
beforeEach: function() {
|
||||
captionParser = new CaptionParser();
|
||||
captionParser.init();
|
||||
},
|
||||
|
||||
afterEach: function() {
|
||||
captionParser.reset();
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('parse captions from real segment', function(assert) {
|
||||
var trackIds;
|
||||
var timescales;
|
||||
var cc;
|
||||
|
||||
trackIds = probe.videoTrackIds(dashInit);
|
||||
timescales = probe.timescale(dashInit);
|
||||
|
||||
cc = captionParser.parse(dashSegment, trackIds, timescales);
|
||||
|
||||
assert.equal(cc.captions.length, 1);
|
||||
assert.equal(cc.captions[0].text, '00:00:00',
|
||||
'real segment caption has correct text');
|
||||
assert.equal(cc.captions[0].stream, 'CC1',
|
||||
'real segment caption has correct stream');
|
||||
assert.equal(cc.captions[0].startTime, 0,
|
||||
'real segment caption has correct startTime');
|
||||
assert.equal(cc.captions[0].endTime, 119,
|
||||
'real segment caption has correct endTime');
|
||||
assert.equal(cc.captionStreams.CC1, true,
|
||||
'real segment caption streams have correct settings');
|
||||
});
|
||||
|
||||
QUnit.test('parse captions when init segment received late', function(assert) {
|
||||
var trackIds;
|
||||
var timescales;
|
||||
var cc;
|
||||
|
||||
trackIds = probe.videoTrackIds(dashInit);
|
||||
timescales = probe.timescale(dashInit);
|
||||
|
||||
cc = captionParser.parse(dashSegment, [], {});
|
||||
assert.ok(!cc, 'there should not be any parsed captions yet');
|
||||
|
||||
cc = captionParser.parse(dashSegment, trackIds, timescales);
|
||||
assert.equal(cc.captions.length, 1);
|
||||
});
|
||||
|
||||
QUnit.test('parseTrackId for version 0 and version 1 boxes', function(assert) {
|
||||
var v0Captions;
|
||||
var v1Captions;
|
||||
|
||||
v0Captions = captionParser.parse(
|
||||
new Uint8Array(version0Segment), // segment
|
||||
[1], // trackIds
|
||||
{ 1: 90000 }); // timescales);
|
||||
|
||||
assert.equal(v0Captions.captions.length, 1, 'got 1 version0 caption');
|
||||
assert.equal(v0Captions.captions[0].text, 'test string #1',
|
||||
'got the expected version0 caption text');
|
||||
assert.equal(v0Captions.captions[0].stream, 'CC1',
|
||||
'returned the correct caption stream CC1');
|
||||
assert.equal(v0Captions.captions[0].startTime, 10 / 90000,
|
||||
'the start time for version0 caption is correct');
|
||||
assert.equal(v0Captions.captions[0].endTime, 10 / 90000,
|
||||
'the end time for version0 caption is correct');
|
||||
assert.equal(v0Captions.captionStreams.CC1, true,
|
||||
'stream is CC1');
|
||||
assert.ok(!v0Captions.captionStreams.CC4,
|
||||
'stream is not CC4');
|
||||
|
||||
// Clear parsed captions
|
||||
captionParser.clearParsedCaptions();
|
||||
|
||||
v1Captions = captionParser.parse(
|
||||
new Uint8Array(version1Segment),
|
||||
[2], // trackIds
|
||||
{ 2: 90000 }); // timescales
|
||||
|
||||
assert.equal(v1Captions.captions.length, 1, 'got version1 caption');
|
||||
assert.equal(v1Captions.captions[0].text, 'test string #2',
|
||||
'got the expected version1 caption text');
|
||||
assert.equal(v1Captions.captions[0].stream, 'CC4',
|
||||
'returned the correct caption stream CC4');
|
||||
assert.equal(v1Captions.captions[0].startTime, 30 / 90000,
|
||||
'the start time for version1 caption is correct');
|
||||
assert.equal(v1Captions.captions[0].endTime, 30 / 90000,
|
||||
'the end time for version1 caption is correct');
|
||||
assert.equal(v1Captions.captionStreams.CC4, true,
|
||||
'stream is CC4');
|
||||
assert.ok(!v1Captions.captionStreams.CC1,
|
||||
'stream is not CC1');
|
||||
});
|
||||
|
||||
QUnit.test('returns log on invalid sei nal parse', function(assert) {
|
||||
var trackIds;
|
||||
var timescales;
|
||||
var result;
|
||||
var logs = [];
|
||||
|
||||
trackIds = probe.videoTrackIds(malformedSeiInit);
|
||||
timescales = probe.timescale(malformedSeiInit);
|
||||
|
||||
result = captionParser.parse(malformedSei, trackIds, timescales);
|
||||
|
||||
assert.deepEqual(result.logs, [
|
||||
{level: 'warn', message: 'We\'ve encountered a nal unit without data at 189975 for trackId 1. See mux.js#223.'}
|
||||
], 'logged invalid sei nal');
|
||||
});
|
||||
|
||||
// ---------
|
||||
// Test Data
|
||||
// ---------
|
||||
|
||||
// "test string #1", channel 1, field 1
|
||||
packets0 = [
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1420, type: 0 },
|
||||
// RCL, resume caption loading
|
||||
{ ccData: 0x1420, type: 0 },
|
||||
// 'test string #1'
|
||||
{ ccData: characters('te'), type: 0 },
|
||||
{ ccData: characters('st'), type: 0 },
|
||||
{ ccData: characters(' s'), type: 0 },
|
||||
// 'test string #1' continued
|
||||
{ ccData: characters('tr'), type: 0 },
|
||||
{ ccData: characters('in'), type: 0 },
|
||||
{ ccData: characters('g '), type: 0 },
|
||||
{ ccData: characters('#1'), type: 0 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x142f, type: 0 },
|
||||
// EOC, End of Caption. Finished transmitting, begin display
|
||||
{ ccData: 0x142f, type: 0 },
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1420, type: 0 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x142f, type: 0 }
|
||||
];
|
||||
|
||||
// "test string #2", channel 2, field 2
|
||||
packets1 = [
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1d20, type: 1 },
|
||||
// RCL, resume caption loading
|
||||
{ ccData: 0x1d20, type: 1 },
|
||||
// 'test string #2'
|
||||
{ ccData: characters('te'), type: 1 },
|
||||
{ ccData: characters('st'), type: 1 },
|
||||
{ ccData: characters(' s'), type: 1 },
|
||||
// 'test string #2' continued
|
||||
{ ccData: characters('tr'), type: 1 },
|
||||
{ ccData: characters('in'), type: 1 },
|
||||
{ ccData: characters('g '), type: 1 },
|
||||
{ ccData: characters('#2'), type: 1 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x1d2f, type: 1 },
|
||||
// EOC, End of Caption. Finished transmitting, begin display
|
||||
{ ccData: 0x1d2f, type: 1 },
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1d20, type: 1 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x1d2f, type: 1 }
|
||||
];
|
||||
|
||||
/**
|
||||
* version 0:
|
||||
* Uses version 0 boxes, no first sample flags
|
||||
* sample size, flags, duration, composition time offset included.
|
||||
**/
|
||||
version0Moof =
|
||||
box('moof',
|
||||
box('traf',
|
||||
box('tfhd',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // track_ID
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, // base_data_offset
|
||||
0x00, 0x00, 0x00, 0x00, // sample_description_index
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_size
|
||||
0x00, 0x00, 0x00, 0x00), // default_sample_flags
|
||||
box('tfdt',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00), // 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
|
||||
|
||||
version0Segment = version0Moof.concat(makeMdatFromCaptionPackets(packets0));
|
||||
|
||||
/**
|
||||
* version 1:
|
||||
* Uses version 1 boxes, has first sample flags,
|
||||
* other samples include flags and composition time offset only.
|
||||
**/
|
||||
version1Moof =
|
||||
box('moof',
|
||||
box('traf',
|
||||
box('tfhd',
|
||||
0x01, // version
|
||||
0x00, 0x00, 0x18, // flags
|
||||
0x00, 0x00, 0x00, 0x02, // track_ID
|
||||
// no base_data_offset, sample_description_index
|
||||
0x00, 0x00, 0x00, 0x0a, // default_sample_duration = 10
|
||||
0x00, 0x00, 0x00, 0x0a), // default_sample_size = 10
|
||||
box('tfdt',
|
||||
0x01, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x14), // baseMediaDecodeTime = 20,
|
||||
box('trun',
|
||||
0x01, // version
|
||||
0x00, 0x0c, 0x05, // flags: dataOffsetPresent, sampleFlagsPresent,
|
||||
// firstSampleFlagsPresent,
|
||||
// sampleCompositionTimeOffsetsPresent
|
||||
0x00, 0x00, 0x00, 0x02, // sample_count
|
||||
0x00, 0x00, 0x00, 0x00, // data_offset, has first_sample_flags
|
||||
// sample 1
|
||||
0x00, 0x00, 0x00, 0x00, // sample_flags
|
||||
0x00, 0x00, 0x00, 0x0a, // signed sample_composition_time_offset = 10
|
||||
// sample 2
|
||||
0x00, 0x00, 0x00, 0x00, // sample_flags
|
||||
0x00, 0x00, 0x00, 0x14))); // signed sample_composition_time_offset = 20
|
||||
|
||||
version1Segment = version1Moof.concat(makeMdatFromCaptionPackets(packets1));
|
||||
'use strict';
|
||||
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var probe = require('../lib/mp4/probe');
|
||||
var CaptionParser = require('../lib/mp4').CaptionParser;
|
||||
var captionParser;
|
||||
|
||||
var dashInit = segments['dash-608-captions-init.mp4']();
|
||||
// This file includes 2 segments data to force a flush
|
||||
// of the first caption. The second caption is at 200s
|
||||
var dashSegment = segments['dash-608-captions-seg.m4s']();
|
||||
var malformedSei = segments['malformed-sei.m4s']();
|
||||
var malformedSeiInit = segments['malformed-sei-init.mp4']();
|
||||
|
||||
var mp4Helpers = require('./utils/mp4-helpers');
|
||||
var box = mp4Helpers.box;
|
||||
var seiNalUnitGenerator = require('./utils/sei-nal-unit-generator');
|
||||
var makeMdatFromCaptionPackets = seiNalUnitGenerator.makeMdatFromCaptionPackets;
|
||||
var characters = seiNalUnitGenerator.characters;
|
||||
|
||||
var packets0;
|
||||
var version0Moof;
|
||||
var version0Segment;
|
||||
|
||||
var packets1;
|
||||
var version1Moof;
|
||||
var version1Segment;
|
||||
|
||||
QUnit.module('MP4 Caption Parser', {
|
||||
beforeEach: function() {
|
||||
captionParser = new CaptionParser();
|
||||
captionParser.init();
|
||||
},
|
||||
|
||||
afterEach: function() {
|
||||
captionParser.reset();
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('parse captions from real segment', function(assert) {
|
||||
var trackIds;
|
||||
var timescales;
|
||||
var cc;
|
||||
|
||||
trackIds = probe.videoTrackIds(dashInit);
|
||||
timescales = probe.timescale(dashInit);
|
||||
|
||||
cc = captionParser.parse(dashSegment, trackIds, timescales);
|
||||
|
||||
assert.equal(cc.captions.length, 1);
|
||||
assert.equal(cc.captions[0].text, '00:00:00',
|
||||
'real segment caption has correct text');
|
||||
assert.equal(cc.captions[0].stream, 'CC1',
|
||||
'real segment caption has correct stream');
|
||||
assert.equal(cc.captions[0].startTime, 0,
|
||||
'real segment caption has correct startTime');
|
||||
assert.equal(cc.captions[0].endTime, 119,
|
||||
'real segment caption has correct endTime');
|
||||
assert.equal(cc.captionStreams.CC1, true,
|
||||
'real segment caption streams have correct settings');
|
||||
});
|
||||
|
||||
QUnit.test('parse captions when init segment received late', function(assert) {
|
||||
var trackIds;
|
||||
var timescales;
|
||||
var cc;
|
||||
|
||||
trackIds = probe.videoTrackIds(dashInit);
|
||||
timescales = probe.timescale(dashInit);
|
||||
|
||||
cc = captionParser.parse(dashSegment, [], {});
|
||||
assert.ok(!cc, 'there should not be any parsed captions yet');
|
||||
|
||||
cc = captionParser.parse(dashSegment, trackIds, timescales);
|
||||
assert.equal(cc.captions.length, 1);
|
||||
});
|
||||
|
||||
QUnit.test('parseTrackId for version 0 and version 1 boxes', function(assert) {
|
||||
var v0Captions;
|
||||
var v1Captions;
|
||||
|
||||
v0Captions = captionParser.parse(
|
||||
new Uint8Array(version0Segment), // segment
|
||||
[1], // trackIds
|
||||
{ 1: 90000 }); // timescales);
|
||||
|
||||
assert.equal(v0Captions.captions.length, 1, 'got 1 version0 caption');
|
||||
assert.equal(v0Captions.captions[0].text, 'test string #1',
|
||||
'got the expected version0 caption text');
|
||||
assert.equal(v0Captions.captions[0].stream, 'CC1',
|
||||
'returned the correct caption stream CC1');
|
||||
assert.equal(v0Captions.captions[0].startTime, 10 / 90000,
|
||||
'the start time for version0 caption is correct');
|
||||
assert.equal(v0Captions.captions[0].endTime, 10 / 90000,
|
||||
'the end time for version0 caption is correct');
|
||||
assert.equal(v0Captions.captionStreams.CC1, true,
|
||||
'stream is CC1');
|
||||
assert.ok(!v0Captions.captionStreams.CC4,
|
||||
'stream is not CC4');
|
||||
|
||||
// Clear parsed captions
|
||||
captionParser.clearParsedCaptions();
|
||||
|
||||
v1Captions = captionParser.parse(
|
||||
new Uint8Array(version1Segment),
|
||||
[2], // trackIds
|
||||
{ 2: 90000 }); // timescales
|
||||
|
||||
assert.equal(v1Captions.captions.length, 1, 'got version1 caption');
|
||||
assert.equal(v1Captions.captions[0].text, 'test string #2',
|
||||
'got the expected version1 caption text');
|
||||
assert.equal(v1Captions.captions[0].stream, 'CC4',
|
||||
'returned the correct caption stream CC4');
|
||||
assert.equal(v1Captions.captions[0].startTime, 30 / 90000,
|
||||
'the start time for version1 caption is correct');
|
||||
assert.equal(v1Captions.captions[0].endTime, 30 / 90000,
|
||||
'the end time for version1 caption is correct');
|
||||
assert.equal(v1Captions.captionStreams.CC4, true,
|
||||
'stream is CC4');
|
||||
assert.ok(!v1Captions.captionStreams.CC1,
|
||||
'stream is not CC1');
|
||||
});
|
||||
|
||||
QUnit.test('returns log on invalid sei nal parse', function(assert) {
|
||||
var trackIds;
|
||||
var timescales;
|
||||
var result;
|
||||
var logs = [];
|
||||
|
||||
trackIds = probe.videoTrackIds(malformedSeiInit);
|
||||
timescales = probe.timescale(malformedSeiInit);
|
||||
|
||||
result = captionParser.parse(malformedSei, trackIds, timescales);
|
||||
|
||||
assert.deepEqual(result.logs, [
|
||||
{level: 'warn', message: 'We\'ve encountered a nal unit without data at 189975 for trackId 1. See mux.js#223.'}
|
||||
], 'logged invalid sei nal');
|
||||
});
|
||||
|
||||
// ---------
|
||||
// Test Data
|
||||
// ---------
|
||||
|
||||
// "test string #1", channel 1, field 1
|
||||
packets0 = [
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1420, type: 0 },
|
||||
// RCL, resume caption loading
|
||||
{ ccData: 0x1420, type: 0 },
|
||||
// 'test string #1'
|
||||
{ ccData: characters('te'), type: 0 },
|
||||
{ ccData: characters('st'), type: 0 },
|
||||
{ ccData: characters(' s'), type: 0 },
|
||||
// 'test string #1' continued
|
||||
{ ccData: characters('tr'), type: 0 },
|
||||
{ ccData: characters('in'), type: 0 },
|
||||
{ ccData: characters('g '), type: 0 },
|
||||
{ ccData: characters('#1'), type: 0 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x142f, type: 0 },
|
||||
// EOC, End of Caption. Finished transmitting, begin display
|
||||
{ ccData: 0x142f, type: 0 },
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1420, type: 0 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x142f, type: 0 }
|
||||
];
|
||||
|
||||
// "test string #2", channel 2, field 2
|
||||
packets1 = [
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1d20, type: 1 },
|
||||
// RCL, resume caption loading
|
||||
{ ccData: 0x1d20, type: 1 },
|
||||
// 'test string #2'
|
||||
{ ccData: characters('te'), type: 1 },
|
||||
{ ccData: characters('st'), type: 1 },
|
||||
{ ccData: characters(' s'), type: 1 },
|
||||
// 'test string #2' continued
|
||||
{ ccData: characters('tr'), type: 1 },
|
||||
{ ccData: characters('in'), type: 1 },
|
||||
{ ccData: characters('g '), type: 1 },
|
||||
{ ccData: characters('#2'), type: 1 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x1d2f, type: 1 },
|
||||
// EOC, End of Caption. Finished transmitting, begin display
|
||||
{ ccData: 0x1d2f, type: 1 },
|
||||
// Send another command so that the second EOC isn't ignored
|
||||
{ ccData: 0x1d20, type: 1 },
|
||||
// EOC, End of Caption. End display
|
||||
{ ccData: 0x1d2f, type: 1 }
|
||||
];
|
||||
|
||||
/**
|
||||
* version 0:
|
||||
* Uses version 0 boxes, no first sample flags
|
||||
* sample size, flags, duration, composition time offset included.
|
||||
**/
|
||||
version0Moof =
|
||||
box('moof',
|
||||
box('traf',
|
||||
box('tfhd',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // track_ID
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, // base_data_offset
|
||||
0x00, 0x00, 0x00, 0x00, // sample_description_index
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
||||
0x00, 0x00, 0x00, 0x00, // default_sample_size
|
||||
0x00, 0x00, 0x00, 0x00), // default_sample_flags
|
||||
box('tfdt',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00), // 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
|
||||
|
||||
version0Segment = version0Moof.concat(makeMdatFromCaptionPackets(packets0));
|
||||
|
||||
/**
|
||||
* version 1:
|
||||
* Uses version 1 boxes, has first sample flags,
|
||||
* other samples include flags and composition time offset only.
|
||||
**/
|
||||
version1Moof =
|
||||
box('moof',
|
||||
box('traf',
|
||||
box('tfhd',
|
||||
0x01, // version
|
||||
0x00, 0x00, 0x18, // flags
|
||||
0x00, 0x00, 0x00, 0x02, // track_ID
|
||||
// no base_data_offset, sample_description_index
|
||||
0x00, 0x00, 0x00, 0x0a, // default_sample_duration = 10
|
||||
0x00, 0x00, 0x00, 0x0a), // default_sample_size = 10
|
||||
box('tfdt',
|
||||
0x01, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x14), // baseMediaDecodeTime = 20,
|
||||
box('trun',
|
||||
0x01, // version
|
||||
0x00, 0x0c, 0x05, // flags: dataOffsetPresent, sampleFlagsPresent,
|
||||
// firstSampleFlagsPresent,
|
||||
// sampleCompositionTimeOffsetsPresent
|
||||
0x00, 0x00, 0x00, 0x02, // sample_count
|
||||
0x00, 0x00, 0x00, 0x00, // data_offset, has first_sample_flags
|
||||
// sample 1
|
||||
0x00, 0x00, 0x00, 0x00, // sample_flags
|
||||
0x00, 0x00, 0x00, 0x0a, // signed sample_composition_time_offset = 10
|
||||
// sample 2
|
||||
0x00, 0x00, 0x00, 0x00, // sample_flags
|
||||
0x00, 0x00, 0x00, 0x14))); // signed sample_composition_time_offset = 20
|
||||
|
||||
version1Segment = version1Moof.concat(makeMdatFromCaptionPackets(packets1));
|
||||
|
|
6010
node_modules/mux.js/test/caption-stream.test.js
generated
vendored
6010
node_modules/mux.js/test/caption-stream.test.js
generated
vendored
File diff suppressed because it is too large
Load diff
2228
node_modules/mux.js/test/captions.dfxp
generated
vendored
2228
node_modules/mux.js/test/captions.dfxp
generated
vendored
File diff suppressed because it is too large
Load diff
226
node_modules/mux.js/test/exp-golomb.test.js
generated
vendored
226
node_modules/mux.js/test/exp-golomb.test.js
generated
vendored
|
@ -1,113 +1,113 @@
|
|||
/*
|
||||
======== A Handy Little QUnit Reference ========
|
||||
http://api.qunitjs.com/
|
||||
|
||||
Test methods:
|
||||
module(name, {[setup][ ,teardown]})
|
||||
test(name, callback)
|
||||
expect(numberOfAssertions)
|
||||
stop(increment)
|
||||
start(decrement)
|
||||
Test assertions:
|
||||
assert.ok(value, [message])
|
||||
assert.equal(actual, expected, [message])
|
||||
assert.notEqual(actual, expected, [message])
|
||||
assert.deepEqual(actual, expected, [message])
|
||||
assert.notDeepEqual(actual, expected, [message])
|
||||
assert.strictEqual(actual, expected, [message])
|
||||
assert.notStrictEqual(actual, expected, [message])
|
||||
assert.throws(block, [expected], [message])
|
||||
*/
|
||||
var
|
||||
buffer,
|
||||
ExpGolomb = require('../lib/utils/exp-golomb'),
|
||||
expGolomb;
|
||||
|
||||
QUnit.module('Exponential Golomb coding');
|
||||
|
||||
QUnit.test('small numbers are coded correctly', function(assert) {
|
||||
var
|
||||
expected = [
|
||||
[0xF8, 0],
|
||||
[0x5F, 1],
|
||||
[0x7F, 2],
|
||||
[0x27, 3],
|
||||
[0x2F, 4],
|
||||
[0x37, 5],
|
||||
[0x3F, 6],
|
||||
[0x11, 7],
|
||||
[0x13, 8],
|
||||
[0x15, 9]
|
||||
],
|
||||
i = expected.length,
|
||||
result;
|
||||
|
||||
while (i--) {
|
||||
buffer = new Uint8Array([expected[i][0]]);
|
||||
expGolomb = new ExpGolomb(buffer);
|
||||
result = expGolomb.readUnsignedExpGolomb();
|
||||
assert.equal(expected[i][1], result, expected[i][0] + ' is decoded to ' + expected[i][1]);
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('drops working data as it is parsed', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x00, 0xFF]));
|
||||
expGolomb.skipBits(8);
|
||||
assert.equal(8, expGolomb.bitsAvailable(), '8 bits remain');
|
||||
assert.equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
|
||||
});
|
||||
|
||||
QUnit.test('drops working data when skipping leading zeros', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0xFF]));
|
||||
assert.equal(32, expGolomb.skipLeadingZeros(), '32 leading zeros are dropped');
|
||||
assert.equal(8, expGolomb.bitsAvailable(), '8 bits remain');
|
||||
assert.equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
|
||||
});
|
||||
|
||||
QUnit.test('drops working data when skipping leading zeros', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x15, 0xab, 0x40, 0xc8, 0xFF]));
|
||||
assert.equal(3, expGolomb.skipLeadingZeros(), '3 leading zeros are dropped');
|
||||
assert.equal((8 * 4) + 5, expGolomb.bitsAvailable(), '37 bits remain');
|
||||
expGolomb.skipBits(1);
|
||||
assert.equal(0x5a, expGolomb.readBits(8), 'the next bits are read');
|
||||
});
|
||||
|
||||
QUnit.test('skipBits correctly across word-boundaries', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x15, 0x00, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x00]));
|
||||
assert.equal(expGolomb.readUnsignedExpGolomb(), 9, 'the first number is read');
|
||||
expGolomb.skipBits(17);
|
||||
assert.equal(expGolomb.readUnsignedExpGolomb(), 4, 'the second number is read');
|
||||
expGolomb.skipBits(13); // Crosses word boundary
|
||||
assert.equal(expGolomb.readUnsignedExpGolomb(), 4, 'the third number is read');
|
||||
});
|
||||
|
||||
QUnit.test('parses a sequence parameter set', function(assert) {
|
||||
var
|
||||
sps = new Uint8Array([
|
||||
0x27, 0x42, 0xe0, 0x0b,
|
||||
0xa9, 0x18, 0x60, 0x9d,
|
||||
0x80, 0x35, 0x06, 0x01,
|
||||
0x06, 0xb6, 0xc2, 0xb5,
|
||||
0xef, 0x7c, 0x04
|
||||
]),
|
||||
expGolomb = new ExpGolomb(sps);
|
||||
|
||||
assert.strictEqual(expGolomb.readBits(8), 0x27, 'the NAL type specifies an SPS');
|
||||
assert.strictEqual(expGolomb.readBits(8), 66, 'profile_idc is 66');
|
||||
assert.strictEqual(expGolomb.readBits(4), 0x0E, 'constraints 0-3 are correct');
|
||||
|
||||
expGolomb.skipBits(4);
|
||||
assert.strictEqual(expGolomb.readBits(8), 11, 'level_idc is 11');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 0, 'seq_parameter_set_id is 0');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 1, 'log2_max_frame_num_minus4 is 1');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 0, 'pic_order_cnt_type is 0');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 3, 'log2_max_pic_order_cnt_lsb_minus4 is 3');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 2, 'max_num_ref_frames is 2');
|
||||
assert.strictEqual(expGolomb.readBits(1), 0, 'gaps_in_frame_num_value_allowed_flag is false');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 11, 'pic_width_in_mbs_minus1 is 11');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 8, 'pic_height_in_map_units_minus1 is 8');
|
||||
assert.strictEqual(expGolomb.readBits(1), 1, 'frame_mbs_only_flag is true');
|
||||
assert.strictEqual(expGolomb.readBits(1), 1, 'direct_8x8_inference_flag is true');
|
||||
assert.strictEqual(expGolomb.readBits(1), 0, 'frame_cropping_flag is false');
|
||||
});
|
||||
|
||||
/*
|
||||
======== A Handy Little QUnit Reference ========
|
||||
http://api.qunitjs.com/
|
||||
|
||||
Test methods:
|
||||
module(name, {[setup][ ,teardown]})
|
||||
test(name, callback)
|
||||
expect(numberOfAssertions)
|
||||
stop(increment)
|
||||
start(decrement)
|
||||
Test assertions:
|
||||
assert.ok(value, [message])
|
||||
assert.equal(actual, expected, [message])
|
||||
assert.notEqual(actual, expected, [message])
|
||||
assert.deepEqual(actual, expected, [message])
|
||||
assert.notDeepEqual(actual, expected, [message])
|
||||
assert.strictEqual(actual, expected, [message])
|
||||
assert.notStrictEqual(actual, expected, [message])
|
||||
assert.throws(block, [expected], [message])
|
||||
*/
|
||||
var
|
||||
buffer,
|
||||
ExpGolomb = require('../lib/utils/exp-golomb'),
|
||||
expGolomb;
|
||||
|
||||
QUnit.module('Exponential Golomb coding');
|
||||
|
||||
QUnit.test('small numbers are coded correctly', function(assert) {
|
||||
var
|
||||
expected = [
|
||||
[0xF8, 0],
|
||||
[0x5F, 1],
|
||||
[0x7F, 2],
|
||||
[0x27, 3],
|
||||
[0x2F, 4],
|
||||
[0x37, 5],
|
||||
[0x3F, 6],
|
||||
[0x11, 7],
|
||||
[0x13, 8],
|
||||
[0x15, 9]
|
||||
],
|
||||
i = expected.length,
|
||||
result;
|
||||
|
||||
while (i--) {
|
||||
buffer = new Uint8Array([expected[i][0]]);
|
||||
expGolomb = new ExpGolomb(buffer);
|
||||
result = expGolomb.readUnsignedExpGolomb();
|
||||
assert.equal(expected[i][1], result, expected[i][0] + ' is decoded to ' + expected[i][1]);
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('drops working data as it is parsed', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x00, 0xFF]));
|
||||
expGolomb.skipBits(8);
|
||||
assert.equal(8, expGolomb.bitsAvailable(), '8 bits remain');
|
||||
assert.equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
|
||||
});
|
||||
|
||||
QUnit.test('drops working data when skipping leading zeros', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x00, 0x00, 0x00, 0x00, 0xFF]));
|
||||
assert.equal(32, expGolomb.skipLeadingZeros(), '32 leading zeros are dropped');
|
||||
assert.equal(8, expGolomb.bitsAvailable(), '8 bits remain');
|
||||
assert.equal(0xFF, expGolomb.readBits(8), 'the second byte is read');
|
||||
});
|
||||
|
||||
QUnit.test('drops working data when skipping leading zeros', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x15, 0xab, 0x40, 0xc8, 0xFF]));
|
||||
assert.equal(3, expGolomb.skipLeadingZeros(), '3 leading zeros are dropped');
|
||||
assert.equal((8 * 4) + 5, expGolomb.bitsAvailable(), '37 bits remain');
|
||||
expGolomb.skipBits(1);
|
||||
assert.equal(0x5a, expGolomb.readBits(8), 'the next bits are read');
|
||||
});
|
||||
|
||||
QUnit.test('skipBits correctly across word-boundaries', function(assert) {
|
||||
var expGolomb = new ExpGolomb(new Uint8Array([0x15, 0x00, 0x00, 0x28, 0x00, 0x0a, 0x00, 0x00]));
|
||||
assert.equal(expGolomb.readUnsignedExpGolomb(), 9, 'the first number is read');
|
||||
expGolomb.skipBits(17);
|
||||
assert.equal(expGolomb.readUnsignedExpGolomb(), 4, 'the second number is read');
|
||||
expGolomb.skipBits(13); // Crosses word boundary
|
||||
assert.equal(expGolomb.readUnsignedExpGolomb(), 4, 'the third number is read');
|
||||
});
|
||||
|
||||
QUnit.test('parses a sequence parameter set', function(assert) {
|
||||
var
|
||||
sps = new Uint8Array([
|
||||
0x27, 0x42, 0xe0, 0x0b,
|
||||
0xa9, 0x18, 0x60, 0x9d,
|
||||
0x80, 0x35, 0x06, 0x01,
|
||||
0x06, 0xb6, 0xc2, 0xb5,
|
||||
0xef, 0x7c, 0x04
|
||||
]),
|
||||
expGolomb = new ExpGolomb(sps);
|
||||
|
||||
assert.strictEqual(expGolomb.readBits(8), 0x27, 'the NAL type specifies an SPS');
|
||||
assert.strictEqual(expGolomb.readBits(8), 66, 'profile_idc is 66');
|
||||
assert.strictEqual(expGolomb.readBits(4), 0x0E, 'constraints 0-3 are correct');
|
||||
|
||||
expGolomb.skipBits(4);
|
||||
assert.strictEqual(expGolomb.readBits(8), 11, 'level_idc is 11');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 0, 'seq_parameter_set_id is 0');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 1, 'log2_max_frame_num_minus4 is 1');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 0, 'pic_order_cnt_type is 0');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 3, 'log2_max_pic_order_cnt_lsb_minus4 is 3');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 2, 'max_num_ref_frames is 2');
|
||||
assert.strictEqual(expGolomb.readBits(1), 0, 'gaps_in_frame_num_value_allowed_flag is false');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 11, 'pic_width_in_mbs_minus1 is 11');
|
||||
assert.strictEqual(expGolomb.readUnsignedExpGolomb(), 8, 'pic_height_in_map_units_minus1 is 8');
|
||||
assert.strictEqual(expGolomb.readBits(1), 1, 'frame_mbs_only_flag is true');
|
||||
assert.strictEqual(expGolomb.readBits(1), 1, 'direct_8x8_inference_flag is true');
|
||||
assert.strictEqual(expGolomb.readBits(1), 0, 'frame_cropping_flag is false');
|
||||
});
|
||||
|
||||
|
|
150
node_modules/mux.js/test/m2ts-probe.test.js
generated
vendored
150
node_modules/mux.js/test/m2ts-probe.test.js
generated
vendored
|
@ -1,75 +1,75 @@
|
|||
'use strict';
|
||||
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
probe = require('../lib/m2ts/probe.js'),
|
||||
testSegment = segments['test-segment.ts'](),
|
||||
stuffedPesPacket = segments['test-stuffed-pes.ts']();
|
||||
|
||||
/**
|
||||
* All subarray indices verified with the use of thumbcoil.
|
||||
*/
|
||||
var patPacket = testSegment.subarray(188, 376);
|
||||
var pmtPid = 4095;
|
||||
var programMapTable = {
|
||||
256: 0x1B,
|
||||
257: 0x0F
|
||||
};
|
||||
var pmtPacket = testSegment.subarray(376, 564);
|
||||
var pesPacket = testSegment.subarray(564, 752);
|
||||
var videoPacket = testSegment.subarray(564, 1692);
|
||||
var videoNoKeyFramePacket = testSegment.subarray(1880, 2820);
|
||||
var audioPacket = testSegment.subarray(6956, 7144);
|
||||
var notPusiPacket = testSegment.subarray(1316, 1504);
|
||||
|
||||
QUnit.module('M2TS Probe');
|
||||
|
||||
QUnit.test('correctly parses packet type', function(assert) {
|
||||
assert.equal(probe.parseType(patPacket), 'pat', 'parses pat type');
|
||||
assert.equal(probe.parseType(pmtPacket), null,
|
||||
'cannot determine type of pmt packet when pmt pid has not been parsed yet');
|
||||
assert.equal(probe.parseType(pmtPacket, pmtPid), 'pmt', 'parses pmt type');
|
||||
assert.equal(probe.parseType(pesPacket), null,
|
||||
'cannot determine type of pes packet when pmt pid has not been parsed yet');
|
||||
assert.equal(probe.parseType(pesPacket, pmtPid), 'pes', 'parses pes type');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses pmt pid from pat packet', function(assert) {
|
||||
assert.equal(probe.parsePat(patPacket), pmtPid, 'parses pmt pid from pat');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses program map table from pmt packet', function(assert) {
|
||||
assert.deepEqual(probe.parsePmt(pmtPacket), programMapTable, 'generates correct pmt');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses payload unit start indicator', function(assert) {
|
||||
assert.ok(probe.parsePayloadUnitStartIndicator(pesPacket),
|
||||
'detects payload unit start indicator');
|
||||
assert.ok(!probe.parsePayloadUnitStartIndicator(notPusiPacket),
|
||||
'detects no payload unit start indicator');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses type of pes packet', function(assert) {
|
||||
assert.equal(probe.parsePesType(videoPacket, programMapTable), 'video',
|
||||
'parses video pes type');
|
||||
assert.equal(probe.parsePesType(audioPacket, programMapTable), 'audio',
|
||||
'parses audio pes type');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses dts and pts values of pes packet', function(assert) {
|
||||
var videoPes = probe.parsePesTime(videoPacket);
|
||||
assert.equal(videoPes.dts, 126000, 'correct dts value');
|
||||
assert.equal(videoPes.pts, 126000, 'correct pts value');
|
||||
|
||||
videoPes = probe.parsePesTime(stuffedPesPacket);
|
||||
assert.equal(videoPes, null,
|
||||
'correctly returned null when there is no packet data, only stuffing');
|
||||
});
|
||||
|
||||
QUnit.test('correctly determines if video pes packet contains a key frame', function(assert) {
|
||||
assert.ok(probe.videoPacketContainsKeyFrame(videoPacket), 'detects key frame in packet');
|
||||
assert.ok(!probe.videoPacketContainsKeyFrame(videoNoKeyFramePacket),
|
||||
'detects no key frame in packet');
|
||||
});
|
||||
'use strict';
|
||||
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
probe = require('../lib/m2ts/probe.js'),
|
||||
testSegment = segments['test-segment.ts'](),
|
||||
stuffedPesPacket = segments['test-stuffed-pes.ts']();
|
||||
|
||||
/**
|
||||
* All subarray indices verified with the use of thumbcoil.
|
||||
*/
|
||||
var patPacket = testSegment.subarray(188, 376);
|
||||
var pmtPid = 4095;
|
||||
var programMapTable = {
|
||||
256: 0x1B,
|
||||
257: 0x0F
|
||||
};
|
||||
var pmtPacket = testSegment.subarray(376, 564);
|
||||
var pesPacket = testSegment.subarray(564, 752);
|
||||
var videoPacket = testSegment.subarray(564, 1692);
|
||||
var videoNoKeyFramePacket = testSegment.subarray(1880, 2820);
|
||||
var audioPacket = testSegment.subarray(6956, 7144);
|
||||
var notPusiPacket = testSegment.subarray(1316, 1504);
|
||||
|
||||
QUnit.module('M2TS Probe');
|
||||
|
||||
QUnit.test('correctly parses packet type', function(assert) {
|
||||
assert.equal(probe.parseType(patPacket), 'pat', 'parses pat type');
|
||||
assert.equal(probe.parseType(pmtPacket), null,
|
||||
'cannot determine type of pmt packet when pmt pid has not been parsed yet');
|
||||
assert.equal(probe.parseType(pmtPacket, pmtPid), 'pmt', 'parses pmt type');
|
||||
assert.equal(probe.parseType(pesPacket), null,
|
||||
'cannot determine type of pes packet when pmt pid has not been parsed yet');
|
||||
assert.equal(probe.parseType(pesPacket, pmtPid), 'pes', 'parses pes type');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses pmt pid from pat packet', function(assert) {
|
||||
assert.equal(probe.parsePat(patPacket), pmtPid, 'parses pmt pid from pat');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses program map table from pmt packet', function(assert) {
|
||||
assert.deepEqual(probe.parsePmt(pmtPacket), programMapTable, 'generates correct pmt');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses payload unit start indicator', function(assert) {
|
||||
assert.ok(probe.parsePayloadUnitStartIndicator(pesPacket),
|
||||
'detects payload unit start indicator');
|
||||
assert.ok(!probe.parsePayloadUnitStartIndicator(notPusiPacket),
|
||||
'detects no payload unit start indicator');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses type of pes packet', function(assert) {
|
||||
assert.equal(probe.parsePesType(videoPacket, programMapTable), 'video',
|
||||
'parses video pes type');
|
||||
assert.equal(probe.parsePesType(audioPacket, programMapTable), 'audio',
|
||||
'parses audio pes type');
|
||||
});
|
||||
|
||||
QUnit.test('correctly parses dts and pts values of pes packet', function(assert) {
|
||||
var videoPes = probe.parsePesTime(videoPacket);
|
||||
assert.equal(videoPes.dts, 126000, 'correct dts value');
|
||||
assert.equal(videoPes.pts, 126000, 'correct pts value');
|
||||
|
||||
videoPes = probe.parsePesTime(stuffedPesPacket);
|
||||
assert.equal(videoPes, null,
|
||||
'correctly returned null when there is no packet data, only stuffing');
|
||||
});
|
||||
|
||||
QUnit.test('correctly determines if video pes packet contains a key frame', function(assert) {
|
||||
assert.ok(probe.videoPacketContainsKeyFrame(videoPacket), 'detects key frame in packet');
|
||||
assert.ok(!probe.videoPacketContainsKeyFrame(videoNoKeyFramePacket),
|
||||
'detects no key frame in packet');
|
||||
});
|
||||
|
|
22
node_modules/mux.js/test/metadata-stream-test-worker.js
generated
vendored
22
node_modules/mux.js/test/metadata-stream-test-worker.js
generated
vendored
|
@ -1,11 +1,11 @@
|
|||
var mp2t, metadataStream;
|
||||
|
||||
mp2t = require('../lib/m2ts');
|
||||
metadataStream = new mp2t.MetadataStream();
|
||||
|
||||
self.addEventListener('message', function(e) {
|
||||
metadataStream.on('data', function(data) {
|
||||
self.postMessage(data);
|
||||
});
|
||||
metadataStream.push(e.data);
|
||||
});
|
||||
var mp2t, metadataStream;
|
||||
|
||||
mp2t = require('../lib/m2ts');
|
||||
metadataStream = new mp2t.MetadataStream();
|
||||
|
||||
self.addEventListener('message', function(e) {
|
||||
metadataStream.on('data', function(data) {
|
||||
self.postMessage(data);
|
||||
});
|
||||
metadataStream.push(e.data);
|
||||
});
|
||||
|
|
1238
node_modules/mux.js/test/metadata-stream.test.js
generated
vendored
1238
node_modules/mux.js/test/metadata-stream.test.js
generated
vendored
File diff suppressed because it is too large
Load diff
1162
node_modules/mux.js/test/mp4-generator.test.js
generated
vendored
1162
node_modules/mux.js/test/mp4-generator.test.js
generated
vendored
File diff suppressed because it is too large
Load diff
2242
node_modules/mux.js/test/mp4-inspector.test.js
generated
vendored
2242
node_modules/mux.js/test/mp4-inspector.test.js
generated
vendored
File diff suppressed because it is too large
Load diff
814
node_modules/mux.js/test/mp4-probe.test.js
generated
vendored
814
node_modules/mux.js/test/mp4-probe.test.js
generated
vendored
|
@ -1,407 +1,407 @@
|
|||
'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
|
||||
'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
|
||||
|
|
284
node_modules/mux.js/test/partial.test.js
generated
vendored
284
node_modules/mux.js/test/partial.test.js
generated
vendored
|
@ -1,142 +1,142 @@
|
|||
var Transmuxer = require('../lib/partial/transmuxer.js');
|
||||
var utils = require('./utils');
|
||||
var generatePMT = utils.generatePMT;
|
||||
var videoPes = utils.videoPes;
|
||||
var audioPes = utils.audioPes;
|
||||
var packetize = utils.packetize;
|
||||
var PAT = utils.PAT;
|
||||
|
||||
QUnit.module('Partial Transmuxer - Options');
|
||||
[
|
||||
{options: {keepOriginalTimestamps: false}},
|
||||
{options: {keepOriginalTimestamps: true}},
|
||||
{options: {keepOriginalTimestamps: false, baseMediaDecodeTime: 15000}},
|
||||
{options: {keepOriginalTimestamps: true, baseMediaDecodeTime: 15000}},
|
||||
{options: {keepOriginalTimestamps: false}, baseMediaSetter: 15000},
|
||||
{options: {keepOriginalTimestamps: true}, baseMediaSetter: 15000}
|
||||
].forEach(function(test) {
|
||||
var createTransmuxer = function() {
|
||||
var transmuxer = new Transmuxer(test.options);
|
||||
|
||||
if (test.baseMediaSetter) {
|
||||
transmuxer.setBaseMediaDecodeTime(test.baseMediaSetter);
|
||||
}
|
||||
|
||||
return transmuxer;
|
||||
};
|
||||
|
||||
var name = '';
|
||||
|
||||
Object.keys(test.options).forEach(function(optionName) {
|
||||
name += '' + optionName + ' ' + test.options[optionName] + ' ';
|
||||
});
|
||||
|
||||
if (test.baseMediaSetter) {
|
||||
name += 'baseMediaDecodeTime setter ' + test.baseMediaSetter;
|
||||
}
|
||||
|
||||
QUnit.test('Audio frames after video not trimmed, ' + name, function(assert) {
|
||||
var
|
||||
segments = [],
|
||||
earliestDts = 15000,
|
||||
transmuxer = createTransmuxer();
|
||||
|
||||
transmuxer.on('data', function(segment) {
|
||||
segments.push(segment);
|
||||
});
|
||||
|
||||
// the following transmuxer pushes add tiny video and
|
||||
// audio data to the transmuxer. When we add the data
|
||||
// we also set the pts/dts time so that audio should
|
||||
// not be trimmed.
|
||||
transmuxer.push(packetize(PAT));
|
||||
transmuxer.push(packetize(generatePMT({
|
||||
hasVideo: true,
|
||||
hasAudio: true
|
||||
})));
|
||||
|
||||
transmuxer.push(packetize(audioPes([
|
||||
0x19, 0x47
|
||||
], true, earliestDts + 1)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x09, 0x01 // access_unit_delimiter_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x08, 0x01 // pic_parameter_set_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x07, // seq_parameter_set_rbsp
|
||||
0x27, 0x42, 0xe0, 0x0b,
|
||||
0xa9, 0x18, 0x60, 0x9d,
|
||||
0x80, 0x53, 0x06, 0x01,
|
||||
0x06, 0xb6, 0xc2, 0xb5,
|
||||
0xef, 0x7c, 0x04
|
||||
], false, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x05, 0x01 // slice_layer_without_partitioning_rbsp_idr
|
||||
], true, earliestDts)));
|
||||
transmuxer.flush();
|
||||
|
||||
// the partial transmuxer only generates a video segment
|
||||
// when all audio frames are trimmed. So we should have an audio and video
|
||||
// segment
|
||||
assert.equal(segments.length, 2, 'generated a video and an audio segment');
|
||||
assert.equal(segments[0].type, 'video', 'video segment exists');
|
||||
assert.equal(segments[1].type, 'audio', 'audio segment exists');
|
||||
});
|
||||
|
||||
QUnit.test('Audio frames trimmed before video, ' + name, function(assert) {
|
||||
var
|
||||
segments = [],
|
||||
earliestDts = 15000,
|
||||
baseTime = test.options.baseMediaDecodeTime || test.baseMediaSetter || 0,
|
||||
transmuxer = createTransmuxer();
|
||||
|
||||
transmuxer.on('data', function(segment) {
|
||||
segments.push(segment);
|
||||
});
|
||||
|
||||
// the following transmuxer pushes add tiny video and
|
||||
// audio data to the transmuxer. When we add the data
|
||||
// we also set the pts/dts time so that audio should
|
||||
// be trimmed.
|
||||
transmuxer.push(packetize(PAT));
|
||||
transmuxer.push(packetize(generatePMT({
|
||||
hasVideo: true,
|
||||
hasAudio: true
|
||||
})));
|
||||
|
||||
transmuxer.push(packetize(audioPes([
|
||||
0x19, 0x47
|
||||
], true, earliestDts - baseTime - 1)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x09, 0x01 // access_unit_delimiter_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x08, 0x01 // pic_parameter_set_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x07, // seq_parameter_set_rbsp
|
||||
0x27, 0x42, 0xe0, 0x0b,
|
||||
0xa9, 0x18, 0x60, 0x9d,
|
||||
0x80, 0x53, 0x06, 0x01,
|
||||
0x06, 0xb6, 0xc2, 0xb5,
|
||||
0xef, 0x7c, 0x04
|
||||
], false, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x05, 0x01 // slice_layer_without_partitioning_rbsp_idr
|
||||
], true, earliestDts)));
|
||||
transmuxer.flush();
|
||||
|
||||
// the partial transmuxer only generates a video segment
|
||||
// when all audio frames are trimmed.
|
||||
if (test.options.keepOriginalTimestamps && !baseTime) {
|
||||
assert.equal(segments.length, 2, 'generated both a video/audio segment');
|
||||
assert.equal(segments[0].type, 'video', 'segment is video');
|
||||
assert.equal(segments[1].type, 'audio', 'segment is audio');
|
||||
} else {
|
||||
assert.equal(segments.length, 1, 'generated only a video segment');
|
||||
assert.equal(segments[0].type, 'video', 'segment is video');
|
||||
}
|
||||
});
|
||||
});
|
||||
var Transmuxer = require('../lib/partial/transmuxer.js');
|
||||
var utils = require('./utils');
|
||||
var generatePMT = utils.generatePMT;
|
||||
var videoPes = utils.videoPes;
|
||||
var audioPes = utils.audioPes;
|
||||
var packetize = utils.packetize;
|
||||
var PAT = utils.PAT;
|
||||
|
||||
QUnit.module('Partial Transmuxer - Options');
|
||||
[
|
||||
{options: {keepOriginalTimestamps: false}},
|
||||
{options: {keepOriginalTimestamps: true}},
|
||||
{options: {keepOriginalTimestamps: false, baseMediaDecodeTime: 15000}},
|
||||
{options: {keepOriginalTimestamps: true, baseMediaDecodeTime: 15000}},
|
||||
{options: {keepOriginalTimestamps: false}, baseMediaSetter: 15000},
|
||||
{options: {keepOriginalTimestamps: true}, baseMediaSetter: 15000}
|
||||
].forEach(function(test) {
|
||||
var createTransmuxer = function() {
|
||||
var transmuxer = new Transmuxer(test.options);
|
||||
|
||||
if (test.baseMediaSetter) {
|
||||
transmuxer.setBaseMediaDecodeTime(test.baseMediaSetter);
|
||||
}
|
||||
|
||||
return transmuxer;
|
||||
};
|
||||
|
||||
var name = '';
|
||||
|
||||
Object.keys(test.options).forEach(function(optionName) {
|
||||
name += '' + optionName + ' ' + test.options[optionName] + ' ';
|
||||
});
|
||||
|
||||
if (test.baseMediaSetter) {
|
||||
name += 'baseMediaDecodeTime setter ' + test.baseMediaSetter;
|
||||
}
|
||||
|
||||
QUnit.test('Audio frames after video not trimmed, ' + name, function(assert) {
|
||||
var
|
||||
segments = [],
|
||||
earliestDts = 15000,
|
||||
transmuxer = createTransmuxer();
|
||||
|
||||
transmuxer.on('data', function(segment) {
|
||||
segments.push(segment);
|
||||
});
|
||||
|
||||
// the following transmuxer pushes add tiny video and
|
||||
// audio data to the transmuxer. When we add the data
|
||||
// we also set the pts/dts time so that audio should
|
||||
// not be trimmed.
|
||||
transmuxer.push(packetize(PAT));
|
||||
transmuxer.push(packetize(generatePMT({
|
||||
hasVideo: true,
|
||||
hasAudio: true
|
||||
})));
|
||||
|
||||
transmuxer.push(packetize(audioPes([
|
||||
0x19, 0x47
|
||||
], true, earliestDts + 1)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x09, 0x01 // access_unit_delimiter_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x08, 0x01 // pic_parameter_set_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x07, // seq_parameter_set_rbsp
|
||||
0x27, 0x42, 0xe0, 0x0b,
|
||||
0xa9, 0x18, 0x60, 0x9d,
|
||||
0x80, 0x53, 0x06, 0x01,
|
||||
0x06, 0xb6, 0xc2, 0xb5,
|
||||
0xef, 0x7c, 0x04
|
||||
], false, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x05, 0x01 // slice_layer_without_partitioning_rbsp_idr
|
||||
], true, earliestDts)));
|
||||
transmuxer.flush();
|
||||
|
||||
// the partial transmuxer only generates a video segment
|
||||
// when all audio frames are trimmed. So we should have an audio and video
|
||||
// segment
|
||||
assert.equal(segments.length, 2, 'generated a video and an audio segment');
|
||||
assert.equal(segments[0].type, 'video', 'video segment exists');
|
||||
assert.equal(segments[1].type, 'audio', 'audio segment exists');
|
||||
});
|
||||
|
||||
QUnit.test('Audio frames trimmed before video, ' + name, function(assert) {
|
||||
var
|
||||
segments = [],
|
||||
earliestDts = 15000,
|
||||
baseTime = test.options.baseMediaDecodeTime || test.baseMediaSetter || 0,
|
||||
transmuxer = createTransmuxer();
|
||||
|
||||
transmuxer.on('data', function(segment) {
|
||||
segments.push(segment);
|
||||
});
|
||||
|
||||
// the following transmuxer pushes add tiny video and
|
||||
// audio data to the transmuxer. When we add the data
|
||||
// we also set the pts/dts time so that audio should
|
||||
// be trimmed.
|
||||
transmuxer.push(packetize(PAT));
|
||||
transmuxer.push(packetize(generatePMT({
|
||||
hasVideo: true,
|
||||
hasAudio: true
|
||||
})));
|
||||
|
||||
transmuxer.push(packetize(audioPes([
|
||||
0x19, 0x47
|
||||
], true, earliestDts - baseTime - 1)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x09, 0x01 // access_unit_delimiter_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x08, 0x01 // pic_parameter_set_rbsp
|
||||
], true, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x07, // seq_parameter_set_rbsp
|
||||
0x27, 0x42, 0xe0, 0x0b,
|
||||
0xa9, 0x18, 0x60, 0x9d,
|
||||
0x80, 0x53, 0x06, 0x01,
|
||||
0x06, 0xb6, 0xc2, 0xb5,
|
||||
0xef, 0x7c, 0x04
|
||||
], false, earliestDts)));
|
||||
transmuxer.push(packetize(videoPes([
|
||||
0x05, 0x01 // slice_layer_without_partitioning_rbsp_idr
|
||||
], true, earliestDts)));
|
||||
transmuxer.flush();
|
||||
|
||||
// the partial transmuxer only generates a video segment
|
||||
// when all audio frames are trimmed.
|
||||
if (test.options.keepOriginalTimestamps && !baseTime) {
|
||||
assert.equal(segments.length, 2, 'generated both a video/audio segment');
|
||||
assert.equal(segments[0].type, 'video', 'segment is video');
|
||||
assert.equal(segments[1].type, 'audio', 'segment is audio');
|
||||
} else {
|
||||
assert.equal(segments.length, 1, 'generated only a video segment');
|
||||
assert.equal(segments[0].type, 'video', 'segment is video');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
96
node_modules/mux.js/test/stream.test.js
generated
vendored
96
node_modules/mux.js/test/stream.test.js
generated
vendored
|
@ -1,48 +1,48 @@
|
|||
'use strict';
|
||||
|
||||
var
|
||||
stream,
|
||||
Stream = require('../lib/utils/stream'),
|
||||
QUnit = require('qunit');
|
||||
|
||||
QUnit.module('Stream', {
|
||||
beforeEach: function() {
|
||||
stream = new Stream();
|
||||
stream.init();
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('trigger calls listeners', function(assert) {
|
||||
var args = [];
|
||||
|
||||
stream.on('test', function(data) {
|
||||
args.push(data);
|
||||
});
|
||||
|
||||
stream.trigger('test', 1);
|
||||
stream.trigger('test', 2);
|
||||
|
||||
assert.deepEqual(args, [1, 2]);
|
||||
});
|
||||
|
||||
QUnit.test('callbacks can remove themselves', function(assert) {
|
||||
var args1 = [], args2 = [], args3 = [];
|
||||
|
||||
stream.on('test', function(event) {
|
||||
args1.push(event);
|
||||
});
|
||||
stream.on('test', function t(event) {
|
||||
args2.push(event);
|
||||
stream.off('test', t);
|
||||
});
|
||||
stream.on('test', function(event) {
|
||||
args3.push(event);
|
||||
});
|
||||
|
||||
stream.trigger('test', 1);
|
||||
stream.trigger('test', 2);
|
||||
|
||||
assert.deepEqual(args1, [1, 2], 'first callback ran all times');
|
||||
assert.deepEqual(args2, [1], 'second callback removed after first run');
|
||||
assert.deepEqual(args3, [1, 2], 'third callback ran all times');
|
||||
});
|
||||
'use strict';
|
||||
|
||||
var
|
||||
stream,
|
||||
Stream = require('../lib/utils/stream'),
|
||||
QUnit = require('qunit');
|
||||
|
||||
QUnit.module('Stream', {
|
||||
beforeEach: function() {
|
||||
stream = new Stream();
|
||||
stream.init();
|
||||
}
|
||||
});
|
||||
|
||||
QUnit.test('trigger calls listeners', function(assert) {
|
||||
var args = [];
|
||||
|
||||
stream.on('test', function(data) {
|
||||
args.push(data);
|
||||
});
|
||||
|
||||
stream.trigger('test', 1);
|
||||
stream.trigger('test', 2);
|
||||
|
||||
assert.deepEqual(args, [1, 2]);
|
||||
});
|
||||
|
||||
QUnit.test('callbacks can remove themselves', function(assert) {
|
||||
var args1 = [], args2 = [], args3 = [];
|
||||
|
||||
stream.on('test', function(event) {
|
||||
args1.push(event);
|
||||
});
|
||||
stream.on('test', function t(event) {
|
||||
args2.push(event);
|
||||
stream.off('test', t);
|
||||
});
|
||||
stream.on('test', function(event) {
|
||||
args3.push(event);
|
||||
});
|
||||
|
||||
stream.trigger('test', 1);
|
||||
stream.trigger('test', 2);
|
||||
|
||||
assert.deepEqual(args1, [1, 2], 'first callback ran all times');
|
||||
assert.deepEqual(args2, [1], 'second callback removed after first run');
|
||||
assert.deepEqual(args3, [1, 2], 'third callback ran all times');
|
||||
});
|
||||
|
|
9312
node_modules/mux.js/test/transmuxer.test.js
generated
vendored
9312
node_modules/mux.js/test/transmuxer.test.js
generated
vendored
File diff suppressed because it is too large
Load diff
410
node_modules/mux.js/test/ts-inspector.test.js
generated
vendored
410
node_modules/mux.js/test/ts-inspector.test.js
generated
vendored
|
@ -1,205 +1,205 @@
|
|||
'use strict';
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
tsInspector = require('../lib/tools/ts-inspector.js'),
|
||||
StreamTypes = require('../lib/m2ts/stream-types.js'),
|
||||
tsSegment = segments['test-segment.ts'](),
|
||||
tsNoAudioSegment = segments['test-no-audio-segment.ts'](),
|
||||
aacSegment = segments['test-aac-segment.aac'](),
|
||||
utils = require('./utils'),
|
||||
inspect = tsInspector.inspect,
|
||||
parseAudioPes_ = tsInspector.parseAudioPes_,
|
||||
packetize = utils.packetize,
|
||||
audioPes = utils.audioPes,
|
||||
PES_TIMESCALE = 90000;
|
||||
|
||||
QUnit.module('TS Inspector');
|
||||
|
||||
QUnit.test('returns null for empty segment input', function(assert) {
|
||||
assert.equal(inspect(new Uint8Array([])), null, 'returned null');
|
||||
});
|
||||
|
||||
QUnit.test('can parse a ts segment', function(assert) {
|
||||
var expected = {
|
||||
video: [
|
||||
{
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'video',
|
||||
pts: 924000,
|
||||
dts: 924000,
|
||||
ptsTime: 924000 / PES_TIMESCALE,
|
||||
dtsTime: 924000 / PES_TIMESCALE
|
||||
}
|
||||
],
|
||||
firstKeyFrame: {
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
audio: [
|
||||
{
|
||||
type: 'audio',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'audio',
|
||||
pts: 859518,
|
||||
dts: 859518,
|
||||
ptsTime: 859518 / PES_TIMESCALE,
|
||||
dtsTime: 859518 / PES_TIMESCALE
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(inspect(tsSegment), expected, 'parses ts segment timing data');
|
||||
});
|
||||
|
||||
QUnit.test('adjusts timestamp values based on provided reference', function(assert) {
|
||||
var rollover = Math.pow(2, 33);
|
||||
|
||||
var expected = {
|
||||
video: [
|
||||
{
|
||||
type: 'video',
|
||||
pts: (126000 + rollover),
|
||||
dts: (126000 + rollover),
|
||||
ptsTime: (126000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (126000 + rollover) / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'video',
|
||||
pts: (924000 + rollover),
|
||||
dts: (924000 + rollover),
|
||||
ptsTime: (924000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (924000 + rollover) / PES_TIMESCALE
|
||||
}
|
||||
],
|
||||
firstKeyFrame: {
|
||||
type: 'video',
|
||||
pts: (126000 + rollover),
|
||||
dts: (126000 + rollover),
|
||||
ptsTime: (126000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (126000 + rollover) / PES_TIMESCALE
|
||||
},
|
||||
audio: [
|
||||
{
|
||||
type: 'audio',
|
||||
pts: (126000 + rollover),
|
||||
dts: (126000 + rollover),
|
||||
ptsTime: (126000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (126000 + rollover) / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'audio',
|
||||
pts: (859518 + rollover),
|
||||
dts: (859518 + rollover),
|
||||
ptsTime: (859518 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (859518 + rollover) / PES_TIMESCALE
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(inspect(tsSegment, rollover - 1), expected,
|
||||
'adjusts inspected time data to account for pts rollover');
|
||||
});
|
||||
|
||||
QUnit.test('can parse an aac segment', function(assert) {
|
||||
var expected = {
|
||||
audio: [
|
||||
{
|
||||
type: 'audio',
|
||||
pts: 895690,
|
||||
dts: 895690,
|
||||
ptsTime: 895690 / PES_TIMESCALE,
|
||||
dtsTime: 895690 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'audio',
|
||||
pts: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)),
|
||||
dts: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)),
|
||||
ptsTime: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)) / PES_TIMESCALE,
|
||||
dtsTime: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)) / PES_TIMESCALE
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(inspect(aacSegment), expected, 'parses aac segment timing data');
|
||||
});
|
||||
|
||||
QUnit.test('can parse ts segment with no audio muxed in', function(assert) {
|
||||
var expected = {
|
||||
video: [
|
||||
{
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'video',
|
||||
pts: 924000,
|
||||
dts: 924000,
|
||||
ptsTime: 924000 / PES_TIMESCALE,
|
||||
dtsTime: 924000 / PES_TIMESCALE
|
||||
}
|
||||
],
|
||||
firstKeyFrame: {
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
}
|
||||
};
|
||||
|
||||
var actual = inspect(tsNoAudioSegment);
|
||||
|
||||
assert.equal(typeof actual.audio, 'undefined', 'results do not contain audio info');
|
||||
assert.deepEqual(actual, expected,
|
||||
'parses ts segment without audio timing data');
|
||||
});
|
||||
|
||||
QUnit.test('can parse audio PES when it\'s the only packet in a stream', function(assert) {
|
||||
var
|
||||
pts = 90000,
|
||||
pmt = {
|
||||
// fake pmt pid that doesn't clash with the audio pid
|
||||
pid: 0x10,
|
||||
table: {
|
||||
// pid copied over from default of audioPes function
|
||||
0x12: StreamTypes.ADTS_STREAM_TYPE
|
||||
}
|
||||
},
|
||||
result = { audio: [] };
|
||||
|
||||
parseAudioPes_(packetize(audioPes([0x00], true, pts)), pmt, result);
|
||||
|
||||
// note that both the first and last packet timings are the same, as there's only one
|
||||
// packet to parse
|
||||
assert.deepEqual(
|
||||
result.audio,
|
||||
[{
|
||||
dts: pts,
|
||||
pts: pts,
|
||||
type: 'audio'
|
||||
}, {
|
||||
dts: pts,
|
||||
pts: pts,
|
||||
type: 'audio'
|
||||
}],
|
||||
'parses audio pes for timing info');
|
||||
});
|
||||
'use strict';
|
||||
var segments = require('data-files!segments');
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
tsInspector = require('../lib/tools/ts-inspector.js'),
|
||||
StreamTypes = require('../lib/m2ts/stream-types.js'),
|
||||
tsSegment = segments['test-segment.ts'](),
|
||||
tsNoAudioSegment = segments['test-no-audio-segment.ts'](),
|
||||
aacSegment = segments['test-aac-segment.aac'](),
|
||||
utils = require('./utils'),
|
||||
inspect = tsInspector.inspect,
|
||||
parseAudioPes_ = tsInspector.parseAudioPes_,
|
||||
packetize = utils.packetize,
|
||||
audioPes = utils.audioPes,
|
||||
PES_TIMESCALE = 90000;
|
||||
|
||||
QUnit.module('TS Inspector');
|
||||
|
||||
QUnit.test('returns null for empty segment input', function(assert) {
|
||||
assert.equal(inspect(new Uint8Array([])), null, 'returned null');
|
||||
});
|
||||
|
||||
QUnit.test('can parse a ts segment', function(assert) {
|
||||
var expected = {
|
||||
video: [
|
||||
{
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'video',
|
||||
pts: 924000,
|
||||
dts: 924000,
|
||||
ptsTime: 924000 / PES_TIMESCALE,
|
||||
dtsTime: 924000 / PES_TIMESCALE
|
||||
}
|
||||
],
|
||||
firstKeyFrame: {
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
audio: [
|
||||
{
|
||||
type: 'audio',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'audio',
|
||||
pts: 859518,
|
||||
dts: 859518,
|
||||
ptsTime: 859518 / PES_TIMESCALE,
|
||||
dtsTime: 859518 / PES_TIMESCALE
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(inspect(tsSegment), expected, 'parses ts segment timing data');
|
||||
});
|
||||
|
||||
QUnit.test('adjusts timestamp values based on provided reference', function(assert) {
|
||||
var rollover = Math.pow(2, 33);
|
||||
|
||||
var expected = {
|
||||
video: [
|
||||
{
|
||||
type: 'video',
|
||||
pts: (126000 + rollover),
|
||||
dts: (126000 + rollover),
|
||||
ptsTime: (126000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (126000 + rollover) / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'video',
|
||||
pts: (924000 + rollover),
|
||||
dts: (924000 + rollover),
|
||||
ptsTime: (924000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (924000 + rollover) / PES_TIMESCALE
|
||||
}
|
||||
],
|
||||
firstKeyFrame: {
|
||||
type: 'video',
|
||||
pts: (126000 + rollover),
|
||||
dts: (126000 + rollover),
|
||||
ptsTime: (126000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (126000 + rollover) / PES_TIMESCALE
|
||||
},
|
||||
audio: [
|
||||
{
|
||||
type: 'audio',
|
||||
pts: (126000 + rollover),
|
||||
dts: (126000 + rollover),
|
||||
ptsTime: (126000 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (126000 + rollover) / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'audio',
|
||||
pts: (859518 + rollover),
|
||||
dts: (859518 + rollover),
|
||||
ptsTime: (859518 + rollover) / PES_TIMESCALE,
|
||||
dtsTime: (859518 + rollover) / PES_TIMESCALE
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(inspect(tsSegment, rollover - 1), expected,
|
||||
'adjusts inspected time data to account for pts rollover');
|
||||
});
|
||||
|
||||
QUnit.test('can parse an aac segment', function(assert) {
|
||||
var expected = {
|
||||
audio: [
|
||||
{
|
||||
type: 'audio',
|
||||
pts: 895690,
|
||||
dts: 895690,
|
||||
ptsTime: 895690 / PES_TIMESCALE,
|
||||
dtsTime: 895690 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'audio',
|
||||
pts: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)),
|
||||
dts: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)),
|
||||
ptsTime: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)) / PES_TIMESCALE,
|
||||
dtsTime: (895690 + (430 * 1024 * PES_TIMESCALE / 44100)) / PES_TIMESCALE
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
assert.deepEqual(inspect(aacSegment), expected, 'parses aac segment timing data');
|
||||
});
|
||||
|
||||
QUnit.test('can parse ts segment with no audio muxed in', function(assert) {
|
||||
var expected = {
|
||||
video: [
|
||||
{
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
},
|
||||
{
|
||||
type: 'video',
|
||||
pts: 924000,
|
||||
dts: 924000,
|
||||
ptsTime: 924000 / PES_TIMESCALE,
|
||||
dtsTime: 924000 / PES_TIMESCALE
|
||||
}
|
||||
],
|
||||
firstKeyFrame: {
|
||||
type: 'video',
|
||||
pts: 126000,
|
||||
dts: 126000,
|
||||
ptsTime: 126000 / PES_TIMESCALE,
|
||||
dtsTime: 126000 / PES_TIMESCALE
|
||||
}
|
||||
};
|
||||
|
||||
var actual = inspect(tsNoAudioSegment);
|
||||
|
||||
assert.equal(typeof actual.audio, 'undefined', 'results do not contain audio info');
|
||||
assert.deepEqual(actual, expected,
|
||||
'parses ts segment without audio timing data');
|
||||
});
|
||||
|
||||
QUnit.test('can parse audio PES when it\'s the only packet in a stream', function(assert) {
|
||||
var
|
||||
pts = 90000,
|
||||
pmt = {
|
||||
// fake pmt pid that doesn't clash with the audio pid
|
||||
pid: 0x10,
|
||||
table: {
|
||||
// pid copied over from default of audioPes function
|
||||
0x12: StreamTypes.ADTS_STREAM_TYPE
|
||||
}
|
||||
},
|
||||
result = { audio: [] };
|
||||
|
||||
parseAudioPes_(packetize(audioPes([0x00], true, pts)), pmt, result);
|
||||
|
||||
// note that both the first and last packet timings are the same, as there's only one
|
||||
// packet to parse
|
||||
assert.deepEqual(
|
||||
result.audio,
|
||||
[{
|
||||
dts: pts,
|
||||
pts: pts,
|
||||
type: 'audio'
|
||||
}, {
|
||||
dts: pts,
|
||||
pts: pts,
|
||||
type: 'audio'
|
||||
}],
|
||||
'parses audio pes for timing info');
|
||||
});
|
||||
|
|
64
node_modules/mux.js/test/utils.bin.test.js
generated
vendored
64
node_modules/mux.js/test/utils.bin.test.js
generated
vendored
|
@ -1,32 +1,32 @@
|
|||
var
|
||||
QUnit = require('qunit'),
|
||||
toUnsigned = require('../lib/utils/bin').toUnsigned;
|
||||
|
||||
QUnit.module('Binary Utils');
|
||||
|
||||
QUnit.test('converts values to unsigned integers after bitwise operations', function(assert) {
|
||||
var bytes;
|
||||
|
||||
bytes = [0, 0, 124, 129];
|
||||
|
||||
assert.equal(toUnsigned(bytes[0] << 24 |
|
||||
bytes[1] << 16 |
|
||||
bytes[2] << 8 |
|
||||
bytes[3]),
|
||||
31873, 'positive signed result stays positive');
|
||||
|
||||
bytes = [150, 234, 221, 192];
|
||||
|
||||
// sanity check
|
||||
assert.equal(bytes[0] << 24 |
|
||||
bytes[1] << 16 |
|
||||
bytes[2] << 8 |
|
||||
bytes[3],
|
||||
-1762992704, 'bitwise operation produces negative signed result');
|
||||
|
||||
assert.equal(toUnsigned(bytes[0] << 24 |
|
||||
bytes[1] << 16 |
|
||||
bytes[2] << 8 |
|
||||
bytes[3]),
|
||||
2531974592, 'negative signed result becomes unsigned positive');
|
||||
});
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
toUnsigned = require('../lib/utils/bin').toUnsigned;
|
||||
|
||||
QUnit.module('Binary Utils');
|
||||
|
||||
QUnit.test('converts values to unsigned integers after bitwise operations', function(assert) {
|
||||
var bytes;
|
||||
|
||||
bytes = [0, 0, 124, 129];
|
||||
|
||||
assert.equal(toUnsigned(bytes[0] << 24 |
|
||||
bytes[1] << 16 |
|
||||
bytes[2] << 8 |
|
||||
bytes[3]),
|
||||
31873, 'positive signed result stays positive');
|
||||
|
||||
bytes = [150, 234, 221, 192];
|
||||
|
||||
// sanity check
|
||||
assert.equal(bytes[0] << 24 |
|
||||
bytes[1] << 16 |
|
||||
bytes[2] << 8 |
|
||||
bytes[3],
|
||||
-1762992704, 'bitwise operation produces negative signed result');
|
||||
|
||||
assert.equal(toUnsigned(bytes[0] << 24 |
|
||||
bytes[1] << 16 |
|
||||
bytes[2] << 8 |
|
||||
bytes[3]),
|
||||
2531974592, 'negative signed result becomes unsigned positive');
|
||||
});
|
||||
|
|
362
node_modules/mux.js/test/utils.clock.test.js
generated
vendored
362
node_modules/mux.js/test/utils.clock.test.js
generated
vendored
|
@ -1,181 +1,181 @@
|
|||
'use strict';
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
clock = require('../lib/utils/clock');
|
||||
|
||||
QUnit.module('Clock Utils');
|
||||
|
||||
QUnit.test('converts from seconds to video timestamps', function(assert) {
|
||||
assert.equal(clock.secondsToVideoTs(0), 0, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(1), 90000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(10), 900000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(-1), -90000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(3), 270000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(0.1), 9000, 'converts seconds to video timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from seconds to audio timestamps', function(assert) {
|
||||
assert.equal(clock.secondsToAudioTs(0, 90000),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(1, 90000),
|
||||
90000,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(-1, 90000),
|
||||
-90000,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(3, 90000),
|
||||
270000,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(0, 44100),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(1, 44100),
|
||||
44100,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(3, 44100),
|
||||
132300,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(-1, 44100),
|
||||
-44100,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(0.1, 44100),
|
||||
4410,
|
||||
'converts seconds to audio timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from video timestamp to seconds', function(assert) {
|
||||
assert.equal(clock.videoTsToSeconds(0), 0, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(90000), 1, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(900000), 10, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(-90000), -1, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(270000), 3, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(9000), 0.1, 'converts video timestamp to seconds');
|
||||
});
|
||||
|
||||
QUnit.test('converts from audio timestamp to seconds', function(assert) {
|
||||
assert.equal(clock.audioTsToSeconds(0, 90000),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(90000, 90000),
|
||||
1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(-90000, 90000),
|
||||
-1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(270000, 90000),
|
||||
3,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(0, 44100),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(44100, 44100),
|
||||
1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(132300, 44100),
|
||||
3,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(-44100, 44100),
|
||||
-1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(4410, 44100),
|
||||
0.1,
|
||||
'converts seconds to audio timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from audio timestamp to video timestamp', function(assert) {
|
||||
assert.equal(clock.audioTsToVideoTs(0, 90000),
|
||||
0,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(90000, 90000),
|
||||
90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(900000, 90000),
|
||||
900000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(-90000, 90000),
|
||||
-90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(270000, 90000),
|
||||
270000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(9000, 90000),
|
||||
9000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(0, 44100),
|
||||
0,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(44100, 44100),
|
||||
90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(441000, 44100),
|
||||
900000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(-44100, 44100),
|
||||
-90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(132300, 44100),
|
||||
270000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(4410, 44100),
|
||||
9000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from video timestamp to audio timestamp', function(assert) {
|
||||
assert.equal(clock.videoTsToAudioTs(0, 90000),
|
||||
0,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(90000, 90000),
|
||||
90000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(900000, 90000),
|
||||
900000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(-90000, 90000),
|
||||
-90000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(270000, 90000),
|
||||
270000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(9000, 90000),
|
||||
9000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(0, 44100),
|
||||
0,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(90000, 44100),
|
||||
44100,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(900000, 44100),
|
||||
441000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(-90000, 44100),
|
||||
-44100,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(270000, 44100),
|
||||
132300,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(9000, 44100),
|
||||
4410,
|
||||
'converts video timestamp to audio timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from metadata timestamp to seconds', function(assert) {
|
||||
assert.equal(clock.metadataTsToSeconds(90000, 90000, false),
|
||||
0,
|
||||
'converts metadata timestamp to seconds and adjusts by timelineStartPts');
|
||||
|
||||
assert.equal(clock.metadataTsToSeconds(270000, 90000, false),
|
||||
2,
|
||||
'converts metadata timestamp to seconds and adjusts by timelineStartPts');
|
||||
|
||||
assert.equal(clock.metadataTsToSeconds(90000, 90000, true),
|
||||
1,
|
||||
'converts metadata timestamp to seconds while keeping original timestamps');
|
||||
|
||||
assert.equal(clock.metadataTsToSeconds(180000, 0, true),
|
||||
2,
|
||||
'converts metadata timestamp to seconds while keeping original timestamps');
|
||||
});
|
||||
'use strict';
|
||||
|
||||
var
|
||||
QUnit = require('qunit'),
|
||||
clock = require('../lib/utils/clock');
|
||||
|
||||
QUnit.module('Clock Utils');
|
||||
|
||||
QUnit.test('converts from seconds to video timestamps', function(assert) {
|
||||
assert.equal(clock.secondsToVideoTs(0), 0, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(1), 90000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(10), 900000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(-1), -90000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(3), 270000, 'converts seconds to video timestamp');
|
||||
assert.equal(clock.secondsToVideoTs(0.1), 9000, 'converts seconds to video timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from seconds to audio timestamps', function(assert) {
|
||||
assert.equal(clock.secondsToAudioTs(0, 90000),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(1, 90000),
|
||||
90000,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(-1, 90000),
|
||||
-90000,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(3, 90000),
|
||||
270000,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(0, 44100),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(1, 44100),
|
||||
44100,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(3, 44100),
|
||||
132300,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(-1, 44100),
|
||||
-44100,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.secondsToAudioTs(0.1, 44100),
|
||||
4410,
|
||||
'converts seconds to audio timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from video timestamp to seconds', function(assert) {
|
||||
assert.equal(clock.videoTsToSeconds(0), 0, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(90000), 1, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(900000), 10, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(-90000), -1, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(270000), 3, 'converts video timestamp to seconds');
|
||||
assert.equal(clock.videoTsToSeconds(9000), 0.1, 'converts video timestamp to seconds');
|
||||
});
|
||||
|
||||
QUnit.test('converts from audio timestamp to seconds', function(assert) {
|
||||
assert.equal(clock.audioTsToSeconds(0, 90000),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(90000, 90000),
|
||||
1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(-90000, 90000),
|
||||
-1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(270000, 90000),
|
||||
3,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(0, 44100),
|
||||
0,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(44100, 44100),
|
||||
1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(132300, 44100),
|
||||
3,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(-44100, 44100),
|
||||
-1,
|
||||
'converts seconds to audio timestamp');
|
||||
assert.equal(clock.audioTsToSeconds(4410, 44100),
|
||||
0.1,
|
||||
'converts seconds to audio timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from audio timestamp to video timestamp', function(assert) {
|
||||
assert.equal(clock.audioTsToVideoTs(0, 90000),
|
||||
0,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(90000, 90000),
|
||||
90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(900000, 90000),
|
||||
900000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(-90000, 90000),
|
||||
-90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(270000, 90000),
|
||||
270000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(9000, 90000),
|
||||
9000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(0, 44100),
|
||||
0,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(44100, 44100),
|
||||
90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(441000, 44100),
|
||||
900000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(-44100, 44100),
|
||||
-90000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(132300, 44100),
|
||||
270000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
assert.equal(clock.audioTsToVideoTs(4410, 44100),
|
||||
9000,
|
||||
'converts audio timestamp to video timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from video timestamp to audio timestamp', function(assert) {
|
||||
assert.equal(clock.videoTsToAudioTs(0, 90000),
|
||||
0,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(90000, 90000),
|
||||
90000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(900000, 90000),
|
||||
900000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(-90000, 90000),
|
||||
-90000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(270000, 90000),
|
||||
270000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(9000, 90000),
|
||||
9000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(0, 44100),
|
||||
0,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(90000, 44100),
|
||||
44100,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(900000, 44100),
|
||||
441000,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(-90000, 44100),
|
||||
-44100,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(270000, 44100),
|
||||
132300,
|
||||
'converts video timestamp to audio timestamp');
|
||||
assert.equal(clock.videoTsToAudioTs(9000, 44100),
|
||||
4410,
|
||||
'converts video timestamp to audio timestamp');
|
||||
});
|
||||
|
||||
QUnit.test('converts from metadata timestamp to seconds', function(assert) {
|
||||
assert.equal(clock.metadataTsToSeconds(90000, 90000, false),
|
||||
0,
|
||||
'converts metadata timestamp to seconds and adjusts by timelineStartPts');
|
||||
|
||||
assert.equal(clock.metadataTsToSeconds(270000, 90000, false),
|
||||
2,
|
||||
'converts metadata timestamp to seconds and adjusts by timelineStartPts');
|
||||
|
||||
assert.equal(clock.metadataTsToSeconds(90000, 90000, true),
|
||||
1,
|
||||
'converts metadata timestamp to seconds while keeping original timestamps');
|
||||
|
||||
assert.equal(clock.metadataTsToSeconds(180000, 0, true),
|
||||
2,
|
||||
'converts metadata timestamp to seconds while keeping original timestamps');
|
||||
});
|
||||
|
|
654
node_modules/mux.js/test/utils.js
generated
vendored
654
node_modules/mux.js/test/utils.js
generated
vendored
|
@ -1,327 +1,327 @@
|
|||
var
|
||||
mp2t = require('../lib/m2ts'),
|
||||
id3Generator = require('./utils/id3-generator'),
|
||||
MP2T_PACKET_LENGTH = mp2t.MP2T_PACKET_LENGTH,
|
||||
PMT,
|
||||
PAT,
|
||||
generatePMT,
|
||||
pesHeader,
|
||||
packetize,
|
||||
transportPacket,
|
||||
videoPes,
|
||||
adtsFrame,
|
||||
audioPes,
|
||||
timedMetadataPes,
|
||||
binaryStringToArrayOfBytes,
|
||||
leftPad;
|
||||
|
||||
|
||||
PMT = [
|
||||
0x47, // sync byte
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0010 0000
|
||||
0x40, 0x10,
|
||||
// tsc:01 afc:01 cc:0000 pointer_field:0000 0000
|
||||
0x50, 0x00,
|
||||
// tid:0000 0010 ssi:0 0:0 r:00 sl:0000 0001 1100
|
||||
0x02, 0x00, 0x1c,
|
||||
// pn:0000 0000 0000 0001
|
||||
0x00, 0x01,
|
||||
// r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
|
||||
0x01, 0x00, 0x00,
|
||||
// r:000 ppid:0 0011 1111 1111
|
||||
0x03, 0xff,
|
||||
// r:0000 pil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
// h264
|
||||
// st:0001 1010 r:000 epid:0 0000 0001 0001
|
||||
0x1b, 0x00, 0x11,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
// adts
|
||||
// st:0000 1111 r:000 epid:0 0000 0001 0010
|
||||
0x0f, 0x00, 0x12,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
|
||||
// timed metadata
|
||||
// st:0001 0111 r:000 epid:0 0000 0001 0011
|
||||
0x15, 0x00, 0x13,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
|
||||
// crc
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
];
|
||||
|
||||
/*
|
||||
Packet Header:
|
||||
| sb | tei pusi tp pid:5 | pid | tsc afc cc |
|
||||
with af:
|
||||
| afl | ... | <data> |
|
||||
without af:
|
||||
| <data> |
|
||||
|
||||
PAT:
|
||||
| pf? | ... |
|
||||
| tid | ssi '0' r sl:4 | sl | tsi:8 |
|
||||
| tsi | r vn cni | sn | lsn |
|
||||
|
||||
with program_number == '0':
|
||||
| pn | pn | r np:5 | np |
|
||||
otherwise:
|
||||
| pn | pn | r pmp:5 | pmp |
|
||||
*/
|
||||
|
||||
PAT = [
|
||||
0x47, // sync byte
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0000 0000
|
||||
0x40, 0x00,
|
||||
// tsc:01 afc:01 cc:0000 pointer_field:0000 0000
|
||||
0x50, 0x00,
|
||||
// tid:0000 0000 ssi:0 0:0 r:00 sl:0000 0000 0000
|
||||
0x00, 0x00, 0x00,
|
||||
// tsi:0000 0000 0000 0000
|
||||
0x00, 0x00,
|
||||
// r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
|
||||
0x01, 0x00, 0x00,
|
||||
// pn:0000 0000 0000 0001
|
||||
0x00, 0x01,
|
||||
// r:000 pmp:0 0000 0010 0000
|
||||
0x00, 0x10,
|
||||
// crc32:0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
];
|
||||
|
||||
generatePMT = function(options) {
|
||||
var PMT = [
|
||||
0x47, // sync byte
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0010 0000
|
||||
0x40, 0x10,
|
||||
// tsc:01 afc:01 cc:0000 pointer_field:0000 0000
|
||||
0x50, 0x00,
|
||||
// tid:0000 0010 ssi:0 0:0 r:00 sl:0000 0001 1100
|
||||
0x02, 0x00, 0x1c,
|
||||
// pn:0000 0000 0000 0001
|
||||
0x00, 0x01,
|
||||
// r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
|
||||
0x01, 0x00, 0x00,
|
||||
// r:000 ppid:0 0011 1111 1111
|
||||
0x03, 0xff,
|
||||
// r:0000 pil:0000 0000 0000
|
||||
0x00, 0x00];
|
||||
|
||||
if (options.hasVideo) {
|
||||
// h264
|
||||
PMT = PMT.concat([
|
||||
// st:0001 1010 r:000 epid:0 0000 0001 0001
|
||||
0x1b, 0x00, 0x11,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00
|
||||
]);
|
||||
}
|
||||
|
||||
if (options.hasAudio) {
|
||||
// adts
|
||||
PMT = PMT.concat([
|
||||
// st:0000 1111 r:000 epid:0 0000 0001 0010
|
||||
0x0f, 0x00, 0x12,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00
|
||||
]);
|
||||
}
|
||||
|
||||
if (options.hasMetadata) {
|
||||
// timed metadata
|
||||
PMT = PMT.concat([
|
||||
// st:0001 0111 r:000 epid:0 0000 0001 0011
|
||||
0x15, 0x00, 0x13,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00
|
||||
]);
|
||||
}
|
||||
|
||||
// crc
|
||||
return PMT.concat([0x00, 0x00, 0x00, 0x00]);
|
||||
};
|
||||
|
||||
pesHeader = function(first, pts, dataLength) {
|
||||
if (!dataLength) {
|
||||
dataLength = 0;
|
||||
} else {
|
||||
// Add the pes header length (only the portion after the
|
||||
// pes_packet_length field)
|
||||
dataLength += 3;
|
||||
}
|
||||
|
||||
// PES_packet(), Rec. ITU-T H.222.0, Table 2-21
|
||||
var result = [
|
||||
// pscp:0000 0000 0000 0000 0000 0001
|
||||
0x00, 0x00, 0x01,
|
||||
// sid:0000 0000 ppl:0000 0000 0000 0000
|
||||
0x00, 0x00, 0x00,
|
||||
// 10 psc:00 pp:0 dai:1 c:0 ooc:0
|
||||
0x84,
|
||||
// pdf:?0 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0
|
||||
0x20 | (pts ? 0x80 : 0x00),
|
||||
// phdl:0000 0000
|
||||
(first ? 0x01 : 0x00) + (pts ? 0x05 : 0x00)
|
||||
];
|
||||
|
||||
// Only store 15 bits of the PTS for QUnit.testing purposes
|
||||
if (pts) {
|
||||
var
|
||||
pts32 = Math.floor(pts / 2), // right shift by 1
|
||||
leftMostBit = ((pts32 & 0x80000000) >>> 31) & 0x01,
|
||||
firstThree;
|
||||
|
||||
pts = pts & 0xffffffff; // remove left most bit
|
||||
firstThree = (leftMostBit << 3) | (((pts & 0xc0000000) >>> 29) & 0x06) | 0x01;
|
||||
result.push((0x2 << 4) | firstThree);
|
||||
result.push((pts >>> 22) & 0xff);
|
||||
result.push(((pts >>> 14) | 0x01) & 0xff);
|
||||
result.push((pts >>> 7) & 0xff);
|
||||
result.push(((pts << 1) | 0x01) & 0xff);
|
||||
|
||||
// Add the bytes spent on the pts info
|
||||
dataLength += 5;
|
||||
}
|
||||
if (first) {
|
||||
result.push(0x00);
|
||||
dataLength += 1;
|
||||
}
|
||||
|
||||
// Finally set the pes_packet_length field
|
||||
result[4] = (dataLength & 0x0000FF00) >> 8;
|
||||
result[5] = dataLength & 0x000000FF;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
packetize = function(data) {
|
||||
var packet = new Uint8Array(MP2T_PACKET_LENGTH);
|
||||
packet.set(data);
|
||||
return packet;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create transport stream PES packets
|
||||
* @param pid {uint8} - the program identifier (PID)
|
||||
* @param data {arraylike} - the payload bytes
|
||||
* @payload first {boolean} - true if this PES should be a payload
|
||||
* unit start
|
||||
*/
|
||||
transportPacket = function(pid, data, first, pts, isVideoData) {
|
||||
var
|
||||
adaptationFieldLength = 188 - data.length - 14 - (first ? 1 : 0) - (pts ? 5 : 0),
|
||||
// transport_packet(), Rec. ITU-T H.222.0, Table 2-2
|
||||
result = [
|
||||
// sync byte
|
||||
0x47,
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0001 0001
|
||||
0x40, pid,
|
||||
// tsc:01 afc:11 cc:0000
|
||||
0x70
|
||||
].concat([
|
||||
// afl
|
||||
adaptationFieldLength & 0xff,
|
||||
// di:0 rai:0 espi:0 pf:0 of:0 spf:0 tpdf:0 afef:0
|
||||
0x00
|
||||
]),
|
||||
i;
|
||||
|
||||
i = adaptationFieldLength - 1;
|
||||
while (i--) {
|
||||
// stuffing_bytes
|
||||
result.push(0xff);
|
||||
}
|
||||
|
||||
// PES_packet(), Rec. ITU-T H.222.0, Table 2-21
|
||||
result = result.concat(pesHeader(first, pts, isVideoData ? 0 : data.length));
|
||||
|
||||
return result.concat(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create video PES packets
|
||||
* @param data {arraylike} - the payload bytes
|
||||
* @payload first {boolean} - true if this PES should be a payload
|
||||
* unit start
|
||||
*/
|
||||
videoPes = function(data, first, pts) {
|
||||
return transportPacket(0x11, [
|
||||
// NAL unit start code
|
||||
0x00, 0x00, 0x01
|
||||
].concat(data), first, pts, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create audio ADTS frame header
|
||||
* @param dataLength {number} - the payload byte count
|
||||
*/
|
||||
adtsFrame = function(dataLength) {
|
||||
var frameLength = dataLength + 7;
|
||||
return [
|
||||
0xff, 0xf1, // no CRC
|
||||
0x10, // AAC Main, 44.1KHz
|
||||
0xb0 | ((frameLength & 0x1800) >> 11), // 2 channels
|
||||
(frameLength & 0x7f8) >> 3,
|
||||
((frameLength & 0x07) << 5) + 7, // frame length in bytes
|
||||
0x00 // one AAC per ADTS frame
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create audio PES packets
|
||||
* @param data {arraylike} - the payload bytes
|
||||
* @payload first {boolean} - true if this PES should be a payload
|
||||
* unit start
|
||||
*/
|
||||
audioPes = function(data, first, pts) {
|
||||
return transportPacket(0x12,
|
||||
adtsFrame(data.length).concat(data),
|
||||
first, pts);
|
||||
};
|
||||
|
||||
timedMetadataPes = function(data) {
|
||||
var id3 = id3Generator;
|
||||
return transportPacket(0x13, id3.id3Tag(id3.id3Frame('PRIV', 0x00, 0x01)));
|
||||
};
|
||||
|
||||
binaryStringToArrayOfBytes = function(string) {
|
||||
var
|
||||
array = [],
|
||||
arrayIndex = 0,
|
||||
stringIndex = 0;
|
||||
|
||||
while (stringIndex < string.length) {
|
||||
array[arrayIndex] = parseInt(string.slice(stringIndex, stringIndex + 8), 2);
|
||||
|
||||
arrayIndex++;
|
||||
// next byte
|
||||
stringIndex += 8;
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
leftPad = function(string, targetLength) {
|
||||
if (string.length >= targetLength) {
|
||||
return string;
|
||||
}
|
||||
return new Array(targetLength - string.length + 1).join('0') + string;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
PMT: PMT,
|
||||
PAT: PAT,
|
||||
generatePMT: generatePMT,
|
||||
pesHeader: pesHeader,
|
||||
packetize: packetize,
|
||||
transportPacket: transportPacket,
|
||||
videoPes: videoPes,
|
||||
adtsFrame: adtsFrame,
|
||||
audioPes: audioPes,
|
||||
timedMetadataPes: timedMetadataPes,
|
||||
binaryStringToArrayOfBytes: binaryStringToArrayOfBytes,
|
||||
leftPad: leftPad
|
||||
};
|
||||
var
|
||||
mp2t = require('../lib/m2ts'),
|
||||
id3Generator = require('./utils/id3-generator'),
|
||||
MP2T_PACKET_LENGTH = mp2t.MP2T_PACKET_LENGTH,
|
||||
PMT,
|
||||
PAT,
|
||||
generatePMT,
|
||||
pesHeader,
|
||||
packetize,
|
||||
transportPacket,
|
||||
videoPes,
|
||||
adtsFrame,
|
||||
audioPes,
|
||||
timedMetadataPes,
|
||||
binaryStringToArrayOfBytes,
|
||||
leftPad;
|
||||
|
||||
|
||||
PMT = [
|
||||
0x47, // sync byte
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0010 0000
|
||||
0x40, 0x10,
|
||||
// tsc:01 afc:01 cc:0000 pointer_field:0000 0000
|
||||
0x50, 0x00,
|
||||
// tid:0000 0010 ssi:0 0:0 r:00 sl:0000 0001 1100
|
||||
0x02, 0x00, 0x1c,
|
||||
// pn:0000 0000 0000 0001
|
||||
0x00, 0x01,
|
||||
// r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
|
||||
0x01, 0x00, 0x00,
|
||||
// r:000 ppid:0 0011 1111 1111
|
||||
0x03, 0xff,
|
||||
// r:0000 pil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
// h264
|
||||
// st:0001 1010 r:000 epid:0 0000 0001 0001
|
||||
0x1b, 0x00, 0x11,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
// adts
|
||||
// st:0000 1111 r:000 epid:0 0000 0001 0010
|
||||
0x0f, 0x00, 0x12,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
|
||||
// timed metadata
|
||||
// st:0001 0111 r:000 epid:0 0000 0001 0011
|
||||
0x15, 0x00, 0x13,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00,
|
||||
|
||||
// crc
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
];
|
||||
|
||||
/*
|
||||
Packet Header:
|
||||
| sb | tei pusi tp pid:5 | pid | tsc afc cc |
|
||||
with af:
|
||||
| afl | ... | <data> |
|
||||
without af:
|
||||
| <data> |
|
||||
|
||||
PAT:
|
||||
| pf? | ... |
|
||||
| tid | ssi '0' r sl:4 | sl | tsi:8 |
|
||||
| tsi | r vn cni | sn | lsn |
|
||||
|
||||
with program_number == '0':
|
||||
| pn | pn | r np:5 | np |
|
||||
otherwise:
|
||||
| pn | pn | r pmp:5 | pmp |
|
||||
*/
|
||||
|
||||
PAT = [
|
||||
0x47, // sync byte
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0000 0000
|
||||
0x40, 0x00,
|
||||
// tsc:01 afc:01 cc:0000 pointer_field:0000 0000
|
||||
0x50, 0x00,
|
||||
// tid:0000 0000 ssi:0 0:0 r:00 sl:0000 0000 0000
|
||||
0x00, 0x00, 0x00,
|
||||
// tsi:0000 0000 0000 0000
|
||||
0x00, 0x00,
|
||||
// r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
|
||||
0x01, 0x00, 0x00,
|
||||
// pn:0000 0000 0000 0001
|
||||
0x00, 0x01,
|
||||
// r:000 pmp:0 0000 0010 0000
|
||||
0x00, 0x10,
|
||||
// crc32:0000 0000 0000 0000 0000 0000 0000 0000
|
||||
0x00, 0x00, 0x00, 0x00
|
||||
];
|
||||
|
||||
generatePMT = function(options) {
|
||||
var PMT = [
|
||||
0x47, // sync byte
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0010 0000
|
||||
0x40, 0x10,
|
||||
// tsc:01 afc:01 cc:0000 pointer_field:0000 0000
|
||||
0x50, 0x00,
|
||||
// tid:0000 0010 ssi:0 0:0 r:00 sl:0000 0001 1100
|
||||
0x02, 0x00, 0x1c,
|
||||
// pn:0000 0000 0000 0001
|
||||
0x00, 0x01,
|
||||
// r:00 vn:00 000 cni:1 sn:0000 0000 lsn:0000 0000
|
||||
0x01, 0x00, 0x00,
|
||||
// r:000 ppid:0 0011 1111 1111
|
||||
0x03, 0xff,
|
||||
// r:0000 pil:0000 0000 0000
|
||||
0x00, 0x00];
|
||||
|
||||
if (options.hasVideo) {
|
||||
// h264
|
||||
PMT = PMT.concat([
|
||||
// st:0001 1010 r:000 epid:0 0000 0001 0001
|
||||
0x1b, 0x00, 0x11,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00
|
||||
]);
|
||||
}
|
||||
|
||||
if (options.hasAudio) {
|
||||
// adts
|
||||
PMT = PMT.concat([
|
||||
// st:0000 1111 r:000 epid:0 0000 0001 0010
|
||||
0x0f, 0x00, 0x12,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00
|
||||
]);
|
||||
}
|
||||
|
||||
if (options.hasMetadata) {
|
||||
// timed metadata
|
||||
PMT = PMT.concat([
|
||||
// st:0001 0111 r:000 epid:0 0000 0001 0011
|
||||
0x15, 0x00, 0x13,
|
||||
// r:0000 esil:0000 0000 0000
|
||||
0x00, 0x00
|
||||
]);
|
||||
}
|
||||
|
||||
// crc
|
||||
return PMT.concat([0x00, 0x00, 0x00, 0x00]);
|
||||
};
|
||||
|
||||
pesHeader = function(first, pts, dataLength) {
|
||||
if (!dataLength) {
|
||||
dataLength = 0;
|
||||
} else {
|
||||
// Add the pes header length (only the portion after the
|
||||
// pes_packet_length field)
|
||||
dataLength += 3;
|
||||
}
|
||||
|
||||
// PES_packet(), Rec. ITU-T H.222.0, Table 2-21
|
||||
var result = [
|
||||
// pscp:0000 0000 0000 0000 0000 0001
|
||||
0x00, 0x00, 0x01,
|
||||
// sid:0000 0000 ppl:0000 0000 0000 0000
|
||||
0x00, 0x00, 0x00,
|
||||
// 10 psc:00 pp:0 dai:1 c:0 ooc:0
|
||||
0x84,
|
||||
// pdf:?0 ef:1 erf:0 dtmf:0 acif:0 pcf:0 pef:0
|
||||
0x20 | (pts ? 0x80 : 0x00),
|
||||
// phdl:0000 0000
|
||||
(first ? 0x01 : 0x00) + (pts ? 0x05 : 0x00)
|
||||
];
|
||||
|
||||
// Only store 15 bits of the PTS for QUnit.testing purposes
|
||||
if (pts) {
|
||||
var
|
||||
pts32 = Math.floor(pts / 2), // right shift by 1
|
||||
leftMostBit = ((pts32 & 0x80000000) >>> 31) & 0x01,
|
||||
firstThree;
|
||||
|
||||
pts = pts & 0xffffffff; // remove left most bit
|
||||
firstThree = (leftMostBit << 3) | (((pts & 0xc0000000) >>> 29) & 0x06) | 0x01;
|
||||
result.push((0x2 << 4) | firstThree);
|
||||
result.push((pts >>> 22) & 0xff);
|
||||
result.push(((pts >>> 14) | 0x01) & 0xff);
|
||||
result.push((pts >>> 7) & 0xff);
|
||||
result.push(((pts << 1) | 0x01) & 0xff);
|
||||
|
||||
// Add the bytes spent on the pts info
|
||||
dataLength += 5;
|
||||
}
|
||||
if (first) {
|
||||
result.push(0x00);
|
||||
dataLength += 1;
|
||||
}
|
||||
|
||||
// Finally set the pes_packet_length field
|
||||
result[4] = (dataLength & 0x0000FF00) >> 8;
|
||||
result[5] = dataLength & 0x000000FF;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
packetize = function(data) {
|
||||
var packet = new Uint8Array(MP2T_PACKET_LENGTH);
|
||||
packet.set(data);
|
||||
return packet;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create transport stream PES packets
|
||||
* @param pid {uint8} - the program identifier (PID)
|
||||
* @param data {arraylike} - the payload bytes
|
||||
* @payload first {boolean} - true if this PES should be a payload
|
||||
* unit start
|
||||
*/
|
||||
transportPacket = function(pid, data, first, pts, isVideoData) {
|
||||
var
|
||||
adaptationFieldLength = 188 - data.length - 14 - (first ? 1 : 0) - (pts ? 5 : 0),
|
||||
// transport_packet(), Rec. ITU-T H.222.0, Table 2-2
|
||||
result = [
|
||||
// sync byte
|
||||
0x47,
|
||||
// tei:0 pusi:1 tp:0 pid:0 0000 0001 0001
|
||||
0x40, pid,
|
||||
// tsc:01 afc:11 cc:0000
|
||||
0x70
|
||||
].concat([
|
||||
// afl
|
||||
adaptationFieldLength & 0xff,
|
||||
// di:0 rai:0 espi:0 pf:0 of:0 spf:0 tpdf:0 afef:0
|
||||
0x00
|
||||
]),
|
||||
i;
|
||||
|
||||
i = adaptationFieldLength - 1;
|
||||
while (i--) {
|
||||
// stuffing_bytes
|
||||
result.push(0xff);
|
||||
}
|
||||
|
||||
// PES_packet(), Rec. ITU-T H.222.0, Table 2-21
|
||||
result = result.concat(pesHeader(first, pts, isVideoData ? 0 : data.length));
|
||||
|
||||
return result.concat(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create video PES packets
|
||||
* @param data {arraylike} - the payload bytes
|
||||
* @payload first {boolean} - true if this PES should be a payload
|
||||
* unit start
|
||||
*/
|
||||
videoPes = function(data, first, pts) {
|
||||
return transportPacket(0x11, [
|
||||
// NAL unit start code
|
||||
0x00, 0x00, 0x01
|
||||
].concat(data), first, pts, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create audio ADTS frame header
|
||||
* @param dataLength {number} - the payload byte count
|
||||
*/
|
||||
adtsFrame = function(dataLength) {
|
||||
var frameLength = dataLength + 7;
|
||||
return [
|
||||
0xff, 0xf1, // no CRC
|
||||
0x10, // AAC Main, 44.1KHz
|
||||
0xb0 | ((frameLength & 0x1800) >> 11), // 2 channels
|
||||
(frameLength & 0x7f8) >> 3,
|
||||
((frameLength & 0x07) << 5) + 7, // frame length in bytes
|
||||
0x00 // one AAC per ADTS frame
|
||||
];
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper function to create audio PES packets
|
||||
* @param data {arraylike} - the payload bytes
|
||||
* @payload first {boolean} - true if this PES should be a payload
|
||||
* unit start
|
||||
*/
|
||||
audioPes = function(data, first, pts) {
|
||||
return transportPacket(0x12,
|
||||
adtsFrame(data.length).concat(data),
|
||||
first, pts);
|
||||
};
|
||||
|
||||
timedMetadataPes = function(data) {
|
||||
var id3 = id3Generator;
|
||||
return transportPacket(0x13, id3.id3Tag(id3.id3Frame('PRIV', 0x00, 0x01)));
|
||||
};
|
||||
|
||||
binaryStringToArrayOfBytes = function(string) {
|
||||
var
|
||||
array = [],
|
||||
arrayIndex = 0,
|
||||
stringIndex = 0;
|
||||
|
||||
while (stringIndex < string.length) {
|
||||
array[arrayIndex] = parseInt(string.slice(stringIndex, stringIndex + 8), 2);
|
||||
|
||||
arrayIndex++;
|
||||
// next byte
|
||||
stringIndex += 8;
|
||||
}
|
||||
|
||||
return array;
|
||||
};
|
||||
|
||||
leftPad = function(string, targetLength) {
|
||||
if (string.length >= targetLength) {
|
||||
return string;
|
||||
}
|
||||
return new Array(targetLength - string.length + 1).join('0') + string;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
PMT: PMT,
|
||||
PAT: PAT,
|
||||
generatePMT: generatePMT,
|
||||
pesHeader: pesHeader,
|
||||
packetize: packetize,
|
||||
transportPacket: transportPacket,
|
||||
videoPes: videoPes,
|
||||
adtsFrame: adtsFrame,
|
||||
audioPes: audioPes,
|
||||
timedMetadataPes: timedMetadataPes,
|
||||
binaryStringToArrayOfBytes: binaryStringToArrayOfBytes,
|
||||
leftPad: leftPad
|
||||
};
|
||||
|
|
21632
node_modules/mux.js/test/utils/cc708-pink-underscore.js
generated
vendored
21632
node_modules/mux.js/test/utils/cc708-pink-underscore.js
generated
vendored
File diff suppressed because it is too large
Load diff
148
node_modules/mux.js/test/utils/id3-generator.js
generated
vendored
148
node_modules/mux.js/test/utils/id3-generator.js
generated
vendored
|
@ -1,74 +1,74 @@
|
|||
/**
|
||||
* Helper functions for creating ID3 metadata.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var stringToInts, stringToCString, id3Tag, id3Frame;
|
||||
|
||||
stringToInts = function(string) {
|
||||
var result = [], i;
|
||||
for (i = 0; i < string.length; i++) {
|
||||
result[i] = string.charCodeAt(i);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
stringToCString = function(string) {
|
||||
return stringToInts(string).concat([0x00]);
|
||||
};
|
||||
|
||||
id3Tag = function() {
|
||||
var
|
||||
frames = Array.prototype.concat.apply([], Array.prototype.slice.call(arguments)),
|
||||
result = stringToInts('ID3').concat([
|
||||
0x03, 0x00, // version 3.0 of ID3v2 (aka ID3v.2.3.0)
|
||||
0x40, // flags. include an extended header
|
||||
0x00, 0x00, 0x00, 0x00, // size. set later
|
||||
|
||||
// extended header
|
||||
0x00, 0x00, 0x00, 0x06, // extended header size. no CRC
|
||||
0x00, 0x00, // extended flags
|
||||
0x00, 0x00, 0x00, 0x02 // size of padding
|
||||
], frames),
|
||||
size;
|
||||
|
||||
// size is stored as a sequence of four 7-bit integers with the
|
||||
// high bit of each byte set to zero
|
||||
size = result.length - 10;
|
||||
|
||||
result[6] = (size >>> 21) & 0x7f;
|
||||
result[7] = (size >>> 14) & 0x7f;
|
||||
result[8] = (size >>> 7) & 0x7f;
|
||||
result[9] = size & 0x7f;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
id3Frame = function(type) {
|
||||
var result = stringToInts(type).concat([
|
||||
0x00, 0x00, 0x00, 0x00, // size
|
||||
0xe0, 0x00 // flags. tag/file alter preservation, read-only
|
||||
]),
|
||||
size = result.length - 10;
|
||||
|
||||
// append the fields of the ID3 frame
|
||||
result = result.concat.apply(result, Array.prototype.slice.call(arguments, 1));
|
||||
|
||||
// set the size
|
||||
size = result.length - 10;
|
||||
|
||||
result[4] = (size >>> 21) & 0x7f;
|
||||
result[5] = (size >>> 14) & 0x7f;
|
||||
result[6] = (size >>> 7) & 0x7f;
|
||||
result[7] = size & 0x7f;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
stringToInts: stringToInts,
|
||||
stringToCString: stringToCString,
|
||||
id3Tag: id3Tag,
|
||||
id3Frame: id3Frame
|
||||
};
|
||||
/**
|
||||
* Helper functions for creating ID3 metadata.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var stringToInts, stringToCString, id3Tag, id3Frame;
|
||||
|
||||
stringToInts = function(string) {
|
||||
var result = [], i;
|
||||
for (i = 0; i < string.length; i++) {
|
||||
result[i] = string.charCodeAt(i);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
stringToCString = function(string) {
|
||||
return stringToInts(string).concat([0x00]);
|
||||
};
|
||||
|
||||
id3Tag = function() {
|
||||
var
|
||||
frames = Array.prototype.concat.apply([], Array.prototype.slice.call(arguments)),
|
||||
result = stringToInts('ID3').concat([
|
||||
0x03, 0x00, // version 3.0 of ID3v2 (aka ID3v.2.3.0)
|
||||
0x40, // flags. include an extended header
|
||||
0x00, 0x00, 0x00, 0x00, // size. set later
|
||||
|
||||
// extended header
|
||||
0x00, 0x00, 0x00, 0x06, // extended header size. no CRC
|
||||
0x00, 0x00, // extended flags
|
||||
0x00, 0x00, 0x00, 0x02 // size of padding
|
||||
], frames),
|
||||
size;
|
||||
|
||||
// size is stored as a sequence of four 7-bit integers with the
|
||||
// high bit of each byte set to zero
|
||||
size = result.length - 10;
|
||||
|
||||
result[6] = (size >>> 21) & 0x7f;
|
||||
result[7] = (size >>> 14) & 0x7f;
|
||||
result[8] = (size >>> 7) & 0x7f;
|
||||
result[9] = size & 0x7f;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
id3Frame = function(type) {
|
||||
var result = stringToInts(type).concat([
|
||||
0x00, 0x00, 0x00, 0x00, // size
|
||||
0xe0, 0x00 // flags. tag/file alter preservation, read-only
|
||||
]),
|
||||
size = result.length - 10;
|
||||
|
||||
// append the fields of the ID3 frame
|
||||
result = result.concat.apply(result, Array.prototype.slice.call(arguments, 1));
|
||||
|
||||
// set the size
|
||||
size = result.length - 10;
|
||||
|
||||
result[4] = (size >>> 21) & 0x7f;
|
||||
result[5] = (size >>> 14) & 0x7f;
|
||||
result[6] = (size >>> 7) & 0x7f;
|
||||
result[7] = size & 0x7f;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
stringToInts: stringToInts,
|
||||
stringToCString: stringToCString,
|
||||
id3Tag: id3Tag,
|
||||
id3Frame: id3Frame
|
||||
};
|
||||
|
|
1992
node_modules/mux.js/test/utils/mixed-608-708-captions.js
generated
vendored
1992
node_modules/mux.js/test/utils/mixed-608-708-captions.js
generated
vendored
File diff suppressed because it is too large
Load diff
630
node_modules/mux.js/test/utils/mp4-helpers.js
generated
vendored
630
node_modules/mux.js/test/utils/mp4-helpers.js
generated
vendored
|
@ -1,315 +1,315 @@
|
|||
/**
|
||||
* Helper functions for creating test MP4 data.
|
||||
*/
|
||||
'use strict';
|
||||
var box, typeBytes, unityMatrix;
|
||||
|
||||
module.exports = {};
|
||||
|
||||
// ----------------------
|
||||
// Box Generation Helpers
|
||||
// ----------------------
|
||||
|
||||
module.exports.typeBytes = typeBytes = function(type) {
|
||||
return [
|
||||
type.charCodeAt(0),
|
||||
type.charCodeAt(1),
|
||||
type.charCodeAt(2),
|
||||
type.charCodeAt(3)
|
||||
];
|
||||
};
|
||||
|
||||
module.exports.box = box = function(type) {
|
||||
var
|
||||
array = Array.prototype.slice.call(arguments, 1),
|
||||
result = [],
|
||||
size,
|
||||
i;
|
||||
|
||||
// "unwrap" any arrays that were passed as arguments
|
||||
// e.g. box('etc', 1, [2, 3], 4) -> box('etc', 1, 2, 3, 4)
|
||||
for (i = 0; i < array.length; i++) {
|
||||
if (array[i] instanceof Array) {
|
||||
array.splice.apply(array, [i, 1].concat(array[i]));
|
||||
}
|
||||
}
|
||||
|
||||
size = 8 + array.length;
|
||||
|
||||
result[0] = (size & 0xFF000000) >> 24;
|
||||
result[1] = (size & 0x00FF0000) >> 16;
|
||||
result[2] = (size & 0x0000FF00) >> 8;
|
||||
result[3] = size & 0xFF;
|
||||
result = result.concat(typeBytes(type));
|
||||
result = result.concat(array);
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports.unityMatrix = unityMatrix = [
|
||||
0, 0, 0x10, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0x10, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0x40, 0, 0, 0
|
||||
];
|
||||
|
||||
// ------------
|
||||
// Example Data
|
||||
// ------------
|
||||
|
||||
module.exports.sampleMoov =
|
||||
box('moov',
|
||||
box('mvhd',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, // creation_time
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, // modification_time
|
||||
0x00, 0x00, 0x03, 0xe8, // timescale = 1000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
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), // next_track_ID
|
||||
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
|
||||
unityMatrix,
|
||||
0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
|
||||
0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
|
||||
box('edts',
|
||||
box('elst',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x00, // segment_duration
|
||||
0x00, 0x00, 0x04, 0x00, // media_time
|
||||
0x00, 0x01, 0x80, 0x00)), // media_rate
|
||||
box('mdia',
|
||||
box('mdhd',
|
||||
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, 0x01, 0x5f, 0x90, // timescale = 90000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
|
||||
0x15, 0xc7, // 'eng' language
|
||||
0x00, 0x00),
|
||||
box('hdlr',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // pre_defined
|
||||
typeBytes('vide'), // handler_type
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
typeBytes('one'), 0x00), // name
|
||||
box('minf',
|
||||
box('dinf',
|
||||
box('dref',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
box('url ',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x01))), // flags
|
||||
box('stbl',
|
||||
box('stsd',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // entry_count
|
||||
box('avc1',
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, // box content
|
||||
typeBytes('avcC'), // codec profile type
|
||||
0x00, 0x4d, 0x40, 0x0d)), // codec parameters
|
||||
box('stts',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
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, 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, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01), // chunk_offset
|
||||
box('stss',
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01), // sync_sample
|
||||
box('ctts',
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01, // sample_count
|
||||
0x00, 0x00, 0x00, 0x01))))), // sample_offset
|
||||
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, 0x02, // 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
|
||||
unityMatrix,
|
||||
0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
|
||||
0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
|
||||
box('edts',
|
||||
box('elst',
|
||||
0x01, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segment_duration
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // media_time
|
||||
0x00, 0x01, 0x80, 0x00)), // media_rate
|
||||
box('mdia',
|
||||
box('mdhd',
|
||||
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, 0x01, 0x5f, 0x90, // timescale = 90000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
|
||||
0x15, 0xc7, // 'eng' language
|
||||
0x00, 0x00),
|
||||
box('hdlr',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // pre_defined
|
||||
typeBytes('soun'), // handler_type
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
typeBytes('one'), 0x00), // name
|
||||
box('minf',
|
||||
box('dinf',
|
||||
box('dref',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
box('url ',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x01))), // flags
|
||||
box('stbl',
|
||||
box('stsd',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // entry_count
|
||||
box('mp4a',
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
typeBytes('esds'), // codec profile type
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, // box content
|
||||
0x40, 0x0a, // codec params
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00)), // codec params
|
||||
box('stts',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
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, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x02, // first_chunk
|
||||
0x00, 0x00, 0x00, 0x03, // samples_per_chunk
|
||||
0x00, 0x00, 0x00, 0x01), // sample_description_index
|
||||
box('ctts',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01, // sample_count
|
||||
0xff, 0xff, 0xff, 0xff), // sample_offset
|
||||
box('stco',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01)))))); // chunk_offset
|
||||
/**
|
||||
* Helper functions for creating test MP4 data.
|
||||
*/
|
||||
'use strict';
|
||||
var box, typeBytes, unityMatrix;
|
||||
|
||||
module.exports = {};
|
||||
|
||||
// ----------------------
|
||||
// Box Generation Helpers
|
||||
// ----------------------
|
||||
|
||||
module.exports.typeBytes = typeBytes = function(type) {
|
||||
return [
|
||||
type.charCodeAt(0),
|
||||
type.charCodeAt(1),
|
||||
type.charCodeAt(2),
|
||||
type.charCodeAt(3)
|
||||
];
|
||||
};
|
||||
|
||||
module.exports.box = box = function(type) {
|
||||
var
|
||||
array = Array.prototype.slice.call(arguments, 1),
|
||||
result = [],
|
||||
size,
|
||||
i;
|
||||
|
||||
// "unwrap" any arrays that were passed as arguments
|
||||
// e.g. box('etc', 1, [2, 3], 4) -> box('etc', 1, 2, 3, 4)
|
||||
for (i = 0; i < array.length; i++) {
|
||||
if (array[i] instanceof Array) {
|
||||
array.splice.apply(array, [i, 1].concat(array[i]));
|
||||
}
|
||||
}
|
||||
|
||||
size = 8 + array.length;
|
||||
|
||||
result[0] = (size & 0xFF000000) >> 24;
|
||||
result[1] = (size & 0x00FF0000) >> 16;
|
||||
result[2] = (size & 0x0000FF00) >> 8;
|
||||
result[3] = size & 0xFF;
|
||||
result = result.concat(typeBytes(type));
|
||||
result = result.concat(array);
|
||||
return result;
|
||||
};
|
||||
|
||||
module.exports.unityMatrix = unityMatrix = [
|
||||
0, 0, 0x10, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0x10, 0,
|
||||
0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0x40, 0, 0, 0
|
||||
];
|
||||
|
||||
// ------------
|
||||
// Example Data
|
||||
// ------------
|
||||
|
||||
module.exports.sampleMoov =
|
||||
box('moov',
|
||||
box('mvhd',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, // creation_time
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, // modification_time
|
||||
0x00, 0x00, 0x03, 0xe8, // timescale = 1000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
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), // next_track_ID
|
||||
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
|
||||
unityMatrix,
|
||||
0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
|
||||
0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
|
||||
box('edts',
|
||||
box('elst',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x00, // segment_duration
|
||||
0x00, 0x00, 0x04, 0x00, // media_time
|
||||
0x00, 0x01, 0x80, 0x00)), // media_rate
|
||||
box('mdia',
|
||||
box('mdhd',
|
||||
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, 0x01, 0x5f, 0x90, // timescale = 90000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
|
||||
0x15, 0xc7, // 'eng' language
|
||||
0x00, 0x00),
|
||||
box('hdlr',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // pre_defined
|
||||
typeBytes('vide'), // handler_type
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
typeBytes('one'), 0x00), // name
|
||||
box('minf',
|
||||
box('dinf',
|
||||
box('dref',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
box('url ',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x01))), // flags
|
||||
box('stbl',
|
||||
box('stsd',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // entry_count
|
||||
box('avc1',
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, // box content
|
||||
typeBytes('avcC'), // codec profile type
|
||||
0x00, 0x4d, 0x40, 0x0d)), // codec parameters
|
||||
box('stts',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
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, 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, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01), // chunk_offset
|
||||
box('stss',
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01), // sync_sample
|
||||
box('ctts',
|
||||
0x00, // version 0
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01, // sample_count
|
||||
0x00, 0x00, 0x00, 0x01))))), // sample_offset
|
||||
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, 0x02, // 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
|
||||
unityMatrix,
|
||||
0x01, 0x2c, 0x00, 0x00, // 300 in 16.16 fixed-point
|
||||
0x00, 0x96, 0x00, 0x00), // 150 in 16.16 fixed-point
|
||||
box('edts',
|
||||
box('elst',
|
||||
0x01, // version
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // segment_duration
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // media_time
|
||||
0x00, 0x01, 0x80, 0x00)), // media_rate
|
||||
box('mdia',
|
||||
box('mdhd',
|
||||
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, 0x01, 0x5f, 0x90, // timescale = 90000
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x02, 0x58, // 600 = 0x258 duration
|
||||
0x15, 0xc7, // 'eng' language
|
||||
0x00, 0x00),
|
||||
box('hdlr',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // pre_defined
|
||||
typeBytes('soun'), // handler_type
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
0x00, 0x00, 0x00, 0x00, // reserved
|
||||
typeBytes('one'), 0x00), // name
|
||||
box('minf',
|
||||
box('dinf',
|
||||
box('dref',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
box('url ',
|
||||
0x00, // version
|
||||
0x00, 0x00, 0x01))), // flags
|
||||
box('stbl',
|
||||
box('stsd',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x00, // entry_count
|
||||
box('mp4a',
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
typeBytes('esds'), // codec profile type
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, 0x00, // box content
|
||||
0x00, 0x00, 0x00, // box content
|
||||
0x40, 0x0a, // codec params
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00)), // codec params
|
||||
box('stts',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
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, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x02, // first_chunk
|
||||
0x00, 0x00, 0x00, 0x03, // samples_per_chunk
|
||||
0x00, 0x00, 0x00, 0x01), // sample_description_index
|
||||
box('ctts',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01, // sample_count
|
||||
0xff, 0xff, 0xff, 0xff), // sample_offset
|
||||
box('stco',
|
||||
0x01, // version 1
|
||||
0x00, 0x00, 0x00, // flags
|
||||
0x00, 0x00, 0x00, 0x01, // entry_count
|
||||
0x00, 0x00, 0x00, 0x01)))))); // chunk_offset
|
||||
|
|
274
node_modules/mux.js/test/utils/sei-nal-unit-generator.js
generated
vendored
274
node_modules/mux.js/test/utils/sei-nal-unit-generator.js
generated
vendored
|
@ -1,137 +1,137 @@
|
|||
/**
|
||||
* Helper functions for creating 608/708 SEI NAL units
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var box = require('./mp4-helpers').box;
|
||||
|
||||
// Create SEI nal-units from Caption packets
|
||||
var makeSeiFromCaptionPacket = function(caption) {
|
||||
return {
|
||||
pts: caption.pts,
|
||||
dts: caption.dts,
|
||||
nalUnitType: 'sei_rbsp',
|
||||
escapedRBSP: new Uint8Array([
|
||||
0x04, // payload_type === user_data_registered_itu_t_t35
|
||||
|
||||
0x0e, // payload_size
|
||||
|
||||
181, // itu_t_t35_country_code
|
||||
0x00, 0x31, // itu_t_t35_provider_code
|
||||
0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
|
||||
0x03, // user_data_type_code, 0x03 is cc_data
|
||||
|
||||
// 110 00001
|
||||
0xc1, // process_cc_data, cc_count
|
||||
0xff, // reserved
|
||||
// 1111 1100
|
||||
(0xfc | caption.type), // cc_valid, cc_type (608, field 1)
|
||||
(caption.ccData & 0xff00) >> 8, // cc_data_1
|
||||
caption.ccData & 0xff, // cc_data_2 without parity bit set
|
||||
|
||||
0xff // marker_bits
|
||||
])
|
||||
};
|
||||
};
|
||||
|
||||
// Create SEI nal-units from Caption packets
|
||||
var makeSeiFromMultipleCaptionPackets = function(captionHash) {
|
||||
var pts = captionHash.pts,
|
||||
dts = captionHash.dts,
|
||||
captions = captionHash.captions;
|
||||
|
||||
var data = [];
|
||||
captions.forEach(function(caption) {
|
||||
data.push(0xfc | caption.type);
|
||||
data.push((caption.ccData & 0xff00) >> 8);
|
||||
data.push(caption.ccData & 0xff);
|
||||
});
|
||||
|
||||
return {
|
||||
pts: pts,
|
||||
dts: dts,
|
||||
nalUnitType: 'sei_rbsp',
|
||||
escapedRBSP: new Uint8Array([
|
||||
0x04, // payload_type === user_data_registered_itu_t_t35
|
||||
|
||||
(0x0b + (captions.length * 3)), // payload_size
|
||||
|
||||
181, // itu_t_t35_country_code
|
||||
0x00, 0x31, // itu_t_t35_provider_code
|
||||
0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
|
||||
0x03, // user_data_type_code, 0x03 is cc_data
|
||||
|
||||
// 110 00001
|
||||
(0x6 << 5) | captions.length, // process_cc_data, cc_count
|
||||
0xff // reserved
|
||||
].concat(data).concat([0xff /* marker bits */])
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
var makeMdatFromCaptionPackets = function(packets) {
|
||||
var mdat = ['mdat'];
|
||||
var seis = packets.map(makeSeiFromCaptionPacket);
|
||||
|
||||
seis.forEach(function(sei) {
|
||||
mdat.push(0x00);
|
||||
mdat.push(0x00);
|
||||
mdat.push(0x00);
|
||||
mdat.push(sei.escapedRBSP.length + 1); // nal length
|
||||
mdat.push(0x06); // declare nal type as SEI
|
||||
// SEI message
|
||||
for (var i = 0; i < sei.escapedRBSP.length; i++) {
|
||||
var byte = sei.escapedRBSP[i];
|
||||
|
||||
mdat.push(byte);
|
||||
}
|
||||
});
|
||||
|
||||
return box.apply(null, mdat);
|
||||
};
|
||||
|
||||
// Returns a ccData byte-pair for a two character string. That is,
|
||||
// it converts a string like 'hi' into the two-byte number that
|
||||
// would be parsed back as 'hi' when provided as ccData.
|
||||
var characters = function(text) {
|
||||
if (text.length !== 2) {
|
||||
throw new Error('ccdata must be specified two characters at a time');
|
||||
}
|
||||
return (text.charCodeAt(0) << 8) | text.charCodeAt(1);
|
||||
};
|
||||
|
||||
// Returns a ccData byte-pair including
|
||||
// Header for 708 packet
|
||||
// Header for the first service block
|
||||
// seq should increment by 1 for each byte pair mod 3 (0,1,2,0,1,2,...)
|
||||
// sizeCode is the number of byte pairs in the packet (including header)
|
||||
// serviceNum is the service number of the first service block
|
||||
// blockSize is the size of the first service block in bytes (no header)
|
||||
// If there's only one service block, the blockSize should be (sizeCode-1)*2
|
||||
var packetHeader708 = function(seq, sizeCode, serviceNum, blockSize) {
|
||||
var b1 = (seq << 6) | sizeCode;
|
||||
var b2 = (serviceNum << 5) | blockSize;
|
||||
return (b1 << 8) | b2;
|
||||
};
|
||||
|
||||
// Returns a ccData byte-pair to execute a 708 DSW command
|
||||
// Takes an array of window indicies to display
|
||||
var displayWindows708 = function(windows) {
|
||||
var cmd = 0x8900;
|
||||
|
||||
windows.forEach(function(winIdx) {
|
||||
cmd |= (0x01 << winIdx);
|
||||
});
|
||||
|
||||
return cmd;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
makeSeiFromCaptionPacket: makeSeiFromCaptionPacket,
|
||||
makeSeiFromMultipleCaptionPackets: makeSeiFromMultipleCaptionPackets,
|
||||
makeMdatFromCaptionPackets: makeMdatFromCaptionPackets,
|
||||
characters: characters,
|
||||
packetHeader708: packetHeader708,
|
||||
displayWindows708: displayWindows708
|
||||
};
|
||||
/**
|
||||
* Helper functions for creating 608/708 SEI NAL units
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var box = require('./mp4-helpers').box;
|
||||
|
||||
// Create SEI nal-units from Caption packets
|
||||
var makeSeiFromCaptionPacket = function(caption) {
|
||||
return {
|
||||
pts: caption.pts,
|
||||
dts: caption.dts,
|
||||
nalUnitType: 'sei_rbsp',
|
||||
escapedRBSP: new Uint8Array([
|
||||
0x04, // payload_type === user_data_registered_itu_t_t35
|
||||
|
||||
0x0e, // payload_size
|
||||
|
||||
181, // itu_t_t35_country_code
|
||||
0x00, 0x31, // itu_t_t35_provider_code
|
||||
0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
|
||||
0x03, // user_data_type_code, 0x03 is cc_data
|
||||
|
||||
// 110 00001
|
||||
0xc1, // process_cc_data, cc_count
|
||||
0xff, // reserved
|
||||
// 1111 1100
|
||||
(0xfc | caption.type), // cc_valid, cc_type (608, field 1)
|
||||
(caption.ccData & 0xff00) >> 8, // cc_data_1
|
||||
caption.ccData & 0xff, // cc_data_2 without parity bit set
|
||||
|
||||
0xff // marker_bits
|
||||
])
|
||||
};
|
||||
};
|
||||
|
||||
// Create SEI nal-units from Caption packets
|
||||
var makeSeiFromMultipleCaptionPackets = function(captionHash) {
|
||||
var pts = captionHash.pts,
|
||||
dts = captionHash.dts,
|
||||
captions = captionHash.captions;
|
||||
|
||||
var data = [];
|
||||
captions.forEach(function(caption) {
|
||||
data.push(0xfc | caption.type);
|
||||
data.push((caption.ccData & 0xff00) >> 8);
|
||||
data.push(caption.ccData & 0xff);
|
||||
});
|
||||
|
||||
return {
|
||||
pts: pts,
|
||||
dts: dts,
|
||||
nalUnitType: 'sei_rbsp',
|
||||
escapedRBSP: new Uint8Array([
|
||||
0x04, // payload_type === user_data_registered_itu_t_t35
|
||||
|
||||
(0x0b + (captions.length * 3)), // payload_size
|
||||
|
||||
181, // itu_t_t35_country_code
|
||||
0x00, 0x31, // itu_t_t35_provider_code
|
||||
0x47, 0x41, 0x39, 0x34, // user_identifier, "GA94"
|
||||
0x03, // user_data_type_code, 0x03 is cc_data
|
||||
|
||||
// 110 00001
|
||||
(0x6 << 5) | captions.length, // process_cc_data, cc_count
|
||||
0xff // reserved
|
||||
].concat(data).concat([0xff /* marker bits */])
|
||||
)
|
||||
};
|
||||
};
|
||||
|
||||
var makeMdatFromCaptionPackets = function(packets) {
|
||||
var mdat = ['mdat'];
|
||||
var seis = packets.map(makeSeiFromCaptionPacket);
|
||||
|
||||
seis.forEach(function(sei) {
|
||||
mdat.push(0x00);
|
||||
mdat.push(0x00);
|
||||
mdat.push(0x00);
|
||||
mdat.push(sei.escapedRBSP.length + 1); // nal length
|
||||
mdat.push(0x06); // declare nal type as SEI
|
||||
// SEI message
|
||||
for (var i = 0; i < sei.escapedRBSP.length; i++) {
|
||||
var byte = sei.escapedRBSP[i];
|
||||
|
||||
mdat.push(byte);
|
||||
}
|
||||
});
|
||||
|
||||
return box.apply(null, mdat);
|
||||
};
|
||||
|
||||
// Returns a ccData byte-pair for a two character string. That is,
|
||||
// it converts a string like 'hi' into the two-byte number that
|
||||
// would be parsed back as 'hi' when provided as ccData.
|
||||
var characters = function(text) {
|
||||
if (text.length !== 2) {
|
||||
throw new Error('ccdata must be specified two characters at a time');
|
||||
}
|
||||
return (text.charCodeAt(0) << 8) | text.charCodeAt(1);
|
||||
};
|
||||
|
||||
// Returns a ccData byte-pair including
|
||||
// Header for 708 packet
|
||||
// Header for the first service block
|
||||
// seq should increment by 1 for each byte pair mod 3 (0,1,2,0,1,2,...)
|
||||
// sizeCode is the number of byte pairs in the packet (including header)
|
||||
// serviceNum is the service number of the first service block
|
||||
// blockSize is the size of the first service block in bytes (no header)
|
||||
// If there's only one service block, the blockSize should be (sizeCode-1)*2
|
||||
var packetHeader708 = function(seq, sizeCode, serviceNum, blockSize) {
|
||||
var b1 = (seq << 6) | sizeCode;
|
||||
var b2 = (serviceNum << 5) | blockSize;
|
||||
return (b1 << 8) | b2;
|
||||
};
|
||||
|
||||
// Returns a ccData byte-pair to execute a 708 DSW command
|
||||
// Takes an array of window indicies to display
|
||||
var displayWindows708 = function(windows) {
|
||||
var cmd = 0x8900;
|
||||
|
||||
windows.forEach(function(winIdx) {
|
||||
cmd |= (0x01 << winIdx);
|
||||
});
|
||||
|
||||
return cmd;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
makeSeiFromCaptionPacket: makeSeiFromCaptionPacket,
|
||||
makeSeiFromMultipleCaptionPackets: makeSeiFromMultipleCaptionPackets,
|
||||
makeMdatFromCaptionPackets: makeMdatFromCaptionPackets,
|
||||
characters: characters,
|
||||
packetHeader708: packetHeader708,
|
||||
displayWindows708: displayWindows708
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue