First release

This commit is contained in:
Owen Quinlan 2021-07-02 19:29:34 +10:00
commit fa6c85266e
2339 changed files with 761050 additions and 0 deletions

View file

@ -0,0 +1,321 @@
import QUnit from 'qunit';
import {
bytesToString,
stringToBytes,
toUint8,
concatTypedArrays,
toHexString,
toBinaryString,
bytesToNumber,
numberToBytes,
bytesMatch
} from '../src/byte-helpers.js';
import window from 'global/window';
const arrayNames = [];
const BigInt = window.BigInt;
[
'Array',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array'
].forEach(function(name) {
if (window[name]) {
arrayNames.push(name);
}
});
QUnit.module('bytesToString');
const testString = 'hello竜';
const testBytes = toUint8([
// h
0x68,
// e
0x65,
// l
0x6c,
// l
0x6c,
// o
0x6f,
// 竜
0xe7, 0xab, 0x9c
]);
const rawBytes = toUint8([0x47, 0x40, 0x00, 0x10, 0x00, 0x00, 0xb0, 0x0d, 0x00, 0x01]);
QUnit.test('should function as expected', function(assert) {
arrayNames.forEach(function(name) {
const testObj = name === 'Array' ? testBytes : new window[name](testBytes);
assert.equal(bytesToString(testObj), testString, `testString work as a string arg with ${name}`);
assert.equal(bytesToString(new window[name]()), '', `empty ${name} returns empty string`);
});
assert.equal(bytesToString(), '', 'undefined returns empty string');
assert.equal(bytesToString(null), '', 'null returns empty string');
assert.equal(bytesToString(stringToBytes(testString)), testString, 'stringToBytes -> bytesToString works');
});
QUnit.module('stringToBytes');
QUnit.test('should function as expected', function(assert) {
assert.deepEqual(stringToBytes(testString), testBytes, 'returns an array of bytes');
assert.deepEqual(stringToBytes(), toUint8(), 'empty array for undefined');
assert.deepEqual(stringToBytes(null), toUint8(), 'empty array for null');
assert.deepEqual(stringToBytes(''), toUint8(), 'empty array for empty string');
assert.deepEqual(stringToBytes(10), toUint8([0x31, 0x30]), 'converts numbers to strings');
assert.deepEqual(stringToBytes(bytesToString(testBytes)), testBytes, 'bytesToString -> stringToBytes works');
assert.deepEqual(stringToBytes(bytesToString(rawBytes), true), rawBytes, 'equal to original with raw bytes mode');
assert.notDeepEqual(stringToBytes(bytesToString(rawBytes)), rawBytes, 'without raw byte mode works, not equal');
});
QUnit.module('toUint8');
QUnit.test('should function as expected', function(assert) {
const tests = {
undef: {
data: undefined,
expected: new Uint8Array()
},
null: {
data: null,
expected: new Uint8Array()
},
string: {
data: 'foo',
expected: new Uint8Array()
},
NaN: {
data: NaN,
expected: new Uint8Array()
},
object: {
data: {},
expected: new Uint8Array()
},
number: {
data: 0x11,
expected: new Uint8Array([0x11])
}
};
Object.keys(tests).forEach(function(name) {
const {data, expected} = tests[name];
const result = toUint8(data);
assert.ok(result instanceof Uint8Array, `obj is a Uint8Array for ${name}`);
assert.deepEqual(result, expected, `data is as expected for ${name}`);
});
arrayNames.forEach(function(name) {
const testObj = name === 'Array' ? testBytes : new window[name](testBytes);
const uint = toUint8(testObj);
assert.ok(uint instanceof Uint8Array && uint.length > 0, `converted ${name} to Uint8Array`);
});
});
QUnit.module('concatTypedArrays');
QUnit.test('should function as expected', function(assert) {
const tests = {
undef: {
data: concatTypedArrays(),
expected: toUint8([])
},
empty: {
data: concatTypedArrays(toUint8([])),
expected: toUint8([])
},
single: {
data: concatTypedArrays([0x01]),
expected: toUint8([0x01])
},
array: {
data: concatTypedArrays([0x01], [0x02]),
expected: toUint8([0x01, 0x02])
},
uint: {
data: concatTypedArrays(toUint8([0x01]), toUint8([0x02])),
expected: toUint8([0x01, 0x02])
},
buffer: {
data: concatTypedArrays(toUint8([0x01]).buffer, toUint8([0x02]).buffer),
expected: toUint8([0x01, 0x02])
},
manyarray: {
data: concatTypedArrays([0x01], [0x02], [0x03], [0x04]),
expected: toUint8([0x01, 0x02, 0x03, 0x04])
},
manyuint: {
data: concatTypedArrays(toUint8([0x01]), toUint8([0x02]), toUint8([0x03]), toUint8([0x04])),
expected: toUint8([0x01, 0x02, 0x03, 0x04])
}
};
Object.keys(tests).forEach(function(name) {
const {data, expected} = tests[name];
assert.ok(data instanceof Uint8Array, `obj is a Uint8Array for ${name}`);
assert.deepEqual(data, expected, `data is as expected for ${name}`);
});
});
QUnit.module('toHexString');
QUnit.test('should function as expected', function(assert) {
assert.equal(toHexString(0xFF), 'ff', 'works with single value');
assert.equal(toHexString([0xFF, 0xaa]), 'ffaa', 'works with array');
assert.equal(toHexString(toUint8([0xFF, 0xaa])), 'ffaa', 'works with uint8');
assert.equal(toHexString(toUint8([0xFF, 0xaa]).buffer), 'ffaa', 'works with buffer');
assert.equal(toHexString(toUint8([0xFF, 0xaa, 0xbb]).subarray(1, 3)), 'aabb', 'works with subarray');
assert.equal(toHexString([0x01, 0x02, 0x03]), '010203', 'works with single digits');
});
QUnit.module('toBinaryString');
QUnit.test('should function as expected', function(assert) {
const ff = '11111111';
const aa = '10101010';
const bb = '10111011';
const zerof = '00001111';
const one = '00000001';
const zero = '00000000';
const fzero = '11110000';
assert.equal(toBinaryString(0xFF), ff, 'works with single value');
assert.equal(toBinaryString([0xFF, 0xaa]), ff + aa, 'works with array');
assert.equal(toBinaryString(toUint8([0xFF, 0xbb])), ff + bb, 'works with uint8');
assert.equal(toBinaryString(toUint8([0xFF, 0xaa]).buffer), ff + aa, 'works with buffer');
assert.equal(toBinaryString(toUint8([0xFF, 0xaa, 0xbb]).subarray(1, 3)), aa + bb, 'works with subarray');
assert.equal(toBinaryString([0x0F, 0x01, 0xF0, 0x00]), zerof + one + fzero + zero, 'works with varying digits digits');
});
QUnit.module('bytesToNumber');
QUnit.test('sanity', function(assert) {
assert.equal(bytesToNumber(0xFF), 0xFF, 'single value');
assert.equal(bytesToNumber([0xFF, 0x01]), 0xFF01, 'works with array');
assert.equal(bytesToNumber(toUint8([0xFF, 0xbb])), 0xFFBB, 'works with uint8');
assert.equal(bytesToNumber(toUint8([0xFF, 0xaa]).buffer), 0xFFAA, 'works with buffer');
assert.equal(bytesToNumber(toUint8([0xFF, 0xaa, 0xbb]).subarray(1, 3)), 0xAABB, 'works with subarray');
});
QUnit.test('unsigned and littleEndian work', function(assert) {
// works with any number of bits
assert.equal(bytesToNumber([0xFF]), 0xFF, 'u8');
assert.equal(bytesToNumber([0xFF, 0xAA]), 0xFFAA, 'u16');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB]), 0xFFAABB, 'u24');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC]), 0xFFAABBCC, 'u32');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD]), 0xFFAABBCCDD, 'u40');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE]), 0xFFAABBCCDDEE, 'u48');
assert.equal(bytesToNumber([0xFF], {le: true}), 0xFF, 'u8 le');
assert.equal(bytesToNumber([0xFF, 0xAA], {le: true}), 0xAAFF, 'u16 le');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB], {le: true}), 0xBBAAFF, 'u24 le');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC], {le: true}), 0xCCBBAAFF, 'u32 le');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD], {le: true}), 0xDDCCBBAAFF, 'u40 le');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE], {le: true}), 0xEEDDCCBBAAFF, 'u48 le');
if (BigInt) {
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x99]), 0xFFAABBCCDDEE99, 'u56');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x99, 0x88]), 0xFFAABBCCDDEE9988, 'u64');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x99], {le: true}), 0x99EEDDCCBBAAFF, 'u56 le');
assert.equal(bytesToNumber([0xFF, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0x99, 0x88], {le: true}), 0x8899EEDDCCBBAAFF, 'u64 le');
}
});
QUnit.test('signed and littleEndian work', function(assert) {
assert.equal(bytesToNumber([0xF0], {signed: true}), -16, 'i8');
assert.equal(bytesToNumber([0x80, 0x70], {signed: true}), -32656, 'i16');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f], {signed: true}), -8359777, 'i24');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f, 0xFF], {signed: true}), -2140102657, 'i32');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f, 0xFF, 0x10], {signed: true}), -547866280176, 'i40');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f, 0xFF, 0x10, 0x89], {signed: true}), -140253767724919, 'i48');
assert.equal(bytesToNumber([0xF0], {signed: true, le: true}), -16, 'i8 le');
assert.equal(bytesToNumber([0x80, 0x70], {signed: true, le: true}), 0x7080, 'i16 le');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f], {signed: true, le: true}), -6328192, 'i24 le');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f, 0xFF], {signed: true, le: true}), -6328192, 'i32 le');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f, 0xFF, 0x10], {signed: true, le: true}), 73008115840, 'i40 le');
assert.equal(bytesToNumber([0x80, 0x70, 0x9f, 0xFF, 0x10, 0x89], {signed: true, le: true}), -130768875589504, 'i48 le');
if (BigInt) {
assert.equal(bytesToNumber([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], {signed: true}), -1, 'i56');
assert.equal(bytesToNumber([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], {signed: true}), -1, 'i64');
assert.equal(bytesToNumber([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], {signed: true, le: true}), -1, 'i56 le');
assert.equal(bytesToNumber([0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], {signed: true, le: true}), -1, 'i64 le');
}
});
QUnit.module('numberToBytes');
QUnit.test('unsigned negative and positive', function(assert) {
assert.deepEqual(numberToBytes(), toUint8([0x00]), 'no bytes');
assert.deepEqual(numberToBytes(0xFF), toUint8([0xFF]), 'u8');
assert.deepEqual(numberToBytes(0xFFaa), toUint8([0xFF, 0xaa]), 'u16');
assert.deepEqual(numberToBytes(0xFFaabb), toUint8([0xFF, 0xaa, 0xbb]), 'u24');
assert.deepEqual(numberToBytes(0xFFaabbcc), toUint8([0xFF, 0xaa, 0xbb, 0xcc]), 'u32');
assert.deepEqual(numberToBytes(0xFFaabbccdd), toUint8([0xFF, 0xaa, 0xbb, 0xcc, 0xdd]), 'u40');
assert.deepEqual(numberToBytes(0xFFaabbccddee), toUint8([0xFF, 0xaa, 0xbb, 0xcc, 0xdd, 0xee]), 'u48');
assert.deepEqual(numberToBytes(-16), toUint8([0xF0]), 'negative to u8');
assert.deepEqual(numberToBytes(-32640), toUint8([0x80, 0x80]), 'negative to u16');
assert.deepEqual(numberToBytes(-3264062), toUint8([0xce, 0x31, 0xc2]), 'negative to u24');
assert.deepEqual(numberToBytes(-2139062144), toUint8([0x80, 0x80, 0x80, 0x80]), 'negative to u32');
assert.deepEqual(numberToBytes(-3139062144), toUint8([0xff, 0x44, 0xe5, 0xb6, 0x80]), 'negative u40');
assert.deepEqual(numberToBytes(-3139062144444), toUint8([0xfd, 0x25, 0x21, 0x50, 0xe2, 0x44]), 'negative u48');
if (BigInt) {
assert.deepEqual(numberToBytes(BigInt('0xFFaabbccddee99')), toUint8([0xFF, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x99]), 'u56');
assert.deepEqual(numberToBytes(BigInt('0xFFaabbccddee9988')), toUint8([0xFF, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0x99, 0x88]), 'u64');
assert.deepEqual(numberToBytes(BigInt('-31390621444448812')), toUint8([0x90, 0x7a, 0x65, 0x67, 0x86, 0x5d, 0xd4]), 'negative to u56');
assert.deepEqual(numberToBytes(BigInt('-9187201950435737472')), toUint8([0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80]), 'u64');
}
});
QUnit.test('unsigned littleEndian negative and positive', function(assert) {
assert.deepEqual(numberToBytes(0xFF, {le: true}), toUint8([0xFF]), 'u8');
assert.deepEqual(numberToBytes(0xFFaa, {le: true}), toUint8([0xaa, 0xFF]), 'u16');
assert.deepEqual(numberToBytes(0xFFaabb, {le: true}), toUint8([0xbb, 0xaa, 0xFF]), 'u24');
assert.deepEqual(numberToBytes(0xFFaabbcc, {le: true}), toUint8([0xcc, 0xbb, 0xaa, 0xff]), 'u32');
assert.deepEqual(numberToBytes(0xFFaabbccdd, {le: true}), toUint8([0xdd, 0xcc, 0xbb, 0xaa, 0xff]), 'u40');
assert.deepEqual(numberToBytes(0xFFaabbccddee, {le: true}), toUint8([0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0xff]), 'u48');
assert.deepEqual(numberToBytes(-16, {le: true}), toUint8([0xF0]), 'negative to u8');
assert.deepEqual(numberToBytes(-32640, {le: true}), toUint8([0x80, 0x80]), 'negative to u16');
assert.deepEqual(numberToBytes(-3264062, {le: true}), toUint8([0xc2, 0x31, 0xce]), 'negative to u24');
assert.deepEqual(numberToBytes(-2139062144, {le: true}), toUint8([0x80, 0x80, 0x80, 0x80]), 'negative to u32');
assert.deepEqual(numberToBytes(-3139062144, {le: true}), toUint8([0x80, 0xb6, 0xe5, 0x44, 0xff]), 'negative u40');
assert.deepEqual(numberToBytes(-3139062144444, {le: true}), toUint8([0x44, 0xe2, 0x50, 0x21, 0x25, 0xfd]), 'negative u48');
if (BigInt) {
assert.deepEqual(numberToBytes(BigInt('0xFFaabbccddee99'), {le: true}), toUint8([0x99, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0xff]), 'u56');
assert.deepEqual(numberToBytes(BigInt('0xFFaabbccddee9988'), {le: true}), toUint8([0x88, 0x99, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0xff]), 'u64');
assert.deepEqual(numberToBytes(BigInt('-31390621444448812'), {le: true}), toUint8([0xd4, 0x5d, 0x86, 0x67, 0x65, 0x7a, 0x90]), 'negative to u56');
assert.deepEqual(numberToBytes(BigInt('-9187201950435737472'), {le: true}), toUint8([0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80]), 'u64');
}
});
QUnit.module('bytesMatch');
QUnit.test('should function as expected', function(assert) {
assert.equal(bytesMatch(), false, 'no a or b bytes, false');
assert.equal(bytesMatch(null, []), false, 'no a bytes, false');
assert.equal(bytesMatch([]), false, 'no b bytes, false');
assert.equal(bytesMatch([0x00], [0x00, 0x02]), false, 'not enough bytes');
assert.equal(bytesMatch([0x00], [0x00], {offset: 1}), false, 'not due to offset');
assert.equal(bytesMatch([0xbb, 0xaa], [0xaa]), false, 'bytes do not match');
assert.equal(bytesMatch([0xaa], [0xaa], {mask: [0x10]}), false, 'bytes do not match due to mask');
assert.equal(bytesMatch([0xaa], [0xaa]), true, 'bytes match');
assert.equal(bytesMatch([0xbb, 0xaa], [0xbb]), true, 'bytes match more a');
assert.equal(bytesMatch([0xbb, 0xaa], [0xaa], {offset: 1}), true, 'bytes match with offset');
assert.equal(bytesMatch([0xaa], [0x20], {mask: [0x20]}), true, 'bytes match with mask');
assert.equal(bytesMatch([0xbb, 0xaa], [0x20], {mask: [0x20], offset: 1}), true, 'bytes match with mask and offset');
assert.equal(bytesMatch([0xbb, 0xaa, 0xaa], [0x20, 0x20], {mask: [0x20, 0x20], offset: 1}), true, 'bytes match with many masks and offset');
});

