download
raw
8.34 kB
import { getMediaSource } from './mediasource-helper';
import { isHEVC } from './mp4-tools';
export const userAgentHevcSupportIsInaccurate = () => {
return /\(Windows.+Firefox\//i.test(navigator.userAgent);
};
// from http://mp4ra.org/codecs.html
// values indicate codec selection preference (lower is higher priority)
export const sampleEntryCodesISO = {
audio: {
a3ds: 1,
'ac-3': 0.95,
'ac-4': 1,
alac: 0.9,
alaw: 1,
dra1: 1,
'dts+': 1,
'dts-': 1,
dtsc: 1,
dtse: 1,
dtsh: 1,
'ec-3': 0.9,
enca: 1,
fLaC: 0.9, // MP4-RA listed codec entry for FLAC
flac: 0.9, // legacy browser codec name for FLAC
FLAC: 0.9, // some manifests may list "FLAC" with Apple's tools
g719: 1,
g726: 1,
m4ae: 1,
mha1: 1,
mha2: 1,
mhm1: 1,
mhm2: 1,
mlpa: 1,
mp4a: 1,
'raw ': 1,
Opus: 1,
opus: 1, // browsers expect this to be lowercase despite MP4RA says 'Opus'
samr: 1,
sawb: 1,
sawp: 1,
sevc: 1,
sqcp: 1,
ssmv: 1,
twos: 1,
ulaw: 1,
},
video: {
avc1: 1,
avc2: 1,
avc3: 1,
avc4: 1,
avcp: 1,
av01: 0.8,
dav1: 0.8,
drac: 1,
dva1: 1,
dvav: 1,
dvh1: 0.7,
dvhe: 0.7,
encv: 1,
hev1: 0.75,
hvc1: 0.75,
mjp2: 1,
mp4v: 1,
mvc1: 1,
mvc2: 1,
mvc3: 1,
mvc4: 1,
resv: 1,
rv60: 1,
s263: 1,
svc1: 1,
svc2: 1,
'vc-1': 1,
vp08: 1,
vp09: 0.9,
},
text: {
stpp: 1,
wvtt: 1,
},
} as const;
export type CodecType = 'audio' | 'video';
export function isCodecType(codec: string, type: CodecType): boolean {
const typeCodes = sampleEntryCodesISO[type];
return !!typeCodes && !!typeCodes[codec.slice(0, 4)];
}
export function areCodecsMediaSourceSupported(
codecs: string,
type: CodecType,
preferManagedMediaSource = true,
): boolean {
return !codecs
.split(',')
.some(
(codec) =>
!isCodecMediaSourceSupported(codec, type, preferManagedMediaSource),
);
}
function isCodecMediaSourceSupported(
codec: string,
type: CodecType,
preferManagedMediaSource = true,
): boolean {
const MediaSource = getMediaSource(preferManagedMediaSource);
return MediaSource?.isTypeSupported(mimeTypeForCodec(codec, type)) ?? false;
}
export function mimeTypeForCodec(codec: string, type: CodecType): string {
return `${type}/mp4;codecs=${codec}`;
}
export function videoCodecPreferenceValue(
videoCodec: string | undefined,
): number {
if (videoCodec) {
const fourCC = videoCodec.substring(0, 4);
return sampleEntryCodesISO.video[fourCC];
}
return 2;
}
export function codecsSetSelectionPreferenceValue(codecSet: string): number {
const limitedHevcSupport = userAgentHevcSupportIsInaccurate();
return codecSet.split(',').reduce((num, fourCC) => {
const lowerPriority = limitedHevcSupport && isHEVC(fourCC);
const preferenceValue = lowerPriority
? 9
: sampleEntryCodesISO.video[fourCC];
if (preferenceValue) {
return (preferenceValue * 2 + num) / (num ? 3 : 2);
}
return (sampleEntryCodesISO.audio[fourCC] + num) / (num ? 2 : 1);
}, 0);
}
interface CodecNameCache {
flac?: string;
opus?: string;
}
const CODEC_COMPATIBLE_NAMES: CodecNameCache = {};
type LowerCaseCodecType = 'flac' | 'opus';
function getCodecCompatibleNameLower(
lowerCaseCodec: LowerCaseCodecType,
preferManagedMediaSource = true,
): string {
if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]!;
}
const codecsToCheck = {
// Idealy fLaC and Opus would be first (spec-compliant) but
// some browsers will report that fLaC is supported then fail.
// see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728
flac: ['flac', 'fLaC', 'FLAC'],
opus: ['opus', 'Opus'],
// Replace audio codec info if browser does not support mp4a.40.34,
// and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"'
'mp4a.40.34': ['mp3'],
}[lowerCaseCodec];
for (let i = 0; i < codecsToCheck.length; i++) {
if (
isCodecMediaSourceSupported(
codecsToCheck[i],
'audio',
preferManagedMediaSource,
)
) {
CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i];
return codecsToCheck[i];
} else if (
codecsToCheck[i] === 'mp3' &&
getMediaSource(preferManagedMediaSource)?.isTypeSupported('audio/mpeg')
) {
return '';
}
}
return lowerCaseCodec;
}
const AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i;
export function getCodecCompatibleName(
codec: string,
preferManagedMediaSource = true,
): string {
return codec.replace(AUDIO_CODEC_REGEXP, (m) =>
getCodecCompatibleNameLower(
m.toLowerCase() as LowerCaseCodecType,
preferManagedMediaSource,
),
);
}
export function replaceVideoCodec(
originalCodecs: string | undefined,
newVideoCodec: string | undefined,
): string | undefined {
const codecs: string[] = [];
if (originalCodecs) {
const allCodecs = originalCodecs.split(',');
for (let i = 0; i < allCodecs.length; i++) {
if (!isCodecType(allCodecs[i], 'video')) {
codecs.push(allCodecs[i]);
}
}
}
if (newVideoCodec) {
codecs.push(newVideoCodec);
}
return codecs.join(',');
}
export function pickMostCompleteCodecName(
parsedCodec: string | undefined,
levelCodec: string | undefined,
): string | undefined {
// Parsing of mp4a codecs strings in mp4-tools from media is incomplete as of d8c6c7a
// so use level codec is parsed codec is unavailable or incomplete
if (
parsedCodec &&
(parsedCodec.length > 4 ||
['ac-3', 'ec-3', 'alac', 'fLaC', 'Opus'].indexOf(parsedCodec) !== -1)
) {
if (
isCodecSupportedAsType(parsedCodec, 'audio') ||
isCodecSupportedAsType(parsedCodec, 'video')
) {
return parsedCodec;
}
}
if (levelCodec) {
const levelCodecs = levelCodec.split(',');
if (levelCodecs.length > 1) {
if (parsedCodec) {
for (let i = levelCodecs.length; i--; ) {
if (levelCodecs[i].substring(0, 4) === parsedCodec.substring(0, 4)) {
return levelCodecs[i];
}
}
}
return levelCodecs[0];
}
}
return levelCodec || parsedCodec;
}
function isCodecSupportedAsType(codec: string, type: CodecType): boolean {
return isCodecType(codec, type) && isCodecMediaSourceSupported(codec, type);
}
export function convertAVC1ToAVCOTI(videoCodecs: string): string {
// Convert avc1 codec string from RFC-4281 to RFC-6381 for MediaSource.isTypeSupported
// Examples: avc1.66.30 to avc1.42001e and avc1.77.30,avc1.66.30 to avc1.4d001e,avc1.42001e.
const codecs = videoCodecs.split(',');
for (let i = 0; i < codecs.length; i++) {
const avcdata = codecs[i].split('.');
// only convert codec strings starting with avc1 (Examples: avc1.64001f,dvh1.05.07)
if (avcdata.length > 2 && avcdata[0] === 'avc1') {
codecs[i] = `avc1.${parseInt(avcdata[1]).toString(16)}${(
'000' + parseInt(avcdata[2]).toString(16)
).slice(-4)}`;
}
}
return codecs.join(',');
}
export function fillInMissingAV01Params(videoCodec: string): string {
// Used to fill in incomplete AV1 playlist CODECS strings for mediaCapabilities.decodingInfo queries
if (videoCodec.startsWith('av01.')) {
const av1params = videoCodec.split('.');
const placeholders = ['0', '111', '01', '01', '01', '0'];
for (let i = av1params.length; i > 4 && i < 10; i++) {
av1params[i] = placeholders[i - 4];
}
return av1params.join('.');
}
return videoCodec;
}
export interface TypeSupported {
mpeg: boolean;
mp3: boolean;
ac3: boolean;
}
export function getM2TSSupportedAudioTypes(
preferManagedMediaSource: boolean,
): TypeSupported {
const MediaSource = getMediaSource(preferManagedMediaSource) || {
isTypeSupported: () => false,
};
return {
mpeg: MediaSource.isTypeSupported('audio/mpeg'),
mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'),
ac3: __USE_M2TS_ADVANCED_CODECS__
? MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"')
: false,
};
}
export function getCodecsForMimeType(mimeType: string): string {
return mimeType.replace(/^.+codecs=["']?([^"']+).*$/, '$1');
}

Xet Storage Details

Size:
8.34 kB
·
Xet hash:
a5d19328fbdc9272975ed7a7091e2061cd18b185d1826ee90f9bed86afc54a7d

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.