Vulture/VApp/node_modules/@videojs/vhs-utils/test/codecs.test.js

516 lines
16 KiB
JavaScript
Raw Normal View History

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;
this.oldManagedMediaSource = window.ManagedMediaSource;
},
afterEach() {
window.MediaSource = this.oldMediaSource;
window.ManagedMediaSource = this.oldManagedMediaSource;
}
});
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.test('works as expected when ManagedMediaSource supported but checks not requested', function(assert) {
window.MediaSource = {isTypeSupported: () => false};
window.ManagedMediaSource = {isTypeSupported: () => true};
assert.notOk(browserSupportsCodec('test'), 'isTypeSupported false, browser does not support codec, ManangedMediaSource would support, but not requested');
window.MediaSource = null;
window.ManagedMediaSource = {isTypeSupported: () => true};
assert.notOk(browserSupportsCodec('test'), 'no MediaSource, browser does not support codec, ManangedMediaSource would support, but not requested');
window.MediaSource = {isTypeSupported: null};
window.ManagedMediaSource = {isTypeSupported: () => true};
assert.notOk(browserSupportsCodec('test'), 'no isTypeSupported, browser does not support codec, ManangedMediaSource would support, but not requested');
});
QUnit.test('works as expected with ManagedMediaSource checks', function(assert) {
window.MediaSource = {isTypeSupported: () => false};
window.ManagedMediaSource = {isTypeSupported: () => true};
assert.ok(browserSupportsCodec('test', true), 'isTypeSupported true, MediaSource does not support codec, but ManangedMediaSource does and was requested');
window.MediaSource = null;
window.ManagedMediaSource = {isTypeSupported: () => true};
assert.ok(browserSupportsCodec('test', true), 'isTypeSupported true, no MediaSource, but ManangedMediaSource supports codec and was requested');
window.MediaSource = {isTypeSupported: null};
window.ManagedMediaSource = {isTypeSupported: () => true};
assert.ok(browserSupportsCodec('test', true), 'isTypeSupported true, no isTypeSupported on MediaSource, but ManangedMediaSource supports and was requested');
window.MediaSource = null;
window.ManagedMediaSource = {isTypeSupported: () => false};
assert.notOk(browserSupportsCodec('test', true), 'isTypeSupported false, no MediaSource and ManagedMediaSource does not support codec');
window.MediaSource = null;
window.ManagedMediaSource = null;
assert.notOk(browserSupportsCodec('test', true), 'no MediaSource nor ManagedMediaSource');
window.MediaSource = null;
window.MediaSource = {isTypeSupported: null};
assert.notOk(browserSupportsCodec('test', true), 'no isTypeSupported on ManagaedMediaSource, 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');
});