472
node_modules/@videojs/vhs-utils/test/codecs.test.js generated vendored Normal file
View file

@ -0,0 +1,472 @@
import window from 'global/window';
import QUnit from 'qunit';
import {
mapLegacyAvcCodecs,
translateLegacyCodecs,
parseCodecs,
codecsFromDefault,
isVideoCodec,
isAudioCodec,
muxerSupportsCodec,
browserSupportsCodec,
getMimeForCodec
} from '../src/codecs';
const supportedMuxerCodecs = [
'mp4a',
'avc1'
];
const unsupportedMuxerCodecs = [
'hvc1',
'ac-3',
'ec-3',
'mp3'
];
QUnit.module('Legacy Codecs');
QUnit.test('maps legacy AVC codecs', function(assert) {
assert.equal(
mapLegacyAvcCodecs('avc1.deadbeef'),
'avc1.deadbeef',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('avc1.dead.beef, mp4a.something'),
'avc1.dead.beef, mp4a.something',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('avc1.dead.beef,mp4a.something'),
'avc1.dead.beef,mp4a.something',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('mp4a.something,avc1.dead.beef'),
'mp4a.something,avc1.dead.beef',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('mp4a.something, avc1.dead.beef'),
'mp4a.something, avc1.dead.beef',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('avc1.42001e'),
'avc1.42001e',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('avc1.4d0020,mp4a.40.2'),
'avc1.4d0020,mp4a.40.2',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('mp4a.40.2,avc1.4d0020'),
'mp4a.40.2,avc1.4d0020',
'does nothing for non legacy pattern'
);
assert.equal(
mapLegacyAvcCodecs('mp4a.40.40'),
'mp4a.40.40',
'does nothing for non video codecs'
);
assert.equal(
mapLegacyAvcCodecs('avc1.66.30'),
'avc1.42001e',
'translates legacy video codec alone'
);
assert.equal(
mapLegacyAvcCodecs('avc1.66.30, mp4a.40.2'),
'avc1.42001e, mp4a.40.2',
'translates legacy video codec when paired with audio'
);
assert.equal(
mapLegacyAvcCodecs('mp4a.40.2, avc1.66.30'),
'mp4a.40.2, avc1.42001e',
'translates video codec when specified second'
);
});
QUnit.test('translates legacy codecs', function(assert) {
assert.deepEqual(
translateLegacyCodecs(['avc1.66.30', 'avc1.66.30']),
['avc1.42001e', 'avc1.42001e'],
'translates legacy avc1.66.30 codec'
);
assert.deepEqual(
translateLegacyCodecs(['avc1.42C01E', 'avc1.42C01E']),
['avc1.42C01E', 'avc1.42C01E'],
'does not translate modern codecs'
);
assert.deepEqual(
translateLegacyCodecs(['avc1.42C01E', 'avc1.66.30']),
['avc1.42C01E', 'avc1.42001e'],
'only translates legacy codecs when mixed'
);
assert.deepEqual(
translateLegacyCodecs(['avc1.4d0020', 'avc1.100.41', 'avc1.77.41',
'avc1.77.32', 'avc1.77.31', 'avc1.77.30',
'avc1.66.30', 'avc1.66.21', 'avc1.42C01e']),
['avc1.4d0020', 'avc1.640029', 'avc1.4d0029',
'avc1.4d0020', 'avc1.4d001f', 'avc1.4d001e',
'avc1.42001e', 'avc1.420015', 'avc1.42C01e'],
'translates a whole bunch'
);
});
QUnit.module('parseCodecs');
QUnit.test('parses text only codec string', function(assert) {
assert.deepEqual(
parseCodecs('stpp.ttml.im1t'),
[{mediaType: 'text', type: 'stpp.ttml.im1t', details: ''}],
'parsed text only codec string'
);
});
QUnit.test('parses video only codec string', function(assert) {
assert.deepEqual(
parseCodecs('avc1.42001e'),
[{mediaType: 'video', type: 'avc1', details: '.42001e'}],
'parsed video only codec string'
);
});
QUnit.test('parses audio only codec string', function(assert) {
assert.deepEqual(
parseCodecs('mp4a.40.2'),
[{mediaType: 'audio', type: 'mp4a', details: '.40.2'}],
'parsed audio only codec string'
);
});
QUnit.test('parses video, audio, and text codec string', function(assert) {
assert.deepEqual(
parseCodecs('avc1.42001e, mp4a.40.2, stpp.ttml.im1t'),
[
{mediaType: 'video', type: 'avc1', details: '.42001e'},
{mediaType: 'audio', type: 'mp4a', details: '.40.2'},
{mediaType: 'text', type: 'stpp.ttml.im1t', details: ''}
],
'parsed video, audio, and text codec string'
);
});
QUnit.test('parses video, audio, and text codec with mixed case', function(assert) {
assert.deepEqual(
parseCodecs('AvC1.42001E, Mp4A.40.E, stpp.TTML.im1T'),
[
{mediaType: 'video', type: 'AvC1', details: '.42001E'},
{mediaType: 'audio', type: 'Mp4A', details: '.40.E'},
{mediaType: 'text', type: 'stpp.TTML.im1T', details: ''}
],
'parsed video, audio, and text codec string'
);
});
QUnit.test('parses two unknown codec', function(assert) {
assert.deepEqual(
parseCodecs('fake.codec, other-fake'),
[
{mediaType: 'unknown', type: 'fake.codec', details: ''},
{mediaType: 'unknown', type: 'other-fake', details: ''}
],
'parsed faked codecs as video/audio'
);
});
QUnit.test('parses an unknown codec with a known audio', function(assert) {
assert.deepEqual(
parseCodecs('fake.codec, mp4a.40.2'),
[
{mediaType: 'unknown', type: 'fake.codec', details: ''},
{mediaType: 'audio', type: 'mp4a', details: '.40.2'}
],
'parsed audio and unknwon'
);
});
QUnit.test('parses an unknown codec with a known video', function(assert) {
assert.deepEqual(
parseCodecs('avc1.42001e, other-fake'),
[
{mediaType: 'video', type: 'avc1', details: '.42001e'},
{mediaType: 'unknown', type: 'other-fake', details: ''}
],
'parsed video and unknown'
);
});
QUnit.test('parses an unknown codec with a known text', function(assert) {
assert.deepEqual(
parseCodecs('stpp.ttml.im1t, other-fake'),
[
{mediaType: 'text', type: 'stpp.ttml.im1t', details: ''},
{mediaType: 'unknown', type: 'other-fake', details: ''}
],
'parsed text and unknown'
);
});
QUnit.test('parses an unknown codec with a known audio/video/text', function(assert) {
assert.deepEqual(
parseCodecs('fake.codec, avc1.42001e, mp4a.40.2, stpp.ttml.im1t'),
[
{mediaType: 'unknown', type: 'fake.codec', details: ''},
{mediaType: 'video', type: 'avc1', details: '.42001e'},
{mediaType: 'audio', type: 'mp4a', details: '.40.2'},
{mediaType: 'text', type: 'stpp.ttml.im1t', details: ''}
],
'parsed video/audio/text and unknown codecs'
);
});
QUnit.module('codecsFromDefault');
QUnit.test('returns falsey when no audio group ID', function(assert) {
assert.notOk(
codecsFromDefault(
{ mediaGroups: { AUDIO: {} } },
'',
),
'returns falsey when no audio group ID'
);
});
QUnit.test('returns falsey when no matching audio group', function(assert) {
assert.notOk(
codecsFromDefault(
{
mediaGroups: {
AUDIO: {
au1: {
en: {
default: false,
playlists: [{
attributes: { CODECS: 'mp4a.40.2' }
}]
},
es: {
default: true,
playlists: [{
attributes: { CODECS: 'mp4a.40.5' }
}]
}
}
}
}
},
'au2'
),
'returned falsey when no matching audio group'
);
});
QUnit.test('returns falsey when no default for audio group', function(assert) {
assert.notOk(
codecsFromDefault(
{
mediaGroups: {
AUDIO: {
au1: {
en: {
default: false,
playlists: [{
attributes: { CODECS: 'mp4a.40.2' }
}]
},
es: {
default: false,
playlists: [{
attributes: { CODECS: 'mp4a.40.5' }
}]
}
}
}
}
},
'au1'
),
'returned falsey when no default for audio group'
);
});
QUnit.test('returns parsed audio codecs for default in audio group', function(assert) {
assert.deepEqual(
codecsFromDefault(
{
mediaGroups: {
AUDIO: {
au1: {
en: {
default: false,
playlists: [{
attributes: { CODECS: 'mp4a.40.2, mp4a.40.20' }
}]
},
es: {
default: true,
playlists: [{
attributes: { CODECS: 'mp4a.40.5, mp4a.40.7' }
}]
}
}
}
}
},
'au1'
),
[
{mediaType: 'audio', type: 'mp4a', details: '.40.5'},
{mediaType: 'audio', type: 'mp4a', details: '.40.7'}
],
'returned parsed codec audio profile'
);
});
QUnit.module('isVideoCodec');
QUnit.test('works as expected', function(assert) {
[
'av1',
'avc01',
'avc1',
'avc02',
'avc2',
'vp09',
'vp9',
'vp8',
'vp08',
'hvc1',
'hev1',
'theora',
'mp4v'
].forEach(function(codec) {
assert.ok(isVideoCodec(codec), `"${codec}" is seen as a video codec`);
assert.ok(isVideoCodec(` ${codec} `), `" ${codec} " is seen as video codec`);
assert.ok(isVideoCodec(codec.toUpperCase()), `"${codec.toUpperCase()}" is seen as video codec`);
});
['invalid', 'foo', 'mp4a', 'opus', 'vorbis'].forEach(function(codec) {
assert.notOk(isVideoCodec(codec), `${codec} is not a video codec`);
});
});
QUnit.module('isAudioCodec');
QUnit.test('works as expected', function(assert) {
[
'mp4a',
'flac',
'vorbis',
'opus',
'ac-3',
'ac-4',
'ec-3',
'alac',
'speex',
'aac',
'mp3'
].forEach(function(codec) {
assert.ok(isAudioCodec(codec), `"${codec}" is seen as an audio codec`);
assert.ok(isAudioCodec(` ${codec} `), `" ${codec} " is seen as an audio codec`);
assert.ok(isAudioCodec(codec.toUpperCase()), `"${codec.toUpperCase()}" is seen as an audio codec`);
});
['invalid', 'foo', 'bar', 'avc1', 'av1'].forEach(function(codec) {
assert.notOk(isAudioCodec(codec), `${codec} is not an audio codec`);
});
});
QUnit.module('muxerSupportsCodec');
QUnit.test('works as expected', function(assert) {
const validMuxerCodecs = [];
const invalidMuxerCodecs = [];
unsupportedMuxerCodecs.forEach(function(badCodec) {
invalidMuxerCodecs.push(badCodec);
supportedMuxerCodecs.forEach(function(goodCodec) {
invalidMuxerCodecs.push(`${goodCodec}, ${badCodec}`);
});
});
// generate all combinations of valid codecs
supportedMuxerCodecs.forEach(function(codec, i) {
validMuxerCodecs.push(codec);
supportedMuxerCodecs.forEach(function(_codec, z) {
if (z === i) {
return;
}
validMuxerCodecs.push(`${codec}, ${_codec}`);
validMuxerCodecs.push(`${codec},${_codec}`);
});
});
validMuxerCodecs.forEach(function(codec) {
assert.ok(muxerSupportsCodec(codec), `"${codec}" is supported`);
assert.ok(muxerSupportsCodec(` ${codec} `), `" ${codec} " is supported`);
assert.ok(muxerSupportsCodec(codec.toUpperCase()), `"${codec.toUpperCase()}" is supported`);
});
invalidMuxerCodecs.forEach(function(codec) {
assert.notOk(muxerSupportsCodec(codec), `${codec} not supported`);
});
});
QUnit.module('browserSupportsCodec', {
beforeEach() {
this.oldMediaSource = window.MediaSource;
},
afterEach() {
window.MediaSource = this.oldMediaSource;
}
});
QUnit.test('works as expected', function(assert) {
window.MediaSource = {isTypeSupported: () => true};
assert.ok(browserSupportsCodec('test'), 'isTypeSupported true, browser does support codec');
window.MediaSource = {isTypeSupported: () => false};
assert.notOk(browserSupportsCodec('test'), 'isTypeSupported false, browser does not support codec');
window.MediaSource = null;
assert.notOk(browserSupportsCodec('test'), 'no MediaSource, browser does not support codec');
window.MediaSource = {isTypeSupported: null};
assert.notOk(browserSupportsCodec('test'), 'no isTypeSupported, browser does not support codec');
});
QUnit.module('getMimeForCodec');
QUnit.test('works as expected', function(assert) {
// mp4
assert.equal(getMimeForCodec('vp9,mp4a'), 'video/mp4;codecs="vp9,mp4a"', 'mp4 video/audio works');
assert.equal(getMimeForCodec('vp9'), 'video/mp4;codecs="vp9"', 'mp4 video works');
assert.equal(getMimeForCodec('mp4a'), 'audio/mp4;codecs="mp4a"', 'mp4 audio works');
// webm
assert.equal(getMimeForCodec('vp8,opus'), 'video/webm;codecs="vp8,opus"', 'webm video/audio works');
assert.equal(getMimeForCodec('vp8'), 'video/webm;codecs="vp8"', 'webm video works');
assert.equal(getMimeForCodec('vorbis'), 'audio/webm;codecs="vorbis"', 'webm audio works');
// ogg
assert.equal(getMimeForCodec('theora,vorbis'), 'video/ogg;codecs="theora,vorbis"', 'ogg video/audio works');
assert.equal(getMimeForCodec('theora'), 'video/ogg;codecs="theora"', 'ogg video works');
// ogg will never be selected for audio only
// mixed
assert.equal(getMimeForCodec('opus'), 'audio/mp4;codecs="opus"', 'mp4 takes priority over everything');
assert.equal(getMimeForCodec('vorbis'), 'audio/webm;codecs="vorbis"', 'webm takes priority over ogg');
assert.equal(getMimeForCodec('foo'), 'video/mp4;codecs="foo"', 'mp4 is the default');
assert.notOk(getMimeForCodec(), 'invalid codec returns undefined');
assert.equal(getMimeForCodec('Mp4A.40.2,AvC1.42001E'), 'video/mp4;codecs="Mp4A.40.2,AvC1.42001E"', 'case is preserved');
assert.equal(getMimeForCodec('stpp.ttml.im1t'), 'application/mp4;codecs="stpp.ttml.im1t"', 'text is parsed');
});

197
node_modules/@videojs/vhs-utils/test/container.test.js generated vendored Normal file
View file

@ -0,0 +1,197 @@
import QUnit from 'qunit';
import {detectContainerForBytes, isLikelyFmp4MediaSegment} from '../src/containers.js';
import {stringToBytes, concatTypedArrays, toUint8} from '../src/byte-helpers.js';
const filler = (size) => {
const view = new Uint8Array(size);
for (let i = 0; i < size; i++) {
view[i] = 0;
}
return view;
};
const otherMp4Data = concatTypedArrays([0x00, 0x00, 0x00, 0x00], stringToBytes('stypiso'));
const id3Data = Array.prototype.slice.call(concatTypedArrays(
stringToBytes('ID3'),
// id3 header is 10 bytes without footer
// 10th byte is length 0x23 or 35 in decimal
// so a total length of 45
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23],
// add in the id3 content
filler(35)
));
const id3DataWithFooter = Array.prototype.slice.call(concatTypedArrays(
stringToBytes('ID3'),
// id3 header is 20 bytes with footer
// "we have a footer" is the sixth byte
// 10th byte is length of 0x23 or 35 in decimal
// so a total length of 55
[0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x23],
// add in the id3 content
filler(45)
));
const testData = {
// EBML tag + dataSize
// followed by DocType + dataSize and then actual data for that tag
'mkv': concatTypedArrays([0x1a, 0x45, 0xdf, 0xa3, 0x99, 0x42, 0x82, 0x88], stringToBytes('matroska')),
'webm': concatTypedArrays([0x1a, 0x45, 0xdf, 0xa3, 0x99, 0x42, 0x82, 0x88], stringToBytes('webm')),
'flac': stringToBytes('fLaC'),
'ogg': stringToBytes('OggS'),
'aac': toUint8([0xFF, 0xF1]),
'ac3': toUint8([0x0B, 0x77]),
'mp3': toUint8([0xFF, 0xFB]),
'3gp': concatTypedArrays([0x00, 0x00, 0x00, 0x00], stringToBytes('ftyp3g')),
'mp4': concatTypedArrays([0x00, 0x00, 0x00, 0x00], stringToBytes('ftypiso')),
'mov': concatTypedArrays([0x00, 0x00, 0x00, 0x00], stringToBytes('ftypqt')),
'avi': toUint8([0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x41, 0x56, 0x49]),
'wav': toUint8([0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45]),
'ts': toUint8([0x47]),
// seq_parameter_set_rbsp
'h264': toUint8([0x00, 0x00, 0x00, 0x01, 0x67, 0x42, 0xc0, 0x0d, 0xd9, 0x01, 0xa1, 0xfa, 0x10, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x95, 0xe0, 0xf1, 0x42, 0xa4, 0x80, 0x00, 0x00, 0x00, 0x01]),
// video_parameter_set_rbsp
'h265': toUint8([0x00, 0x00, 0x00, 0x01, 0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x24, 0x08, 0x00, 0x00, 0x00, 0x9c, 0x08, 0x00, 0x00, 0x00, 0x00, 0x78, 0x95, 0x98, 0x09, 0x00, 0x00, 0x00, 0x01])
};
// seq_parameter_set_rbsp
const h265seq = toUint8([
0x00, 0x00, 0x00, 0x01,
0x42, 0x01, 0x01, 0x21,
0x60, 0x00, 0x00, 0x00,
0x90, 0x00, 0x00, 0x00,
0x00, 0x00, 0x78, 0xa0,
0x0d, 0x08, 0x0f, 0x16,
0x59, 0x59, 0xa4, 0x93,
0x2b, 0x9a, 0x02, 0x00,
0x00, 0x00, 0x64, 0x00,
0x00, 0x09, 0x5e, 0x10,
0x00, 0x00, 0x00, 0x01
]);
const h264shortnal = Array.prototype.slice.call(testData.h264);
// remove 0x00 from the front
h264shortnal.splice(0, 1);
// remove 0x00 from the back
h264shortnal.splice(h264shortnal.length - 2, 1);
const h265shortnal = Array.prototype.slice.call(testData.h265);
// remove 0x00 from the front
h265shortnal.splice(0, 1);
// remove 0x00 from the back
h265shortnal.splice(h265shortnal.length - 2, 1);
QUnit.module('detectContainerForBytes');
QUnit.test('should identify known types', function(assert) {
Object.keys(testData).forEach(function(key) {
const data = new Uint8Array(testData[key]);
assert.equal(detectContainerForBytes(testData[key]), key, `found ${key} with Array`);
assert.equal(detectContainerForBytes(data.buffer), key, `found ${key} with ArrayBuffer`);
assert.equal(detectContainerForBytes(data), key, `found ${key} with Uint8Array`);
});
const mp4Bytes = concatTypedArrays([0x00, 0x00, 0x00, 0x00], stringToBytes('styp'));
assert.equal(detectContainerForBytes(mp4Bytes), 'mp4', 'styp mp4 detected as mp4');
// mp3/aac/flac/ac3 audio can have id3 data before the
// signature for the file, so we need to handle that.
['mp3', 'aac', 'flac', 'ac3'].forEach(function(type) {
const dataWithId3 = concatTypedArrays(id3Data, testData[type]);
const dataWithId3Footer = concatTypedArrays(id3DataWithFooter, testData[type]);
const recursiveDataWithId3 = concatTypedArrays(
id3Data,
id3Data,
id3Data,
testData[type]
);
const recursiveDataWithId3Footer = concatTypedArrays(
id3DataWithFooter,
id3DataWithFooter,
id3DataWithFooter,
testData[type]
);
const differentId3Sections = concatTypedArrays(
id3DataWithFooter,
id3Data,
id3DataWithFooter,
id3Data,
testData[type]
);
assert.equal(detectContainerForBytes(dataWithId3), type, `id3 skipped and ${type} detected`);
assert.equal(detectContainerForBytes(dataWithId3Footer), type, `id3 + footer skipped and ${type} detected`);
assert.equal(detectContainerForBytes(recursiveDataWithId3), type, `id3 x3 skipped and ${type} detected`);
assert.equal(detectContainerForBytes(recursiveDataWithId3Footer), type, `id3 + footer x3 skipped and ${type} detected`);
assert.equal(detectContainerForBytes(differentId3Sections), type, `id3 with/without footer skipped and ${type} detected`);
});
const notTs = concatTypedArrays(testData.ts, filler(188));
const longTs = concatTypedArrays(testData.ts, filler(187), testData.ts);
const unsyncTs = concatTypedArrays(filler(187), testData.ts, filler(187), testData.ts);
const badTs = concatTypedArrays(filler(188), testData.ts, filler(187), testData.ts);
assert.equal(detectContainerForBytes(longTs), 'ts', 'long ts data is detected');
assert.equal(detectContainerForBytes(unsyncTs), 'ts', 'unsynced ts is detected');
assert.equal(detectContainerForBytes(badTs), '', 'ts without a sync byte in 188 bytes is not detected');
assert.equal(detectContainerForBytes(notTs), '', 'ts missing 0x47 at 188 is not ts at all');
assert.equal(detectContainerForBytes(otherMp4Data), 'mp4', 'fmp4 detected as mp4');
assert.equal(detectContainerForBytes(new Uint8Array()), '', 'no type');
assert.equal(detectContainerForBytes(), '', 'no type');
assert.equal(detectContainerForBytes(h265seq), 'h265', 'h265 with only seq_parameter_set_rbsp, works');
assert.equal(detectContainerForBytes(h265shortnal), 'h265', 'h265 with short nals works');
assert.equal(detectContainerForBytes(h264shortnal), 'h264', 'h265 with short nals works');
});
const createBox = function(type) {
const size = 0x20;
return concatTypedArrays(
// size bytes
[0x00, 0x00, 0x00, size],
// box identfier styp
stringToBytes(type),
// filler data for size minus identfier and size bytes
filler(size - 8)
);
};
QUnit.module('isLikelyFmp4MediaSegment');
QUnit.test('works as expected', function(assert) {
const fmp4Data = concatTypedArrays(
createBox('styp'),
createBox('sidx'),
createBox('moof')
);
const mp4Data = concatTypedArrays(
createBox('ftyp'),
createBox('sidx'),
createBox('moov')
);
const fmp4Fake = concatTypedArrays(
createBox('test'),
createBox('moof'),
createBox('fooo'),
createBox('bar')
);
assert.ok(isLikelyFmp4MediaSegment(fmp4Data), 'fmp4 is recognized as fmp4');
assert.ok(isLikelyFmp4MediaSegment(fmp4Fake), 'fmp4 with moof and unknown boxes is still fmp4');
assert.ok(isLikelyFmp4MediaSegment(createBox('moof')), 'moof alone is recognized as fmp4');
assert.notOk(isLikelyFmp4MediaSegment(mp4Data), 'mp4 is not recognized');
assert.notOk(isLikelyFmp4MediaSegment(concatTypedArrays(id3DataWithFooter, testData.mp3)), 'bad data is not recognized');
assert.notOk(isLikelyFmp4MediaSegment(new Uint8Array()), 'no errors on empty data');
assert.notOk(isLikelyFmp4MediaSegment(), 'no errors on empty data');
});

View file

@ -0,0 +1,13 @@
import QUnit from 'qunit';
import decodeB64ToUint8Array from '../src/decode-b64-to-uint8-array.js';
QUnit.module('decodeB64ToUint8Array');
// slightly modified version of m3u8 test
// 'parses Widevine #EXT-X-KEY attributes and attaches to manifest'
QUnit.test('can decode', function(assert) {
const b64 = 'AAAAPnBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAAB4iFnNoYWthX2NlYzJmNjRhYTc4OTBhMTFI49yVmwY';
const result = decodeB64ToUint8Array(b64);
assert.deepEqual(result.byteLength, 62, 'decoded');
});

View file

@ -0,0 +1,57 @@
import QUnit from 'qunit';
import formatFiles from 'create-test-data!formats';
import parsingFiles from 'create-test-data!parsing';
import {parseData} from '../src/ebml-helpers.js';
import {doesCodecMatch, codecsFromFile} from './test-helpers.js';
const files = [];
// seperate files into modules by extension
Object.keys(formatFiles).forEach((file) => {
const extension = file.split('.').pop();
if (extension === 'webm' || extension === 'mkv') {
files.push(file);
}
});
QUnit.module('parseData');
files.forEach((file) => QUnit.test(`${file} can be parsed for tracks and blocks`, function(assert) {
const {blocks, tracks} = parseData(formatFiles[file]());
const codecs = codecsFromFile(file);
assert.equal(tracks.length, Object.keys(codecs).length, 'tracks as expected');
tracks.forEach(function(track) {
assert.ok(doesCodecMatch(track.codec, codecs[track.type]), `${track.codec} is ${codecs[track.type]}`);
});
assert.ok(blocks.length, `has ${blocks.length} blocks`);
assert.notOk(blocks.filter((b) => !b.frames.length).length, 'all blocks have frame data');
}));
QUnit.test('xiph and ebml lacing', function(assert) {
const {blocks} = parseData(parsingFiles['xiph-ebml-lacing.mkv']());
assert.ok(blocks.length, `has ${blocks.length} blocks`);
assert.notOk(blocks.filter((b) => !b.frames.length).length, 'all blocks have frame data');
assert.equal(blocks[1].lacing, 1, 'xiph lacing');
assert.equal(blocks[2].lacing, 3, 'ebml lacing');
});
QUnit.test('fixed lacing', function(assert) {
const {blocks} = parseData(parsingFiles['fixed-lacing.mkv']());
assert.ok(blocks.length, `has ${blocks.length} blocks`);
assert.notOk(blocks.filter((b) => !b.frames.length).length, 'all blocks have frame data');
assert.equal(blocks[12].lacing, 2, 'fixed lacing');
});
QUnit.test('live data', function(assert) {
const {blocks} = parseData(parsingFiles['live.mkv']());
assert.ok(blocks.length, 6, 'has 6 blocks, even with "infinite" cluster dataSize');
assert.notOk(blocks.filter((b) => !b.frames.length).length, 'all blocks have frame data');
});

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more