mirror of
https://github.com/video-dev/hls.js.git
synced 2026-05-17 13:30:38 +00:00
chore(deps): update dependency prettier to v3 (#5646)
* chore(deps): update dependency prettier to v3 * run prettier --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Tom Jenkinson <tom@tjenkinson.me>
This commit is contained in:
+11
-11
@@ -10,7 +10,7 @@ const istanbul = require('rollup-plugin-istanbul');
|
||||
const fs = require('fs');
|
||||
|
||||
const pkgJson = JSON.parse(
|
||||
fs.readFileSync('./package.json', { encoding: 'utf-8' })
|
||||
fs.readFileSync('./package.json', { encoding: 'utf-8' }),
|
||||
);
|
||||
|
||||
const BUILD_TYPE = {
|
||||
@@ -53,24 +53,24 @@ const buildConstants = (type, additional = {}) => ({
|
||||
values: {
|
||||
__VERSION__: JSON.stringify(pkgJson.version),
|
||||
__USE_SUBTITLES__: JSON.stringify(
|
||||
type === BUILD_TYPE.full || addSubtitleSupport
|
||||
type === BUILD_TYPE.full || addSubtitleSupport,
|
||||
),
|
||||
__USE_ALT_AUDIO__: JSON.stringify(
|
||||
type === BUILD_TYPE.full || addAltAudioSupport
|
||||
type === BUILD_TYPE.full || addAltAudioSupport,
|
||||
),
|
||||
__USE_EME_DRM__: JSON.stringify(type === BUILD_TYPE.full || addEMESupport),
|
||||
__USE_CMCD__: JSON.stringify(type === BUILD_TYPE.full || addCMCDSupport),
|
||||
__USE_CONTENT_STEERING__: JSON.stringify(
|
||||
type === BUILD_TYPE.full || addContentSteeringSupport
|
||||
type === BUILD_TYPE.full || addContentSteeringSupport,
|
||||
),
|
||||
__USE_VARIABLE_SUBSTITUTION__: JSON.stringify(
|
||||
type === BUILD_TYPE.full || addVariableSubstitutionSupport
|
||||
type === BUILD_TYPE.full || addVariableSubstitutionSupport,
|
||||
),
|
||||
__USE_M2TS_ADVANCED_CODECS__: JSON.stringify(
|
||||
type === BUILD_TYPE.full || addM2TSAdvancedCodecSupport
|
||||
type === BUILD_TYPE.full || addM2TSAdvancedCodecSupport,
|
||||
),
|
||||
__USE_MEDIA_CAPABILITIES__: JSON.stringify(
|
||||
type === BUILD_TYPE.full || addMediaCapabilitiesSupport
|
||||
type === BUILD_TYPE.full || addMediaCapabilitiesSupport,
|
||||
),
|
||||
|
||||
...additional,
|
||||
@@ -132,7 +132,7 @@ const babelTsWithPresetEnvTargets = ({ targets, stripConsole }) =>
|
||||
espath.node.callee = importHelper.addNamed(
|
||||
espath,
|
||||
'isFiniteNumber',
|
||||
path.resolve('src/polyfills/number')
|
||||
path.resolve('src/polyfills/number'),
|
||||
);
|
||||
} else if (
|
||||
espath.get('callee').matchesPattern('Number.MAX_SAFE_INTEGER')
|
||||
@@ -140,7 +140,7 @@ const babelTsWithPresetEnvTargets = ({ targets, stripConsole }) =>
|
||||
espath.node.callee = importHelper.addNamed(
|
||||
espath,
|
||||
'MAX_SAFE_INTEGER',
|
||||
path.resolve('src/polyfills/number')
|
||||
path.resolve('src/polyfills/number'),
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -344,7 +344,7 @@ const configs = Object.entries({
|
||||
replace(
|
||||
buildConstants(BUILD_TYPE.full, {
|
||||
__IN_WORKER__: JSON.stringify(true),
|
||||
})
|
||||
}),
|
||||
),
|
||||
buildBabelLegacyBrowsers({ stripConsole: true }),
|
||||
terser(),
|
||||
@@ -377,7 +377,7 @@ const configs = Object.entries({
|
||||
branch: env.CF_PAGES_BRANCH,
|
||||
commitRef: env.CF_PAGES_COMMIT_SHA,
|
||||
}
|
||||
: null
|
||||
: null,
|
||||
),
|
||||
},
|
||||
}),
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
|
||||
<head>
|
||||
<title>hls.js metrics page</title>
|
||||
|
||||
+1
-1
@@ -235,7 +235,7 @@ You need to provide manifest URL as below:
|
||||
});
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
|
||||
console.log(
|
||||
'manifest loaded, found ' + data.levels.length + ' quality level'
|
||||
'manifest loaded, found ' + data.levels.length + ' quality level',
|
||||
);
|
||||
});
|
||||
hls.loadSource('http://my.streamURL.com/playlist.m3u8');
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
const micromatch = require('micromatch');
|
||||
const prettier = require('prettier');
|
||||
|
||||
const prettierSupportedExtensions = prettier
|
||||
.getSupportInfo()
|
||||
.languages.map(({ extensions }) => extensions)
|
||||
.flat();
|
||||
const addQuotes = (a) => `"${a}"`;
|
||||
|
||||
module.exports = (allStagedFiles) => {
|
||||
module.exports = async (allStagedFiles) => {
|
||||
const prettierSupportedExtensions = (
|
||||
await prettier.getSupportInfo()
|
||||
).languages
|
||||
.map(({ extensions }) => extensions)
|
||||
.flat();
|
||||
|
||||
const eslintFiles = micromatch(allStagedFiles, '**/*.{js,ts}');
|
||||
const prettierFiles = micromatch(
|
||||
allStagedFiles,
|
||||
prettierSupportedExtensions.map((extension) => `**/*${extension}`)
|
||||
prettierSupportedExtensions.map((extension) => `**/*${extension}`),
|
||||
);
|
||||
|
||||
return [
|
||||
|
||||
Generated
+9
-9
@@ -62,7 +62,7 @@
|
||||
"mocha": "10.2.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"npm-run-all": "4.1.5",
|
||||
"prettier": "2.8.8",
|
||||
"prettier": "3.0.1",
|
||||
"promise-polyfill": "8.3.0",
|
||||
"rollup": "3.28.0",
|
||||
"rollup-plugin-istanbul": "4.0.0",
|
||||
@@ -10275,15 +10275,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
|
||||
"integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"prettier": "bin-prettier.js"
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/prettier/prettier?sponsor=1"
|
||||
@@ -20680,9 +20680,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz",
|
||||
"integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==",
|
||||
"dev": true
|
||||
},
|
||||
"printable-characters": {
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@
|
||||
"mocha": "10.2.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"npm-run-all": "4.1.5",
|
||||
"prettier": "2.8.8",
|
||||
"prettier": "3.0.1",
|
||||
"promise-polyfill": "8.3.0",
|
||||
"rollup": "3.28.0",
|
||||
"rollup-plugin-istanbul": "4.0.0",
|
||||
|
||||
+4
-4
@@ -13,20 +13,20 @@ module.exports = ({ configType = [] }) => {
|
||||
} else {
|
||||
// Filter out enabled configs
|
||||
const enabledEntries = configs.filter(([name]) =>
|
||||
requestedConfigs.includes(name)
|
||||
requestedConfigs.includes(name),
|
||||
);
|
||||
if (!enabledEntries.length) {
|
||||
throw new Error(
|
||||
`Couldn't find a valid config with the names ${JSON.stringify(
|
||||
requestedConfigs
|
||||
)}. Known configs are: ${configs.map(([name]) => name).join(', ')}`
|
||||
requestedConfigs,
|
||||
)}. Known configs are: ${configs.map(([name]) => name).join(', ')}`,
|
||||
);
|
||||
}
|
||||
configEntries = enabledEntries;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Building configs: ${configEntries.map(([name]) => name).join(', ')}.\n`
|
||||
`Building configs: ${configEntries.map(([name]) => name).join(', ')}.\n`,
|
||||
);
|
||||
return configEntries.map(([, config]) => config);
|
||||
};
|
||||
|
||||
@@ -24,8 +24,8 @@ async function versionPublished() {
|
||||
//https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md
|
||||
const response = await fetch(
|
||||
`https://registry.npmjs.org/${encodeURIComponent(
|
||||
packageJson.name
|
||||
)}/${encodeURIComponent(packageJson.version)}`
|
||||
packageJson.name,
|
||||
)}/${encodeURIComponent(packageJson.version)}`,
|
||||
);
|
||||
if (response.status === 200) {
|
||||
return true;
|
||||
|
||||
@@ -23,7 +23,7 @@ try {
|
||||
// 1.2.3-0.caaanary.custom => bad
|
||||
// 1.2.3-0.caaanary.custom.0.canary.503 => now lower than 1.2.3-0.canary.501
|
||||
throw new Error(
|
||||
`It's possible that "${newVersion}" has a lower precedense than an existing canary version which is not allowed.`
|
||||
`It's possible that "${newVersion}" has a lower precedense than an existing canary version which is not allowed.`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -44,7 +44,7 @@ try {
|
||||
const suffix = process.env.CF_PAGES
|
||||
? `pr.${process.env.CF_PAGES_BRANCH.replace(
|
||||
/[^a-zA-Z-]/g,
|
||||
'-'
|
||||
'-',
|
||||
)}.${getCommitHash().slice(0, 8)}`
|
||||
: `0.canary.${getCommitNum()}`;
|
||||
|
||||
@@ -53,18 +53,18 @@ try {
|
||||
|
||||
if (!versionParser.isGreaterOrEqual(newVersion, latestVersion)) {
|
||||
throw new Error(
|
||||
`New version "${newVersion}" is not >= latest version "${latestVersion}" on this branch.`
|
||||
`New version "${newVersion}" is not >= latest version "${latestVersion}" on this branch.`,
|
||||
);
|
||||
}
|
||||
|
||||
const foundPreviousVersion = versionParser
|
||||
.getPotentialPreviousStableVersions(`v${newVersion}`)
|
||||
.every((potentialPreviousVersion) =>
|
||||
hasTag(`v${potentialPreviousVersion}`)
|
||||
hasTag(`v${potentialPreviousVersion}`),
|
||||
);
|
||||
if (!foundPreviousVersion) {
|
||||
throw new Error(
|
||||
'Could not find a previous version. The tag must follow a previous stable version number.'
|
||||
'Could not find a previous version. The tag must follow a previous stable version number.',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -84,7 +84,7 @@ export type DRMSystemConfiguration = {
|
||||
this: Hls,
|
||||
initDataType: string,
|
||||
initData: ArrayBuffer | null,
|
||||
keyContext: MediaKeySessionContext
|
||||
keyContext: MediaKeySessionContext,
|
||||
) =>
|
||||
| { initDataType: string; initData: ArrayBuffer | null }
|
||||
| undefined
|
||||
@@ -101,13 +101,13 @@ export type EMEControllerConfig = {
|
||||
xhr: XMLHttpRequest,
|
||||
url: string,
|
||||
keyContext: MediaKeySessionContext,
|
||||
licenseChallenge: Uint8Array
|
||||
licenseChallenge: Uint8Array,
|
||||
) => void | Uint8Array | Promise<Uint8Array | void>;
|
||||
licenseResponseCallback?: (
|
||||
this: Hls,
|
||||
xhr: XMLHttpRequest,
|
||||
url: string,
|
||||
keyContext: MediaKeySessionContext
|
||||
keyContext: MediaKeySessionContext,
|
||||
) => ArrayBuffer;
|
||||
emeEnabled: boolean;
|
||||
widevineLicenseUrl?: string;
|
||||
@@ -532,7 +532,7 @@ function timelineConfig(): TimelineControllerConfig {
|
||||
*/
|
||||
export function mergeConfig(
|
||||
defaultConfig: HlsConfig,
|
||||
userConfig: Partial<HlsConfig>
|
||||
userConfig: Partial<HlsConfig>,
|
||||
): HlsConfig {
|
||||
if (
|
||||
(userConfig.liveSyncDurationCount ||
|
||||
@@ -540,7 +540,7 @@ export function mergeConfig(
|
||||
(userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)
|
||||
) {
|
||||
throw new Error(
|
||||
"Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration"
|
||||
"Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -551,7 +551,7 @@ export function mergeConfig(
|
||||
userConfig.liveSyncDurationCount)
|
||||
) {
|
||||
throw new Error(
|
||||
'Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"'
|
||||
'Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -561,7 +561,7 @@ export function mergeConfig(
|
||||
userConfig.liveMaxLatencyDuration <= userConfig.liveSyncDuration)
|
||||
) {
|
||||
throw new Error(
|
||||
'Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"'
|
||||
'Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -609,10 +609,10 @@ export function mergeConfig(
|
||||
if (report.length) {
|
||||
logger.warn(
|
||||
`hls.js config: "${report.join(
|
||||
'", "'
|
||||
'", "',
|
||||
)}" setting(s) are deprecated, use "${policyName}": ${JSON.stringify(
|
||||
userConfig[policyName]
|
||||
)}`
|
||||
userConfig[policyName],
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -644,7 +644,7 @@ export function enableStreamingMode(config) {
|
||||
if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
|
||||
// If a developer has configured their own loader, respect that choice
|
||||
logger.log(
|
||||
'[config]: Custom loader detected, cannot enable progressive streaming'
|
||||
'[config]: Custom loader detected, cannot enable progressive streaming',
|
||||
);
|
||||
config.progressive = false;
|
||||
} else {
|
||||
|
||||
@@ -65,7 +65,7 @@ class AbrController implements AbrComponentAPI {
|
||||
return new EwmaBandWidthEstimator(
|
||||
config.abrEwmaSlowVoD,
|
||||
config.abrEwmaFastVoD,
|
||||
config.abrEwmaDefaultEstimate
|
||||
config.abrEwmaDefaultEstimate,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ class AbrController implements AbrComponentAPI {
|
||||
|
||||
protected onManifestLoading(
|
||||
event: Events.MANIFEST_LOADING,
|
||||
data: ManifestLoadingData
|
||||
data: ManifestLoadingData,
|
||||
) {
|
||||
this.lastLoadedFragLevel = -1;
|
||||
this.lastLevelLoadSec = 0;
|
||||
@@ -140,7 +140,7 @@ class AbrController implements AbrComponentAPI {
|
||||
|
||||
protected onLevelSwitching(
|
||||
event: Events.LEVEL_SWITCHING,
|
||||
data: LevelSwitchingData
|
||||
data: LevelSwitchingData,
|
||||
): void {
|
||||
this.clearTimer();
|
||||
}
|
||||
@@ -161,7 +161,7 @@ class AbrController implements AbrComponentAPI {
|
||||
timeToFirstByteSec: number,
|
||||
bandwidth: number,
|
||||
fragSizeBits: number,
|
||||
isSwitch: boolean
|
||||
isSwitch: boolean,
|
||||
): number {
|
||||
const fragLoadSec = timeToFirstByteSec + fragSizeBits / bandwidth;
|
||||
const playlistLoadSec = isSwitch ? this.lastLevelLoadSec : 0;
|
||||
@@ -284,7 +284,7 @@ class AbrController implements AbrComponentAPI {
|
||||
ttfbEstimate / 1000,
|
||||
bwe,
|
||||
duration * levelNextBitrate,
|
||||
!levels[nextLoadLevel].details
|
||||
!levels[nextLoadLevel].details,
|
||||
);
|
||||
if (fragLevelNextLoadedDelay < bufferStarvationDelay) {
|
||||
break;
|
||||
@@ -305,7 +305,7 @@ class AbrController implements AbrComponentAPI {
|
||||
// If there has been loading progress, sample bandwidth using loading time offset by minimum TTFB time
|
||||
this.bwEstimator.sample(
|
||||
timeLoading - Math.min(ttfbEstimate, ttfb),
|
||||
stats.loaded
|
||||
stats.loaded,
|
||||
);
|
||||
} else {
|
||||
// If there has been no loading progress, sample TTFB
|
||||
@@ -319,7 +319,7 @@ class AbrController implements AbrComponentAPI {
|
||||
Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s
|
||||
Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s
|
||||
Estimated load time for down switch fragment: ${fragLevelNextLoadedDelay.toFixed(
|
||||
3
|
||||
3,
|
||||
)} s
|
||||
TTFB estimate: ${ttfb}
|
||||
Current BW estimate: ${
|
||||
@@ -336,7 +336,7 @@ class AbrController implements AbrComponentAPI {
|
||||
|
||||
protected onFragLoaded(
|
||||
event: Events.FRAG_LOADED,
|
||||
{ frag, part }: FragLoadedData
|
||||
{ frag, part }: FragLoadedData,
|
||||
) {
|
||||
const stats = part ? part.stats : frag.stats;
|
||||
if (frag.type === PlaylistLevelType.MAIN) {
|
||||
@@ -378,7 +378,7 @@ class AbrController implements AbrComponentAPI {
|
||||
|
||||
protected onFragBuffered(
|
||||
event: Events.FRAG_BUFFERED,
|
||||
data: FragBufferedData
|
||||
data: FragBufferedData,
|
||||
) {
|
||||
const { frag, part } = data;
|
||||
const stats = part?.stats.loaded ? part.stats : frag.stats;
|
||||
@@ -397,7 +397,7 @@ class AbrController implements AbrComponentAPI {
|
||||
stats.loading.start -
|
||||
Math.min(
|
||||
stats.loading.first - stats.loading.start,
|
||||
this.bwEstimator.getEstimateTTFB()
|
||||
this.bwEstimator.getEstimateTTFB(),
|
||||
);
|
||||
this.bwEstimator.sample(processingMs, stats.loaded);
|
||||
stats.bwEstimate = this.getBwEstimate();
|
||||
@@ -430,7 +430,7 @@ class AbrController implements AbrComponentAPI {
|
||||
0,
|
||||
maxStartDelay,
|
||||
1,
|
||||
1
|
||||
1,
|
||||
);
|
||||
if (abrAutoLevel > -1) {
|
||||
return abrAutoLevel;
|
||||
@@ -438,7 +438,7 @@ class AbrController implements AbrComponentAPI {
|
||||
const firstLevel = this.hls.firstLevel;
|
||||
const clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
|
||||
logger.warn(
|
||||
`[abr] Could not find best starting auto level. Defaulting to first in playlist ${firstLevel} clamped to ${clamped}`
|
||||
`[abr] Could not find best starting auto level. Defaulting to first in playlist ${firstLevel} clamped to ${clamped}`,
|
||||
);
|
||||
return clamped;
|
||||
}
|
||||
@@ -529,7 +529,7 @@ class AbrController implements AbrComponentAPI {
|
||||
bufferStarvationDelay,
|
||||
0,
|
||||
bwFactor,
|
||||
bwUpFactor
|
||||
bwUpFactor,
|
||||
);
|
||||
if (bestLevel >= 0) {
|
||||
return bestLevel;
|
||||
@@ -555,10 +555,10 @@ class AbrController implements AbrComponentAPI {
|
||||
maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
|
||||
logger.info(
|
||||
`[abr] bitrate test took ${Math.round(
|
||||
1000 * bitrateTestDelay
|
||||
1000 * bitrateTestDelay,
|
||||
)}ms, set first fragment max fetchDuration to ${Math.round(
|
||||
1000 * maxStarvationDelay
|
||||
)} ms`
|
||||
1000 * maxStarvationDelay,
|
||||
)} ms`,
|
||||
);
|
||||
// don't use conservative factor on bitrate test
|
||||
bwFactor = bwUpFactor = 1;
|
||||
@@ -571,12 +571,12 @@ class AbrController implements AbrComponentAPI {
|
||||
bufferStarvationDelay,
|
||||
maxStarvationDelay,
|
||||
bwFactor,
|
||||
bwUpFactor
|
||||
bwUpFactor,
|
||||
);
|
||||
logger.info(
|
||||
`[abr] ${
|
||||
bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'
|
||||
}, optimal quality level ${bestLevel}`
|
||||
}, optimal quality level ${bestLevel}`,
|
||||
);
|
||||
if (bestLevel > -1) {
|
||||
return bestLevel;
|
||||
@@ -604,7 +604,7 @@ class AbrController implements AbrComponentAPI {
|
||||
bufferStarvationDelay: number,
|
||||
maxStarvationDelay: number,
|
||||
bwFactor: number,
|
||||
bwUpFactor: number
|
||||
bwUpFactor: number,
|
||||
): number {
|
||||
const maxFetchDuration: number = bufferStarvationDelay + maxStarvationDelay;
|
||||
const lastLoadedFragLevel = this.lastLoadedFragLevel;
|
||||
@@ -628,7 +628,7 @@ class AbrController implements AbrComponentAPI {
|
||||
levels,
|
||||
audioTracksByGroup,
|
||||
minAutoLevel,
|
||||
maxAutoLevel
|
||||
maxAutoLevel,
|
||||
));
|
||||
const { codecSet, videoRange, minFramerate, minBitrate } =
|
||||
getStartCodecTier(codecTiers, currentVideoRange, currentBw);
|
||||
@@ -669,13 +669,13 @@ class AbrController implements AbrComponentAPI {
|
||||
mediaCapabilities,
|
||||
currentVideoRange,
|
||||
currentFrameRate,
|
||||
currentBw
|
||||
currentBw,
|
||||
)
|
||||
) {
|
||||
levelInfo.supportedPromise = getMediaDecodingInfoPromise(
|
||||
levelInfo,
|
||||
audioTracksByGroup,
|
||||
mediaCapabilities
|
||||
mediaCapabilities,
|
||||
);
|
||||
levelInfo.supportedPromise.then((decodingInfo) => {
|
||||
levelInfo.supportedResult = decodingInfo;
|
||||
@@ -683,13 +683,13 @@ class AbrController implements AbrComponentAPI {
|
||||
logger.warn(
|
||||
`[abr] MediaCapabilities decodingInfo error: "${
|
||||
decodingInfo.error
|
||||
}" for level ${i} ${JSON.stringify(decodingInfo)}`
|
||||
}" for level ${i} ${JSON.stringify(decodingInfo)}`,
|
||||
);
|
||||
} else if (!decodingInfo.supported) {
|
||||
logger.warn(
|
||||
`[abr] Removing unsupported level ${i} after MediaCapabilities decodingInfo check failed ${JSON.stringify(
|
||||
decodingInfo
|
||||
)}`
|
||||
decodingInfo,
|
||||
)}`,
|
||||
);
|
||||
if (i > 0) {
|
||||
this.hls.removeLevel(i);
|
||||
@@ -746,7 +746,7 @@ class AbrController implements AbrComponentAPI {
|
||||
ttfbEstimateSec,
|
||||
adjustedbw,
|
||||
bitrate * avgDuration,
|
||||
levelDetails === undefined
|
||||
levelDetails === undefined,
|
||||
);
|
||||
|
||||
const canSwitchWithinTolerance =
|
||||
@@ -767,28 +767,28 @@ class AbrController implements AbrComponentAPI {
|
||||
if (levelsSkipped.length) {
|
||||
logger.trace(
|
||||
`[abr] Skipped level(s) ${levelsSkipped.join(
|
||||
','
|
||||
',',
|
||||
)} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${
|
||||
levels[levelsSkipped[0]].codecs
|
||||
}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${
|
||||
level.codecs
|
||||
}" ${currentVideoRange}`
|
||||
}" ${currentVideoRange}`,
|
||||
);
|
||||
}
|
||||
logger.info(
|
||||
`[abr] switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(
|
||||
adjustedbw
|
||||
adjustedbw,
|
||||
)})-bitrate=${Math.round(
|
||||
adjustedbw - bitrate
|
||||
adjustedbw - bitrate,
|
||||
)} ttfb:${ttfbEstimateSec.toFixed(
|
||||
1
|
||||
1,
|
||||
)} avgDuration:${avgDuration.toFixed(
|
||||
1
|
||||
1,
|
||||
)} maxFetchDuration:${maxFetchDuration.toFixed(
|
||||
1
|
||||
1,
|
||||
)} fetchDuration:${fetchDuration.toFixed(
|
||||
1
|
||||
)} firstSelection:${firstSelection} codecSet:${currentCodecSet} videoRange:${currentVideoRange} hls.loadLevel:${loadLevel}`
|
||||
1,
|
||||
)} firstSelection:${firstSelection} codecSet:${currentCodecSet} videoRange:${currentVideoRange} hls.loadLevel:${loadLevel}`,
|
||||
);
|
||||
}
|
||||
// as we are looping from highest to lowest, this will return the best achievable quality level
|
||||
|
||||
@@ -62,14 +62,14 @@ class AudioStreamController
|
||||
constructor(
|
||||
hls: Hls,
|
||||
fragmentTracker: FragmentTracker,
|
||||
keyLoader: KeyLoader
|
||||
keyLoader: KeyLoader,
|
||||
) {
|
||||
super(
|
||||
hls,
|
||||
fragmentTracker,
|
||||
keyLoader,
|
||||
'[audio-stream-controller]',
|
||||
PlaylistLevelType.AUDIO
|
||||
PlaylistLevelType.AUDIO,
|
||||
);
|
||||
this._registerListeners();
|
||||
}
|
||||
@@ -118,7 +118,7 @@ class AudioStreamController
|
||||
// INIT_PTS_FOUND is triggered when the video track parsed in the stream-controller has a new PTS value
|
||||
onInitPtsFound(
|
||||
event: Events.INIT_PTS_FOUND,
|
||||
{ frag, id, initPTS, timescale }: InitPTSFoundData
|
||||
{ frag, id, initPTS, timescale }: InitPTSFoundData,
|
||||
) {
|
||||
// Always update the new INIT PTS
|
||||
// Can change due level switch
|
||||
@@ -146,8 +146,8 @@ class AudioStreamController
|
||||
if (lastCurrentTime > 0 && startPosition === -1) {
|
||||
this.log(
|
||||
`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(
|
||||
3
|
||||
)}`
|
||||
3,
|
||||
)}`,
|
||||
);
|
||||
startPosition = lastCurrentTime;
|
||||
this.state = State.IDLE;
|
||||
@@ -213,7 +213,7 @@ class AudioStreamController
|
||||
} else if (this.videoTrackCC !== this.waitingVideoCC) {
|
||||
// Drop waiting fragment if videoTrackCC has changed since waitingFragment was set and initPTS was not found
|
||||
this.log(
|
||||
`Waiting fragment cc (${frag.cc}) cancelled because video is at cc ${this.videoTrackCC}`
|
||||
`Waiting fragment cc (${frag.cc}) cancelled because video is at cc ${this.videoTrackCC}`,
|
||||
);
|
||||
this.clearWaitingFragment();
|
||||
} else {
|
||||
@@ -222,16 +222,16 @@ class AudioStreamController
|
||||
const bufferInfo = BufferHelper.bufferInfo(
|
||||
this.mediaBuffer,
|
||||
pos,
|
||||
this.config.maxBufferHole
|
||||
this.config.maxBufferHole,
|
||||
);
|
||||
const waitingFragmentAtPosition = fragmentWithinToleranceTest(
|
||||
bufferInfo.end,
|
||||
this.config.maxFragLookUpTolerance,
|
||||
frag
|
||||
frag,
|
||||
);
|
||||
if (waitingFragmentAtPosition < 0) {
|
||||
this.log(
|
||||
`Waiting fragment cc (${frag.cc}) @ ${frag.start} cancelled because another fragment at ${bufferInfo.end} is needed`
|
||||
`Waiting fragment cc (${frag.cc}) @ ${frag.start} cancelled because another fragment at ${bufferInfo.end} is needed`,
|
||||
);
|
||||
this.clearWaitingFragment();
|
||||
}
|
||||
@@ -304,13 +304,13 @@ class AudioStreamController
|
||||
this.afterBufferFlushed(
|
||||
bufferable,
|
||||
ElementaryStreamTypes.AUDIO,
|
||||
PlaylistLevelType.AUDIO
|
||||
PlaylistLevelType.AUDIO,
|
||||
);
|
||||
}
|
||||
|
||||
const bufferInfo = this.getFwdBufferInfo(
|
||||
bufferable,
|
||||
PlaylistLevelType.AUDIO
|
||||
PlaylistLevelType.AUDIO,
|
||||
);
|
||||
if (bufferInfo === null) {
|
||||
return;
|
||||
@@ -325,7 +325,7 @@ class AudioStreamController
|
||||
|
||||
const mainBufferInfo = this.getFwdBufferInfo(
|
||||
this.videoBuffer ? this.videoBuffer : this.media,
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
const bufferLen = bufferInfo.len;
|
||||
const maxBufLen = this.getMaxBufferLength(mainBufferInfo?.len);
|
||||
@@ -344,7 +344,7 @@ class AudioStreamController
|
||||
// if everything is buffered from pos to start or if audio buffer upfront, let's seek to start
|
||||
if (bufferInfo.end > start || bufferInfo.nextStart) {
|
||||
this.log(
|
||||
'Alt audio track ahead of main track, seek to start of alt audio track'
|
||||
'Alt audio track ahead of main track, seek to start of alt audio track',
|
||||
);
|
||||
media.currentTime = start + 0.05;
|
||||
}
|
||||
@@ -370,7 +370,7 @@ class AudioStreamController
|
||||
trackDetails,
|
||||
bufferInfo,
|
||||
PlaylistLevelType.MAIN,
|
||||
maxBufLen
|
||||
maxBufLen,
|
||||
);
|
||||
}
|
||||
if (!frag) {
|
||||
@@ -413,7 +413,7 @@ class AudioStreamController
|
||||
}
|
||||
return Math.min(
|
||||
Math.max(maxConfigBuffer, mainBufferLength),
|
||||
this.config.maxMaxBufferLength
|
||||
this.config.maxMaxBufferLength,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -424,7 +424,7 @@ class AudioStreamController
|
||||
|
||||
onAudioTracksUpdated(
|
||||
event: Events.AUDIO_TRACKS_UPDATED,
|
||||
{ audioTracks }: AudioTracksUpdatedData
|
||||
{ audioTracks }: AudioTracksUpdatedData,
|
||||
) {
|
||||
this.resetTransmuxer();
|
||||
this.levels = audioTracks.map((mediaPlaylist) => new Level(mediaPlaylist));
|
||||
@@ -432,7 +432,7 @@ class AudioStreamController
|
||||
|
||||
onAudioTrackSwitching(
|
||||
event: Events.AUDIO_TRACK_SWITCHING,
|
||||
data: AudioTrackSwitchingData
|
||||
data: AudioTrackSwitchingData,
|
||||
) {
|
||||
// if any URL found on new audio track, it is an alternate audio track
|
||||
const altAudio = !!data.url;
|
||||
@@ -505,7 +505,7 @@ class AudioStreamController
|
||||
newDetails.lastPartSn
|
||||
? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]`
|
||||
: ''
|
||||
},duration:${newDetails.totalduration}`
|
||||
},duration:${newDetails.totalduration}`,
|
||||
);
|
||||
|
||||
const track = levels[trackId];
|
||||
@@ -555,7 +555,7 @@ class AudioStreamController
|
||||
const { config, trackId, levels } = this;
|
||||
if (!levels) {
|
||||
this.warn(
|
||||
`Audio tracks were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`
|
||||
`Audio tracks were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -580,7 +580,7 @@ class AudioStreamController
|
||||
this.hls,
|
||||
PlaylistLevelType.AUDIO,
|
||||
this._handleTransmuxComplete.bind(this),
|
||||
this._handleTransmuxerFlush.bind(this)
|
||||
this._handleTransmuxerFlush.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -600,7 +600,7 @@ class AudioStreamController
|
||||
frag.stats.chunkCount,
|
||||
payload.byteLength,
|
||||
partIndex,
|
||||
partial
|
||||
partial,
|
||||
);
|
||||
transmuxer.push(
|
||||
payload,
|
||||
@@ -612,11 +612,11 @@ class AudioStreamController
|
||||
details.totalduration,
|
||||
accurateTimeOffset,
|
||||
chunkMeta,
|
||||
initPTS
|
||||
initPTS,
|
||||
);
|
||||
} else {
|
||||
this.log(
|
||||
`Unknown video PTS for cc ${frag.cc}, waiting for video PTS before demuxing audio frag ${frag.sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`
|
||||
`Unknown video PTS for cc ${frag.cc}, waiting for video PTS before demuxing audio frag ${frag.sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`,
|
||||
);
|
||||
const { cache } = (this.waitingData = this.waitingData || {
|
||||
frag,
|
||||
@@ -678,7 +678,7 @@ class AudioStreamController
|
||||
this.state
|
||||
}, audioSwitch: ${
|
||||
this.switchingTrack ? this.switchingTrack.name : 'false'
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -745,7 +745,7 @@ class AudioStreamController
|
||||
|
||||
private onBufferFlushed(
|
||||
event: Events.BUFFER_FLUSHED,
|
||||
{ type }: BufferFlushedData
|
||||
{ type }: BufferFlushedData,
|
||||
) {
|
||||
if (type === ElementaryStreamTypes.AUDIO) {
|
||||
this.bufferFlushed = true;
|
||||
@@ -792,7 +792,7 @@ class AudioStreamController
|
||||
level,
|
||||
initSegment.tracks,
|
||||
mapFragment,
|
||||
chunkMeta
|
||||
chunkMeta,
|
||||
);
|
||||
hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, {
|
||||
frag: mapFragment,
|
||||
@@ -816,7 +816,7 @@ class AudioStreamController
|
||||
startPTS,
|
||||
endPTS,
|
||||
startDTS,
|
||||
endDTS
|
||||
endDTS,
|
||||
);
|
||||
this.bufferFragmentData(audio, frag, part, chunkMeta);
|
||||
}
|
||||
@@ -828,7 +828,7 @@ class AudioStreamController
|
||||
frag,
|
||||
details,
|
||||
},
|
||||
id3
|
||||
id3,
|
||||
);
|
||||
hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3);
|
||||
}
|
||||
@@ -839,7 +839,7 @@ class AudioStreamController
|
||||
frag,
|
||||
details,
|
||||
},
|
||||
text
|
||||
text,
|
||||
);
|
||||
hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText);
|
||||
}
|
||||
@@ -849,7 +849,7 @@ class AudioStreamController
|
||||
currentLevel: Level,
|
||||
tracks: TrackSet,
|
||||
frag: Fragment,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
) {
|
||||
if (this.state !== State.PARSING) {
|
||||
return;
|
||||
@@ -869,7 +869,7 @@ class AudioStreamController
|
||||
|
||||
const variantAudioCodecs = currentLevel.audioCodec;
|
||||
this.log(
|
||||
`Init audio buffer, container:${track.container}, codecs[level/parsed]=[${variantAudioCodecs}/${track.codec}]`
|
||||
`Init audio buffer, container:${track.container}, codecs[level/parsed]=[${variantAudioCodecs}/${track.codec}]`,
|
||||
);
|
||||
// SourceBuffer will use track.levelCodec if defined
|
||||
if (variantAudioCodecs && variantAudioCodecs.split(',').length === 1) {
|
||||
@@ -895,7 +895,7 @@ class AudioStreamController
|
||||
protected loadFragment(
|
||||
frag: Fragment,
|
||||
track: Level,
|
||||
targetBufferTime: number
|
||||
targetBufferTime: number,
|
||||
) {
|
||||
// only load if fragment is not loaded or if in audio switch
|
||||
const fragState = this.fragmentTracker.getState(frag);
|
||||
@@ -911,7 +911,7 @@ class AudioStreamController
|
||||
this._loadInitSegment(frag, track);
|
||||
} else if (track.details?.live && !this.initPTS[frag.cc]) {
|
||||
this.log(
|
||||
`Waiting for video PTS in continuity counter ${frag.cc} of live stream before loading audio fragment ${frag.sn} of level ${this.trackId}`
|
||||
`Waiting for video PTS in continuity counter ${frag.cc} of live stream before loading audio fragment ${frag.sn} of level ${this.trackId}`,
|
||||
);
|
||||
this.state = State.WAITING_INIT_PTS;
|
||||
const mainDetails = this.mainDetails;
|
||||
|
||||
@@ -66,21 +66,21 @@ class AudioTrackController extends BasePlaylistController {
|
||||
|
||||
protected onManifestParsed(
|
||||
event: Events.MANIFEST_PARSED,
|
||||
data: ManifestParsedData
|
||||
data: ManifestParsedData,
|
||||
): void {
|
||||
this.tracks = data.audioTracks || [];
|
||||
}
|
||||
|
||||
protected onAudioTrackLoaded(
|
||||
event: Events.AUDIO_TRACK_LOADED,
|
||||
data: AudioTrackLoadedData
|
||||
data: AudioTrackLoadedData,
|
||||
): void {
|
||||
const { id, groupId, details } = data;
|
||||
const trackInActiveGroup = this.tracksInGroup[id];
|
||||
|
||||
if (!trackInActiveGroup || trackInActiveGroup.groupId !== groupId) {
|
||||
this.warn(
|
||||
`Track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup.groupId}`
|
||||
`Track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup.groupId}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class AudioTrackController extends BasePlaylistController {
|
||||
const curDetails = trackInActiveGroup.details;
|
||||
trackInActiveGroup.details = data.details;
|
||||
this.log(
|
||||
`audio-track ${id} "${trackInActiveGroup.name}" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`
|
||||
`audio-track ${id} "${trackInActiveGroup.name}" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`,
|
||||
);
|
||||
|
||||
if (id === this.trackId) {
|
||||
@@ -98,14 +98,14 @@ class AudioTrackController extends BasePlaylistController {
|
||||
|
||||
protected onLevelLoading(
|
||||
event: Events.LEVEL_LOADING,
|
||||
data: LevelLoadingData
|
||||
data: LevelLoadingData,
|
||||
): void {
|
||||
this.switchLevel(data.level);
|
||||
}
|
||||
|
||||
protected onLevelSwitching(
|
||||
event: Events.LEVEL_SWITCHING,
|
||||
data: LevelSwitchingData
|
||||
data: LevelSwitchingData,
|
||||
): void {
|
||||
this.switchLevel(data.level);
|
||||
}
|
||||
@@ -122,7 +122,7 @@ class AudioTrackController extends BasePlaylistController {
|
||||
this.groupId = audioGroupId || null;
|
||||
|
||||
const audioTracks = this.tracks.filter(
|
||||
(track): boolean => !audioGroupId || track.groupId === audioGroupId
|
||||
(track): boolean => !audioGroupId || track.groupId === audioGroupId,
|
||||
);
|
||||
|
||||
// Disable selectDefaultTrack if there are no default tracks
|
||||
@@ -136,7 +136,7 @@ class AudioTrackController extends BasePlaylistController {
|
||||
this.tracksInGroup = audioTracks;
|
||||
const audioTracksUpdated: AudioTracksUpdatedData = { audioTracks };
|
||||
this.log(
|
||||
`Updating audio tracks, ${audioTracks.length} track(s) found in group:${audioGroupId}`
|
||||
`Updating audio tracks, ${audioTracks.length} track(s) found in group:${audioGroupId}`,
|
||||
);
|
||||
this.hls.trigger(Events.AUDIO_TRACKS_UPDATED, audioTracksUpdated);
|
||||
|
||||
@@ -197,7 +197,7 @@ class AudioTrackController extends BasePlaylistController {
|
||||
const track = tracks[newId];
|
||||
const { groupId, name } = track;
|
||||
this.log(
|
||||
`Switching to audio-track ${newId} "${name}" lang:${track.lang} group:${groupId}`
|
||||
`Switching to audio-track ${newId} "${name}" lang:${track.lang} group:${groupId}`,
|
||||
);
|
||||
this.trackId = newId;
|
||||
this.currentTrack = track;
|
||||
@@ -222,7 +222,7 @@ class AudioTrackController extends BasePlaylistController {
|
||||
this.setAudioTrack(trackId);
|
||||
} else {
|
||||
const error = new Error(
|
||||
`No track found for running audio group-ID: ${this.groupId} track count: ${audioTracks.length}`
|
||||
`No track found for running audio group-ID: ${this.groupId} track count: ${audioTracks.length}`,
|
||||
);
|
||||
this.warn(error.message);
|
||||
|
||||
@@ -271,13 +271,13 @@ class AudioTrackController extends BasePlaylistController {
|
||||
url = hlsUrlParameters.addDirectives(url);
|
||||
} catch (error) {
|
||||
this.warn(
|
||||
`Could not construct new URL with HLS Delivery Directives: ${error}`
|
||||
`Could not construct new URL with HLS Delivery Directives: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
// track not retrieved yet, or live playlist we need to (re)load it
|
||||
this.log(
|
||||
`loading audio-track playlist ${id} "${audioTrack.name}" lang:${audioTrack.lang} group:${groupId}`
|
||||
`loading audio-track playlist ${id} "${audioTrack.name}" lang:${audioTrack.lang} group:${groupId}`,
|
||||
);
|
||||
this.clearTimer();
|
||||
this.hls.trigger(Events.AUDIO_TRACK_LOADING, {
|
||||
|
||||
@@ -54,7 +54,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
|
||||
protected switchParams(
|
||||
playlistUri: string,
|
||||
previous: LevelDetails | undefined
|
||||
previous: LevelDetails | undefined,
|
||||
): HlsUrlParameters | undefined {
|
||||
const renditionReports = previous?.renditionReports;
|
||||
if (renditionReports) {
|
||||
@@ -66,7 +66,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
uri = new self.URL(attr.URI, previous.url).href;
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`Could not construct new URL for Rendition Report: ${error}`
|
||||
`Could not construct new URL for Rendition Report: ${error}`,
|
||||
);
|
||||
uri = attr.URI || '';
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
if (this.hls.config.lowLatencyMode) {
|
||||
const currentGoal = Math.min(
|
||||
previous.age - previous.partTarget,
|
||||
previous.targetduration
|
||||
previous.targetduration,
|
||||
);
|
||||
if (part >= 0 && currentGoal > previous.partTarget) {
|
||||
part += 1;
|
||||
@@ -95,7 +95,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
return new HlsUrlParameters(
|
||||
msn,
|
||||
part >= 0 ? part : undefined,
|
||||
HlsSkip.No
|
||||
HlsSkip.No,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
}
|
||||
|
||||
protected shouldLoadPlaylist(
|
||||
playlist: Level | MediaPlaylist | null | undefined
|
||||
playlist: Level | MediaPlaylist | null | undefined,
|
||||
): boolean {
|
||||
return (
|
||||
this.canLoad &&
|
||||
@@ -120,7 +120,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
}
|
||||
|
||||
protected shouldReloadPlaylist(
|
||||
playlist: Level | MediaPlaylist | null | undefined
|
||||
playlist: Level | MediaPlaylist | null | undefined,
|
||||
): boolean {
|
||||
return (
|
||||
this.timer === -1 &&
|
||||
@@ -132,7 +132,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
protected playlistLoaded(
|
||||
index: number,
|
||||
data: LevelLoadedData | AudioTrackLoadedData | TrackLoadedData,
|
||||
previousDetails?: LevelDetails
|
||||
previousDetails?: LevelDetails,
|
||||
) {
|
||||
const { details, stats } = data;
|
||||
|
||||
@@ -152,7 +152,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
details.advanced
|
||||
? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex
|
||||
: 'MISSED'
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
}
|
||||
// Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments
|
||||
@@ -187,14 +187,14 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
const cdnAge = lastAdvanced + details.ageHeader;
|
||||
let currentGoal = Math.min(
|
||||
cdnAge - details.partTarget,
|
||||
details.targetduration * 1.5
|
||||
details.targetduration * 1.5,
|
||||
);
|
||||
if (currentGoal > 0) {
|
||||
if (previousDetails && currentGoal > previousDetails.tuneInGoal) {
|
||||
// If we attempted to get the next or latest playlist update, but currentGoal increased,
|
||||
// then we either can't catchup, or the "age" header cannot be trusted.
|
||||
this.warn(
|
||||
`CDN Tune-in goal increased from: ${previousDetails.tuneInGoal} to: ${currentGoal} with playlist age: ${details.age}`
|
||||
`CDN Tune-in goal increased from: ${previousDetails.tuneInGoal} to: ${currentGoal} with playlist age: ${details.age}`,
|
||||
);
|
||||
currentGoal = 0;
|
||||
} else {
|
||||
@@ -202,7 +202,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
msn += segments;
|
||||
if (part !== undefined) {
|
||||
const parts = Math.round(
|
||||
(currentGoal % details.targetduration) / details.partTarget
|
||||
(currentGoal % details.targetduration) / details.partTarget,
|
||||
);
|
||||
part += parts;
|
||||
}
|
||||
@@ -210,8 +210,8 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
`CDN Tune-in age: ${
|
||||
details.ageHeader
|
||||
}s last advanced ${lastAdvanced.toFixed(
|
||||
2
|
||||
)}s goal: ${currentGoal} skip sn ${segments} to part ${part}`
|
||||
2,
|
||||
)}s goal: ${currentGoal} skip sn ${segments} to part ${part}`,
|
||||
);
|
||||
}
|
||||
details.tuneInGoal = currentGoal;
|
||||
@@ -220,7 +220,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
details,
|
||||
data.deliveryDirectives,
|
||||
msn,
|
||||
part
|
||||
part,
|
||||
);
|
||||
if (lowLatencyMode || !lastPart) {
|
||||
this.loadPlaylist(deliveryDirectives);
|
||||
@@ -231,7 +231,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
details,
|
||||
data.deliveryDirectives,
|
||||
msn,
|
||||
part
|
||||
part,
|
||||
);
|
||||
}
|
||||
const bufferInfo = this.hls.mainForwardBufferInfo;
|
||||
@@ -239,7 +239,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
const distanceToLiveEdgeMs = (details.edge - position) * 1000;
|
||||
const reloadInterval = computeReloadInterval(
|
||||
details,
|
||||
distanceToLiveEdgeMs
|
||||
distanceToLiveEdgeMs,
|
||||
);
|
||||
if (details.updated && now > this.requestScheduled + reloadInterval) {
|
||||
this.requestScheduled = stats.loading.start;
|
||||
@@ -262,8 +262,8 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
estimatedTimeUntilUpdate = Math.max(0, estimatedTimeUntilUpdate);
|
||||
this.log(
|
||||
`reload live playlist ${index} in ${Math.round(
|
||||
estimatedTimeUntilUpdate
|
||||
)} ms`
|
||||
estimatedTimeUntilUpdate,
|
||||
)} ms`,
|
||||
);
|
||||
// this.log(
|
||||
// `live reload ${details.updated ? 'REFRESHED' : 'MISSED'}
|
||||
@@ -283,7 +283,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
|
||||
this.timer = self.setTimeout(
|
||||
() => this.loadPlaylist(deliveryDirectives),
|
||||
estimatedTimeUntilUpdate
|
||||
estimatedTimeUntilUpdate,
|
||||
);
|
||||
} else {
|
||||
this.clearTimer();
|
||||
@@ -294,7 +294,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
details: LevelDetails,
|
||||
previousDeliveryDirectives: HlsUrlParameters | null,
|
||||
msn?: number,
|
||||
part?: number
|
||||
part?: number,
|
||||
): HlsUrlParameters {
|
||||
let skip = getSkipValue(details, msn);
|
||||
if (previousDeliveryDirectives?.skip && details.deltaUpdateFailed) {
|
||||
@@ -326,7 +326,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
this.warn(
|
||||
`Retrying playlist loading ${retryCount + 1}/${
|
||||
retryConfig.maxNumRetry
|
||||
} after "${errorDetails}" without delivery-directives`
|
||||
} after "${errorDetails}" without delivery-directives`,
|
||||
);
|
||||
this.loadPlaylist();
|
||||
} else {
|
||||
@@ -336,7 +336,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
||||
this.warn(
|
||||
`Retrying playlist loading ${retryCount + 1}/${
|
||||
retryConfig.maxNumRetry
|
||||
} after "${errorDetails}" in ${delay}ms`
|
||||
} after "${errorDetails}" in ${delay}ms`,
|
||||
);
|
||||
}
|
||||
// `levelRetry = true` used to inform other controllers that a retry is happening
|
||||
|
||||
@@ -109,7 +109,7 @@ export default class BaseStreamController
|
||||
fragmentTracker: FragmentTracker,
|
||||
keyLoader: KeyLoader,
|
||||
logPrefix: string,
|
||||
playlistType: PlaylistLevelType
|
||||
playlistType: PlaylistLevelType,
|
||||
) {
|
||||
super();
|
||||
this.playlistType = playlistType;
|
||||
@@ -152,7 +152,7 @@ export default class BaseStreamController
|
||||
|
||||
protected _streamEnded(
|
||||
bufferInfo: BufferInfo,
|
||||
levelDetails: LevelDetails
|
||||
levelDetails: LevelDetails,
|
||||
): boolean {
|
||||
// If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
|
||||
// of nothing loading/loaded return false
|
||||
@@ -176,7 +176,7 @@ export default class BaseStreamController
|
||||
// part mismatches for independent audio and video playlists/segments.
|
||||
const lastPartBuffered = BufferHelper.isBuffered(
|
||||
this.media,
|
||||
lastPart.start + lastPart.duration / 2
|
||||
lastPart.start + lastPart.duration / 2,
|
||||
);
|
||||
return lastPartBuffered;
|
||||
}
|
||||
@@ -194,7 +194,7 @@ export default class BaseStreamController
|
||||
|
||||
protected onMediaAttached(
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachedData
|
||||
data: MediaAttachedData,
|
||||
) {
|
||||
const media = (this.media = this.mediaBuffer = data.media);
|
||||
this.onvseeking = this.onMediaSeeking.bind(this) as EventListener;
|
||||
@@ -235,13 +235,13 @@ export default class BaseStreamController
|
||||
const bufferInfo = BufferHelper.bufferInfo(
|
||||
mediaBuffer ? mediaBuffer : media,
|
||||
currentTime,
|
||||
config.maxBufferHole
|
||||
config.maxBufferHole,
|
||||
);
|
||||
|
||||
this.log(
|
||||
`media seeking to ${
|
||||
Number.isFinite(currentTime) ? currentTime.toFixed(3) : currentTime
|
||||
}, state: ${state}`
|
||||
}, state: ${state}`,
|
||||
);
|
||||
|
||||
if (this.state === State.ENDED) {
|
||||
@@ -263,7 +263,7 @@ export default class BaseStreamController
|
||||
if (currentTime < fragStartOffset || pastFragment) {
|
||||
if (pastFragment && fragCurrent.loader) {
|
||||
this.log(
|
||||
'seeking outside of buffer while fragment load in progress, cancel fragment load'
|
||||
'seeking outside of buffer while fragment load in progress, cancel fragment load',
|
||||
);
|
||||
fragCurrent.abortRequests();
|
||||
this.resetLoadingState();
|
||||
@@ -279,7 +279,7 @@ export default class BaseStreamController
|
||||
currentTime,
|
||||
Infinity,
|
||||
this.playlistType,
|
||||
true
|
||||
true,
|
||||
);
|
||||
|
||||
this.lastCurrentTime = currentTime;
|
||||
@@ -301,7 +301,7 @@ export default class BaseStreamController
|
||||
|
||||
protected onManifestLoaded(
|
||||
event: Events.MANIFEST_LOADED,
|
||||
data: ManifestLoadedData
|
||||
data: ManifestLoadedData,
|
||||
): void {
|
||||
this.startTimeOffset = data.startTimeOffset;
|
||||
this.initPTS = [];
|
||||
@@ -338,7 +338,7 @@ export default class BaseStreamController
|
||||
protected loadFragment(
|
||||
frag: Fragment,
|
||||
level: Level,
|
||||
targetBufferTime: number
|
||||
targetBufferTime: number,
|
||||
) {
|
||||
this._loadFragForPlayback(frag, level, targetBufferTime);
|
||||
}
|
||||
@@ -346,16 +346,16 @@ export default class BaseStreamController
|
||||
private _loadFragForPlayback(
|
||||
frag: Fragment,
|
||||
level: Level,
|
||||
targetBufferTime: number
|
||||
targetBufferTime: number,
|
||||
) {
|
||||
const progressCallback: FragmentLoadProgressCallback = (
|
||||
data: FragLoadedData
|
||||
data: FragLoadedData,
|
||||
) => {
|
||||
if (this.fragContextChanged(frag)) {
|
||||
this.warn(
|
||||
`Fragment ${frag.sn}${
|
||||
data.part ? ' p: ' + data.part.index : ''
|
||||
} of level ${frag.level} was dropped during download.`
|
||||
} of level ${frag.level} was dropped during download.`,
|
||||
);
|
||||
this.fragmentTracker.removeFragment(frag);
|
||||
return;
|
||||
@@ -407,11 +407,11 @@ export default class BaseStreamController
|
||||
const playlistType = frag.type as PlaylistLevelType;
|
||||
const bufferedInfo = this.getFwdBufferInfo(
|
||||
this.mediaBuffer,
|
||||
playlistType
|
||||
playlistType,
|
||||
);
|
||||
const minForwardBufferLength = Math.max(
|
||||
frag.duration,
|
||||
bufferedInfo ? bufferedInfo.len : this.config.maxBufferLength
|
||||
bufferedInfo ? bufferedInfo.len : this.config.maxBufferLength,
|
||||
);
|
||||
if (this.reduceMaxBufferLength(minForwardBufferLength)) {
|
||||
fragmentTracker.removeFragment(frag);
|
||||
@@ -436,7 +436,7 @@ export default class BaseStreamController
|
||||
protected flushMainBuffer(
|
||||
startOffset: number,
|
||||
endOffset: number,
|
||||
type: SourceBufferName | null = null
|
||||
type: SourceBufferName | null = null,
|
||||
) {
|
||||
if (!(startOffset - endOffset)) {
|
||||
return;
|
||||
@@ -476,7 +476,7 @@ export default class BaseStreamController
|
||||
.decrypt(
|
||||
new Uint8Array(payload),
|
||||
decryptData.key.buffer,
|
||||
decryptData.iv.buffer
|
||||
decryptData.iv.buffer,
|
||||
)
|
||||
.catch((err) => {
|
||||
hls.trigger(Events.ERROR, {
|
||||
@@ -551,7 +551,7 @@ export default class BaseStreamController
|
||||
media
|
||||
? TimeRanges.toString(BufferHelper.getBuffered(media))
|
||||
: '(detached)'
|
||||
})`
|
||||
})`,
|
||||
);
|
||||
if (frag.sn !== 'initSegment') {
|
||||
if (frag.type !== PlaylistLevelType.SUBTITLE) {
|
||||
@@ -565,7 +565,7 @@ export default class BaseStreamController
|
||||
const level = this.levels?.[frag.level];
|
||||
if (level?.fragmentError) {
|
||||
this.log(
|
||||
`Resetting level fragment error count of ${level.fragmentError} on frag buffered`
|
||||
`Resetting level fragment error count of ${level.fragmentError} on frag buffered`,
|
||||
);
|
||||
level.fragmentError = 0;
|
||||
}
|
||||
@@ -605,26 +605,26 @@ export default class BaseStreamController
|
||||
frag.stats.chunkCount + 1,
|
||||
0,
|
||||
part ? part.index : -1,
|
||||
!complete
|
||||
!complete,
|
||||
);
|
||||
transmuxer.flush(chunkMeta);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
protected _handleFragmentLoadProgress(
|
||||
frag: PartsLoadedData | FragLoadedData
|
||||
frag: PartsLoadedData | FragLoadedData,
|
||||
) {}
|
||||
|
||||
protected _doFragLoad(
|
||||
frag: Fragment,
|
||||
level: Level,
|
||||
targetBufferTime: number | null = null,
|
||||
progressCallback?: FragmentLoadProgressCallback
|
||||
progressCallback?: FragmentLoadProgressCallback,
|
||||
): Promise<PartsLoadedData | FragLoadedData | null> {
|
||||
const details = level?.details;
|
||||
if (!this.levels || !details) {
|
||||
throw new Error(
|
||||
`frag load aborted, missing level${details ? '' : ' detail'}s`
|
||||
`frag load aborted, missing level${details ? '' : ' detail'}s`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -633,7 +633,7 @@ export default class BaseStreamController
|
||||
this.log(
|
||||
`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${
|
||||
this.logPrefix === '[stream-controller]' ? 'level' : 'track'
|
||||
} ${frag.level}`
|
||||
} ${frag.level}`,
|
||||
);
|
||||
this.state = State.KEY_LOADING;
|
||||
this.fragCurrent = frag;
|
||||
@@ -649,7 +649,7 @@ export default class BaseStreamController
|
||||
this.hls.trigger(Events.KEY_LOADING, { frag });
|
||||
if (this.fragCurrent === null) {
|
||||
keyLoadingPromise = Promise.reject(
|
||||
new Error(`frag load aborted, context changed in KEY_LOADING`)
|
||||
new Error(`frag load aborted, context changed in KEY_LOADING`),
|
||||
);
|
||||
}
|
||||
} else if (!frag.encrypted && details.encryptedFragments.length) {
|
||||
@@ -674,8 +674,8 @@ export default class BaseStreamController
|
||||
}] parts [0-${partIndex}-${partList.length - 1}] ${
|
||||
this.logPrefix === '[stream-controller]' ? 'level' : 'track'
|
||||
}: ${frag.level}, target: ${parseFloat(
|
||||
targetBufferTime.toFixed(3)
|
||||
)}`
|
||||
targetBufferTime.toFixed(3),
|
||||
)}`,
|
||||
);
|
||||
this.nextLoadPosition = part.start + part.duration;
|
||||
this.state = State.FRAG_LOADING;
|
||||
@@ -693,7 +693,7 @@ export default class BaseStreamController
|
||||
frag,
|
||||
part,
|
||||
level,
|
||||
progressCallback
|
||||
progressCallback,
|
||||
);
|
||||
})
|
||||
.catch((error) => this.handleFragLoadError(error));
|
||||
@@ -702,7 +702,7 @@ export default class BaseStreamController
|
||||
frag,
|
||||
part,
|
||||
level,
|
||||
progressCallback
|
||||
progressCallback,
|
||||
).catch((error: LoadError) => this.handleFragLoadError(error));
|
||||
}
|
||||
this.hls.trigger(Events.FRAG_LOADING, {
|
||||
@@ -713,8 +713,8 @@ export default class BaseStreamController
|
||||
if (this.fragCurrent === null) {
|
||||
return Promise.reject(
|
||||
new Error(
|
||||
`frag load aborted, context changed in FRAG_LOADING parts`
|
||||
)
|
||||
`frag load aborted, context changed in FRAG_LOADING parts`,
|
||||
),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
@@ -733,7 +733,7 @@ export default class BaseStreamController
|
||||
details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''
|
||||
}${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${
|
||||
frag.level
|
||||
}, target: ${parseFloat(targetBufferTime.toFixed(3))}`
|
||||
}, target: ${parseFloat(targetBufferTime.toFixed(3))}`,
|
||||
);
|
||||
// Don't update nextLoadPosition for fragments which are not buffered
|
||||
if (Number.isFinite(frag.sn as number) && !this.bitrateTest) {
|
||||
@@ -759,7 +759,7 @@ export default class BaseStreamController
|
||||
result = Promise.all([
|
||||
this.fragmentLoader.load(
|
||||
frag,
|
||||
dataOnProgress ? progressCallback : undefined
|
||||
dataOnProgress ? progressCallback : undefined,
|
||||
),
|
||||
keyLoadingPromise,
|
||||
])
|
||||
@@ -774,7 +774,7 @@ export default class BaseStreamController
|
||||
this.hls.trigger(Events.FRAG_LOADING, { frag, targetBufferTime });
|
||||
if (this.fragCurrent === null) {
|
||||
return Promise.reject(
|
||||
new Error(`frag load aborted, context changed in FRAG_LOADING`)
|
||||
new Error(`frag load aborted, context changed in FRAG_LOADING`),
|
||||
);
|
||||
}
|
||||
return result;
|
||||
@@ -784,7 +784,7 @@ export default class BaseStreamController
|
||||
frag: Fragment,
|
||||
fromPart: Part,
|
||||
level: Level,
|
||||
progressCallback: FragmentLoadProgressCallback
|
||||
progressCallback: FragmentLoadProgressCallback,
|
||||
): Promise<PartsLoadedData | null> {
|
||||
return new Promise(
|
||||
(resolve: ResolveFragLoaded, reject: RejectFragLoaded) => {
|
||||
@@ -813,7 +813,7 @@ export default class BaseStreamController
|
||||
.catch(reject);
|
||||
};
|
||||
loadPart(fromPart);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -859,13 +859,13 @@ export default class BaseStreamController
|
||||
}
|
||||
|
||||
protected getCurrentContext(
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
): { frag: Fragment; part: Part | null; level: Level } | null {
|
||||
const { levels, fragCurrent } = this;
|
||||
const { level: levelIndex, sn, part: partIndex } = chunkMeta;
|
||||
if (!levels?.[levelIndex]) {
|
||||
this.warn(
|
||||
`Levels object was unset while buffering fragment ${sn} of level ${levelIndex}. The current chunk will not be buffered.`
|
||||
`Levels object was unset while buffering fragment ${sn} of level ${levelIndex}. The current chunk will not be buffered.`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -888,7 +888,7 @@ export default class BaseStreamController
|
||||
frag: Fragment,
|
||||
part: Part | null,
|
||||
chunkMeta: ChunkMetadata,
|
||||
noBacktracking?: boolean
|
||||
noBacktracking?: boolean,
|
||||
) {
|
||||
if (!data || this.state !== State.PARSING) {
|
||||
return;
|
||||
@@ -940,11 +940,11 @@ export default class BaseStreamController
|
||||
const fragDuration = frag.duration;
|
||||
const segmentFraction = Math.min(
|
||||
this.config.maxFragLookUpTolerance * 2,
|
||||
fragDuration * 0.25
|
||||
fragDuration * 0.25,
|
||||
);
|
||||
const start = Math.max(
|
||||
Math.min(frag.start - segmentFraction, bufferInfo.end - segmentFraction),
|
||||
currentTime + segmentFraction
|
||||
currentTime + segmentFraction,
|
||||
);
|
||||
if (frag.start - start > segmentFraction) {
|
||||
this.flushMainBuffer(start, frag.start);
|
||||
@@ -953,7 +953,7 @@ export default class BaseStreamController
|
||||
|
||||
protected getFwdBufferInfo(
|
||||
bufferable: Bufferable | null,
|
||||
type: PlaylistLevelType
|
||||
type: PlaylistLevelType,
|
||||
): BufferInfo | null {
|
||||
const pos = this.getLoadPosition();
|
||||
if (!Number.isFinite(pos)) {
|
||||
@@ -965,7 +965,7 @@ export default class BaseStreamController
|
||||
protected getFwdBufferInfoAtPos(
|
||||
bufferable: Bufferable | null,
|
||||
pos: number,
|
||||
type: PlaylistLevelType
|
||||
type: PlaylistLevelType,
|
||||
): BufferInfo | null {
|
||||
const {
|
||||
config: { maxBufferHole },
|
||||
@@ -978,7 +978,7 @@ export default class BaseStreamController
|
||||
return BufferHelper.bufferInfo(
|
||||
bufferable,
|
||||
pos,
|
||||
Math.max(bufferInfo.nextStart, maxBufferHole)
|
||||
Math.max(bufferInfo.nextStart, maxBufferHole),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -991,7 +991,7 @@ export default class BaseStreamController
|
||||
if (levelBitrate) {
|
||||
maxBufLen = Math.max(
|
||||
(8 * config.maxBufferSize) / levelBitrate,
|
||||
config.maxBufferLength
|
||||
config.maxBufferLength,
|
||||
);
|
||||
} else {
|
||||
maxBufLen = config.maxBufferLength;
|
||||
@@ -1013,11 +1013,11 @@ export default class BaseStreamController
|
||||
|
||||
protected getAppendedFrag(
|
||||
position: number,
|
||||
playlistType: PlaylistLevelType = PlaylistLevelType.MAIN
|
||||
playlistType: PlaylistLevelType = PlaylistLevelType.MAIN,
|
||||
): Fragment | null {
|
||||
const fragOrPart = this.fragmentTracker.getAppendedFrag(
|
||||
position,
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
if (fragOrPart && 'fragment' in fragOrPart) {
|
||||
return fragOrPart.fragment;
|
||||
@@ -1027,7 +1027,7 @@ export default class BaseStreamController
|
||||
|
||||
protected getNextFragment(
|
||||
pos: number,
|
||||
levelDetails: LevelDetails
|
||||
levelDetails: LevelDetails,
|
||||
): Fragment | null {
|
||||
const fragments = levelDetails.fragments;
|
||||
const fragLen = fragments.length;
|
||||
@@ -1045,7 +1045,7 @@ export default class BaseStreamController
|
||||
const initialLiveManifestSize = config.initialLiveManifestSize;
|
||||
if (fragLen < initialLiveManifestSize) {
|
||||
this.warn(
|
||||
`Not enough fragments to start playback (have: ${fragLen}, need: ${initialLiveManifestSize})`
|
||||
`Not enough fragments to start playback (have: ${fragLen}, need: ${initialLiveManifestSize})`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -1094,12 +1094,12 @@ export default class BaseStreamController
|
||||
levelDetails: LevelDetails,
|
||||
bufferInfo: BufferInfo,
|
||||
playlistType: PlaylistLevelType,
|
||||
maxBufLen: number
|
||||
maxBufLen: number,
|
||||
): Fragment | null {
|
||||
const gapStart = frag.gap;
|
||||
const nextFragment = this.getNextFragment(
|
||||
this.nextLoadPosition,
|
||||
levelDetails
|
||||
levelDetails,
|
||||
);
|
||||
if (nextFragment === null) {
|
||||
return nextFragment;
|
||||
@@ -1110,7 +1110,7 @@ export default class BaseStreamController
|
||||
const nextbufferInfo = this.getFwdBufferInfoAtPos(
|
||||
this.mediaBuffer ? this.mediaBuffer : this.media,
|
||||
bufferInfo.nextStart,
|
||||
playlistType
|
||||
playlistType,
|
||||
);
|
||||
if (
|
||||
nextbufferInfo !== null &&
|
||||
@@ -1118,7 +1118,7 @@ export default class BaseStreamController
|
||||
) {
|
||||
// Returning here might result in not finding an audio and video candiate to skip to
|
||||
this.log(
|
||||
`buffer full after gaps in "${playlistType}" playlist starting at sn: ${frag.sn}`
|
||||
`buffer full after gaps in "${playlistType}" playlist starting at sn: ${frag.sn}`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -1138,7 +1138,7 @@ export default class BaseStreamController
|
||||
getNextPart(
|
||||
partList: Part[],
|
||||
frag: Fragment,
|
||||
targetBufferTime: number
|
||||
targetBufferTime: number,
|
||||
): number {
|
||||
let nextPart = -1;
|
||||
let contiguous = false;
|
||||
@@ -1165,7 +1165,7 @@ export default class BaseStreamController
|
||||
|
||||
private loadedEndOfParts(
|
||||
partList: Part[],
|
||||
targetBufferTime: number
|
||||
targetBufferTime: number,
|
||||
): boolean {
|
||||
const lastPart = partList[partList.length - 1];
|
||||
return lastPart && targetBufferTime > lastPart.start && lastPart.loaded;
|
||||
@@ -1178,7 +1178,7 @@ export default class BaseStreamController
|
||||
*/
|
||||
protected getInitialLiveFragment(
|
||||
levelDetails: LevelDetails,
|
||||
fragments: Array<Fragment>
|
||||
fragments: Array<Fragment>,
|
||||
): Fragment | null {
|
||||
const fragPrevious = this.fragPrevious;
|
||||
let frag: Fragment | null = null;
|
||||
@@ -1186,12 +1186,12 @@ export default class BaseStreamController
|
||||
if (levelDetails.hasProgramDateTime) {
|
||||
// Prefer using PDT, because it can be accurate enough to choose the correct fragment without knowing the level sliding
|
||||
this.log(
|
||||
`Live playlist, switching playlist, load frag with same PDT: ${fragPrevious.programDateTime}`
|
||||
`Live playlist, switching playlist, load frag with same PDT: ${fragPrevious.programDateTime}`,
|
||||
);
|
||||
frag = findFragmentByPDT(
|
||||
fragments,
|
||||
fragPrevious.endProgramDateTime,
|
||||
this.config.maxFragLookUpTolerance
|
||||
this.config.maxFragLookUpTolerance,
|
||||
);
|
||||
}
|
||||
if (!frag) {
|
||||
@@ -1208,7 +1208,7 @@ export default class BaseStreamController
|
||||
this.log(
|
||||
`Live playlist, switching playlist, load frag with next SN: ${
|
||||
frag!.sn
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1218,7 +1218,7 @@ export default class BaseStreamController
|
||||
frag = findFragWithCC(fragments, fragPrevious.cc);
|
||||
if (frag) {
|
||||
this.log(
|
||||
`Live playlist, switching playlist, load frag with same CC: ${frag.sn}`
|
||||
`Live playlist, switching playlist, load frag with same CC: ${frag.sn}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1230,7 +1230,7 @@ export default class BaseStreamController
|
||||
frag = this.getFragmentAtPosition(
|
||||
liveStart,
|
||||
this.bitrateTest ? levelDetails.fragmentEnd : levelDetails.edge,
|
||||
levelDetails
|
||||
levelDetails,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1244,7 +1244,7 @@ export default class BaseStreamController
|
||||
protected getFragmentAtPosition(
|
||||
bufferEnd: number,
|
||||
end: number,
|
||||
levelDetails: LevelDetails
|
||||
levelDetails: LevelDetails,
|
||||
): Fragment | null {
|
||||
const { config } = this;
|
||||
let { fragPrevious } = this;
|
||||
@@ -1273,7 +1273,7 @@ export default class BaseStreamController
|
||||
fragPrevious,
|
||||
fragments,
|
||||
bufferEnd,
|
||||
lookupTolerance
|
||||
lookupTolerance,
|
||||
);
|
||||
} else {
|
||||
// reach end of playlist
|
||||
@@ -1349,10 +1349,10 @@ export default class BaseStreamController
|
||||
if (media.readyState) {
|
||||
this.warn(
|
||||
`Playback: ${currentTime.toFixed(
|
||||
3
|
||||
3,
|
||||
)} is located too far from the end of live sliding playlist: ${end}, reset currentTime to : ${liveSyncPosition.toFixed(
|
||||
3
|
||||
)}`
|
||||
3,
|
||||
)}`,
|
||||
);
|
||||
media.currentTime = liveSyncPosition;
|
||||
}
|
||||
@@ -1362,7 +1362,7 @@ export default class BaseStreamController
|
||||
|
||||
protected alignPlaylists(
|
||||
details: LevelDetails,
|
||||
previousDetails?: LevelDetails
|
||||
previousDetails?: LevelDetails,
|
||||
): number {
|
||||
const { levels, levelLastLoaded, fragPrevious } = this;
|
||||
const lastLevel: Level | null =
|
||||
@@ -1386,7 +1386,7 @@ export default class BaseStreamController
|
||||
previousDetails ? previousDetails.startSN : 'na'
|
||||
}->${details.startSN} prev-sn: ${
|
||||
fragPrevious ? fragPrevious.sn : 'na'
|
||||
} fragments: ${length}`
|
||||
} fragments: ${length}`,
|
||||
);
|
||||
return alignedSlidingStart;
|
||||
}
|
||||
@@ -1425,12 +1425,12 @@ export default class BaseStreamController
|
||||
}
|
||||
startPosition = Math.min(
|
||||
Math.max(sliding, startPosition),
|
||||
sliding + details.totalduration
|
||||
sliding + details.totalduration,
|
||||
);
|
||||
this.log(
|
||||
`Start time offset ${startTimeOffset} found in ${
|
||||
offsetInMultivariantPlaylist ? 'multivariant' : 'media'
|
||||
} playlist, adjust startPosition to ${startPosition}`
|
||||
} playlist, adjust startPosition to ${startPosition}`,
|
||||
);
|
||||
this.startPosition = startPosition;
|
||||
} else if (details.live) {
|
||||
@@ -1463,7 +1463,7 @@ export default class BaseStreamController
|
||||
this.warn(
|
||||
`Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${
|
||||
frag.level
|
||||
} was aborted`
|
||||
} was aborted`,
|
||||
);
|
||||
this.resetFragmentLoading(frag);
|
||||
}
|
||||
@@ -1481,7 +1481,7 @@ export default class BaseStreamController
|
||||
|
||||
protected onFragmentOrKeyLoadError(
|
||||
filterType: PlaylistLevelType,
|
||||
data: ErrorData
|
||||
data: ErrorData,
|
||||
) {
|
||||
if (data.chunkMeta && !data.frag) {
|
||||
const context = this.getCurrentContext(data.chunkMeta);
|
||||
@@ -1496,7 +1496,7 @@ export default class BaseStreamController
|
||||
}
|
||||
if (this.fragContextChanged(frag)) {
|
||||
this.warn(
|
||||
`Frag load error must match current frag to retry ${frag.url} > ${this.fragCurrent?.url}`
|
||||
`Frag load error must match current frag to retry ${frag.url} > ${this.fragCurrent?.url}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1519,7 +1519,7 @@ export default class BaseStreamController
|
||||
data.details
|
||||
}, retrying loading ${retryCount + 1}/${
|
||||
retryConfig.maxNumRetry
|
||||
} in ${delay}ms`
|
||||
} in ${delay}ms`,
|
||||
);
|
||||
errorAction.resolved = true;
|
||||
this.retryDate = self.performance.now() + delay;
|
||||
@@ -1536,7 +1536,7 @@ export default class BaseStreamController
|
||||
}
|
||||
} else {
|
||||
logger.warn(
|
||||
`${data.details} reached or exceeded max retry (${retryCount})`
|
||||
`${data.details} reached or exceeded max retry (${retryCount})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1557,7 +1557,7 @@ export default class BaseStreamController
|
||||
const playlistType = data.parent as PlaylistLevelType;
|
||||
const bufferedInfo = this.getFwdBufferInfo(
|
||||
this.mediaBuffer,
|
||||
playlistType
|
||||
playlistType,
|
||||
);
|
||||
// 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end
|
||||
// reduce max buf len if current position is buffered
|
||||
@@ -1571,7 +1571,7 @@ export default class BaseStreamController
|
||||
// this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708
|
||||
// in that case flush the whole audio buffer to recover
|
||||
this.warn(
|
||||
`Buffer full error while media.currentTime is not buffered, flush ${playlistType} buffer`
|
||||
`Buffer full error while media.currentTime is not buffered, flush ${playlistType} buffer`,
|
||||
);
|
||||
}
|
||||
if (data.frag) {
|
||||
@@ -1602,7 +1602,7 @@ export default class BaseStreamController
|
||||
protected afterBufferFlushed(
|
||||
media: Bufferable,
|
||||
bufferType: SourceBufferName,
|
||||
playlistType: PlaylistLevelType
|
||||
playlistType: PlaylistLevelType,
|
||||
) {
|
||||
if (!media) {
|
||||
return;
|
||||
@@ -1613,7 +1613,7 @@ export default class BaseStreamController
|
||||
this.fragmentTracker.detectEvictedFragments(
|
||||
bufferType,
|
||||
bufferedTimeRanges,
|
||||
playlistType
|
||||
playlistType,
|
||||
);
|
||||
if (this.state === State.ENDED) {
|
||||
this.resetLoadingState();
|
||||
@@ -1646,7 +1646,7 @@ export default class BaseStreamController
|
||||
|
||||
protected resetWhenMissingContext(chunkMeta: ChunkMetadata) {
|
||||
this.warn(
|
||||
`The loading context changed while buffering fragment ${chunkMeta.sn} of level ${chunkMeta.level}. This chunk will not be buffered.`
|
||||
`The loading context changed while buffering fragment ${chunkMeta.sn} of level ${chunkMeta.level}. This chunk will not be buffered.`,
|
||||
);
|
||||
this.removeUnbufferedFrags();
|
||||
this.resetStartWhenNotLoaded(this.levelLastLoaded ?? chunkMeta.level);
|
||||
@@ -1659,7 +1659,7 @@ export default class BaseStreamController
|
||||
Infinity,
|
||||
this.playlistType,
|
||||
false,
|
||||
true
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1667,7 +1667,7 @@ export default class BaseStreamController
|
||||
frag: Fragment,
|
||||
part: Part | null,
|
||||
level: Level,
|
||||
partial: boolean
|
||||
partial: boolean,
|
||||
) {
|
||||
const details = level.details as LevelDetails;
|
||||
if (!details) {
|
||||
@@ -1684,7 +1684,7 @@ export default class BaseStreamController
|
||||
// The new transmuxer will be configured with a time offset matching the next fragment start,
|
||||
// preventing the timeline from shifting.
|
||||
this.warn(
|
||||
`Could not parse fragment ${frag.sn} ${type} duration reliably (${parsedDuration})`
|
||||
`Could not parse fragment ${frag.sn} ${type} duration reliably (${parsedDuration})`,
|
||||
);
|
||||
return result || false;
|
||||
}
|
||||
@@ -1696,7 +1696,7 @@ export default class BaseStreamController
|
||||
info.startPTS,
|
||||
info.endPTS,
|
||||
info.startDTS,
|
||||
info.endDTS
|
||||
info.endDTS,
|
||||
);
|
||||
this.hls.trigger(Events.LEVEL_PTS_UPDATED, {
|
||||
details,
|
||||
@@ -1711,11 +1711,11 @@ export default class BaseStreamController
|
||||
}
|
||||
return result;
|
||||
},
|
||||
false
|
||||
false,
|
||||
);
|
||||
if (!parsed && this.transmuxer?.error === null) {
|
||||
const error = new Error(
|
||||
`Found no media in fragment ${frag.sn} of level ${frag.level} resetting transmuxer to fallback to playlist timing`
|
||||
`Found no media in fragment ${frag.sn} of level ${frag.level} resetting transmuxer to fallback to playlist timing`,
|
||||
);
|
||||
if (level.fragmentError === 0) {
|
||||
// Mark and track the odd empty segment as a gap to avoid reloading
|
||||
@@ -1755,7 +1755,7 @@ export default class BaseStreamController
|
||||
this.fragmentTracker.removeAllFragments();
|
||||
this.resetTransmuxer();
|
||||
this.resetStartWhenNotLoaded(
|
||||
this.levelLastLoaded ?? this.fragCurrent?.level ?? 0
|
||||
this.levelLastLoaded ?? this.fragCurrent?.level ?? 0,
|
||||
);
|
||||
this.resetLoadingState();
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
protected onManifestParsed(
|
||||
event: Events.MANIFEST_PARSED,
|
||||
data: ManifestParsedData
|
||||
data: ManifestParsedData,
|
||||
) {
|
||||
// in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller
|
||||
// sourcebuffers will be created all at once when the expected nb of tracks will be reached
|
||||
@@ -173,7 +173,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
protected onMediaAttaching(
|
||||
event: Events.MEDIA_ATTACHING,
|
||||
data: MediaAttachingData
|
||||
data: MediaAttachingData,
|
||||
) {
|
||||
const media = (this.media = data.media);
|
||||
if (media && MediaSource) {
|
||||
@@ -203,7 +203,7 @@ export default class BufferController implements ComponentAPI {
|
||||
mediaSource.endOfStream();
|
||||
} catch (err) {
|
||||
this.warn(
|
||||
`onMediaDetaching: ${err.message} while calling endOfStream`
|
||||
`onMediaDetaching: ${err.message} while calling endOfStream`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -268,7 +268,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
protected onBufferCodecs(
|
||||
event: Events.BUFFER_CODECS,
|
||||
data: BufferCodecsData
|
||||
data: BufferCodecsData,
|
||||
) {
|
||||
const sourceBufferCount = this.getSourceBufferTypes().length;
|
||||
|
||||
@@ -281,16 +281,16 @@ export default class BufferController implements ComponentAPI {
|
||||
data[trackName];
|
||||
const currentCodedFull = pickMostCompleteCodecName(
|
||||
track.codec,
|
||||
track.levelCodec
|
||||
track.levelCodec,
|
||||
);
|
||||
const currentCodec = currentCodedFull.replace(
|
||||
VIDEO_CODEC_PROFILE_REPLACE,
|
||||
'$1'
|
||||
'$1',
|
||||
);
|
||||
let trackCodec = pickMostCompleteCodecName(codec, levelCodec);
|
||||
const nextCodec = trackCodec.replace(
|
||||
VIDEO_CODEC_PROFILE_REPLACE,
|
||||
'$1'
|
||||
'$1',
|
||||
);
|
||||
if (currentCodec !== nextCodec) {
|
||||
if (trackName.slice(0, 5) === 'audio') {
|
||||
@@ -322,7 +322,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
this.bufferCodecEventsExpected = Math.max(
|
||||
this.bufferCodecEventsExpected - 1,
|
||||
0
|
||||
0,
|
||||
);
|
||||
if (this.mediaSource && this.mediaSource.readyState === 'open') {
|
||||
this.checkPendingTracks();
|
||||
@@ -352,7 +352,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
protected onBufferAppending(
|
||||
event: Events.BUFFER_APPENDING,
|
||||
eventData: BufferAppendingData
|
||||
eventData: BufferAppendingData,
|
||||
) {
|
||||
const { hls, operationQueue, tracks } = this;
|
||||
const { data, type, frag, part, chunkMeta } = eventData;
|
||||
@@ -394,7 +394,7 @@ export default class BufferController implements ComponentAPI {
|
||||
const delta = fragStart - sb.timestampOffset;
|
||||
if (Math.abs(delta) >= 0.1) {
|
||||
this.log(
|
||||
`Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${frag.sn})`
|
||||
`Updating audio SourceBuffer timestampOffset to ${fragStart} (delta: ${delta}) sn: ${frag.sn})`,
|
||||
);
|
||||
sb.timestampOffset = fragStart;
|
||||
}
|
||||
@@ -463,7 +463,7 @@ export default class BufferController implements ComponentAPI {
|
||||
browser is able to evict some data from sourcebuffer. Retrying can help recover.
|
||||
*/
|
||||
this.warn(
|
||||
`Failed ${appendErrorCount}/${hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer`
|
||||
`Failed ${appendErrorCount}/${hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer`,
|
||||
);
|
||||
if (appendErrorCount >= hls.config.appendErrorMaxRetry) {
|
||||
event.fatal = true;
|
||||
@@ -477,7 +477,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
protected onBufferFlushing(
|
||||
event: Events.BUFFER_FLUSHING,
|
||||
data: BufferFlushingData
|
||||
data: BufferFlushingData,
|
||||
) {
|
||||
const { operationQueue } = this;
|
||||
const flushOperation = (type: SourceBufferName): BufferOperation => ({
|
||||
@@ -485,7 +485,7 @@ export default class BufferController implements ComponentAPI {
|
||||
this,
|
||||
type,
|
||||
data.startOffset,
|
||||
data.endOffset
|
||||
data.endOffset,
|
||||
),
|
||||
onStart: () => {
|
||||
// logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`);
|
||||
@@ -542,7 +542,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
if (buffersAppendedTo.length === 0) {
|
||||
this.warn(
|
||||
`Fragments must have at least one ElementaryStreamType set. type: ${frag.type} level: ${frag.level} sn: ${frag.sn}`
|
||||
`Fragments must have at least one ElementaryStreamType set. type: ${frag.type} level: ${frag.level} sn: ${frag.sn}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -581,7 +581,7 @@ export default class BufferController implements ComponentAPI {
|
||||
if (!mediaSource || mediaSource.readyState !== 'open') {
|
||||
if (mediaSource) {
|
||||
this.log(
|
||||
`Could not call mediaSource.endOfStream(). mediaSource.readyState: ${mediaSource.readyState}`
|
||||
`Could not call mediaSource.endOfStream(). mediaSource.readyState: ${mediaSource.readyState}`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -595,7 +595,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
protected onLevelUpdated(
|
||||
event: Events.LEVEL_UPDATED,
|
||||
{ details }: LevelUpdatedData
|
||||
{ details }: LevelUpdatedData,
|
||||
) {
|
||||
if (!details.fragments.length) {
|
||||
return;
|
||||
@@ -659,7 +659,7 @@ export default class BufferController implements ComponentAPI {
|
||||
buffered.end(buffered.length - 1) - currentTime < targetDuration * 2
|
||||
) {
|
||||
this.log(
|
||||
`Cannot flush ${type} back buffer while SourceBuffer is in ended state`
|
||||
`Cannot flush ${type} back buffer while SourceBuffer is in ended state`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -720,7 +720,7 @@ export default class BufferController implements ComponentAPI {
|
||||
const start = Math.max(0, fragments[0].start);
|
||||
const end = Math.max(start, start + levelDetails.totalduration);
|
||||
this.log(
|
||||
`Media Source duration is set to ${mediaSource.duration}. Setting seekable range to ${start}-${end}.`
|
||||
`Media Source duration is set to ${mediaSource.duration}. Setting seekable range to ${start}-${end}.`,
|
||||
);
|
||||
mediaSource.setLiveSeekableRange(start, end);
|
||||
}
|
||||
@@ -750,7 +750,7 @@ export default class BufferController implements ComponentAPI {
|
||||
});
|
||||
} else {
|
||||
const error = new Error(
|
||||
'could not create source buffer for media codec(s)'
|
||||
'could not create source buffer for media codec(s)',
|
||||
);
|
||||
this.hls.trigger(Events.ERROR, {
|
||||
type: ErrorTypes.MEDIA_ERROR,
|
||||
@@ -773,7 +773,7 @@ export default class BufferController implements ComponentAPI {
|
||||
const track = tracks[trackName as keyof TrackSet];
|
||||
if (!track) {
|
||||
throw Error(
|
||||
`source buffer exists for track ${trackName}, however track does not`
|
||||
`source buffer exists for track ${trackName}, however track does not`,
|
||||
);
|
||||
}
|
||||
// use levelCodec as first priority
|
||||
@@ -844,7 +844,7 @@ export default class BufferController implements ComponentAPI {
|
||||
const { media, _objectUrl } = this;
|
||||
if (media && media.src !== _objectUrl) {
|
||||
this.error(
|
||||
`Media element src was set while attaching MediaSource (${_objectUrl} > ${media.src})`
|
||||
`Media element src was set while attaching MediaSource (${_objectUrl} > ${media.src})`,
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -889,13 +889,13 @@ export default class BufferController implements ComponentAPI {
|
||||
private removeExecutor(
|
||||
type: SourceBufferName,
|
||||
startOffset: number,
|
||||
endOffset: number
|
||||
endOffset: number,
|
||||
) {
|
||||
const { media, mediaSource, operationQueue, sourceBuffer } = this;
|
||||
const sb = sourceBuffer[type];
|
||||
if (!media || !mediaSource || !sb) {
|
||||
this.warn(
|
||||
`Attempting to remove from the ${type} SourceBuffer, but it does not exist`
|
||||
`Attempting to remove from the ${type} SourceBuffer, but it does not exist`,
|
||||
);
|
||||
operationQueue.shiftAndExecuteNext(type);
|
||||
return;
|
||||
@@ -911,7 +911,7 @@ export default class BufferController implements ComponentAPI {
|
||||
if (removeEnd > removeStart && (!sb.ending || sb.ended)) {
|
||||
sb.ended = false;
|
||||
this.log(
|
||||
`Removing [${removeStart},${removeEnd}] from the ${type} SourceBuffer`
|
||||
`Removing [${removeStart},${removeEnd}] from the ${type} SourceBuffer`,
|
||||
);
|
||||
sb.remove(removeStart, removeEnd);
|
||||
} else {
|
||||
@@ -926,7 +926,7 @@ export default class BufferController implements ComponentAPI {
|
||||
if (!sb) {
|
||||
if (!this.pendingTracks[type]) {
|
||||
throw new Error(
|
||||
`Attempting to append to the ${type} SourceBuffer, but it does not exist`
|
||||
`Attempting to append to the ${type} SourceBuffer, but it does not exist`,
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -941,7 +941,7 @@ export default class BufferController implements ComponentAPI {
|
||||
// upon completion, since we already do it here
|
||||
private blockBuffers(
|
||||
onUnblocked: () => void,
|
||||
buffers: Array<SourceBufferName> = this.getSourceBufferTypes()
|
||||
buffers: Array<SourceBufferName> = this.getSourceBufferTypes(),
|
||||
) {
|
||||
if (!buffers.length) {
|
||||
this.log('Blocking operation requested, but no SourceBuffers exist');
|
||||
@@ -952,7 +952,7 @@ export default class BufferController implements ComponentAPI {
|
||||
|
||||
// logger.debug(`[buffer-controller]: Blocking ${buffers} SourceBuffer`);
|
||||
const blockingOperations = buffers.map((type) =>
|
||||
operationQueue.appendBlocker(type as SourceBufferName)
|
||||
operationQueue.appendBlocker(type as SourceBufferName),
|
||||
);
|
||||
Promise.all(blockingOperations).then(() => {
|
||||
// logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`);
|
||||
@@ -976,7 +976,7 @@ export default class BufferController implements ComponentAPI {
|
||||
private addBufferListener(
|
||||
type: SourceBufferName,
|
||||
event: string,
|
||||
fn: Function
|
||||
fn: Function,
|
||||
) {
|
||||
const buffer = this.sourceBuffer[type];
|
||||
if (!buffer) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class BufferOperationQueue {
|
||||
public append(
|
||||
operation: BufferOperation,
|
||||
type: SourceBufferName,
|
||||
pending?: boolean
|
||||
pending?: boolean,
|
||||
) {
|
||||
const queue = this.queues[type];
|
||||
queue.push(operation);
|
||||
@@ -62,7 +62,7 @@ export default class BufferOperationQueue {
|
||||
operation.execute();
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`[buffer-operation-queue]: Exception executing "${type}" SourceBuffer operation: ${error}`
|
||||
`[buffer-operation-queue]: Exception executing "${type}" SourceBuffer operation: ${error}`,
|
||||
);
|
||||
operation.onError(error);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ class CapLevelController implements ComponentAPI {
|
||||
|
||||
protected onFpsDropLevelCapping(
|
||||
event: Events.FPS_DROP_LEVEL_CAPPING,
|
||||
data: FPSDropLevelCappingData
|
||||
data: FPSDropLevelCappingData,
|
||||
) {
|
||||
// Don't add a restricted level more than once
|
||||
const level = this.hls.levels[data.droppedLevel];
|
||||
@@ -93,7 +93,7 @@ class CapLevelController implements ComponentAPI {
|
||||
|
||||
protected onMediaAttaching(
|
||||
event: Events.MEDIA_ATTACHING,
|
||||
data: MediaAttachingData
|
||||
data: MediaAttachingData,
|
||||
) {
|
||||
this.media = data.media instanceof HTMLVideoElement ? data.media : null;
|
||||
this.clientRect = null;
|
||||
@@ -104,7 +104,7 @@ class CapLevelController implements ComponentAPI {
|
||||
|
||||
protected onManifestParsed(
|
||||
event: Events.MANIFEST_PARSED,
|
||||
data: ManifestParsedData
|
||||
data: ManifestParsedData,
|
||||
) {
|
||||
const hls = this.hls;
|
||||
this.restrictedLevels = [];
|
||||
@@ -117,7 +117,7 @@ class CapLevelController implements ComponentAPI {
|
||||
|
||||
private onLevelsUpdated(
|
||||
event: Events.LEVELS_UPDATED,
|
||||
data: LevelsUpdatedData
|
||||
data: LevelsUpdatedData,
|
||||
) {
|
||||
if (this.timer && Number.isFinite(this.autoLevelCapping)) {
|
||||
this.detectPlayerSize();
|
||||
@@ -128,7 +128,7 @@ class CapLevelController implements ComponentAPI {
|
||||
// to the first level
|
||||
protected onBufferCodecs(
|
||||
event: Events.BUFFER_CODECS,
|
||||
data: BufferCodecsData
|
||||
data: BufferCodecsData,
|
||||
) {
|
||||
const hls = this.hls;
|
||||
if (hls.config.capLevelToPlayerSize && data.video) {
|
||||
@@ -153,7 +153,7 @@ class CapLevelController implements ComponentAPI {
|
||||
const maxLevel = this.getMaxLevel(levels.length - 1);
|
||||
if (maxLevel !== this.autoLevelCapping) {
|
||||
logger.log(
|
||||
`Setting autoLevelCapping to ${maxLevel}: ${levels[maxLevel].height}p@${levels[maxLevel].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`
|
||||
`Setting autoLevelCapping to ${maxLevel}: ${levels[maxLevel].height}p@${levels[maxLevel].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`,
|
||||
);
|
||||
}
|
||||
hls.autoLevelCapping = maxLevel;
|
||||
@@ -180,14 +180,14 @@ class CapLevelController implements ComponentAPI {
|
||||
}
|
||||
|
||||
const validLevels = levels.filter(
|
||||
(level, index) => this.isLevelAllowed(level) && index <= capLevelIndex
|
||||
(level, index) => this.isLevelAllowed(level) && index <= capLevelIndex,
|
||||
);
|
||||
|
||||
this.clientRect = null;
|
||||
return CapLevelController.getMaxLevelByMediaSize(
|
||||
validLevels,
|
||||
this.mediaWidth,
|
||||
this.mediaHeight
|
||||
this.mediaHeight,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ class CapLevelController implements ComponentAPI {
|
||||
static getMaxLevelByMediaSize(
|
||||
levels: Array<Level>,
|
||||
width: number,
|
||||
height: number
|
||||
height: number,
|
||||
): number {
|
||||
if (!levels?.length) {
|
||||
return -1;
|
||||
@@ -284,7 +284,7 @@ class CapLevelController implements ComponentAPI {
|
||||
// to determine whether we've chosen the greatest bandwidth for the media's dimensions
|
||||
const atGreatestBandwidth = (
|
||||
curLevel: Level,
|
||||
nextLevel: Level | undefined
|
||||
nextLevel: Level | undefined,
|
||||
) => {
|
||||
if (!nextLevel) {
|
||||
return true;
|
||||
|
||||
@@ -83,7 +83,7 @@ export default class CMCDController implements ComponentAPI {
|
||||
|
||||
private onMediaAttached(
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachedData
|
||||
data: MediaAttachedData,
|
||||
) {
|
||||
this.media = data.media;
|
||||
this.media.addEventListener('waiting', this.onWaiting);
|
||||
@@ -104,7 +104,7 @@ export default class CMCDController implements ComponentAPI {
|
||||
|
||||
private onBufferCreated(
|
||||
event: Events.BUFFER_CREATED,
|
||||
data: BufferCreatedData
|
||||
data: BufferCreatedData,
|
||||
) {
|
||||
this.audioBuffer = data.tracks.audio?.buffer;
|
||||
this.videoBuffer = data.tracks.video?.buffer;
|
||||
@@ -297,7 +297,7 @@ export default class CMCDController implements ComponentAPI {
|
||||
const info = BufferHelper.bufferInfo(
|
||||
buffer,
|
||||
media.currentTime,
|
||||
this.config.maxBufferHole
|
||||
this.config.maxBufferHole,
|
||||
);
|
||||
|
||||
return info.len * 1000;
|
||||
@@ -337,7 +337,7 @@ export default class CMCDController implements ComponentAPI {
|
||||
load(
|
||||
context: PlaylistLoaderContext,
|
||||
config: LoaderConfiguration,
|
||||
callbacks: LoaderCallbacks<PlaylistLoaderContext>
|
||||
callbacks: LoaderCallbacks<PlaylistLoaderContext>,
|
||||
) {
|
||||
apply(context);
|
||||
this.loader.load(context, config, callbacks);
|
||||
@@ -379,7 +379,7 @@ export default class CMCDController implements ComponentAPI {
|
||||
load(
|
||||
context: FragmentLoaderContext,
|
||||
config: LoaderConfiguration,
|
||||
callbacks: LoaderCallbacks<FragmentLoaderContext>
|
||||
callbacks: LoaderCallbacks<FragmentLoaderContext>,
|
||||
) {
|
||||
apply(context);
|
||||
this.loader.load(context, config, callbacks);
|
||||
|
||||
@@ -96,7 +96,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
if (this.updated) {
|
||||
const ttl = Math.max(
|
||||
this.timeToLoad * 1000 - (performance.now() - this.updated),
|
||||
0
|
||||
0,
|
||||
);
|
||||
this.scheduleRefresh(this.uri, ttl);
|
||||
} else {
|
||||
@@ -148,7 +148,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
|
||||
private onManifestLoaded(
|
||||
event: Events.MANIFEST_LOADED,
|
||||
data: ManifestLoadedData
|
||||
data: ManifestLoadedData,
|
||||
) {
|
||||
const { contentSteering } = data;
|
||||
if (contentSteering === null) {
|
||||
@@ -163,7 +163,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
|
||||
private onManifestParsed(
|
||||
event: Events.MANIFEST_PARSED,
|
||||
data: ManifestParsedData
|
||||
data: ManifestParsedData,
|
||||
) {
|
||||
this.audioTracks = data.audioTracks;
|
||||
this.subtitleTracks = data.subtitleTracks;
|
||||
@@ -203,14 +203,14 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
if (pathwayLevels.length === 0) {
|
||||
const pathwayId = levels[0].pathwayId;
|
||||
this.log(
|
||||
`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to "${pathwayId}"`
|
||||
`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to "${pathwayId}"`,
|
||||
);
|
||||
pathwayLevels = this.getLevelsForPathway(pathwayId);
|
||||
this.pathwayId = pathwayId;
|
||||
}
|
||||
if (pathwayLevels.length !== levels.length) {
|
||||
this.log(
|
||||
`Found ${pathwayLevels.length}/${levels.length} levels in Pathway "${this.pathwayId}"`
|
||||
`Found ${pathwayLevels.length}/${levels.length} levels in Pathway "${this.pathwayId}"`,
|
||||
);
|
||||
return pathwayLevels;
|
||||
}
|
||||
@@ -261,7 +261,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
levelAfterChange.bitrate !== selectedLevel.bitrate
|
||||
) {
|
||||
this.log(
|
||||
`Unstable Pathways change from bitrate ${selectedLevel.bitrate} to ${levelAfterChange.bitrate}`
|
||||
`Unstable Pathways change from bitrate ${selectedLevel.bitrate} to ${levelAfterChange.bitrate}`,
|
||||
);
|
||||
}
|
||||
this.hls.nextLoadLevel = selectedIndex;
|
||||
@@ -295,7 +295,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
baseLevel.uri,
|
||||
baseLevel.attrs['STABLE-VARIANT-ID'],
|
||||
'PER-VARIANT-URIS',
|
||||
uriReplacement
|
||||
uriReplacement,
|
||||
);
|
||||
const attributes = new AttrList(baseLevel.attrs);
|
||||
attributes['PATHWAY-ID'] = cloneId;
|
||||
@@ -316,20 +316,20 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
addGroupId(clonedLevel, 'audio', clonedAudioGroupId);
|
||||
addGroupId(clonedLevel, 'text', clonedSubtitleGroupId);
|
||||
return clonedLevel;
|
||||
}
|
||||
},
|
||||
);
|
||||
levels.push(...clonedVariants);
|
||||
cloneRenditionGroups(
|
||||
this.audioTracks,
|
||||
audioGroupCloneMap,
|
||||
uriReplacement,
|
||||
cloneId
|
||||
cloneId,
|
||||
);
|
||||
cloneRenditionGroups(
|
||||
this.subtitleTracks,
|
||||
subtitleGroupCloneMap,
|
||||
uriReplacement,
|
||||
cloneId
|
||||
cloneId,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -377,7 +377,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
response: LoaderResponse,
|
||||
stats: LoaderStats,
|
||||
context: LoaderContext,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => {
|
||||
this.log(`Loaded steering manifest: "${url}"`);
|
||||
const steeringData = response.data as SteeringManifest;
|
||||
@@ -398,7 +398,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
} catch (error) {
|
||||
this.enabled = false;
|
||||
this.log(
|
||||
`Failed to parse Steering Manifest RELOAD-URI: ${reloadUri}`
|
||||
`Failed to parse Steering Manifest RELOAD-URI: ${reloadUri}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -423,10 +423,10 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
error: { code: number; text: string },
|
||||
context: LoaderContext,
|
||||
networkDetails: any,
|
||||
stats: LoaderStats
|
||||
stats: LoaderStats,
|
||||
) => {
|
||||
this.log(
|
||||
`Error loading steering manifest: ${error.code} ${error.text} (${context.url})`
|
||||
`Error loading steering manifest: ${error.code} ${error.text} (${context.url})`,
|
||||
);
|
||||
this.stopLoad();
|
||||
if (error.code === 410) {
|
||||
@@ -452,7 +452,7 @@ export default class ContentSteeringController implements NetworkComponentAPI {
|
||||
onTimeout: (
|
||||
stats: LoaderStats,
|
||||
context: LoaderContext,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => {
|
||||
this.log(`Timeout loading steering manifest (${context.url})`);
|
||||
this.scheduleRefresh(this.uri || context.url);
|
||||
@@ -475,7 +475,7 @@ function cloneRenditionGroups(
|
||||
tracks: MediaPlaylist[] | null,
|
||||
groupCloneMap: Record<string, string>,
|
||||
uriReplacement: UriReplacement,
|
||||
cloneId: string
|
||||
cloneId: string,
|
||||
) {
|
||||
if (!tracks) {
|
||||
return;
|
||||
@@ -491,7 +491,7 @@ function cloneRenditionGroups(
|
||||
track.url,
|
||||
track.attrs['STABLE-RENDITION-ID'],
|
||||
'PER-RENDITION-URIS',
|
||||
uriReplacement
|
||||
uriReplacement,
|
||||
);
|
||||
clonedTrack.groupId = clonedTrack.attrs['GROUP-ID'] =
|
||||
groupCloneMap[audioGroupId];
|
||||
@@ -506,7 +506,7 @@ function performUriReplacement(
|
||||
uri: string,
|
||||
stableId: string | undefined,
|
||||
perOptionKey: 'PER-VARIANT-URIS' | 'PER-RENDITION-URIS',
|
||||
uriReplacement: UriReplacement
|
||||
uriReplacement: UriReplacement,
|
||||
): string {
|
||||
const {
|
||||
HOST: host,
|
||||
|
||||
@@ -148,7 +148,7 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
`no license server URL configured for key-system "${keySystem}"`
|
||||
`no license server URL configured for key-system "${keySystem}"`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
|
||||
private attemptKeySystemAccess(
|
||||
keySystemsToAttempt: KeySystems[]
|
||||
keySystemsToAttempt: KeySystems[],
|
||||
): Promise<{ keySystem: KeySystems; mediaKeys: MediaKeys }> {
|
||||
const levels = this.hls.levels;
|
||||
const uniqueCodec = (value: string | undefined, i, a): value is string =>
|
||||
@@ -185,7 +185,7 @@ class EMEController implements ComponentAPI {
|
||||
keySystem: KeySystems;
|
||||
mediaKeys: MediaKeys;
|
||||
}) => void,
|
||||
reject: (Error) => void
|
||||
reject: (Error) => void,
|
||||
) => {
|
||||
const attempt = (keySystems) => {
|
||||
const keySystem = keySystems.shift();
|
||||
@@ -205,20 +205,20 @@ class EMEController implements ComponentAPI {
|
||||
error,
|
||||
fatal: true,
|
||||
},
|
||||
error.message
|
||||
)
|
||||
error.message,
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
attempt(keySystemsToAttempt);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private requestMediaKeySystemAccess(
|
||||
keySystem: KeySystems,
|
||||
supportedConfigurations: MediaKeySystemConfiguration[]
|
||||
supportedConfigurations: MediaKeySystemConfiguration[],
|
||||
): Promise<MediaKeySystemAccess> {
|
||||
const { requestMediaKeySystemAccessFunc } = this.config;
|
||||
if (!(typeof requestMediaKeySystemAccessFunc === 'function')) {
|
||||
@@ -238,14 +238,14 @@ class EMEController implements ComponentAPI {
|
||||
private getMediaKeysPromise(
|
||||
keySystem: KeySystems,
|
||||
audioCodecs: string[],
|
||||
videoCodecs: string[]
|
||||
videoCodecs: string[],
|
||||
): Promise<MediaKeys> {
|
||||
// This can throw, but is caught in event handler callpath
|
||||
const mediaKeySystemConfigs = getSupportedMediaKeySystemConfigurations(
|
||||
keySystem,
|
||||
audioCodecs,
|
||||
videoCodecs,
|
||||
this.config.drmSystemOptions
|
||||
this.config.drmSystemOptions,
|
||||
);
|
||||
const keySystemAccessPromises: KeySystemAccessPromises =
|
||||
this.keySystemAccessPromises[keySystem];
|
||||
@@ -253,12 +253,12 @@ class EMEController implements ComponentAPI {
|
||||
if (!keySystemAccess) {
|
||||
this.log(
|
||||
`Requesting encrypted media "${keySystem}" key-system access with config: ${JSON.stringify(
|
||||
mediaKeySystemConfigs
|
||||
)}`
|
||||
mediaKeySystemConfigs,
|
||||
)}`,
|
||||
);
|
||||
keySystemAccess = this.requestMediaKeySystemAccess(
|
||||
keySystem,
|
||||
mediaKeySystemConfigs
|
||||
mediaKeySystemConfigs,
|
||||
);
|
||||
const keySystemAccessPromises: KeySystemAccessPromises =
|
||||
(this.keySystemAccessPromises[keySystem] = {
|
||||
@@ -266,12 +266,12 @@ class EMEController implements ComponentAPI {
|
||||
});
|
||||
keySystemAccess.catch((error) => {
|
||||
this.log(
|
||||
`Failed to obtain access to key-system "${keySystem}": ${error}`
|
||||
`Failed to obtain access to key-system "${keySystem}": ${error}`,
|
||||
);
|
||||
});
|
||||
return keySystemAccess.then((mediaKeySystemAccess) => {
|
||||
this.log(
|
||||
`Access for key-system "${mediaKeySystemAccess.keySystem}" obtained`
|
||||
`Access for key-system "${mediaKeySystemAccess.keySystem}" obtained`,
|
||||
);
|
||||
|
||||
const certificateRequest = this.fetchServerCertificate(keySystem);
|
||||
@@ -286,7 +286,7 @@ class EMEController implements ComponentAPI {
|
||||
return this.setMediaKeysServerCertificate(
|
||||
mediaKeys,
|
||||
keySystem,
|
||||
certificate
|
||||
certificate,
|
||||
);
|
||||
}
|
||||
return mediaKeys;
|
||||
@@ -295,7 +295,7 @@ class EMEController implements ComponentAPI {
|
||||
|
||||
keySystemAccessPromises.mediaKeys.catch((error) => {
|
||||
this.error(
|
||||
`Failed to create media-keys for "${keySystem}"}: ${error}`
|
||||
`Failed to create media-keys for "${keySystem}"}: ${error}`,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -316,8 +316,8 @@ class EMEController implements ComponentAPI {
|
||||
}): MediaKeySessionContext {
|
||||
this.log(
|
||||
`Creating key-system session "${keySystem}" keyId: ${Hex.hexDump(
|
||||
decryptdata.keyId! || []
|
||||
)}`
|
||||
decryptdata.keyId! || [],
|
||||
)}`,
|
||||
);
|
||||
|
||||
const mediaKeysSession = mediaKeys.createSession();
|
||||
@@ -339,7 +339,7 @@ class EMEController implements ComponentAPI {
|
||||
const decryptdata = mediaKeySessionContext.decryptdata;
|
||||
if (decryptdata.pssh) {
|
||||
const keySessionContext = this.createMediaKeySessionContext(
|
||||
mediaKeySessionContext
|
||||
mediaKeySessionContext,
|
||||
);
|
||||
const keyId = this.getKeyIdString(decryptdata);
|
||||
const scheme = 'cenc';
|
||||
@@ -348,7 +348,7 @@ class EMEController implements ComponentAPI {
|
||||
keySessionContext,
|
||||
scheme,
|
||||
decryptdata.pssh,
|
||||
'expired'
|
||||
'expired',
|
||||
);
|
||||
} else {
|
||||
this.warn(`Could not renew expired session. Missing pssh initData.`);
|
||||
@@ -368,14 +368,14 @@ class EMEController implements ComponentAPI {
|
||||
|
||||
private updateKeySession(
|
||||
mediaKeySessionContext: MediaKeySessionContext,
|
||||
data: Uint8Array
|
||||
data: Uint8Array,
|
||||
): Promise<void> {
|
||||
const keySession = mediaKeySessionContext.mediaKeysSession;
|
||||
this.log(
|
||||
`Updating key-session "${keySession.sessionId}" for keyID ${Hex.hexDump(
|
||||
mediaKeySessionContext.decryptdata?.keyId! || []
|
||||
mediaKeySessionContext.decryptdata?.keyId! || [],
|
||||
)}
|
||||
} (data length: ${data ? data.byteLength : data})`
|
||||
} (data length: ${data ? data.byteLength : data})`,
|
||||
);
|
||||
return keySession.update(data);
|
||||
}
|
||||
@@ -386,7 +386,7 @@ class EMEController implements ComponentAPI {
|
||||
this.log(
|
||||
`Selecting key-system from fragment (sn: ${frag.sn} ${frag.type}: ${
|
||||
frag.level
|
||||
}) key formats ${keyFormats.join(', ')}`
|
||||
}) key formats ${keyFormats.join(', ')}`,
|
||||
);
|
||||
this.keyFormatPromise = this.getKeyFormatPromise(keyFormats);
|
||||
}
|
||||
@@ -394,14 +394,14 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
|
||||
private getKeyFormatPromise(
|
||||
keyFormats: KeySystemFormats[]
|
||||
keyFormats: KeySystemFormats[],
|
||||
): Promise<KeySystemFormats> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const keySystemsInConfig = getKeySystemsForConfig(this.config);
|
||||
const keySystemsToAttempt = keyFormats
|
||||
.map(keySystemFormatToKeySystemDomain)
|
||||
.filter(
|
||||
(value) => !!value && keySystemsInConfig.indexOf(value) !== -1
|
||||
(value) => !!value && keySystemsInConfig.indexOf(value) !== -1,
|
||||
) as any as KeySystems[];
|
||||
return this.getKeySystemSelectionPromise(keySystemsToAttempt)
|
||||
.then(({ keySystem }) => {
|
||||
@@ -410,7 +410,7 @@ class EMEController implements ComponentAPI {
|
||||
resolve(keySystemFormat);
|
||||
} else {
|
||||
reject(
|
||||
new Error(`Unable to find format for key-system "${keySystem}"`)
|
||||
new Error(`Unable to find format for key-system "${keySystem}"`),
|
||||
);
|
||||
}
|
||||
})
|
||||
@@ -433,7 +433,7 @@ class EMEController implements ComponentAPI {
|
||||
({ keySystem, mediaKeys }) => {
|
||||
this.throwIfDestroyed();
|
||||
this.log(
|
||||
`Handle encrypted media sn: ${data.frag.sn} ${data.frag.type}: ${data.frag.level} using key ${keyDetails}`
|
||||
`Handle encrypted media sn: ${data.frag.sn} ${data.frag.type}: ${data.frag.level} using key ${keyDetails}`,
|
||||
);
|
||||
|
||||
return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => {
|
||||
@@ -448,10 +448,10 @@ class EMEController implements ComponentAPI {
|
||||
keySessionContext,
|
||||
scheme,
|
||||
decryptdata.pssh,
|
||||
'playlist-key'
|
||||
'playlist-key',
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
keySessionContextPromise.catch((error) => this.handleError(error));
|
||||
@@ -484,13 +484,13 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
|
||||
private getKeySystemForKeyPromise(
|
||||
decryptdata: LevelKey
|
||||
decryptdata: LevelKey,
|
||||
): Promise<{ keySystem: KeySystems; mediaKeys: MediaKeys }> {
|
||||
const keyId = this.getKeyIdString(decryptdata);
|
||||
const mediaKeySessionContext = this.keyIdToKeySessionPromise[keyId];
|
||||
if (!mediaKeySessionContext) {
|
||||
const keySystem = keySystemFormatToKeySystemDomain(
|
||||
decryptdata.keyFormat as KeySystemFormats
|
||||
decryptdata.keyFormat as KeySystemFormats,
|
||||
);
|
||||
const keySystemsToAttempt = keySystem
|
||||
? [keySystem]
|
||||
@@ -501,7 +501,7 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
|
||||
private getKeySystemSelectionPromise(
|
||||
keySystemsToAttempt: KeySystems[]
|
||||
keySystemsToAttempt: KeySystems[],
|
||||
): Promise<{ keySystem: KeySystems; mediaKeys: MediaKeys }> | never {
|
||||
if (!keySystemsToAttempt.length) {
|
||||
keySystemsToAttempt = getKeySystemsForConfig(this.config);
|
||||
@@ -515,7 +515,7 @@ class EMEController implements ComponentAPI {
|
||||
},
|
||||
`Missing key-system license configuration options ${JSON.stringify({
|
||||
drmSystems: this.config.drmSystems,
|
||||
})}`
|
||||
})}`,
|
||||
);
|
||||
}
|
||||
return this.attemptKeySystemAccess(keySystemsToAttempt);
|
||||
@@ -565,7 +565,7 @@ class EMEController implements ComponentAPI {
|
||||
keyId = psshInfo.data.subarray(8, 24);
|
||||
}
|
||||
keySystemDomain = keySystemIdToKeySystemDomain(
|
||||
psshInfo.systemId as KeySystemIds
|
||||
psshInfo.systemId as KeySystemIds,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -599,7 +599,7 @@ class EMEController implements ComponentAPI {
|
||||
keyContext,
|
||||
initDataType,
|
||||
initData,
|
||||
'encrypted-event-key-match'
|
||||
'encrypted-event-key-match',
|
||||
);
|
||||
});
|
||||
break;
|
||||
@@ -615,7 +615,7 @@ class EMEController implements ComponentAPI {
|
||||
const decryptdata = new LevelKey(
|
||||
'ISO-23001-7',
|
||||
keyIdHex,
|
||||
keySystemToKeySystemFormat(keySystem) ?? ''
|
||||
keySystemToKeySystemFormat(keySystem) ?? '',
|
||||
);
|
||||
decryptdata.pssh = new Uint8Array(initData);
|
||||
decryptdata.keyId = keyId as Uint8Array;
|
||||
@@ -630,10 +630,10 @@ class EMEController implements ComponentAPI {
|
||||
keySessionContext,
|
||||
initDataType,
|
||||
initData,
|
||||
'encrypted-event-no-match'
|
||||
'encrypted-event-no-match',
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
keySessionContextPromise.catch((error) => this.handleError(error));
|
||||
@@ -645,7 +645,7 @@ class EMEController implements ComponentAPI {
|
||||
|
||||
private attemptSetMediaKeys(
|
||||
keySystem: KeySystems,
|
||||
mediaKeys: MediaKeys
|
||||
mediaKeys: MediaKeys,
|
||||
): Promise<void> {
|
||||
const queue = this.setMediaKeysQueue.slice();
|
||||
|
||||
@@ -655,7 +655,7 @@ class EMEController implements ComponentAPI {
|
||||
const setMediaKeysPromise = Promise.all(queue).then(() => {
|
||||
if (!this.media) {
|
||||
throw new Error(
|
||||
'Attempted to set mediaKeys without media element attached'
|
||||
'Attempted to set mediaKeys without media element attached',
|
||||
);
|
||||
}
|
||||
return this.media.setMediaKeys(mediaKeys);
|
||||
@@ -665,7 +665,7 @@ class EMEController implements ComponentAPI {
|
||||
this.log(`Media-keys set for "${keySystem}"`);
|
||||
queue.push(setMediaKeysPromise!);
|
||||
this.setMediaKeysQueue = this.setMediaKeysQueue.filter(
|
||||
(p) => queue.indexOf(p) === -1
|
||||
(p) => queue.indexOf(p) === -1,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -678,7 +678,7 @@ class EMEController implements ComponentAPI {
|
||||
| 'playlist-key'
|
||||
| 'encrypted-event-key-match'
|
||||
| 'encrypted-event-no-match'
|
||||
| 'expired'
|
||||
| 'expired',
|
||||
): Promise<MediaKeySessionContext> | never {
|
||||
const generateRequestFilter =
|
||||
this.config.drmSystems?.[context.keySystem]?.generateRequest;
|
||||
@@ -688,7 +688,7 @@ class EMEController implements ComponentAPI {
|
||||
generateRequestFilter.call(this.hls, initDataType, initData, context);
|
||||
if (!mappedInitData) {
|
||||
throw new Error(
|
||||
'Invalid response from configured generateRequest filter'
|
||||
'Invalid response from configured generateRequest filter',
|
||||
);
|
||||
}
|
||||
initDataType = mappedInitData.initDataType;
|
||||
@@ -712,7 +712,7 @@ class EMEController implements ComponentAPI {
|
||||
this.log(
|
||||
`Generating key-session request for "${reason}": ${keyId} (init data type: ${initDataType} length: ${
|
||||
initData ? initData.byteLength : null
|
||||
})`
|
||||
})`,
|
||||
);
|
||||
|
||||
const licenseStatus = new EventEmitter();
|
||||
@@ -725,7 +725,7 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
const { messageType, message } = event;
|
||||
this.log(
|
||||
`"${messageType}" message event for session "${keySession.sessionId}" message size: ${message.byteLength}`
|
||||
`"${messageType}" message event for session "${keySession.sessionId}" message size: ${message.byteLength}`,
|
||||
);
|
||||
if (
|
||||
messageType === 'license-request' ||
|
||||
@@ -746,7 +746,7 @@ class EMEController implements ComponentAPI {
|
||||
};
|
||||
|
||||
context.mediaKeysSession.onkeystatuseschange = (
|
||||
event: MediaKeyMessageEvent
|
||||
event: MediaKeyMessageEvent,
|
||||
) => {
|
||||
const keySession = context.mediaKeysSession;
|
||||
if (!keySession) {
|
||||
@@ -777,8 +777,8 @@ class EMEController implements ComponentAPI {
|
||||
details: ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED,
|
||||
fatal: false,
|
||||
},
|
||||
'HDCP level output restricted'
|
||||
)
|
||||
'HDCP level output restricted',
|
||||
),
|
||||
);
|
||||
} else if (keyStatus === 'internal-error') {
|
||||
reject(
|
||||
@@ -788,8 +788,8 @@ class EMEController implements ComponentAPI {
|
||||
details: ErrorDetails.KEY_SYSTEM_STATUS_INTERNAL_ERROR,
|
||||
fatal: true,
|
||||
},
|
||||
`key status changed to "${keyStatus}"`
|
||||
)
|
||||
`key status changed to "${keyStatus}"`,
|
||||
),
|
||||
);
|
||||
} else if (keyStatus === 'expired') {
|
||||
reject(new Error('key expired while generating request'));
|
||||
@@ -797,14 +797,14 @@ class EMEController implements ComponentAPI {
|
||||
this.warn(`unhandled key status change "${keyStatus}"`);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return context.mediaKeysSession
|
||||
.generateRequest(initDataType, initData)
|
||||
.then(() => {
|
||||
this.log(
|
||||
`Request generated for key-session "${context.mediaKeysSession?.sessionId}" keyId: ${keyId}`
|
||||
`Request generated for key-session "${context.mediaKeysSession?.sessionId}" keyId: ${keyId}`,
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -815,7 +815,7 @@ class EMEController implements ComponentAPI {
|
||||
error,
|
||||
fatal: false,
|
||||
},
|
||||
`Error generating key-session request: ${error}`
|
||||
`Error generating key-session request: ${error}`,
|
||||
);
|
||||
})
|
||||
.then(() => keyUsablePromise)
|
||||
@@ -837,18 +837,18 @@ class EMEController implements ComponentAPI {
|
||||
`key status change "${status}" for keyStatuses keyId: ${Hex.hexDump(
|
||||
'buffer' in keyId
|
||||
? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength)
|
||||
: new Uint8Array(keyId)
|
||||
: new Uint8Array(keyId),
|
||||
)} session keyId: ${Hex.hexDump(
|
||||
new Uint8Array(mediaKeySessionContext.decryptdata.keyId || [])
|
||||
)} uri: ${mediaKeySessionContext.decryptdata.uri}`
|
||||
new Uint8Array(mediaKeySessionContext.decryptdata.keyId || []),
|
||||
)} uri: ${mediaKeySessionContext.decryptdata.uri}`,
|
||||
);
|
||||
mediaKeySessionContext.keyStatus = status;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private fetchServerCertificate(
|
||||
keySystem: KeySystems
|
||||
keySystem: KeySystems,
|
||||
): Promise<BufferSource | void> {
|
||||
const config = this.config;
|
||||
const Loader = config.loader;
|
||||
@@ -890,8 +890,8 @@ class EMEController implements ComponentAPI {
|
||||
...response,
|
||||
},
|
||||
},
|
||||
`"${keySystem}" certificate request failed (${url}). Status: ${response.code} (${response.text})`
|
||||
)
|
||||
`"${keySystem}" certificate request failed (${url}). Status: ${response.code} (${response.text})`,
|
||||
),
|
||||
);
|
||||
},
|
||||
onTimeout: (stats, context, networkDetails) => {
|
||||
@@ -908,8 +908,8 @@ class EMEController implements ComponentAPI {
|
||||
data: undefined,
|
||||
},
|
||||
},
|
||||
`"${keySystem}" certificate request timed out (${url})`
|
||||
)
|
||||
`"${keySystem}" certificate request timed out (${url})`,
|
||||
),
|
||||
);
|
||||
},
|
||||
onAbort: (stats, context, networkDetails) => {
|
||||
@@ -923,7 +923,7 @@ class EMEController implements ComponentAPI {
|
||||
private setMediaKeysServerCertificate(
|
||||
mediaKeys: MediaKeys,
|
||||
keySystem: KeySystems,
|
||||
cert: BufferSource
|
||||
cert: BufferSource,
|
||||
): Promise<MediaKeys> {
|
||||
return new Promise((resolve, reject) => {
|
||||
mediaKeys
|
||||
@@ -932,7 +932,7 @@ class EMEController implements ComponentAPI {
|
||||
this.log(
|
||||
`setServerCertificate ${
|
||||
success ? 'success' : 'not supported by CDM'
|
||||
} (${cert?.byteLength}) on "${keySystem}"`
|
||||
} (${cert?.byteLength}) on "${keySystem}"`,
|
||||
);
|
||||
resolve(mediaKeys);
|
||||
})
|
||||
@@ -946,8 +946,8 @@ class EMEController implements ComponentAPI {
|
||||
error,
|
||||
fatal: true,
|
||||
},
|
||||
error.message
|
||||
)
|
||||
error.message,
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -955,7 +955,7 @@ class EMEController implements ComponentAPI {
|
||||
|
||||
private renewLicense(
|
||||
context: MediaKeySessionContext,
|
||||
keyMessage: ArrayBuffer
|
||||
keyMessage: ArrayBuffer,
|
||||
): Promise<void> {
|
||||
return this.requestLicense(context, new Uint8Array(keyMessage)).then(
|
||||
(data: ArrayBuffer) => {
|
||||
@@ -968,17 +968,17 @@ class EMEController implements ComponentAPI {
|
||||
error,
|
||||
fatal: true,
|
||||
},
|
||||
error.message
|
||||
error.message,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private unpackPlayReadyKeyMessage(
|
||||
xhr: XMLHttpRequest,
|
||||
licenseChallenge: Uint8Array
|
||||
licenseChallenge: Uint8Array,
|
||||
): Uint8Array {
|
||||
// On Edge, the raw license message is UTF-16-encoded XML. We need
|
||||
// to unpack the Challenge element (base64-encoded string containing the
|
||||
@@ -987,7 +987,7 @@ class EMEController implements ComponentAPI {
|
||||
// For PlayReady CDMs, we need to dig the Challenge out of the XML.
|
||||
const xmlString = String.fromCharCode.apply(
|
||||
null,
|
||||
new Uint16Array(licenseChallenge.buffer)
|
||||
new Uint16Array(licenseChallenge.buffer),
|
||||
);
|
||||
if (!xmlString.includes('PlayReadyKeyMessage')) {
|
||||
// This does not appear to be a wrapped message as on Edge. Some
|
||||
@@ -999,7 +999,7 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
const keyMessageXml = new DOMParser().parseFromString(
|
||||
xmlString,
|
||||
'application/xml'
|
||||
'application/xml',
|
||||
);
|
||||
// Set request headers.
|
||||
const headers = keyMessageXml.querySelectorAll('HttpHeader');
|
||||
@@ -1026,7 +1026,7 @@ class EMEController implements ComponentAPI {
|
||||
xhr: XMLHttpRequest,
|
||||
url: string,
|
||||
keysListItem: MediaKeySessionContext,
|
||||
licenseChallenge: Uint8Array
|
||||
licenseChallenge: Uint8Array,
|
||||
): Promise<{ xhr: XMLHttpRequest; licenseChallenge: Uint8Array }> {
|
||||
const licenseXhrSetup = this.config.licenseXhrSetup;
|
||||
|
||||
@@ -1046,7 +1046,7 @@ class EMEController implements ComponentAPI {
|
||||
xhr,
|
||||
url,
|
||||
keysListItem,
|
||||
licenseChallenge
|
||||
licenseChallenge,
|
||||
);
|
||||
})
|
||||
.catch((error: Error) => {
|
||||
@@ -1062,7 +1062,7 @@ class EMEController implements ComponentAPI {
|
||||
xhr,
|
||||
url,
|
||||
keysListItem,
|
||||
licenseChallenge
|
||||
licenseChallenge,
|
||||
);
|
||||
})
|
||||
.then((licenseXhrSetupResult) => {
|
||||
@@ -1079,7 +1079,7 @@ class EMEController implements ComponentAPI {
|
||||
|
||||
private requestLicense(
|
||||
keySessionContext: MediaKeySessionContext,
|
||||
licenseChallenge: Uint8Array
|
||||
licenseChallenge: Uint8Array,
|
||||
): Promise<ArrayBuffer> {
|
||||
const keyLoadPolicy = this.config.keyLoadPolicy.default;
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -1098,7 +1098,7 @@ class EMEController implements ComponentAPI {
|
||||
this.log(
|
||||
`License received ${
|
||||
data instanceof ArrayBuffer ? data.byteLength : data
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
const licenseResponseCallback = this.config.licenseResponseCallback;
|
||||
if (licenseResponseCallback) {
|
||||
@@ -1107,7 +1107,7 @@ class EMEController implements ComponentAPI {
|
||||
this.hls,
|
||||
xhr,
|
||||
url,
|
||||
keySessionContext
|
||||
keySessionContext,
|
||||
);
|
||||
} catch (error) {
|
||||
this.error(error);
|
||||
@@ -1136,18 +1136,18 @@ class EMEController implements ComponentAPI {
|
||||
text: xhr.statusText,
|
||||
},
|
||||
},
|
||||
`License Request XHR failed (${url}). Status: ${xhr.status} (${xhr.statusText})`
|
||||
)
|
||||
`License Request XHR failed (${url}). Status: ${xhr.status} (${xhr.statusText})`,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const attemptsLeft =
|
||||
maxNumRetry - this._requestLicenseFailureCount + 1;
|
||||
this.warn(
|
||||
`Retrying license request, ${attemptsLeft} attempts left`
|
||||
`Retrying license request, ${attemptsLeft} attempts left`,
|
||||
);
|
||||
this.requestLicense(keySessionContext, licenseChallenge).then(
|
||||
resolve,
|
||||
reject
|
||||
reject,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1166,18 +1166,18 @@ class EMEController implements ComponentAPI {
|
||||
if (keySessionContext.keySystem == KeySystems.PLAYREADY) {
|
||||
licenseChallenge = this.unpackPlayReadyKeyMessage(
|
||||
xhr,
|
||||
licenseChallenge
|
||||
licenseChallenge,
|
||||
);
|
||||
}
|
||||
xhr.send(licenseChallenge);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private onMediaAttached(
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachedData
|
||||
data: MediaAttachedData,
|
||||
) {
|
||||
if (!this.config.emeEnabled) {
|
||||
return;
|
||||
@@ -1212,15 +1212,15 @@ class EMEController implements ComponentAPI {
|
||||
EMEController.CDMCleanupPromise = Promise.all(
|
||||
mediaKeysList
|
||||
.map((mediaKeySessionContext) =>
|
||||
this.removeSession(mediaKeySessionContext)
|
||||
this.removeSession(mediaKeySessionContext),
|
||||
)
|
||||
.concat(
|
||||
media?.setMediaKeys(null).catch((error) => {
|
||||
this.log(
|
||||
`Could not clear media keys: ${error}. media.src: ${media?.src}`
|
||||
`Could not clear media keys: ${error}. media.src: ${media?.src}`,
|
||||
);
|
||||
})
|
||||
)
|
||||
}),
|
||||
),
|
||||
)
|
||||
.then(() => {
|
||||
if (keySessionCount) {
|
||||
@@ -1230,7 +1230,7 @@ class EMEController implements ComponentAPI {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.log(
|
||||
`Could not close sessions and clear media keys: ${error}. media.src: ${media?.src}`
|
||||
`Could not close sessions and clear media keys: ${error}. media.src: ${media?.src}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1241,7 +1241,7 @@ class EMEController implements ComponentAPI {
|
||||
|
||||
private onManifestLoaded(
|
||||
event: Events.MANIFEST_LOADED,
|
||||
{ sessionKeys }: ManifestLoadedData
|
||||
{ sessionKeys }: ManifestLoadedData,
|
||||
) {
|
||||
if (!sessionKeys || !this.config.emeEnabled) {
|
||||
return;
|
||||
@@ -1256,22 +1256,22 @@ class EMEController implements ComponentAPI {
|
||||
}
|
||||
return formats;
|
||||
},
|
||||
[]
|
||||
[],
|
||||
);
|
||||
this.log(
|
||||
`Selecting key-system from session-keys ${keyFormats.join(', ')}`
|
||||
`Selecting key-system from session-keys ${keyFormats.join(', ')}`,
|
||||
);
|
||||
this.keyFormatPromise = this.getKeyFormatPromise(keyFormats);
|
||||
}
|
||||
}
|
||||
|
||||
private removeSession(
|
||||
mediaKeySessionContext: MediaKeySessionContext
|
||||
mediaKeySessionContext: MediaKeySessionContext,
|
||||
): Promise<void> | void {
|
||||
const { mediaKeysSession, licenseXhr } = mediaKeySessionContext;
|
||||
if (mediaKeysSession) {
|
||||
this.log(
|
||||
`Remove licenses and keys and close session ${mediaKeysSession.sessionId}`
|
||||
`Remove licenses and keys and close session ${mediaKeysSession.sessionId}`,
|
||||
);
|
||||
mediaKeysSession.onmessage = null;
|
||||
mediaKeysSession.onkeystatuseschange = null;
|
||||
@@ -1305,7 +1305,7 @@ class EMEKeyError extends Error {
|
||||
public readonly data: ErrorData;
|
||||
constructor(
|
||||
data: Omit<ErrorData, 'error'> & { error?: Error },
|
||||
message: string
|
||||
message: string,
|
||||
) {
|
||||
super(message);
|
||||
data.error ||= new Error(message);
|
||||
|
||||
@@ -159,7 +159,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
) {
|
||||
data.errorAction = this.getPlaylistRetryOrSwitchAction(
|
||||
data,
|
||||
levelIndex
|
||||
levelIndex,
|
||||
);
|
||||
} else {
|
||||
// Escalate to fatal if not retrying or switching
|
||||
@@ -173,7 +173,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
if (typeof context?.level === 'number') {
|
||||
data.errorAction = this.getPlaylistRetryOrSwitchAction(
|
||||
data,
|
||||
context.level
|
||||
context.level,
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -194,7 +194,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
// otherwise allow playlist retry count to reach max error retries
|
||||
data.errorAction = this.getPlaylistRetryOrSwitchAction(
|
||||
data,
|
||||
hls.loadLevel
|
||||
hls.loadLevel,
|
||||
);
|
||||
data.errorAction.action =
|
||||
NetworkErrorAction.SendAlternateToPenaltyBox;
|
||||
@@ -224,7 +224,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
case ErrorDetails.BUFFER_APPEND_ERROR:
|
||||
data.errorAction = this.getLevelSwitchAction(
|
||||
data,
|
||||
data.level ?? hls.loadLevel
|
||||
data.level ?? hls.loadLevel,
|
||||
);
|
||||
return;
|
||||
case ErrorDetails.INTERNAL_EXCEPTION:
|
||||
@@ -255,7 +255,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
|
||||
private getPlaylistRetryOrSwitchAction(
|
||||
data: ErrorData,
|
||||
levelIndex: number | null | undefined
|
||||
levelIndex: number | null | undefined,
|
||||
): IErrorAction {
|
||||
const hls = this.hls;
|
||||
const retryConfig = getRetryConfig(hls.config.playlistLoadPolicy, data);
|
||||
@@ -265,7 +265,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
retryConfig,
|
||||
retryCount,
|
||||
isTimeoutError(data),
|
||||
httpStatus
|
||||
httpStatus,
|
||||
);
|
||||
if (retry) {
|
||||
return {
|
||||
@@ -292,11 +292,11 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
const { fragLoadPolicy, keyLoadPolicy } = hls.config;
|
||||
const retryConfig = getRetryConfig(
|
||||
data.details.startsWith('key') ? keyLoadPolicy : fragLoadPolicy,
|
||||
data
|
||||
data,
|
||||
);
|
||||
const fragmentErrors = hls.levels.reduce(
|
||||
(acc, level) => acc + level.fragmentError,
|
||||
0
|
||||
0,
|
||||
);
|
||||
// Switch levels when out of retried or level index out of bounds
|
||||
if (level) {
|
||||
@@ -308,7 +308,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
retryConfig,
|
||||
fragmentErrors,
|
||||
isTimeoutError(data),
|
||||
httpStatus
|
||||
httpStatus,
|
||||
);
|
||||
if (retry) {
|
||||
return {
|
||||
@@ -332,7 +332,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
|
||||
private getLevelSwitchAction(
|
||||
data: ErrorData,
|
||||
levelIndex: number | null | undefined
|
||||
levelIndex: number | null | undefined,
|
||||
): IErrorAction {
|
||||
const hls = this.hls;
|
||||
if (levelIndex === null || levelIndex === undefined) {
|
||||
@@ -371,7 +371,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
isVideoCodecError &&
|
||||
levels.some(
|
||||
({ codecSet, audioCodec }) =>
|
||||
level.codecSet !== codecSet && level.audioCodec === audioCodec
|
||||
level.codecSet !== codecSet && level.audioCodec === audioCodec,
|
||||
);
|
||||
const { type: playlistErrorType, groupId: playlistErrorGroupId } =
|
||||
data.context ?? {};
|
||||
@@ -391,7 +391,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
const fragCandidate = findFragmentByPTS(
|
||||
data.frag,
|
||||
levelDetails.fragments,
|
||||
data.frag.start
|
||||
data.frag.start,
|
||||
);
|
||||
if (fragCandidate?.gap) {
|
||||
continue;
|
||||
@@ -491,7 +491,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
errorAction.resolved = true;
|
||||
}
|
||||
this.warn(
|
||||
`Restricting playback to HDCP-LEVEL of "${hls.maxHdcpLevel}" or lower`
|
||||
`Restricting playback to HDCP-LEVEL of "${hls.maxHdcpLevel}" or lower`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
@@ -536,7 +536,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
this.warn(
|
||||
`Switching to Redundant Stream ${newUrlId + 1}/${redundantLevels}: "${
|
||||
level.url[newUrlId]
|
||||
}" after ${data.details}`
|
||||
}" after ${data.details}`,
|
||||
);
|
||||
this.playlistError = 0;
|
||||
hls.levels.forEach((lv) => {
|
||||
@@ -566,7 +566,7 @@ export default class ErrorController implements NetworkComponentAPI {
|
||||
function checkExpired(
|
||||
penalizedRendition: PenalizedRendition,
|
||||
data: ErrorData,
|
||||
currentPenaltyState: PenalizedRendition | undefined
|
||||
currentPenaltyState: PenalizedRendition | undefined,
|
||||
): boolean {
|
||||
// Expire penalty for switching back to rendition after RENDITION_PENALTY_DURATION_MS
|
||||
if (
|
||||
@@ -582,7 +582,7 @@ function checkExpired(
|
||||
const candidateFrag = findFragmentByPTS(
|
||||
null,
|
||||
lastErrorDetails.fragments,
|
||||
position
|
||||
position,
|
||||
);
|
||||
if (candidateFrag && !candidateFrag.gap) {
|
||||
return true;
|
||||
|
||||
@@ -46,7 +46,7 @@ class FPSController implements ComponentAPI {
|
||||
|
||||
protected onMediaAttaching(
|
||||
event: Events.MEDIA_ATTACHING,
|
||||
data: MediaAttachingData
|
||||
data: MediaAttachingData,
|
||||
) {
|
||||
const config = this.hls.config;
|
||||
if (config.capLevelOnFPSDrop) {
|
||||
@@ -60,7 +60,7 @@ class FPSController implements ComponentAPI {
|
||||
self.clearInterval(this.timer);
|
||||
this.timer = self.setInterval(
|
||||
this.checkFPSInterval.bind(this),
|
||||
config.fpsDroppedMonitoringPeriod
|
||||
config.fpsDroppedMonitoringPeriod,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ class FPSController implements ComponentAPI {
|
||||
checkFPS(
|
||||
video: HTMLVideoElement,
|
||||
decodedFrames: number,
|
||||
droppedFrames: number
|
||||
droppedFrames: number,
|
||||
) {
|
||||
const currentTime = performance.now();
|
||||
if (decodedFrames) {
|
||||
@@ -92,7 +92,7 @@ class FPSController implements ComponentAPI {
|
||||
let currentLevel = hls.currentLevel;
|
||||
logger.warn(
|
||||
'drop FPS ratio greater than max allowed value for currentLevel: ' +
|
||||
currentLevel
|
||||
currentLevel,
|
||||
);
|
||||
if (
|
||||
currentLevel > 0 &&
|
||||
@@ -124,14 +124,14 @@ class FPSController implements ComponentAPI {
|
||||
this.checkFPS(
|
||||
video,
|
||||
videoPlaybackQuality.totalVideoFrames,
|
||||
videoPlaybackQuality.droppedVideoFrames
|
||||
videoPlaybackQuality.droppedVideoFrames,
|
||||
);
|
||||
} else {
|
||||
// HTMLVideoElement doesn't include the webkit types
|
||||
this.checkFPS(
|
||||
video,
|
||||
(video as any).webkitDecodedFrameCount as number,
|
||||
(video as any).webkitDroppedFrameCount as number
|
||||
(video as any).webkitDroppedFrameCount as number,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Fragment } from '../loader/fragment';
|
||||
export function findFragmentByPDT(
|
||||
fragments: Array<Fragment>,
|
||||
PDTValue: number | null,
|
||||
maxFragLookUpTolerance: number
|
||||
maxFragLookUpTolerance: number,
|
||||
): Fragment | null {
|
||||
if (
|
||||
PDTValue === null ||
|
||||
@@ -57,7 +57,7 @@ export function findFragmentByPTS(
|
||||
fragPrevious: Fragment | null,
|
||||
fragments: Array<Fragment>,
|
||||
bufferEnd: number = 0,
|
||||
maxFragLookUpTolerance: number = 0
|
||||
maxFragLookUpTolerance: number = 0,
|
||||
): Fragment | null {
|
||||
let fragNext: Fragment | null = null;
|
||||
if (fragPrevious) {
|
||||
@@ -79,7 +79,7 @@ export function findFragmentByPTS(
|
||||
// We might be seeking past the tolerance so find the best match
|
||||
const foundFragment = BinarySearch.search(
|
||||
fragments,
|
||||
fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance)
|
||||
fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance),
|
||||
);
|
||||
if (foundFragment && (foundFragment !== fragPrevious || !fragNext)) {
|
||||
return foundFragment;
|
||||
@@ -98,7 +98,7 @@ export function findFragmentByPTS(
|
||||
export function fragmentWithinToleranceTest(
|
||||
bufferEnd = 0,
|
||||
maxFragLookUpTolerance = 0,
|
||||
candidate: Fragment
|
||||
candidate: Fragment,
|
||||
) {
|
||||
// eagerly accept an accurate match (no tolerance)
|
||||
if (
|
||||
@@ -123,7 +123,7 @@ export function fragmentWithinToleranceTest(
|
||||
// Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments
|
||||
const candidateLookupTolerance = Math.min(
|
||||
maxFragLookUpTolerance,
|
||||
candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)
|
||||
candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0),
|
||||
);
|
||||
if (
|
||||
candidate.start + candidate.duration - candidateLookupTolerance <=
|
||||
@@ -152,12 +152,12 @@ export function fragmentWithinToleranceTest(
|
||||
export function pdtWithinToleranceTest(
|
||||
pdtBufferEnd: number,
|
||||
maxFragLookUpTolerance: number,
|
||||
candidate: Fragment
|
||||
candidate: Fragment,
|
||||
): boolean {
|
||||
const candidateLookupTolerance =
|
||||
Math.min(
|
||||
maxFragLookUpTolerance,
|
||||
candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)
|
||||
candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0),
|
||||
) * 1000;
|
||||
|
||||
// endProgramDateTime can be null, default to zero
|
||||
@@ -167,7 +167,7 @@ export function pdtWithinToleranceTest(
|
||||
|
||||
export function findFragWithCC(
|
||||
fragments: Fragment[],
|
||||
cc: number
|
||||
cc: number,
|
||||
): Fragment | null {
|
||||
return BinarySearch.search(fragments, (candidate) => {
|
||||
if (candidate.cc < cc) {
|
||||
|
||||
@@ -77,7 +77,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
*/
|
||||
public getAppendedFrag(
|
||||
position: number,
|
||||
levelType: PlaylistLevelType
|
||||
levelType: PlaylistLevelType,
|
||||
): Fragment | Part | null {
|
||||
const activeParts = this.activePartLists[levelType];
|
||||
if (activeParts) {
|
||||
@@ -106,7 +106,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
*/
|
||||
public getBufferedFrag(
|
||||
position: number,
|
||||
levelType: PlaylistLevelType
|
||||
levelType: PlaylistLevelType,
|
||||
): Fragment | null {
|
||||
const { fragments } = this;
|
||||
const keys = Object.keys(fragments);
|
||||
@@ -131,7 +131,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
elementaryStream: SourceBufferName,
|
||||
timeRange: TimeRanges,
|
||||
playlistType: PlaylistLevelType,
|
||||
appendedPart?: Part | null
|
||||
appendedPart?: Part | null,
|
||||
) {
|
||||
if (this.timeRanges) {
|
||||
this.timeRanges[elementaryStream] = timeRange;
|
||||
@@ -161,7 +161,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
const isNotBuffered = !this.isTimeBuffered(
|
||||
time.startPTS,
|
||||
time.endPTS,
|
||||
timeRange
|
||||
timeRange,
|
||||
);
|
||||
if (isNotBuffered) {
|
||||
// Unregister partial fragment as it needs to load again to be reused
|
||||
@@ -200,7 +200,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
frag,
|
||||
part,
|
||||
partial,
|
||||
timeRange
|
||||
timeRange,
|
||||
);
|
||||
});
|
||||
fragmentEntity.loaded = null;
|
||||
@@ -225,7 +225,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
return;
|
||||
}
|
||||
this.activePartLists[levelType] = activeParts.filter(
|
||||
(part) => (part.fragment.sn as number) >= snToKeep
|
||||
(part) => (part.fragment.sn as number) >= snToKeep,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
fragment: Fragment,
|
||||
part: Part | null,
|
||||
partial: boolean,
|
||||
timeRange: TimeRanges
|
||||
timeRange: TimeRanges,
|
||||
): FragmentBufferedRange {
|
||||
const buffered: FragmentBufferedRange = {
|
||||
time: [],
|
||||
@@ -354,7 +354,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
private isTimeBuffered(
|
||||
startPTS: number,
|
||||
endPTS: number,
|
||||
timeRange: TimeRanges
|
||||
timeRange: TimeRanges,
|
||||
): boolean {
|
||||
let startTime;
|
||||
let endTime;
|
||||
@@ -397,7 +397,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
|
||||
private onBufferAppended(
|
||||
event: Events.BUFFER_APPENDED,
|
||||
data: BufferAppendedData
|
||||
data: BufferAppendedData,
|
||||
) {
|
||||
const { frag, part, timeRanges } = data;
|
||||
if (frag.sn === 'initSegment') {
|
||||
@@ -419,7 +419,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
elementaryStream,
|
||||
timeRange,
|
||||
playlistType,
|
||||
part
|
||||
part,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -442,7 +442,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
end: number,
|
||||
playlistType: PlaylistLevelType,
|
||||
withGapOnly?: boolean,
|
||||
unbufferedOnly?: boolean
|
||||
unbufferedOnly?: boolean,
|
||||
) {
|
||||
if (withGapOnly && !this.hasGaps) {
|
||||
return;
|
||||
@@ -474,7 +474,7 @@ export class FragmentTracker implements ComponentAPI {
|
||||
if (activeParts) {
|
||||
const snToRemove = fragment.sn;
|
||||
this.activePartLists[fragment.type] = activeParts.filter(
|
||||
(part) => part.fragment.sn !== snToRemove
|
||||
(part) => part.fragment.sn !== snToRemove,
|
||||
);
|
||||
}
|
||||
delete this.fragments[fragKey];
|
||||
|
||||
@@ -64,8 +64,8 @@ export default class GapController {
|
||||
const stalledDuration = self.performance.now() - stalled;
|
||||
logger.warn(
|
||||
`playback not stuck anymore @${currentTime}, after ${Math.round(
|
||||
stalledDuration
|
||||
)}ms`
|
||||
stalledDuration,
|
||||
)}ms`,
|
||||
);
|
||||
this.stallReported = false;
|
||||
}
|
||||
@@ -161,7 +161,7 @@ export default class GapController {
|
||||
const bufferedWithHoles = BufferHelper.bufferInfo(
|
||||
media,
|
||||
currentTime,
|
||||
config.maxBufferHole
|
||||
config.maxBufferHole,
|
||||
);
|
||||
this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ export default class GapController {
|
||||
*/
|
||||
private _tryFixBufferStall(
|
||||
bufferInfo: BufferInfo,
|
||||
stalledDurationMs: number
|
||||
stalledDurationMs: number,
|
||||
) {
|
||||
const { config, fragmentTracker, media } = this;
|
||||
if (media === null) {
|
||||
@@ -226,7 +226,7 @@ export default class GapController {
|
||||
const error = new Error(
|
||||
`Playback stalling at @${
|
||||
media.currentTime
|
||||
} due to low buffer (${JSON.stringify(bufferInfo)})`
|
||||
} due to low buffer (${JSON.stringify(bufferInfo)})`,
|
||||
);
|
||||
logger.warn(error.message);
|
||||
hls.trigger(Events.ERROR, {
|
||||
@@ -268,7 +268,7 @@ export default class GapController {
|
||||
if (currentTime === 0) {
|
||||
const startFrag = fragmentTracker.getAppendedFrag(
|
||||
0,
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
if (startFrag && startTime < startFrag.end) {
|
||||
startGap = true;
|
||||
@@ -279,7 +279,7 @@ export default class GapController {
|
||||
partial ||
|
||||
fragmentTracker.getAppendedFrag(
|
||||
currentTime,
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
if (startProvisioned) {
|
||||
let moreToLoad = false;
|
||||
@@ -301,17 +301,17 @@ export default class GapController {
|
||||
}
|
||||
const targetTime = Math.max(
|
||||
startTime + SKIP_BUFFER_RANGE_START,
|
||||
currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS
|
||||
currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS,
|
||||
);
|
||||
logger.warn(
|
||||
`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`
|
||||
`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`,
|
||||
);
|
||||
this.moved = true;
|
||||
this.stalled = null;
|
||||
media.currentTime = targetTime;
|
||||
if (partial && !partial.gap) {
|
||||
const error = new Error(
|
||||
`fragment loaded with buffer holes, seeking from ${currentTime} to ${targetTime}`
|
||||
`fragment loaded with buffer holes, seeking from ${currentTime} to ${targetTime}`,
|
||||
);
|
||||
hls.trigger(Events.ERROR, {
|
||||
type: ErrorTypes.MEDIA_ERROR,
|
||||
@@ -344,7 +344,7 @@ export default class GapController {
|
||||
const targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
|
||||
// playback stalled in buffered area ... let's nudge currentTime to try to overcome this
|
||||
const error = new Error(
|
||||
`Nudging 'currentTime' from ${currentTime} to ${targetTime}`
|
||||
`Nudging 'currentTime' from ${currentTime} to ${targetTime}`,
|
||||
);
|
||||
logger.warn(error.message);
|
||||
media.currentTime = targetTime;
|
||||
@@ -356,7 +356,7 @@ export default class GapController {
|
||||
});
|
||||
} else {
|
||||
const error = new Error(
|
||||
`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`
|
||||
`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`,
|
||||
);
|
||||
logger.error(error.message);
|
||||
hls.trigger(Events.ERROR, {
|
||||
|
||||
@@ -61,7 +61,7 @@ function hexToArrayBuffer(str): ArrayBuffer {
|
||||
.replace(/^0x/, '')
|
||||
.replace(/([\da-fA-F]{2}) ?/g, '0x$1 ')
|
||||
.replace(/ +$/, '')
|
||||
.split(' ')
|
||||
.split(' '),
|
||||
).buffer;
|
||||
}
|
||||
class ID3TrackController implements ComponentAPI {
|
||||
@@ -110,7 +110,7 @@ class ID3TrackController implements ComponentAPI {
|
||||
// Add ID3 metatadata text track.
|
||||
protected onMediaAttached(
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachedData
|
||||
data: MediaAttachedData,
|
||||
): void {
|
||||
this.media = data.media;
|
||||
}
|
||||
@@ -154,7 +154,7 @@ class ID3TrackController implements ComponentAPI {
|
||||
|
||||
onFragParsingMetadata(
|
||||
event: Events.FRAG_PARSING_METADATA,
|
||||
data: FragParsingMetadataData
|
||||
data: FragParsingMetadataData,
|
||||
) {
|
||||
if (!this.media) {
|
||||
return;
|
||||
@@ -238,7 +238,7 @@ class ID3TrackController implements ComponentAPI {
|
||||
|
||||
onBufferFlushing(
|
||||
event: Events.BUFFER_FLUSHING,
|
||||
{ startOffset, endOffset, type }: BufferFlushingData
|
||||
{ startOffset, endOffset, type }: BufferFlushingData,
|
||||
) {
|
||||
const { id3Track, hls } = this;
|
||||
if (!hls) {
|
||||
@@ -282,7 +282,7 @@ class ID3TrackController implements ComponentAPI {
|
||||
// Remove cues from track not found in details.dateRanges
|
||||
if (id3Track) {
|
||||
const idsToRemove = Object.keys(dateRangeCuesAppended).filter(
|
||||
(id) => !ids.includes(id)
|
||||
(id) => !ids.includes(id),
|
||||
);
|
||||
for (let i = idsToRemove.length; i--; ) {
|
||||
const id = idsToRemove[i];
|
||||
@@ -314,7 +314,7 @@ class ID3TrackController implements ComponentAPI {
|
||||
let durationKnown = appendedDateRangeCues?.durationKnown || false;
|
||||
const startTime = dateRangeDateToTimelineSeconds(
|
||||
dateRange.startDate,
|
||||
dateTimeOffset
|
||||
dateTimeOffset,
|
||||
);
|
||||
let endTime = MAX_CUE_ENDTIME;
|
||||
const endDate = dateRange.endDate;
|
||||
@@ -338,7 +338,7 @@ class ID3TrackController implements ComponentAPI {
|
||||
if (nextDateRangeWithSameClass) {
|
||||
endTime = dateRangeDateToTimelineSeconds(
|
||||
nextDateRangeWithSameClass.startDate,
|
||||
dateTimeOffset
|
||||
dateTimeOffset,
|
||||
);
|
||||
durationKnown = true;
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ export default class LatencyController implements ComponentAPI {
|
||||
targetLatency +
|
||||
Math.min(
|
||||
this.stallCount * liveSyncOnStallIncrease,
|
||||
maxLiveSyncOnStallIncrease
|
||||
maxLiveSyncOnStallIncrease,
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ export default class LatencyController implements ComponentAPI {
|
||||
|
||||
private onMediaAttached(
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachingData
|
||||
data: MediaAttachingData,
|
||||
) {
|
||||
this.media = data.media;
|
||||
this.media.addEventListener('timeupdate', this.timeupdateHandler);
|
||||
@@ -168,7 +168,7 @@ export default class LatencyController implements ComponentAPI {
|
||||
|
||||
private onLevelUpdated(
|
||||
event: Events.LEVEL_UPDATED,
|
||||
{ details }: LevelUpdatedData
|
||||
{ details }: LevelUpdatedData,
|
||||
) {
|
||||
this.levelDetails = details;
|
||||
if (details.advanced) {
|
||||
@@ -186,7 +186,7 @@ export default class LatencyController implements ComponentAPI {
|
||||
this.stallCount++;
|
||||
if (this.levelDetails?.live) {
|
||||
logger.warn(
|
||||
'[playback-rate-controller]: Stall detected, adjusting target latency'
|
||||
'[playback-rate-controller]: Stall detected, adjusting target latency',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -219,7 +219,7 @@ export default class LatencyController implements ComponentAPI {
|
||||
// Playback further than one target duration from target can be considered DVR playback.
|
||||
const liveMinLatencyDuration = Math.min(
|
||||
this.maxLatency,
|
||||
targetLatency + levelDetails.targetduration
|
||||
targetLatency + levelDetails.targetduration,
|
||||
);
|
||||
const inLiveRange = distanceFromTarget < liveMinLatencyDuration;
|
||||
if (
|
||||
@@ -232,7 +232,7 @@ export default class LatencyController implements ComponentAPI {
|
||||
const rate =
|
||||
Math.round(
|
||||
(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled))) *
|
||||
20
|
||||
20,
|
||||
) / 20;
|
||||
media.playbackRate = Math.min(max, Math.max(1, rate));
|
||||
} else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
|
||||
|
||||
@@ -50,7 +50,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
|
||||
constructor(
|
||||
hls: Hls,
|
||||
contentSteeringController: ContentSteeringController | null
|
||||
contentSteeringController: ContentSteeringController | null,
|
||||
) {
|
||||
super(hls, '[level-controller]');
|
||||
this.steering = contentSteeringController;
|
||||
@@ -108,14 +108,14 @@ export default class LevelController extends BasePlaylistController {
|
||||
|
||||
private onManifestLoading(
|
||||
event: Events.MANIFEST_LOADING,
|
||||
data: ManifestLoadingData
|
||||
data: ManifestLoadingData,
|
||||
) {
|
||||
this.resetLevels();
|
||||
}
|
||||
|
||||
protected onManifestLoaded(
|
||||
event: Events.MANIFEST_LOADED,
|
||||
data: ManifestLoadedData
|
||||
data: ManifestLoadedData,
|
||||
) {
|
||||
const levels: Level[] = [];
|
||||
const levelSet: { [key: string]: Level } = {};
|
||||
@@ -169,7 +169,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
|
||||
private filterAndSortMediaOptions(
|
||||
unfilteredLevels: Level[],
|
||||
data: ManifestLoadedData
|
||||
data: ManifestLoadedData,
|
||||
) {
|
||||
let audioTracks: MediaPlaylist[] = [];
|
||||
let subtitleTracks: MediaPlaylist[] = [];
|
||||
@@ -189,14 +189,14 @@ export default class LevelController extends BasePlaylistController {
|
||||
(!audioCodec || areCodecsMediaSourceSupported(audioCodec, 'audio')) &&
|
||||
(!videoCodec || areCodecsMediaSourceSupported(videoCodec, 'video'))
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// remove audio-only and invalid video-range levels if we also have levels with video codecs or RESOLUTION signalled
|
||||
if ((resolutionFound || videoCodecFound) && audioCodecFound) {
|
||||
levels = levels.filter(
|
||||
({ videoCodec, videoRange, width, height }) =>
|
||||
(!!videoCodec || !!(width && height)) && isVideoRange(videoRange)
|
||||
(!!videoCodec || !!(width && height)) && isVideoRange(videoRange),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -207,12 +207,12 @@ export default class LevelController extends BasePlaylistController {
|
||||
if (unfilteredLevels.length) {
|
||||
this.warn(
|
||||
`One or more CODECS in variant not supported: ${JSON.stringify(
|
||||
unfilteredLevels[0].attrs
|
||||
)}`
|
||||
unfilteredLevels[0].attrs,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
const error = new Error(
|
||||
'no level with compatible codecs found in manifest'
|
||||
'no level with compatible codecs found in manifest',
|
||||
);
|
||||
this.hls.trigger(Events.ERROR, {
|
||||
type: ErrorTypes.MEDIA_ERROR,
|
||||
@@ -231,7 +231,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
audioTracks = data.audioTracks.filter(
|
||||
(track) =>
|
||||
!track.audioCodec ||
|
||||
areCodecsMediaSourceSupported(track.audioCodec, 'audio')
|
||||
areCodecsMediaSourceSupported(track.audioCodec, 'audio'),
|
||||
);
|
||||
// Assign ids after filtering as array indices by group-id
|
||||
assignTrackIdsByGroup(audioTracks);
|
||||
@@ -298,13 +298,13 @@ export default class LevelController extends BasePlaylistController {
|
||||
const firstLevelBitrate = firstLevelInPlaylist.bitrate;
|
||||
const bandwidthEstimate = this.hls.bandwidthEstimate;
|
||||
this.log(
|
||||
`manifest loaded, ${levels.length} level(s) found, first bitrate: ${firstLevelBitrate}`
|
||||
`manifest loaded, ${levels.length} level(s) found, first bitrate: ${firstLevelBitrate}`,
|
||||
);
|
||||
// Update default bwe to first variant bitrate as long it has not been configured or set
|
||||
if (this.hls.userConfig?.abrEwmaDefaultEstimate === undefined) {
|
||||
const startingBwEstimate = Math.min(
|
||||
firstLevelBitrate,
|
||||
this.hls.config.abrEwmaDefaultEstimateMax
|
||||
this.hls.config.abrEwmaDefaultEstimateMax,
|
||||
);
|
||||
if (
|
||||
startingBwEstimate > bandwidthEstimate &&
|
||||
@@ -401,7 +401,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
pathwayId ? ' with Pathway ' + pathwayId : ''
|
||||
} from level ${lastLevelIndex}${
|
||||
lastPathwayId ? ' with Pathway ' + lastPathwayId : ''
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
|
||||
const levelSwitchingData: LevelSwitchingData = Object.assign({}, level, {
|
||||
@@ -482,7 +482,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
// reset errors on the successful load of a fragment
|
||||
protected onFragBuffered(
|
||||
event: Events.FRAG_BUFFERED,
|
||||
{ frag }: FragBufferedData
|
||||
{ frag }: FragBufferedData,
|
||||
) {
|
||||
if (frag !== undefined && frag.type === PlaylistLevelType.MAIN) {
|
||||
const el = frag.elementaryStreams;
|
||||
@@ -492,7 +492,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
const level = this._levels[frag.level];
|
||||
if (level?.loadError) {
|
||||
this.log(
|
||||
`Resetting level error count of ${level.loadError} on frag buffered`
|
||||
`Resetting level error count of ${level.loadError} on frag buffered`,
|
||||
);
|
||||
level.loadError = 0;
|
||||
}
|
||||
@@ -526,7 +526,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
|
||||
protected onAudioTrackSwitched(
|
||||
event: Events.AUDIO_TRACK_SWITCHED,
|
||||
data: TrackSwitchedData
|
||||
data: TrackSwitchedData,
|
||||
) {
|
||||
const currentLevel = this.currentLevel;
|
||||
if (!currentLevel) {
|
||||
@@ -568,7 +568,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
url = hlsUrlParameters.addDirectives(url);
|
||||
} catch (error) {
|
||||
this.warn(
|
||||
`Could not construct new URL with HLS Delivery Directives: ${error}`
|
||||
`Could not construct new URL with HLS Delivery Directives: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -584,7 +584,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
: ''
|
||||
} with${pathwayId ? ' Pathway ' + pathwayId : ''} URI ${id + 1}/${
|
||||
currentLevel.url.length
|
||||
} ${url}`
|
||||
} ${url}`,
|
||||
);
|
||||
|
||||
// console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId);
|
||||
@@ -625,12 +625,12 @@ export default class LevelController extends BasePlaylistController {
|
||||
level.url = level.url.filter(filterLevelAndGroupByIdIndex);
|
||||
if (level.audioGroupIds) {
|
||||
level.audioGroupIds = level.audioGroupIds.filter(
|
||||
filterLevelAndGroupByIdIndex
|
||||
filterLevelAndGroupByIdIndex,
|
||||
);
|
||||
}
|
||||
if (level.textGroupIds) {
|
||||
level.textGroupIds = level.textGroupIds.filter(
|
||||
filterLevelAndGroupByIdIndex
|
||||
filterLevelAndGroupByIdIndex,
|
||||
);
|
||||
}
|
||||
level.urlId = 0;
|
||||
@@ -658,7 +658,7 @@ export default class LevelController extends BasePlaylistController {
|
||||
|
||||
private onLevelsUpdated(
|
||||
event: Events.LEVELS_UPDATED,
|
||||
{ levels }: LevelsUpdatedData
|
||||
{ levels }: LevelsUpdatedData,
|
||||
) {
|
||||
this._levels = levels;
|
||||
}
|
||||
|
||||
@@ -60,14 +60,14 @@ export default class StreamController
|
||||
constructor(
|
||||
hls: Hls,
|
||||
fragmentTracker: FragmentTracker,
|
||||
keyLoader: KeyLoader
|
||||
keyLoader: KeyLoader,
|
||||
) {
|
||||
super(
|
||||
hls,
|
||||
fragmentTracker,
|
||||
keyLoader,
|
||||
'[stream-controller]',
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
this._registerListeners();
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export default class StreamController
|
||||
hls.on(
|
||||
Events.FRAG_LOAD_EMERGENCY_ABORTED,
|
||||
this.onFragLoadEmergencyAborted,
|
||||
this
|
||||
this,
|
||||
);
|
||||
hls.on(Events.ERROR, this.onError, this);
|
||||
hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
||||
@@ -104,7 +104,7 @@ export default class StreamController
|
||||
hls.off(
|
||||
Events.FRAG_LOAD_EMERGENCY_ABORTED,
|
||||
this.onFragLoadEmergencyAborted,
|
||||
this
|
||||
this,
|
||||
);
|
||||
hls.off(Events.ERROR, this.onError, this);
|
||||
hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
||||
@@ -147,8 +147,8 @@ export default class StreamController
|
||||
if (lastCurrentTime > 0 && startPosition === -1) {
|
||||
this.log(
|
||||
`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(
|
||||
3
|
||||
)}`
|
||||
3,
|
||||
)}`,
|
||||
);
|
||||
startPosition = lastCurrentTime;
|
||||
}
|
||||
@@ -336,7 +336,7 @@ export default class StreamController
|
||||
levelDetails,
|
||||
bufferInfo,
|
||||
PlaylistLevelType.MAIN,
|
||||
maxBufLen
|
||||
maxBufLen,
|
||||
);
|
||||
}
|
||||
if (!frag) {
|
||||
@@ -352,7 +352,7 @@ export default class StreamController
|
||||
protected loadFragment(
|
||||
frag: Fragment,
|
||||
level: Level,
|
||||
targetBufferTime: number
|
||||
targetBufferTime: number,
|
||||
) {
|
||||
// Check if fragment is not loaded
|
||||
const fragState = this.fragmentTracker.getState(frag);
|
||||
@@ -365,7 +365,7 @@ export default class StreamController
|
||||
this._loadInitSegment(frag, level);
|
||||
} else if (this.bitrateTest) {
|
||||
this.log(
|
||||
`Fragment ${frag.sn} of level ${frag.level} is being downloaded to test bitrate and will not be buffered`
|
||||
`Fragment ${frag.sn} of level ${frag.level} is being downloaded to test bitrate and will not be buffered`,
|
||||
);
|
||||
this._loadBitrateTestFrag(frag, level);
|
||||
} else {
|
||||
@@ -380,7 +380,7 @@ export default class StreamController
|
||||
private getBufferedFrag(position) {
|
||||
return this.fragmentTracker.getBufferedFrag(
|
||||
position,
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -464,10 +464,10 @@ export default class StreamController
|
||||
Math.min(
|
||||
Math.max(
|
||||
fragDuration - this.config.maxFragLookUpTolerance,
|
||||
fragDuration * 0.5
|
||||
fragDuration * 0.5,
|
||||
),
|
||||
fragDuration * 0.75
|
||||
)
|
||||
fragDuration * 0.75,
|
||||
),
|
||||
);
|
||||
this.flushMainBuffer(startPts, Number.POSITIVE_INFINITY);
|
||||
}
|
||||
@@ -499,13 +499,13 @@ export default class StreamController
|
||||
super.flushMainBuffer(
|
||||
startOffset,
|
||||
endOffset,
|
||||
this.altAudio ? 'video' : null
|
||||
this.altAudio ? 'video' : null,
|
||||
);
|
||||
}
|
||||
|
||||
protected onMediaAttached(
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachedData
|
||||
data: MediaAttachedData,
|
||||
) {
|
||||
super.onMediaAttached(event, data);
|
||||
const media = data.media;
|
||||
@@ -517,7 +517,7 @@ export default class StreamController
|
||||
this.config,
|
||||
media,
|
||||
this.fragmentTracker,
|
||||
this.hls
|
||||
this.hls,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -555,7 +555,7 @@ export default class StreamController
|
||||
this.warn(
|
||||
`Main forward buffer length on "seeked" event ${
|
||||
bufferInfo ? bufferInfo.len : 'empty'
|
||||
})`
|
||||
})`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -577,7 +577,7 @@ export default class StreamController
|
||||
|
||||
private onManifestParsed(
|
||||
event: Events.MANIFEST_PARSED,
|
||||
data: ManifestParsedData
|
||||
data: ManifestParsedData,
|
||||
) {
|
||||
let aac = false;
|
||||
let heaac = false;
|
||||
@@ -598,7 +598,7 @@ export default class StreamController
|
||||
this.audioCodecSwitch = aac && heaac && !changeTypeSupported();
|
||||
if (this.audioCodecSwitch) {
|
||||
this.log(
|
||||
'Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC'
|
||||
'Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -636,7 +636,7 @@ export default class StreamController
|
||||
newDetails.lastPartSn
|
||||
? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]`
|
||||
: ''
|
||||
}, cc [${newDetails.startCC}, ${newDetails.endCC}] duration:${duration}`
|
||||
}, cc [${newDetails.startCC}, ${newDetails.endCC}] duration:${duration}`,
|
||||
);
|
||||
|
||||
const curLevel = levels[newLevelId];
|
||||
@@ -698,7 +698,7 @@ export default class StreamController
|
||||
const { levels } = this;
|
||||
if (!levels) {
|
||||
this.warn(
|
||||
`Levels were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`
|
||||
`Levels were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -706,7 +706,7 @@ export default class StreamController
|
||||
const details = currentLevel.details as LevelDetails;
|
||||
if (!details) {
|
||||
this.warn(
|
||||
`Dropping fragment ${frag.sn} of level ${frag.level} after level details were reset`
|
||||
`Dropping fragment ${frag.sn} of level ${frag.level} after level details were reset`,
|
||||
);
|
||||
this.fragmentTracker.removeFragment(frag);
|
||||
return;
|
||||
@@ -726,7 +726,7 @@ export default class StreamController
|
||||
this.hls,
|
||||
PlaylistLevelType.MAIN,
|
||||
this._handleTransmuxComplete.bind(this),
|
||||
this._handleTransmuxerFlush.bind(this)
|
||||
this._handleTransmuxerFlush.bind(this),
|
||||
));
|
||||
const partIndex = part ? part.index : -1;
|
||||
const partial = partIndex !== -1;
|
||||
@@ -736,7 +736,7 @@ export default class StreamController
|
||||
frag.stats.chunkCount,
|
||||
payload.byteLength,
|
||||
partIndex,
|
||||
partial
|
||||
partial,
|
||||
);
|
||||
const initPTS = this.initPTS[frag.cc];
|
||||
|
||||
@@ -750,13 +750,13 @@ export default class StreamController
|
||||
details.totalduration,
|
||||
accurateTimeOffset,
|
||||
chunkMeta,
|
||||
initPTS
|
||||
initPTS,
|
||||
);
|
||||
}
|
||||
|
||||
private onAudioTrackSwitching(
|
||||
event: Events.AUDIO_TRACK_SWITCHING,
|
||||
data: AudioTrackSwitchingData
|
||||
data: AudioTrackSwitchingData,
|
||||
) {
|
||||
// if any URL found on new audio track, it is an alternate audio track
|
||||
const fromAltAudio = this.altAudio;
|
||||
@@ -767,7 +767,7 @@ export default class StreamController
|
||||
if (!altAudio) {
|
||||
if (this.mediaBuffer !== this.media) {
|
||||
this.log(
|
||||
'Switching on main audio, use media.buffered to schedule main fragment loading'
|
||||
'Switching on main audio, use media.buffered to schedule main fragment loading',
|
||||
);
|
||||
this.mediaBuffer = this.media;
|
||||
const fragCurrent = this.fragCurrent;
|
||||
@@ -801,7 +801,7 @@ export default class StreamController
|
||||
|
||||
private onAudioTrackSwitched(
|
||||
event: Events.AUDIO_TRACK_SWITCHED,
|
||||
data: AudioTrackSwitchedData
|
||||
data: AudioTrackSwitchedData,
|
||||
) {
|
||||
const trackId = data.id;
|
||||
const altAudio = !!this.hls.audioTracks[trackId].url;
|
||||
@@ -810,7 +810,7 @@ export default class StreamController
|
||||
// if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered
|
||||
if (videoBuffer && this.mediaBuffer !== videoBuffer) {
|
||||
this.log(
|
||||
'Switching on alternate audio, use video.buffered to schedule main fragment loading'
|
||||
'Switching on alternate audio, use video.buffered to schedule main fragment loading',
|
||||
);
|
||||
this.mediaBuffer = videoBuffer;
|
||||
}
|
||||
@@ -821,7 +821,7 @@ export default class StreamController
|
||||
|
||||
private onBufferCreated(
|
||||
event: Events.BUFFER_CREATED,
|
||||
data: BufferCreatedData
|
||||
data: BufferCreatedData,
|
||||
) {
|
||||
const tracks = data.tracks;
|
||||
let mediaTrack;
|
||||
@@ -845,7 +845,7 @@ export default class StreamController
|
||||
}
|
||||
if (alternate && mediaTrack) {
|
||||
this.log(
|
||||
`Alternate track found, use ${name}.buffered to schedule main fragment loading`
|
||||
`Alternate track found, use ${name}.buffered to schedule main fragment loading`,
|
||||
);
|
||||
this.mediaBuffer = mediaTrack.buffer;
|
||||
} else {
|
||||
@@ -864,7 +864,7 @@ export default class StreamController
|
||||
this.warn(
|
||||
`Fragment ${frag.sn}${part ? ' p: ' + part.index : ''} of level ${
|
||||
frag.level
|
||||
} finished buffering, but was aborted. state: ${this.state}`
|
||||
} finished buffering, but was aborted. state: ${this.state}`,
|
||||
);
|
||||
if (this.state === State.PARSED) {
|
||||
this.state = State.IDLE;
|
||||
@@ -873,7 +873,7 @@ export default class StreamController
|
||||
}
|
||||
const stats = part ? part.stats : frag.stats;
|
||||
this.fragLastKbps = Math.round(
|
||||
(8 * stats.total) / (stats.buffering.end - stats.loading.first)
|
||||
(8 * stats.total) / (stats.buffering.end - stats.loading.first),
|
||||
);
|
||||
if (frag.sn !== 'initSegment') {
|
||||
this.fragPrevious = frag;
|
||||
@@ -959,7 +959,7 @@ export default class StreamController
|
||||
|
||||
private onBufferFlushed(
|
||||
event: Events.BUFFER_FLUSHED,
|
||||
{ type }: BufferFlushedData
|
||||
{ type }: BufferFlushedData,
|
||||
) {
|
||||
if (
|
||||
type !== ElementaryStreamTypes.AUDIO ||
|
||||
@@ -976,7 +976,7 @@ export default class StreamController
|
||||
|
||||
private onLevelsUpdated(
|
||||
event: Events.LEVELS_UPDATED,
|
||||
data: LevelsUpdatedData
|
||||
data: LevelsUpdatedData,
|
||||
) {
|
||||
if (this.level > -1 && this.fragCurrent) {
|
||||
this.level = this.fragCurrent.level;
|
||||
@@ -1003,7 +1003,7 @@ export default class StreamController
|
||||
if (startPosition >= 0 && currentTime < startPosition) {
|
||||
if (media.seeking) {
|
||||
this.log(
|
||||
`could not seek to ${startPosition}, already seeking at ${currentTime}`
|
||||
`could not seek to ${startPosition}, already seeking at ${currentTime}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1020,7 +1020,7 @@ export default class StreamController
|
||||
this.startPosition = startPosition;
|
||||
}
|
||||
this.log(
|
||||
`seek to target start position ${startPosition} from current time ${currentTime}`
|
||||
`seek to target start position ${startPosition} from current time ${currentTime}`,
|
||||
);
|
||||
media.currentTime = startPosition;
|
||||
}
|
||||
@@ -1095,7 +1095,7 @@ export default class StreamController
|
||||
level,
|
||||
initSegment.tracks,
|
||||
mapFragment,
|
||||
chunkMeta
|
||||
chunkMeta,
|
||||
);
|
||||
hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, {
|
||||
frag: mapFragment,
|
||||
@@ -1164,7 +1164,7 @@ export default class StreamController
|
||||
endPTS,
|
||||
frag.start,
|
||||
endDTS,
|
||||
true
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1173,7 +1173,7 @@ export default class StreamController
|
||||
startPTS,
|
||||
endPTS,
|
||||
startDTS,
|
||||
endDTS
|
||||
endDTS,
|
||||
);
|
||||
if (this.backtrackFragment) {
|
||||
this.backtrackFragment = frag;
|
||||
@@ -1183,7 +1183,7 @@ export default class StreamController
|
||||
frag,
|
||||
part,
|
||||
chunkMeta,
|
||||
isFirstFragment || isFirstInDiscontinuity
|
||||
isFirstFragment || isFirstInDiscontinuity,
|
||||
);
|
||||
} else if (isFirstFragment || isFirstInDiscontinuity) {
|
||||
// Mark segment with a gap to avoid loop loading
|
||||
@@ -1209,7 +1209,7 @@ export default class StreamController
|
||||
startPTS,
|
||||
endPTS,
|
||||
startDTS,
|
||||
endDTS
|
||||
endDTS,
|
||||
);
|
||||
this.bufferFragmentData(audio, frag, part, chunkMeta);
|
||||
}
|
||||
@@ -1238,7 +1238,7 @@ export default class StreamController
|
||||
currentLevel: Level,
|
||||
tracks: TrackSet,
|
||||
frag: Fragment,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
) {
|
||||
if (this.state !== State.PARSING) {
|
||||
return;
|
||||
@@ -1283,7 +1283,7 @@ export default class StreamController
|
||||
}
|
||||
if (currentLevel.audioCodec && currentLevel.audioCodec !== audioCodec) {
|
||||
this.log(
|
||||
`Swapping manifest audio codec "${currentLevel.audioCodec}" for "${audioCodec}"`
|
||||
`Swapping manifest audio codec "${currentLevel.audioCodec}" for "${audioCodec}"`,
|
||||
);
|
||||
}
|
||||
audio.levelCodec = audioCodec;
|
||||
@@ -1293,7 +1293,7 @@ export default class StreamController
|
||||
audio.container
|
||||
}, codecs[selected/level/parsed]=[${audioCodec || ''}/${
|
||||
currentLevel.audioCodec || ''
|
||||
}/${audio.codec}]`
|
||||
}/${audio.codec}]`,
|
||||
);
|
||||
}
|
||||
if (video) {
|
||||
@@ -1304,12 +1304,12 @@ export default class StreamController
|
||||
video.container
|
||||
}, codecs[level/parsed]=[${currentLevel.videoCodec || ''}/${
|
||||
video.codec
|
||||
}]`
|
||||
}]`,
|
||||
);
|
||||
}
|
||||
if (audiovideo) {
|
||||
this.log(
|
||||
`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.codecs}/${audiovideo.codec}]`
|
||||
`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.codecs}/${audiovideo.codec}]`,
|
||||
);
|
||||
}
|
||||
this.hls.trigger(Events.BUFFER_CODECS, tracks);
|
||||
@@ -1335,7 +1335,7 @@ export default class StreamController
|
||||
public getMainFwdBufferInfo(): BufferInfo | null {
|
||||
return this.getFwdBufferInfo(
|
||||
this.mediaBuffer ? this.mediaBuffer : this.media,
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -47,14 +47,14 @@ export class SubtitleStreamController
|
||||
constructor(
|
||||
hls: Hls,
|
||||
fragmentTracker: FragmentTracker,
|
||||
keyLoader: KeyLoader
|
||||
keyLoader: KeyLoader,
|
||||
) {
|
||||
super(
|
||||
hls,
|
||||
fragmentTracker,
|
||||
keyLoader,
|
||||
'[subtitle-stream-controller]',
|
||||
PlaylistLevelType.SUBTITLE
|
||||
PlaylistLevelType.SUBTITLE,
|
||||
);
|
||||
this._registerListeners();
|
||||
}
|
||||
@@ -124,7 +124,7 @@ export class SubtitleStreamController
|
||||
|
||||
onSubtitleFragProcessed(
|
||||
event: Events.SUBTITLE_FRAG_PROCESSED,
|
||||
data: SubtitleFragProcessed
|
||||
data: SubtitleFragProcessed,
|
||||
) {
|
||||
const { frag, success } = data;
|
||||
this.fragPrevious = frag;
|
||||
@@ -187,7 +187,7 @@ export class SubtitleStreamController
|
||||
this.fragmentTracker.removeFragmentsInRange(
|
||||
startOffset,
|
||||
endOffsetSubtitles,
|
||||
PlaylistLevelType.SUBTITLE
|
||||
PlaylistLevelType.SUBTITLE,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -217,11 +217,11 @@ export class SubtitleStreamController
|
||||
// Got all new subtitle levels.
|
||||
onSubtitleTracksUpdated(
|
||||
event: Events.SUBTITLE_TRACKS_UPDATED,
|
||||
{ subtitleTracks }: SubtitleTracksUpdatedData
|
||||
{ subtitleTracks }: SubtitleTracksUpdatedData,
|
||||
) {
|
||||
if (subtitleOptionsIdentical(this.levels, subtitleTracks)) {
|
||||
this.levels = subtitleTracks.map(
|
||||
(mediaPlaylist) => new Level(mediaPlaylist)
|
||||
(mediaPlaylist) => new Level(mediaPlaylist),
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -234,7 +234,7 @@ export class SubtitleStreamController
|
||||
this.fragmentTracker.removeFragmentsInRange(
|
||||
0,
|
||||
Number.POSITIVE_INFINITY,
|
||||
PlaylistLevelType.SUBTITLE
|
||||
PlaylistLevelType.SUBTITLE,
|
||||
);
|
||||
this.fragPrevious = null;
|
||||
this.mediaBuffer = null;
|
||||
@@ -242,7 +242,7 @@ export class SubtitleStreamController
|
||||
|
||||
onSubtitleTrackSwitch(
|
||||
event: Events.SUBTITLE_TRACK_SWITCH,
|
||||
data: TrackSwitchedData
|
||||
data: TrackSwitchedData,
|
||||
) {
|
||||
this.currentTrackId = data.id;
|
||||
|
||||
@@ -266,7 +266,7 @@ export class SubtitleStreamController
|
||||
// Got a new set of subtitle fragments.
|
||||
onSubtitleTrackLoaded(
|
||||
event: Events.SUBTITLE_TRACK_LOADED,
|
||||
data: TrackLoadedData
|
||||
data: TrackLoadedData,
|
||||
) {
|
||||
const { details: newDetails, id: trackId } = data;
|
||||
const { currentTrackId, levels } = this;
|
||||
@@ -324,7 +324,7 @@ export class SubtitleStreamController
|
||||
null,
|
||||
newDetails.fragments,
|
||||
this.media.currentTime,
|
||||
0
|
||||
0,
|
||||
);
|
||||
if (!foundFrag) {
|
||||
this.warn('Subtitle playlist not aligned with playback');
|
||||
@@ -356,7 +356,7 @@ export class SubtitleStreamController
|
||||
.decrypt(
|
||||
new Uint8Array(payload),
|
||||
decryptData.key.buffer,
|
||||
decryptData.iv.buffer
|
||||
decryptData.iv.buffer,
|
||||
)
|
||||
.catch((err) => {
|
||||
hls.trigger(Events.ERROR, {
|
||||
@@ -404,13 +404,13 @@ export class SubtitleStreamController
|
||||
const bufferedInfo = BufferHelper.bufferedInfo(
|
||||
this.tracksBuffered[this.currentTrackId] || [],
|
||||
currentTime,
|
||||
config.maxBufferHole
|
||||
config.maxBufferHole,
|
||||
);
|
||||
const { end: targetBufferTime, len: bufferLen } = bufferedInfo;
|
||||
|
||||
const mainBufferInfo = this.getFwdBufferInfo(
|
||||
this.media,
|
||||
PlaylistLevelType.MAIN
|
||||
PlaylistLevelType.MAIN,
|
||||
);
|
||||
const trackDetails = track.details as LevelDetails;
|
||||
const maxBufLen =
|
||||
@@ -434,7 +434,7 @@ export class SubtitleStreamController
|
||||
fragPrevious,
|
||||
fragments,
|
||||
Math.max(fragments[0].start, targetBufferTime),
|
||||
lookupTolerance
|
||||
lookupTolerance,
|
||||
);
|
||||
if (
|
||||
!foundFrag &&
|
||||
@@ -482,7 +482,7 @@ export class SubtitleStreamController
|
||||
protected loadFragment(
|
||||
frag: Fragment,
|
||||
level: Level,
|
||||
targetBufferTime: number
|
||||
targetBufferTime: number,
|
||||
) {
|
||||
this.fragCurrent = frag;
|
||||
if (frag.sn === 'initSegment') {
|
||||
@@ -495,7 +495,7 @@ export class SubtitleStreamController
|
||||
|
||||
get mediaBufferTimeRanges(): Bufferable {
|
||||
return new BufferableInstance(
|
||||
this.tracksBuffered[this.currentTrackId] || []
|
||||
this.tracksBuffered[this.currentTrackId] || [],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -507,12 +507,12 @@ class BufferableInstance implements Bufferable {
|
||||
const getRange = (
|
||||
name: 'start' | 'end',
|
||||
index: number,
|
||||
length: number
|
||||
length: number,
|
||||
): number => {
|
||||
index = index >>> 0;
|
||||
if (index > length - 1) {
|
||||
throw new DOMException(
|
||||
`Failed to execute '${name}' on 'TimeRanges': The index provided (${index}) is greater than the maximum bound (${length})`
|
||||
`Failed to execute '${name}' on 'TimeRanges': The index provided (${index}) is greater than the maximum bound (${length})`,
|
||||
);
|
||||
}
|
||||
return timeranges[index][name];
|
||||
|
||||
@@ -79,7 +79,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
// Listen for subtitle track change, then extract the current track ID.
|
||||
protected onMediaAttached(
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachedData
|
||||
data: MediaAttachedData,
|
||||
): void {
|
||||
this.media = data.media;
|
||||
if (!this.media) {
|
||||
@@ -99,7 +99,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
} else {
|
||||
this.media.textTracks.addEventListener(
|
||||
'change',
|
||||
this.asyncPollTrackChange
|
||||
this.asyncPollTrackChange,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
self.clearInterval(this.subtitlePollingInterval);
|
||||
this.subtitlePollingInterval = self.setInterval(
|
||||
this.trackChangeListener,
|
||||
timeout
|
||||
timeout,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
if (!this.useTextTrackPolling) {
|
||||
this.media.textTracks.removeEventListener(
|
||||
'change',
|
||||
this.asyncPollTrackChange
|
||||
this.asyncPollTrackChange,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -150,14 +150,14 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
// Fired whenever a new manifest is loaded.
|
||||
protected onManifestParsed(
|
||||
event: Events.MANIFEST_PARSED,
|
||||
data: ManifestParsedData
|
||||
data: ManifestParsedData,
|
||||
): void {
|
||||
this.tracks = data.subtitleTracks;
|
||||
}
|
||||
|
||||
protected onSubtitleTrackLoaded(
|
||||
event: Events.SUBTITLE_TRACK_LOADED,
|
||||
data: TrackLoadedData
|
||||
data: TrackLoadedData,
|
||||
): void {
|
||||
const { id, details } = data;
|
||||
const { trackId } = this;
|
||||
@@ -171,7 +171,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
const curDetails = currentTrack.details;
|
||||
currentTrack.details = data.details;
|
||||
this.log(
|
||||
`subtitle track ${id} loaded [${details.startSN}-${details.endSN}]`
|
||||
`subtitle track ${id} loaded [${details.startSN}-${details.endSN}]`,
|
||||
);
|
||||
|
||||
if (id === this.trackId) {
|
||||
@@ -181,14 +181,14 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
|
||||
protected onLevelLoading(
|
||||
event: Events.LEVEL_LOADING,
|
||||
data: LevelLoadingData
|
||||
data: LevelLoadingData,
|
||||
): void {
|
||||
this.switchLevel(data.level);
|
||||
}
|
||||
|
||||
protected onLevelSwitching(
|
||||
event: Events.LEVEL_SWITCHING,
|
||||
data: LevelSwitchingData
|
||||
data: LevelSwitchingData,
|
||||
): void {
|
||||
this.switchLevel(data.level);
|
||||
}
|
||||
@@ -204,7 +204,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
: undefined;
|
||||
if (this.groupId !== textGroupId) {
|
||||
const subtitleTracks = this.tracks.filter(
|
||||
(track): boolean => !textGroupId || track.groupId === textGroupId
|
||||
(track): boolean => !textGroupId || track.groupId === textGroupId,
|
||||
);
|
||||
this.tracksInGroup = subtitleTracks;
|
||||
const initialTrackId =
|
||||
@@ -215,7 +215,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
subtitleTracks,
|
||||
};
|
||||
this.log(
|
||||
`Updating subtitle tracks, ${subtitleTracks.length} track(s) found in "${textGroupId}" group-id`
|
||||
`Updating subtitle tracks, ${subtitleTracks.length} track(s) found in "${textGroupId}" group-id`,
|
||||
);
|
||||
this.hls.trigger(Events.SUBTITLE_TRACKS_UPDATED, subtitleTracksUpdated);
|
||||
|
||||
@@ -289,7 +289,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
url = hlsUrlParameters.addDirectives(url);
|
||||
} catch (error) {
|
||||
this.warn(
|
||||
`Could not construct new URL with HLS Delivery Directives: ${error}`
|
||||
`Could not construct new URL with HLS Delivery Directives: ${error}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -316,7 +316,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
|
||||
const textTracks = filterSubtitleTracks(media.textTracks);
|
||||
const groupTracks = textTracks.filter(
|
||||
(track) => (track as any).groupId === this.groupId
|
||||
(track) => (track as any).groupId === this.groupId,
|
||||
);
|
||||
if (newId === -1) {
|
||||
[].slice.call(textTracks).forEach((track) => {
|
||||
@@ -341,7 +341,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
*/
|
||||
private setSubtitleTrack(
|
||||
newId: number,
|
||||
lastTrack: MediaPlaylist | undefined
|
||||
lastTrack: MediaPlaylist | undefined,
|
||||
): void {
|
||||
const tracks = this.tracksInGroup;
|
||||
|
||||
@@ -376,7 +376,7 @@ class SubtitleTrackController extends BasePlaylistController {
|
||||
`Switching to subtitle-track ${newId}` +
|
||||
(track
|
||||
? ` "${track.name}" lang:${track.lang} group:${track.groupId}`
|
||||
: '')
|
||||
: ''),
|
||||
);
|
||||
this.trackId = newId;
|
||||
if (track) {
|
||||
|
||||
@@ -149,7 +149,7 @@ export class TimelineController implements ComponentAPI {
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
screen: CaptionScreen,
|
||||
cueRanges: Array<[number, number]>
|
||||
cueRanges: Array<[number, number]>,
|
||||
) {
|
||||
// skip cues which overlap more than 50% with previously parsed time ranges
|
||||
let merged = false;
|
||||
@@ -159,7 +159,7 @@ export class TimelineController implements ComponentAPI {
|
||||
cueRange[0],
|
||||
cueRange[1],
|
||||
startTime,
|
||||
endTime
|
||||
endTime,
|
||||
);
|
||||
if (overlap >= 0) {
|
||||
cueRange[0] = Math.min(cueRange[0], startTime);
|
||||
@@ -190,7 +190,7 @@ export class TimelineController implements ComponentAPI {
|
||||
// Triggered when an initial PTS is found; used for synchronisation of WebVTT.
|
||||
private onInitPtsFound(
|
||||
event: Events.INIT_PTS_FOUND,
|
||||
{ frag, id, initPTS, timescale }: InitPTSFoundData
|
||||
{ frag, id, initPTS, timescale }: InitPTSFoundData,
|
||||
) {
|
||||
const { unparsedVttFrags } = this;
|
||||
if (id === 'main') {
|
||||
@@ -274,7 +274,7 @@ export class TimelineController implements ComponentAPI {
|
||||
private createTextTrack(
|
||||
kind: TextTrackKind,
|
||||
label: string,
|
||||
lang?: string
|
||||
lang?: string,
|
||||
): TextTrack | undefined {
|
||||
const media = this.media;
|
||||
if (!media) {
|
||||
@@ -285,7 +285,7 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
private onMediaAttaching(
|
||||
event: Events.MEDIA_ATTACHING,
|
||||
data: MediaAttachingData
|
||||
data: MediaAttachingData,
|
||||
) {
|
||||
this.media = data.media;
|
||||
this._cleanTracks();
|
||||
@@ -338,7 +338,7 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
private onSubtitleTracksUpdated(
|
||||
event: Events.SUBTITLE_TRACKS_UPDATED,
|
||||
data: SubtitleTracksUpdatedData
|
||||
data: SubtitleTracksUpdatedData,
|
||||
) {
|
||||
const tracks: Array<MediaPlaylist> = data.subtitleTracks || [];
|
||||
const hasIMSC1 = tracks.some((track) => track.textCodec === IMSC1_CODEC);
|
||||
@@ -379,7 +379,7 @@ export class TimelineController implements ComponentAPI {
|
||||
textTrack = this.createTextTrack(
|
||||
textTrackKind,
|
||||
track.name,
|
||||
track.lang
|
||||
track.lang,
|
||||
);
|
||||
if (textTrack) {
|
||||
textTrack.mode = 'disabled';
|
||||
@@ -408,7 +408,7 @@ export class TimelineController implements ComponentAPI {
|
||||
}
|
||||
|
||||
private _captionsOrSubtitlesFromCharacteristics(
|
||||
track: MediaPlaylist
|
||||
track: MediaPlaylist,
|
||||
): TextTrackKind {
|
||||
if (track.attrs.CHARACTERISTICS) {
|
||||
if (
|
||||
@@ -424,12 +424,12 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
private onManifestLoaded(
|
||||
event: Events.MANIFEST_LOADED,
|
||||
data: ManifestLoadedData
|
||||
data: ManifestLoadedData,
|
||||
) {
|
||||
if (this.config.enableCEA708Captions && data.captions) {
|
||||
data.captions.forEach((captionsTrack) => {
|
||||
const instreamIdMatch = /(?:CC|SERVICE)([1-4])/.exec(
|
||||
captionsTrack.instreamId as string
|
||||
captionsTrack.instreamId as string,
|
||||
);
|
||||
if (!instreamIdMatch) {
|
||||
return;
|
||||
@@ -483,7 +483,7 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
private onFragLoaded(
|
||||
event: Events.FRAG_LOADED,
|
||||
data: FragDecryptedData | FragLoadedData
|
||||
data: FragDecryptedData | FragLoadedData,
|
||||
) {
|
||||
const { frag, payload } = data;
|
||||
if (frag.type === PlaylistLevelType.SUBTITLE) {
|
||||
@@ -543,7 +543,7 @@ export class TimelineController implements ComponentAPI {
|
||||
frag: frag,
|
||||
error,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -593,7 +593,7 @@ export class TimelineController implements ComponentAPI {
|
||||
frag: frag,
|
||||
error,
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -610,7 +610,7 @@ export class TimelineController implements ComponentAPI {
|
||||
},
|
||||
() => {
|
||||
trackPlaylistMedia.textCodec = 'wvtt';
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -639,7 +639,7 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
private onFragDecrypted(
|
||||
event: Events.FRAG_DECRYPTED,
|
||||
data: FragDecryptedData
|
||||
data: FragDecryptedData,
|
||||
) {
|
||||
const { frag } = data;
|
||||
if (frag.type === PlaylistLevelType.SUBTITLE) {
|
||||
@@ -654,7 +654,7 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
private onFragParsingUserdata(
|
||||
event: Events.FRAG_PARSING_USERDATA,
|
||||
data: FragParsingUserdataData
|
||||
data: FragParsingUserdataData,
|
||||
) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
@@ -685,7 +685,7 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
onBufferFlushing(
|
||||
event: Events.BUFFER_FLUSHING,
|
||||
{ startOffset, endOffset, endOffsetSubtitles, type }: BufferFlushingData
|
||||
{ startOffset, endOffset, endOffsetSubtitles, type }: BufferFlushingData,
|
||||
) {
|
||||
const { media } = this;
|
||||
if (!media || media.currentTime < endOffset) {
|
||||
@@ -696,7 +696,7 @@ export class TimelineController implements ComponentAPI {
|
||||
if (!type || type === 'video') {
|
||||
const { captionsTracks } = this;
|
||||
Object.keys(captionsTracks).forEach((trackName) =>
|
||||
removeCuesInRange(captionsTracks[trackName], startOffset, endOffset)
|
||||
removeCuesInRange(captionsTracks[trackName], startOffset, endOffset),
|
||||
);
|
||||
}
|
||||
if (this.config.renderTextTracksNatively) {
|
||||
@@ -707,8 +707,8 @@ export class TimelineController implements ComponentAPI {
|
||||
removeCuesInRange(
|
||||
textTracks[trackName],
|
||||
startOffset,
|
||||
endOffsetSubtitles
|
||||
)
|
||||
endOffsetSubtitles,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -745,7 +745,7 @@ export class TimelineController implements ComponentAPI {
|
||||
|
||||
function canReuseVttTextTrack(
|
||||
inUseTrack: (TextTrack & { textTrack1?; textTrack2? }) | null,
|
||||
manifestTrack: MediaPlaylist
|
||||
manifestTrack: MediaPlaylist,
|
||||
): boolean {
|
||||
return (
|
||||
!!inUseTrack &&
|
||||
|
||||
@@ -81,7 +81,7 @@ export default class Decrypter {
|
||||
public decrypt(
|
||||
data: Uint8Array | ArrayBuffer,
|
||||
key: ArrayBuffer,
|
||||
iv: ArrayBuffer
|
||||
iv: ArrayBuffer,
|
||||
): Promise<ArrayBuffer> {
|
||||
if (this.useSoftware) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -102,7 +102,7 @@ export default class Decrypter {
|
||||
public softwareDecrypt(
|
||||
data: Uint8Array,
|
||||
key: ArrayBuffer,
|
||||
iv: ArrayBuffer
|
||||
iv: ArrayBuffer,
|
||||
): ArrayBuffer | null {
|
||||
const { currentIV, currentResult, remainderData } = this;
|
||||
this.logOnce('JS AES decrypt');
|
||||
@@ -146,7 +146,7 @@ export default class Decrypter {
|
||||
public webCryptoDecrypt(
|
||||
data: Uint8Array,
|
||||
key: ArrayBuffer,
|
||||
iv: ArrayBuffer
|
||||
iv: ArrayBuffer,
|
||||
): Promise<ArrayBuffer> {
|
||||
const subtle = this.subtle;
|
||||
if (this.key !== key || !this.fastAesKey) {
|
||||
@@ -166,7 +166,7 @@ export default class Decrypter {
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.warn(
|
||||
`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`
|
||||
`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`,
|
||||
);
|
||||
|
||||
return this.onWebCryptoError(data, key, iv);
|
||||
|
||||
@@ -22,7 +22,7 @@ class AACDemuxer extends BaseAudioDemuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
trackDuration: number
|
||||
trackDuration: number,
|
||||
) {
|
||||
super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration);
|
||||
this._audioTrack = {
|
||||
@@ -72,14 +72,14 @@ class AACDemuxer extends BaseAudioDemuxer {
|
||||
this.observer,
|
||||
data,
|
||||
offset,
|
||||
track.manifestCodec
|
||||
track.manifestCodec,
|
||||
);
|
||||
const frame = ADTS.appendFrame(
|
||||
track,
|
||||
data,
|
||||
offset,
|
||||
this.basePTS as number,
|
||||
this.frameIndex
|
||||
this.frameIndex,
|
||||
);
|
||||
if (frame && frame.missing === 0) {
|
||||
return frame;
|
||||
|
||||
@@ -16,7 +16,7 @@ export class AC3Demuxer extends BaseAudioDemuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
trackDuration: number
|
||||
trackDuration: number,
|
||||
) {
|
||||
super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration);
|
||||
this._audioTrack = {
|
||||
@@ -41,14 +41,14 @@ export class AC3Demuxer extends BaseAudioDemuxer {
|
||||
appendFrame(
|
||||
track: DemuxedAudioTrack,
|
||||
data: Uint8Array,
|
||||
offset: number
|
||||
offset: number,
|
||||
): AudioFrame | void {
|
||||
const frameLength = appendFrame(
|
||||
track,
|
||||
data,
|
||||
offset,
|
||||
this.basePTS as number,
|
||||
this.frameIndex
|
||||
this.frameIndex,
|
||||
);
|
||||
if (frameLength !== -1) {
|
||||
const sample = track.samples[track.samples.length - 1];
|
||||
@@ -86,7 +86,7 @@ export function appendFrame(
|
||||
data: Uint8Array,
|
||||
start: number,
|
||||
pts: number,
|
||||
frameIndex: number
|
||||
frameIndex: number,
|
||||
): number {
|
||||
if (start + 8 > data.length) {
|
||||
return -1; // not enough bytes left
|
||||
|
||||
@@ -29,7 +29,7 @@ export function getAudioConfig(
|
||||
observer,
|
||||
data: Uint8Array,
|
||||
offset: number,
|
||||
audioCodec: string
|
||||
audioCodec: string,
|
||||
): AudioConfig | void {
|
||||
let adtsObjectType: number;
|
||||
let adtsExtensionSamplingIndex: number;
|
||||
@@ -57,7 +57,7 @@ export function getAudioConfig(
|
||||
// byte 3
|
||||
adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6;
|
||||
logger.log(
|
||||
`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`
|
||||
`manifest codec:${audioCodec}, ADTS type:${adtsObjectType}, samplingIndex:${adtsSamplingIndex}`,
|
||||
);
|
||||
// firefox: freq less than 24kHz = AAC SBR (HE-AAC)
|
||||
if (/firefox/i.test(userAgent)) {
|
||||
@@ -230,7 +230,7 @@ export function initTrackConfig(
|
||||
observer: HlsEventEmitter,
|
||||
data: Uint8Array,
|
||||
offset: number,
|
||||
audioCodec: string
|
||||
audioCodec: string,
|
||||
) {
|
||||
if (!track.samplerate) {
|
||||
const config = getAudioConfig(observer, data, offset, audioCodec);
|
||||
@@ -243,7 +243,7 @@ export function initTrackConfig(
|
||||
track.codec = config.codec;
|
||||
track.manifestCodec = config.manifestCodec;
|
||||
logger.log(
|
||||
`parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`
|
||||
`parsed codec:${track.codec}, rate:${config.samplerate}, channels:${config.channelCount}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -254,7 +254,7 @@ export function getFrameDuration(samplerate: number): number {
|
||||
|
||||
export function parseFrameHeader(
|
||||
data: Uint8Array,
|
||||
offset: number
|
||||
offset: number,
|
||||
): FrameHeader | void {
|
||||
// The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header
|
||||
const headerLength = getHeaderLength(data, offset);
|
||||
@@ -273,7 +273,7 @@ export function appendFrame(
|
||||
data: Uint8Array,
|
||||
offset: number,
|
||||
pts: number,
|
||||
frameIndex: number
|
||||
frameIndex: number,
|
||||
): AudioFrame {
|
||||
const frameDuration = getFrameDuration(track.samplerate as number);
|
||||
const stamp = pts + frameIndex * frameDuration;
|
||||
|
||||
@@ -28,7 +28,7 @@ class BaseAudioDemuxer implements Demuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
trackDuration: number
|
||||
trackDuration: number,
|
||||
) {
|
||||
this._id3Track = {
|
||||
type: 'id3',
|
||||
@@ -59,7 +59,7 @@ class BaseAudioDemuxer implements Demuxer {
|
||||
appendFrame(
|
||||
track: DemuxedAudioTrack,
|
||||
data: Uint8Array,
|
||||
offset: number
|
||||
offset: number,
|
||||
): AudioFrame | void {}
|
||||
|
||||
// feed incoming data to the front of the parsing pipeline
|
||||
@@ -147,10 +147,12 @@ class BaseAudioDemuxer implements Demuxer {
|
||||
demuxSampleAes(
|
||||
data: Uint8Array,
|
||||
keyData: KeyData,
|
||||
timeOffset: number
|
||||
timeOffset: number,
|
||||
): Promise<DemuxerResult> {
|
||||
return Promise.reject(
|
||||
new Error(`[${this}] This demuxer does not support Sample-AES decryption`)
|
||||
new Error(
|
||||
`[${this}] This demuxer does not support Sample-AES decryption`,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -182,7 +184,7 @@ class BaseAudioDemuxer implements Demuxer {
|
||||
export const initPTSFn = (
|
||||
timestamp: number | undefined,
|
||||
timeOffset: number,
|
||||
initPTS: RationalTimestamp | null
|
||||
initPTS: RationalTimestamp | null,
|
||||
): number => {
|
||||
if (Number.isFinite(timestamp as number)) {
|
||||
return timestamp! * 90;
|
||||
|
||||
@@ -12,7 +12,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
trackDuration: number
|
||||
trackDuration: number,
|
||||
) {
|
||||
super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration);
|
||||
this._audioTrack = {
|
||||
@@ -76,7 +76,7 @@ class MP3Demuxer extends BaseAudioDemuxer {
|
||||
data,
|
||||
offset,
|
||||
this.basePTS,
|
||||
this.frameIndex
|
||||
this.frameIndex,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export function appendFrame(
|
||||
data: Uint8Array,
|
||||
offset: number,
|
||||
pts: number,
|
||||
frameIndex: number
|
||||
frameIndex: number,
|
||||
) {
|
||||
// Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference
|
||||
if (offset + 24 > data.length) {
|
||||
|
||||
@@ -29,7 +29,7 @@ export default class ChunkCache {
|
||||
|
||||
function concatUint8Arrays(
|
||||
chunks: Array<Uint8Array>,
|
||||
dataLength: number
|
||||
dataLength: number,
|
||||
): Uint8Array {
|
||||
const result = new Uint8Array(dataLength);
|
||||
let offset = 0;
|
||||
|
||||
+6
-6
@@ -91,7 +91,7 @@ export const isFooter = (data: Uint8Array, offset: number): boolean => {
|
||||
*/
|
||||
export const getID3Data = (
|
||||
data: Uint8Array,
|
||||
offset: number
|
||||
offset: number,
|
||||
): Uint8Array | undefined => {
|
||||
const front = offset;
|
||||
let length = 0;
|
||||
@@ -222,7 +222,7 @@ export const decodeFrame = (frame: RawFrame): Frame | undefined => {
|
||||
};
|
||||
|
||||
const decodePrivFrame = (
|
||||
frame: RawFrame
|
||||
frame: RawFrame,
|
||||
): DecodedFrame<ArrayBuffer> | undefined => {
|
||||
/*
|
||||
Format: <text string>\0<binary data>
|
||||
@@ -279,7 +279,7 @@ const decodeURLFrame = (frame: RawFrame): DecodedFrame<string> | undefined => {
|
||||
let index = 1;
|
||||
const description: string = utf8ArrayToStr(
|
||||
frame.data.subarray(index),
|
||||
true
|
||||
true,
|
||||
);
|
||||
|
||||
index += description.length + 1;
|
||||
@@ -296,7 +296,7 @@ const decodeURLFrame = (frame: RawFrame): DecodedFrame<string> | undefined => {
|
||||
};
|
||||
|
||||
const readTimeStamp = (
|
||||
timeStampFrame: DecodedFrame<ArrayBuffer>
|
||||
timeStampFrame: DecodedFrame<ArrayBuffer>,
|
||||
): number | undefined => {
|
||||
if (timeStampFrame.data.byteLength === 8) {
|
||||
const data = new Uint8Array(timeStampFrame.data);
|
||||
@@ -328,7 +328,7 @@ const readTimeStamp = (
|
||||
*/
|
||||
export const utf8ArrayToStr = (
|
||||
array: Uint8Array,
|
||||
exitOnNull: boolean = false
|
||||
exitOnNull: boolean = false,
|
||||
): string => {
|
||||
const decoder = getTextDecoder();
|
||||
if (decoder) {
|
||||
@@ -381,7 +381,7 @@ export const utf8ArrayToStr = (
|
||||
char2 = array[i++];
|
||||
char3 = array[i++];
|
||||
out += String.fromCharCode(
|
||||
((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0)
|
||||
((c & 0x0f) << 12) | ((char2 & 0x3f) << 6) | ((char3 & 0x3f) << 0),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -19,7 +19,7 @@ export function injectWorker(): WorkerContext {
|
||||
],
|
||||
{
|
||||
type: 'text/javascript',
|
||||
}
|
||||
},
|
||||
);
|
||||
const objectURL = self.URL.createObjectURL(blob);
|
||||
const worker = new self.Worker(objectURL);
|
||||
|
||||
@@ -45,19 +45,19 @@ class MP4Demuxer implements Demuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
trackDuration: number
|
||||
trackDuration: number,
|
||||
) {
|
||||
const videoTrack = (this.videoTrack = dummyTrack(
|
||||
'video',
|
||||
1
|
||||
1,
|
||||
) as PassthroughTrack);
|
||||
const audioTrack = (this.audioTrack = dummyTrack(
|
||||
'audio',
|
||||
1
|
||||
1,
|
||||
) as DemuxedAudioTrack);
|
||||
const captionTrack = (this.txtTrack = dummyTrack(
|
||||
'text',
|
||||
1
|
||||
1,
|
||||
) as DemuxedUserdataTrack);
|
||||
|
||||
this.id3Track = dummyTrack('id3', 1) as DemuxedMetadataTrack;
|
||||
@@ -148,7 +148,7 @@ class MP4Demuxer implements Demuxer {
|
||||
|
||||
private extractID3Track(
|
||||
videoTrack: PassthroughTrack,
|
||||
timeOffset: number
|
||||
timeOffset: number,
|
||||
): DemuxedMetadataTrack {
|
||||
const id3Track = this.id3Track as DemuxedMetadataTrack;
|
||||
if (videoTrack.samples.length) {
|
||||
@@ -188,10 +188,10 @@ class MP4Demuxer implements Demuxer {
|
||||
demuxSampleAes(
|
||||
data: Uint8Array,
|
||||
keyData: KeyData,
|
||||
timeOffset: number
|
||||
timeOffset: number,
|
||||
): Promise<DemuxerResult> {
|
||||
return Promise.reject(
|
||||
new Error('The MP4 demuxer does not support SAMPLE-AES decryption')
|
||||
new Error('The MP4 demuxer does not support SAMPLE-AES decryption'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+12
-12
@@ -29,7 +29,7 @@ class SampleAesDecrypter {
|
||||
return this.decrypter.decrypt(
|
||||
encryptedData,
|
||||
this.keyData.key.buffer,
|
||||
this.keyData.iv.buffer
|
||||
this.keyData.iv.buffer,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ class SampleAesDecrypter {
|
||||
private decryptAacSample(
|
||||
samples: AudioSample[],
|
||||
sampleIndex: number,
|
||||
callback: () => void
|
||||
callback: () => void,
|
||||
) {
|
||||
const curUnit = samples[sampleIndex].unit;
|
||||
if (curUnit.length <= 16) {
|
||||
@@ -47,11 +47,11 @@ class SampleAesDecrypter {
|
||||
}
|
||||
const encryptedData = curUnit.subarray(
|
||||
16,
|
||||
curUnit.length - (curUnit.length % 16)
|
||||
curUnit.length - (curUnit.length % 16),
|
||||
);
|
||||
const encryptedBuffer = encryptedData.buffer.slice(
|
||||
encryptedData.byteOffset,
|
||||
encryptedData.byteOffset + encryptedData.length
|
||||
encryptedData.byteOffset + encryptedData.length,
|
||||
);
|
||||
|
||||
this.decryptBuffer(encryptedBuffer).then((decryptedBuffer: ArrayBuffer) => {
|
||||
@@ -67,7 +67,7 @@ class SampleAesDecrypter {
|
||||
decryptAacSamples(
|
||||
samples: AudioSample[],
|
||||
sampleIndex: number,
|
||||
callback: () => void
|
||||
callback: () => void,
|
||||
) {
|
||||
for (; ; sampleIndex++) {
|
||||
if (sampleIndex >= samples.length) {
|
||||
@@ -100,7 +100,7 @@ class SampleAesDecrypter {
|
||||
) {
|
||||
encryptedData.set(
|
||||
decodedData.subarray(inputPos, inputPos + 16),
|
||||
outputPos
|
||||
outputPos,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ class SampleAesDecrypter {
|
||||
|
||||
getAvcDecryptedUnit(
|
||||
decodedData: Uint8Array,
|
||||
decryptedData: ArrayLike<number> | ArrayBuffer | SharedArrayBuffer
|
||||
decryptedData: ArrayLike<number> | ArrayBuffer | SharedArrayBuffer,
|
||||
) {
|
||||
const uint8DecryptedData = new Uint8Array(decryptedData);
|
||||
let inputPos = 0;
|
||||
@@ -120,7 +120,7 @@ class SampleAesDecrypter {
|
||||
) {
|
||||
decodedData.set(
|
||||
uint8DecryptedData.subarray(inputPos, inputPos + 16),
|
||||
outputPos
|
||||
outputPos,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ class SampleAesDecrypter {
|
||||
sampleIndex: number,
|
||||
unitIndex: number,
|
||||
callback: () => void,
|
||||
curUnit: VideoSampleUnit
|
||||
curUnit: VideoSampleUnit,
|
||||
) {
|
||||
const decodedData = discardEPB(curUnit.data);
|
||||
const encryptedData = this.getAvcEncryptedData(decodedData);
|
||||
@@ -144,7 +144,7 @@ class SampleAesDecrypter {
|
||||
if (!this.decrypter.isSync()) {
|
||||
this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ class SampleAesDecrypter {
|
||||
samples: DemuxedVideoTrackBase['samples'],
|
||||
sampleIndex: number,
|
||||
unitIndex: number,
|
||||
callback: () => void
|
||||
callback: () => void,
|
||||
) {
|
||||
if (samples instanceof Uint8Array) {
|
||||
throw new Error('Cannot decrypt samples of type Uint8Array');
|
||||
@@ -183,7 +183,7 @@ class SampleAesDecrypter {
|
||||
sampleIndex,
|
||||
unitIndex,
|
||||
callback,
|
||||
curUnit
|
||||
curUnit,
|
||||
);
|
||||
|
||||
if (!this.decrypter.isSync()) {
|
||||
|
||||
@@ -42,7 +42,7 @@ export default class TransmuxerInterface {
|
||||
hls: Hls,
|
||||
id: PlaylistLevelType,
|
||||
onTransmuxComplete: (transmuxResult: TransmuxerResult) => void,
|
||||
onFlush: (chunkMeta: ChunkMetadata) => void
|
||||
onFlush: (chunkMeta: ChunkMetadata) => void,
|
||||
) {
|
||||
const config = hls.config;
|
||||
this.hls = hls;
|
||||
@@ -93,7 +93,7 @@ export default class TransmuxerInterface {
|
||||
worker.addEventListener('message', this.onwmsg as any);
|
||||
worker.onerror = (event) => {
|
||||
const error = new Error(
|
||||
`${event.message} (${event.filename}:${event.lineno})`
|
||||
`${event.message} (${event.filename}:${event.lineno})`,
|
||||
);
|
||||
config.enableWorker = false;
|
||||
logger.warn(`Error in "${id}" Web Worker, fallback to inline`);
|
||||
@@ -115,7 +115,7 @@ export default class TransmuxerInterface {
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
`Error setting up "${id}" Web Worker, fallback to inline`,
|
||||
err
|
||||
err,
|
||||
);
|
||||
this.resetWorker();
|
||||
this.error = null;
|
||||
@@ -124,7 +124,7 @@ export default class TransmuxerInterface {
|
||||
m2tsTypeSupported,
|
||||
config,
|
||||
vendor,
|
||||
id
|
||||
id,
|
||||
);
|
||||
}
|
||||
return;
|
||||
@@ -136,7 +136,7 @@ export default class TransmuxerInterface {
|
||||
m2tsTypeSupported,
|
||||
config,
|
||||
vendor,
|
||||
id
|
||||
id,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ export default class TransmuxerInterface {
|
||||
duration: number,
|
||||
accurateTimeOffset: boolean,
|
||||
chunkMeta: ChunkMetadata,
|
||||
defaultInitPTS?: RationalTimestamp
|
||||
defaultInitPTS?: RationalTimestamp,
|
||||
): void {
|
||||
chunkMeta.transmuxing.start = self.performance.now();
|
||||
const { transmuxer } = this;
|
||||
@@ -224,7 +224,7 @@ export default class TransmuxerInterface {
|
||||
accurateTimeOffset,
|
||||
trackSwitch,
|
||||
timeOffset,
|
||||
initSegmentChange
|
||||
initSegmentChange,
|
||||
);
|
||||
if (!contiguous || discontinuity || initSegmentChange) {
|
||||
logger.log(`[transmuxer-interface, ${frag.type}]: Starting new transmux session for sn: ${chunkMeta.sn} p: ${chunkMeta.part} level: ${chunkMeta.level} id: ${chunkMeta.id}
|
||||
@@ -239,7 +239,7 @@ export default class TransmuxerInterface {
|
||||
videoCodec,
|
||||
initSegmentData,
|
||||
duration,
|
||||
defaultInitPTS
|
||||
defaultInitPTS,
|
||||
);
|
||||
this.configureTransmuxer(config);
|
||||
}
|
||||
@@ -258,14 +258,14 @@ export default class TransmuxerInterface {
|
||||
chunkMeta,
|
||||
state,
|
||||
},
|
||||
data instanceof ArrayBuffer ? [data] : []
|
||||
data instanceof ArrayBuffer ? [data] : [],
|
||||
);
|
||||
} else if (transmuxer) {
|
||||
const transmuxResult = transmuxer.push(
|
||||
data,
|
||||
decryptdata,
|
||||
chunkMeta,
|
||||
state
|
||||
state,
|
||||
);
|
||||
if (isPromise(transmuxResult)) {
|
||||
transmuxer.async = true;
|
||||
@@ -277,7 +277,7 @@ export default class TransmuxerInterface {
|
||||
this.transmuxerError(
|
||||
error,
|
||||
chunkMeta,
|
||||
'transmuxer-interface push error'
|
||||
'transmuxer-interface push error',
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@@ -311,13 +311,13 @@ export default class TransmuxerInterface {
|
||||
this.transmuxerError(
|
||||
error,
|
||||
chunkMeta,
|
||||
'transmuxer-interface flush error'
|
||||
'transmuxer-interface flush error',
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.handleFlushResult(
|
||||
transmuxResult as Array<TransmuxerResult>,
|
||||
chunkMeta
|
||||
chunkMeta,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -326,7 +326,7 @@ export default class TransmuxerInterface {
|
||||
private transmuxerError(
|
||||
error: Error,
|
||||
chunkMeta: ChunkMetadata,
|
||||
reason: string
|
||||
reason: string,
|
||||
) {
|
||||
if (!this.hls) {
|
||||
return;
|
||||
@@ -345,7 +345,7 @@ export default class TransmuxerInterface {
|
||||
|
||||
private handleFlushResult(
|
||||
results: Array<TransmuxerResult>,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
) {
|
||||
results.forEach((result) => {
|
||||
this.handleTransmuxComplete(result);
|
||||
|
||||
@@ -44,7 +44,7 @@ function startWorker(self) {
|
||||
data.typeSupported,
|
||||
config,
|
||||
data.vendor,
|
||||
data.id
|
||||
data.id,
|
||||
);
|
||||
enableLogs(config.debug, data.id);
|
||||
forwardWorkerLogs();
|
||||
@@ -61,7 +61,7 @@ function startWorker(self) {
|
||||
data.data,
|
||||
data.decryptdata,
|
||||
data.chunkMeta,
|
||||
data.state
|
||||
data.state,
|
||||
);
|
||||
if (isPromise(transmuxResult)) {
|
||||
self.transmuxer.async = true;
|
||||
@@ -113,7 +113,7 @@ function startWorker(self) {
|
||||
handleFlushResult(
|
||||
self,
|
||||
transmuxResult as Array<TransmuxerResult>,
|
||||
id
|
||||
id,
|
||||
);
|
||||
}
|
||||
break;
|
||||
@@ -126,7 +126,7 @@ function startWorker(self) {
|
||||
|
||||
function emitTransmuxComplete(
|
||||
self: any,
|
||||
transmuxResult: TransmuxerResult
|
||||
transmuxResult: TransmuxerResult,
|
||||
): boolean {
|
||||
if (isEmptyResult(transmuxResult.remuxResult)) {
|
||||
return false;
|
||||
@@ -141,7 +141,7 @@ function emitTransmuxComplete(
|
||||
}
|
||||
self.postMessage(
|
||||
{ event: 'transmuxComplete', data: transmuxResult },
|
||||
transferable
|
||||
transferable,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -150,7 +150,7 @@ function emitTransmuxComplete(
|
||||
// in order to minimize message passing overhead
|
||||
function addToTransferable(
|
||||
transferable: Array<ArrayBuffer>,
|
||||
track: RemuxedTrack
|
||||
track: RemuxedTrack,
|
||||
) {
|
||||
if (track.data1) {
|
||||
transferable.push(track.data1.buffer);
|
||||
@@ -163,11 +163,11 @@ function addToTransferable(
|
||||
function handleFlushResult(
|
||||
self: any,
|
||||
results: Array<TransmuxerResult>,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
) {
|
||||
const parsed = results.reduce(
|
||||
(parsed, result) => emitTransmuxComplete(self, result) || parsed,
|
||||
false
|
||||
false,
|
||||
);
|
||||
if (!parsed) {
|
||||
// Emit at least one "transmuxComplete" message even if media is not found to update stream-controller state to PARSING
|
||||
|
||||
+24
-24
@@ -65,7 +65,7 @@ export default class Transmuxer {
|
||||
typeSupported: TypeSupported,
|
||||
config: HlsConfig,
|
||||
vendor: string,
|
||||
id: PlaylistLevelType
|
||||
id: PlaylistLevelType,
|
||||
) {
|
||||
this.observer = observer;
|
||||
this.typeSupported = typeSupported;
|
||||
@@ -85,7 +85,7 @@ export default class Transmuxer {
|
||||
data: ArrayBuffer,
|
||||
decryptdata: DecryptData | null,
|
||||
chunkMeta: ChunkMetadata,
|
||||
state?: TransmuxState
|
||||
state?: TransmuxState,
|
||||
): TransmuxerResult | Promise<TransmuxerResult> {
|
||||
const stats = chunkMeta.transmuxing;
|
||||
stats.executeStart = now();
|
||||
@@ -122,7 +122,7 @@ export default class Transmuxer {
|
||||
let decryptedData = decrypter.softwareDecrypt(
|
||||
uintData,
|
||||
keyData.key.buffer,
|
||||
keyData.iv.buffer
|
||||
keyData.iv.buffer,
|
||||
);
|
||||
// For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
|
||||
const loadingParts = chunkMeta.part > -1;
|
||||
@@ -143,7 +143,7 @@ export default class Transmuxer {
|
||||
const result = this.push(
|
||||
decryptedData,
|
||||
null,
|
||||
chunkMeta
|
||||
chunkMeta,
|
||||
) as TransmuxerResult;
|
||||
this.decryptionPromise = null;
|
||||
return result;
|
||||
@@ -175,7 +175,7 @@ export default class Transmuxer {
|
||||
audioCodec,
|
||||
videoCodec,
|
||||
duration,
|
||||
decryptdata
|
||||
decryptdata,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ export default class Transmuxer {
|
||||
keyData,
|
||||
timeOffset,
|
||||
accurateTimeOffset,
|
||||
chunkMeta
|
||||
chunkMeta,
|
||||
);
|
||||
const currentState = this.currentTransmuxState;
|
||||
|
||||
@@ -206,7 +206,7 @@ export default class Transmuxer {
|
||||
|
||||
// Due to data caching, flush calls can produce more than one TransmuxerResult (hence the Array type)
|
||||
flush(
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
): TransmuxerResult[] | Promise<TransmuxerResult[]> {
|
||||
const stats = chunkMeta.transmuxing;
|
||||
stats.executeStart = now();
|
||||
@@ -231,7 +231,7 @@ export default class Transmuxer {
|
||||
if (decryptedData) {
|
||||
// Push always returns a TransmuxerResult if decryptdata is null
|
||||
transmuxResults.push(
|
||||
this.push(decryptedData, null, chunkMeta) as TransmuxerResult
|
||||
this.push(decryptedData, null, chunkMeta) as TransmuxerResult,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -259,14 +259,14 @@ export default class Transmuxer {
|
||||
private flushRemux(
|
||||
transmuxResults: TransmuxerResult[],
|
||||
demuxResult: DemuxerResult,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
) {
|
||||
const { audioTrack, videoTrack, id3Track, textTrack } = demuxResult;
|
||||
const { accurateTimeOffset, timeOffset } = this.currentTransmuxState;
|
||||
logger.log(
|
||||
`[transmuxer.ts]: Flushed fragment ${chunkMeta.sn}${
|
||||
chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : ''
|
||||
} of level ${chunkMeta.level}`
|
||||
} of level ${chunkMeta.level}`,
|
||||
);
|
||||
const remuxResult = this.remuxer!.remux(
|
||||
audioTrack,
|
||||
@@ -276,7 +276,7 @@ export default class Transmuxer {
|
||||
timeOffset,
|
||||
accurateTimeOffset,
|
||||
true,
|
||||
this.id
|
||||
this.id,
|
||||
);
|
||||
transmuxResults.push({
|
||||
remuxResult,
|
||||
@@ -309,7 +309,7 @@ export default class Transmuxer {
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
trackDuration: number,
|
||||
decryptdata: DecryptData | null
|
||||
decryptdata: DecryptData | null,
|
||||
) {
|
||||
const { demuxer, remuxer } = this;
|
||||
if (!demuxer || !remuxer) {
|
||||
@@ -319,13 +319,13 @@ export default class Transmuxer {
|
||||
initSegmentData,
|
||||
audioCodec,
|
||||
videoCodec,
|
||||
trackDuration
|
||||
trackDuration,
|
||||
);
|
||||
remuxer.resetInitSegment(
|
||||
initSegmentData,
|
||||
audioCodec,
|
||||
videoCodec,
|
||||
decryptdata
|
||||
decryptdata,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ export default class Transmuxer {
|
||||
keyData: KeyData | null,
|
||||
timeOffset: number,
|
||||
accurateTimeOffset: boolean,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
): TransmuxerResult | Promise<TransmuxerResult> {
|
||||
let result: TransmuxerResult | Promise<TransmuxerResult>;
|
||||
if (keyData && keyData.method === 'SAMPLE-AES') {
|
||||
@@ -354,14 +354,14 @@ export default class Transmuxer {
|
||||
keyData,
|
||||
timeOffset,
|
||||
accurateTimeOffset,
|
||||
chunkMeta
|
||||
chunkMeta,
|
||||
);
|
||||
} else {
|
||||
result = this.transmuxUnencrypted(
|
||||
data,
|
||||
timeOffset,
|
||||
accurateTimeOffset,
|
||||
chunkMeta
|
||||
chunkMeta,
|
||||
);
|
||||
}
|
||||
return result;
|
||||
@@ -371,7 +371,7 @@ export default class Transmuxer {
|
||||
data: Uint8Array,
|
||||
timeOffset: number,
|
||||
accurateTimeOffset: boolean,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
): TransmuxerResult {
|
||||
const { audioTrack, videoTrack, id3Track, textTrack } = (
|
||||
this.demuxer as Demuxer
|
||||
@@ -384,7 +384,7 @@ export default class Transmuxer {
|
||||
timeOffset,
|
||||
accurateTimeOffset,
|
||||
false,
|
||||
this.id
|
||||
this.id,
|
||||
);
|
||||
return {
|
||||
remuxResult,
|
||||
@@ -397,7 +397,7 @@ export default class Transmuxer {
|
||||
decryptData: KeyData,
|
||||
timeOffset: number,
|
||||
accurateTimeOffset: boolean,
|
||||
chunkMeta: ChunkMetadata
|
||||
chunkMeta: ChunkMetadata,
|
||||
): Promise<TransmuxerResult> {
|
||||
return (this.demuxer as Demuxer)
|
||||
.demuxSampleAes(data, decryptData, timeOffset)
|
||||
@@ -410,7 +410,7 @@ export default class Transmuxer {
|
||||
timeOffset,
|
||||
accurateTimeOffset,
|
||||
false,
|
||||
this.id
|
||||
this.id,
|
||||
);
|
||||
return {
|
||||
remuxResult,
|
||||
@@ -463,7 +463,7 @@ export default class Transmuxer {
|
||||
|
||||
function getEncryptionType(
|
||||
data: Uint8Array,
|
||||
decryptData: DecryptData | null
|
||||
decryptData: DecryptData | null,
|
||||
): KeyData | null {
|
||||
let encryptionType: KeyData | null = null;
|
||||
if (
|
||||
@@ -499,7 +499,7 @@ export class TransmuxConfig {
|
||||
videoCodec: string | undefined,
|
||||
initSegmentData: Uint8Array | undefined,
|
||||
duration: number,
|
||||
defaultInitPts?: RationalTimestamp
|
||||
defaultInitPts?: RationalTimestamp,
|
||||
) {
|
||||
this.audioCodec = audioCodec;
|
||||
this.videoCodec = videoCodec;
|
||||
@@ -523,7 +523,7 @@ export class TransmuxState {
|
||||
accurateTimeOffset: boolean,
|
||||
trackSwitch: boolean,
|
||||
timeOffset: number,
|
||||
initSegmentChange: boolean
|
||||
initSegmentChange: boolean,
|
||||
) {
|
||||
this.discontinuity = discontinuity;
|
||||
this.contiguous = contiguous;
|
||||
|
||||
+25
-25
@@ -79,7 +79,7 @@ class TSDemuxer implements Demuxer {
|
||||
constructor(
|
||||
observer: HlsEventEmitter,
|
||||
config: HlsConfig,
|
||||
typeSupported: TypeSupported
|
||||
typeSupported: TypeSupported,
|
||||
) {
|
||||
this.observer = observer;
|
||||
this.config = config;
|
||||
@@ -91,7 +91,7 @@ class TSDemuxer implements Demuxer {
|
||||
const syncOffset = TSDemuxer.syncOffset(data);
|
||||
if (syncOffset > 0) {
|
||||
logger.warn(
|
||||
`MPEG2-TS detected but first sync word found @ offset ${syncOffset}`
|
||||
`MPEG2-TS detected but first sync word found @ offset ${syncOffset}`,
|
||||
);
|
||||
}
|
||||
return syncOffset !== -1;
|
||||
@@ -117,7 +117,7 @@ class TSDemuxer implements Demuxer {
|
||||
scanwindow =
|
||||
Math.min(
|
||||
packetStart + PACKET_LENGTH * 99,
|
||||
data.length - PACKET_LENGTH
|
||||
data.length - PACKET_LENGTH,
|
||||
) + 1;
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,7 @@ class TSDemuxer implements Demuxer {
|
||||
*/
|
||||
static createTrack(
|
||||
type: 'audio' | 'video' | 'id3' | 'text',
|
||||
duration?: number
|
||||
duration?: number,
|
||||
): DemuxedTrack {
|
||||
return {
|
||||
container:
|
||||
@@ -174,7 +174,7 @@ class TSDemuxer implements Demuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string,
|
||||
videoCodec: string,
|
||||
trackDuration: number
|
||||
trackDuration: number,
|
||||
) {
|
||||
this.pmtParsed = false;
|
||||
this._pmtId = -1;
|
||||
@@ -182,7 +182,7 @@ class TSDemuxer implements Demuxer {
|
||||
this._videoTrack = TSDemuxer.createTrack('video') as DemuxedVideoTrack;
|
||||
this._audioTrack = TSDemuxer.createTrack(
|
||||
'audio',
|
||||
trackDuration
|
||||
trackDuration,
|
||||
) as DemuxedAudioTrack;
|
||||
this._id3Track = TSDemuxer.createTrack('id3') as DemuxedMetadataTrack;
|
||||
this._txtTrack = TSDemuxer.createTrack('text') as DemuxedUserdataTrack;
|
||||
@@ -217,7 +217,7 @@ class TSDemuxer implements Demuxer {
|
||||
data: Uint8Array,
|
||||
timeOffset: number,
|
||||
isSampleAes = false,
|
||||
flush = false
|
||||
flush = false,
|
||||
): DemuxerResult {
|
||||
if (!isSampleAes) {
|
||||
this.sampleAes = null;
|
||||
@@ -263,7 +263,7 @@ class TSDemuxer implements Demuxer {
|
||||
this.remainderData = new Uint8Array(
|
||||
data.buffer,
|
||||
len,
|
||||
data.buffer.byteLength - len
|
||||
data.buffer.byteLength - len,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ class TSDemuxer implements Demuxer {
|
||||
textTrack,
|
||||
pes,
|
||||
false,
|
||||
this._duration
|
||||
this._duration,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ class TSDemuxer implements Demuxer {
|
||||
data,
|
||||
offset,
|
||||
this.typeSupported,
|
||||
isSampleAes
|
||||
isSampleAes,
|
||||
);
|
||||
|
||||
// only update track id if track PID found while parsing PMT
|
||||
@@ -387,7 +387,7 @@ class TSDemuxer implements Demuxer {
|
||||
|
||||
if (unknownPID !== null && !pmtParsed) {
|
||||
logger.warn(
|
||||
`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`
|
||||
`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`,
|
||||
);
|
||||
unknownPID = null;
|
||||
// we set it to -188, the += 188 in the for loop will reset start to 0
|
||||
@@ -410,7 +410,7 @@ class TSDemuxer implements Demuxer {
|
||||
|
||||
if (tsPacketErrors > 0) {
|
||||
const error = new Error(
|
||||
`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`
|
||||
`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`,
|
||||
);
|
||||
this.observer.emit(Events.ERROR, Events.ERROR, {
|
||||
type: ErrorTypes.MEDIA_ERROR,
|
||||
@@ -473,7 +473,7 @@ class TSDemuxer implements Demuxer {
|
||||
textTrack as DemuxedUserdataTrack,
|
||||
pes,
|
||||
true,
|
||||
this._duration
|
||||
this._duration,
|
||||
);
|
||||
videoTrack.pesData = null;
|
||||
} else {
|
||||
@@ -499,7 +499,7 @@ class TSDemuxer implements Demuxer {
|
||||
} else {
|
||||
if (audioData?.size) {
|
||||
logger.log(
|
||||
'last AAC PES packet truncated,might overlap between fragments'
|
||||
'last AAC PES packet truncated,might overlap between fragments',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -519,25 +519,25 @@ class TSDemuxer implements Demuxer {
|
||||
public demuxSampleAes(
|
||||
data: Uint8Array,
|
||||
keyData: KeyData,
|
||||
timeOffset: number
|
||||
timeOffset: number,
|
||||
): Promise<DemuxerResult> {
|
||||
const demuxResult = this.demux(
|
||||
data,
|
||||
timeOffset,
|
||||
true,
|
||||
!this.config.progressive
|
||||
!this.config.progressive,
|
||||
);
|
||||
const sampleAes = (this.sampleAes = new SampleAesDecrypter(
|
||||
this.observer,
|
||||
this.config,
|
||||
keyData
|
||||
keyData,
|
||||
));
|
||||
return this.decrypt(demuxResult, sampleAes);
|
||||
}
|
||||
|
||||
private decrypt(
|
||||
demuxResult: DemuxerResult,
|
||||
sampleAes: SampleAesDecrypter
|
||||
sampleAes: SampleAesDecrypter,
|
||||
): Promise<DemuxerResult> {
|
||||
return new Promise((resolve) => {
|
||||
const { audioTrack, videoTrack } = demuxResult;
|
||||
@@ -578,7 +578,7 @@ class TSDemuxer implements Demuxer {
|
||||
const frameOverflowBytes = sampleLength - frameMissingBytes;
|
||||
aacOverFlow.sample.unit.set(
|
||||
data.subarray(0, frameMissingBytes),
|
||||
frameOverflowBytes
|
||||
frameOverflowBytes,
|
||||
);
|
||||
track.samples.push(aacOverFlow.sample);
|
||||
startOffset = aacOverFlow.missing;
|
||||
@@ -621,7 +621,7 @@ class TSDemuxer implements Demuxer {
|
||||
this.observer,
|
||||
data,
|
||||
offset,
|
||||
this.audioCodec as string
|
||||
this.audioCodec as string,
|
||||
);
|
||||
|
||||
let pts: number;
|
||||
@@ -675,7 +675,7 @@ class TSDemuxer implements Demuxer {
|
||||
data,
|
||||
offset,
|
||||
pts,
|
||||
frameIndex
|
||||
frameIndex,
|
||||
);
|
||||
if (frame) {
|
||||
offset += frame.length;
|
||||
@@ -740,7 +740,7 @@ function parsePMT(
|
||||
data: Uint8Array,
|
||||
offset: number,
|
||||
typeSupported: TypeSupported,
|
||||
isSampleAes: boolean
|
||||
isSampleAes: boolean,
|
||||
) {
|
||||
const result = {
|
||||
audioPid: -1,
|
||||
@@ -848,7 +848,7 @@ function parsePMT(
|
||||
if (__USE_M2TS_ADVANCED_CODECS__) {
|
||||
if (typeSupported.ac3 !== true) {
|
||||
logger.log(
|
||||
'AC-3 audio found, not supported in this browser for now'
|
||||
'AC-3 audio found, not supported in this browser for now',
|
||||
);
|
||||
} else {
|
||||
result.audioPid = pid;
|
||||
@@ -945,8 +945,8 @@ function parsePES(stream: ElementaryStreamData): PES | null {
|
||||
if (pesPts - pesDts > 60 * 90000) {
|
||||
logger.warn(
|
||||
`${Math.round(
|
||||
(pesPts - pesDts) / 90000
|
||||
)}s delta between PTS and DTS, align them`
|
||||
(pesPts - pesDts) / 90000,
|
||||
)}s delta between PTS and DTS, align them`,
|
||||
);
|
||||
pesPts = pesDts;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
textTrack: DemuxedUserdataTrack,
|
||||
pes: PES,
|
||||
last: boolean,
|
||||
duration: number
|
||||
duration: number,
|
||||
) {
|
||||
const units = this.parseAVCNALu(track, pes.data);
|
||||
const debug = false;
|
||||
@@ -37,7 +37,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
false,
|
||||
pes.pts,
|
||||
pes.dts,
|
||||
''
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
true,
|
||||
pes.pts,
|
||||
pes.dts,
|
||||
''
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
true,
|
||||
pes.pts,
|
||||
pes.dts,
|
||||
''
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
unit.data,
|
||||
1,
|
||||
pes.pts as number,
|
||||
textTrack.samples
|
||||
textTrack.samples,
|
||||
);
|
||||
break;
|
||||
// SPS
|
||||
@@ -186,7 +186,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
false,
|
||||
pes.pts,
|
||||
pes.dts,
|
||||
debug ? 'AUD ' : ''
|
||||
debug ? 'AUD ' : '',
|
||||
);
|
||||
break;
|
||||
// Filler Data
|
||||
@@ -215,7 +215,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
|
||||
private parseAVCNALu(
|
||||
track: DemuxedVideoTrack,
|
||||
array: Uint8Array
|
||||
array: Uint8Array,
|
||||
): Array<{
|
||||
data: Uint8Array;
|
||||
type: number;
|
||||
@@ -280,7 +280,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
// strip last bytes
|
||||
lastUnit.data = lastUnit.data.subarray(
|
||||
0,
|
||||
lastUnit.data.byteLength - lastState
|
||||
lastUnit.data.byteLength - lastState,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -290,7 +290,7 @@ class AvcVideoParser extends BaseVideoParser {
|
||||
// logger.log('first NALU found with overflow:' + overflow);
|
||||
lastUnit.data = appendUint8Array(
|
||||
lastUnit.data,
|
||||
array.subarray(0, overflow)
|
||||
array.subarray(0, overflow),
|
||||
);
|
||||
lastUnit.state = 0;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ class BaseVideoParser {
|
||||
key: boolean,
|
||||
pts: number | undefined,
|
||||
dts: number | undefined,
|
||||
debug: string
|
||||
debug: string,
|
||||
): ParsedVideoSample {
|
||||
return {
|
||||
key,
|
||||
@@ -27,7 +27,7 @@ class BaseVideoParser {
|
||||
}
|
||||
|
||||
protected getLastNalUnit(
|
||||
samples: VideoSample[]
|
||||
samples: VideoSample[],
|
||||
): VideoSampleUnit | undefined {
|
||||
let VideoSample = this.VideoSample;
|
||||
let lastUnit: VideoSampleUnit | undefined;
|
||||
@@ -44,7 +44,7 @@ class BaseVideoParser {
|
||||
|
||||
protected pushAccessUnit(
|
||||
VideoSample: ParsedVideoSample,
|
||||
videoTrack: DemuxedVideoTrack
|
||||
videoTrack: DemuxedVideoTrack,
|
||||
) {
|
||||
if (VideoSample.units.length && VideoSample.frame) {
|
||||
// if sample does not have PTS/DTS, patch with last sample PTS/DTS
|
||||
@@ -65,7 +65,7 @@ class BaseVideoParser {
|
||||
}
|
||||
if (VideoSample.debug.length) {
|
||||
logger.log(
|
||||
VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug
|
||||
VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -338,7 +338,7 @@ class ExpGolomb {
|
||||
width: Math.ceil(
|
||||
(picWidthInMbsMinus1 + 1) * 16 -
|
||||
frameCropLeftOffset * 2 -
|
||||
frameCropRightOffset * 2
|
||||
frameCropRightOffset * 2,
|
||||
),
|
||||
height:
|
||||
(2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 -
|
||||
|
||||
+51
-51
@@ -173,212 +173,212 @@ export enum Events {
|
||||
export interface HlsListeners {
|
||||
[Events.MEDIA_ATTACHING]: (
|
||||
event: Events.MEDIA_ATTACHING,
|
||||
data: MediaAttachingData
|
||||
data: MediaAttachingData,
|
||||
) => void;
|
||||
[Events.MEDIA_ATTACHED]: (
|
||||
event: Events.MEDIA_ATTACHED,
|
||||
data: MediaAttachedData
|
||||
data: MediaAttachedData,
|
||||
) => void;
|
||||
[Events.MEDIA_DETACHING]: (event: Events.MEDIA_DETACHING) => void;
|
||||
[Events.MEDIA_DETACHED]: (event: Events.MEDIA_DETACHED) => void;
|
||||
[Events.BUFFER_RESET]: (event: Events.BUFFER_RESET) => void;
|
||||
[Events.BUFFER_CODECS]: (
|
||||
event: Events.BUFFER_CODECS,
|
||||
data: BufferCodecsData
|
||||
data: BufferCodecsData,
|
||||
) => void;
|
||||
[Events.BUFFER_CREATED]: (
|
||||
event: Events.BUFFER_CREATED,
|
||||
data: BufferCreatedData
|
||||
data: BufferCreatedData,
|
||||
) => void;
|
||||
[Events.BUFFER_APPENDING]: (
|
||||
event: Events.BUFFER_APPENDING,
|
||||
data: BufferAppendingData
|
||||
data: BufferAppendingData,
|
||||
) => void;
|
||||
[Events.BUFFER_APPENDED]: (
|
||||
event: Events.BUFFER_APPENDED,
|
||||
data: BufferAppendedData
|
||||
data: BufferAppendedData,
|
||||
) => void;
|
||||
[Events.BUFFER_EOS]: (event: Events.BUFFER_EOS, data: BufferEOSData) => void;
|
||||
[Events.BUFFER_FLUSHING]: (
|
||||
event: Events.BUFFER_FLUSHING,
|
||||
data: BufferFlushingData
|
||||
data: BufferFlushingData,
|
||||
) => void;
|
||||
[Events.BUFFER_FLUSHED]: (
|
||||
event: Events.BUFFER_FLUSHED,
|
||||
data: BufferFlushedData
|
||||
data: BufferFlushedData,
|
||||
) => void;
|
||||
[Events.MANIFEST_LOADING]: (
|
||||
event: Events.MANIFEST_LOADING,
|
||||
data: ManifestLoadingData
|
||||
data: ManifestLoadingData,
|
||||
) => void;
|
||||
[Events.MANIFEST_LOADED]: (
|
||||
event: Events.MANIFEST_LOADED,
|
||||
data: ManifestLoadedData
|
||||
data: ManifestLoadedData,
|
||||
) => void;
|
||||
[Events.MANIFEST_PARSED]: (
|
||||
event: Events.MANIFEST_PARSED,
|
||||
data: ManifestParsedData
|
||||
data: ManifestParsedData,
|
||||
) => void;
|
||||
[Events.LEVEL_SWITCHING]: (
|
||||
event: Events.LEVEL_SWITCHING,
|
||||
data: LevelSwitchingData
|
||||
data: LevelSwitchingData,
|
||||
) => void;
|
||||
[Events.LEVEL_SWITCHED]: (
|
||||
event: Events.LEVEL_SWITCHED,
|
||||
data: LevelSwitchedData
|
||||
data: LevelSwitchedData,
|
||||
) => void;
|
||||
[Events.LEVEL_LOADING]: (
|
||||
event: Events.LEVEL_LOADING,
|
||||
data: LevelLoadingData
|
||||
data: LevelLoadingData,
|
||||
) => void;
|
||||
[Events.LEVEL_LOADED]: (
|
||||
event: Events.LEVEL_LOADED,
|
||||
data: LevelLoadedData
|
||||
data: LevelLoadedData,
|
||||
) => void;
|
||||
[Events.LEVEL_UPDATED]: (
|
||||
event: Events.LEVEL_UPDATED,
|
||||
data: LevelUpdatedData
|
||||
data: LevelUpdatedData,
|
||||
) => void;
|
||||
[Events.LEVEL_PTS_UPDATED]: (
|
||||
event: Events.LEVEL_PTS_UPDATED,
|
||||
data: LevelPTSUpdatedData
|
||||
data: LevelPTSUpdatedData,
|
||||
) => void;
|
||||
[Events.LEVELS_UPDATED]: (
|
||||
event: Events.LEVELS_UPDATED,
|
||||
data: LevelsUpdatedData
|
||||
data: LevelsUpdatedData,
|
||||
) => void;
|
||||
[Events.AUDIO_TRACKS_UPDATED]: (
|
||||
event: Events.AUDIO_TRACKS_UPDATED,
|
||||
data: AudioTracksUpdatedData
|
||||
data: AudioTracksUpdatedData,
|
||||
) => void;
|
||||
[Events.AUDIO_TRACK_SWITCHING]: (
|
||||
event: Events.AUDIO_TRACK_SWITCHING,
|
||||
data: AudioTrackSwitchingData
|
||||
data: AudioTrackSwitchingData,
|
||||
) => void;
|
||||
[Events.AUDIO_TRACK_SWITCHED]: (
|
||||
event: Events.AUDIO_TRACK_SWITCHED,
|
||||
data: AudioTrackSwitchedData
|
||||
data: AudioTrackSwitchedData,
|
||||
) => void;
|
||||
[Events.AUDIO_TRACK_LOADING]: (
|
||||
event: Events.AUDIO_TRACK_LOADING,
|
||||
data: TrackLoadingData
|
||||
data: TrackLoadingData,
|
||||
) => void;
|
||||
[Events.AUDIO_TRACK_LOADED]: (
|
||||
event: Events.AUDIO_TRACK_LOADED,
|
||||
data: AudioTrackLoadedData
|
||||
data: AudioTrackLoadedData,
|
||||
) => void;
|
||||
[Events.SUBTITLE_TRACKS_UPDATED]: (
|
||||
event: Events.SUBTITLE_TRACKS_UPDATED,
|
||||
data: SubtitleTracksUpdatedData
|
||||
data: SubtitleTracksUpdatedData,
|
||||
) => void;
|
||||
[Events.SUBTITLE_TRACKS_CLEARED]: (
|
||||
event: Events.SUBTITLE_TRACKS_CLEARED
|
||||
event: Events.SUBTITLE_TRACKS_CLEARED,
|
||||
) => void;
|
||||
[Events.SUBTITLE_TRACK_SWITCH]: (
|
||||
event: Events.SUBTITLE_TRACK_SWITCH,
|
||||
data: SubtitleTrackSwitchData
|
||||
data: SubtitleTrackSwitchData,
|
||||
) => void;
|
||||
[Events.SUBTITLE_TRACK_LOADING]: (
|
||||
event: Events.SUBTITLE_TRACK_LOADING,
|
||||
data: TrackLoadingData
|
||||
data: TrackLoadingData,
|
||||
) => void;
|
||||
[Events.SUBTITLE_TRACK_LOADED]: (
|
||||
event: Events.SUBTITLE_TRACK_LOADED,
|
||||
data: SubtitleTrackLoadedData
|
||||
data: SubtitleTrackLoadedData,
|
||||
) => void;
|
||||
[Events.SUBTITLE_FRAG_PROCESSED]: (
|
||||
event: Events.SUBTITLE_FRAG_PROCESSED,
|
||||
data: SubtitleFragProcessedData
|
||||
data: SubtitleFragProcessedData,
|
||||
) => void;
|
||||
[Events.CUES_PARSED]: (
|
||||
event: Events.CUES_PARSED,
|
||||
data: CuesParsedData
|
||||
data: CuesParsedData,
|
||||
) => void;
|
||||
[Events.NON_NATIVE_TEXT_TRACKS_FOUND]: (
|
||||
event: Events.NON_NATIVE_TEXT_TRACKS_FOUND,
|
||||
data: NonNativeTextTracksData
|
||||
data: NonNativeTextTracksData,
|
||||
) => void;
|
||||
[Events.INIT_PTS_FOUND]: (
|
||||
event: Events.INIT_PTS_FOUND,
|
||||
data: InitPTSFoundData
|
||||
data: InitPTSFoundData,
|
||||
) => void;
|
||||
[Events.FRAG_LOADING]: (
|
||||
event: Events.FRAG_LOADING,
|
||||
data: FragLoadingData
|
||||
data: FragLoadingData,
|
||||
) => void;
|
||||
// [Events.FRAG_LOAD_PROGRESS]: TodoEventType
|
||||
[Events.FRAG_LOAD_EMERGENCY_ABORTED]: (
|
||||
event: Events.FRAG_LOAD_EMERGENCY_ABORTED,
|
||||
data: FragLoadEmergencyAbortedData
|
||||
data: FragLoadEmergencyAbortedData,
|
||||
) => void;
|
||||
[Events.FRAG_LOADED]: (
|
||||
event: Events.FRAG_LOADED,
|
||||
data: FragLoadedData
|
||||
data: FragLoadedData,
|
||||
) => void;
|
||||
[Events.FRAG_DECRYPTED]: (
|
||||
event: Events.FRAG_DECRYPTED,
|
||||
data: FragDecryptedData
|
||||
data: FragDecryptedData,
|
||||
) => void;
|
||||
[Events.FRAG_PARSING_INIT_SEGMENT]: (
|
||||
event: Events.FRAG_PARSING_INIT_SEGMENT,
|
||||
data: FragParsingInitSegmentData
|
||||
data: FragParsingInitSegmentData,
|
||||
) => void;
|
||||
[Events.FRAG_PARSING_USERDATA]: (
|
||||
event: Events.FRAG_PARSING_USERDATA,
|
||||
data: FragParsingUserdataData
|
||||
data: FragParsingUserdataData,
|
||||
) => void;
|
||||
[Events.FRAG_PARSING_METADATA]: (
|
||||
event: Events.FRAG_PARSING_METADATA,
|
||||
data: FragParsingMetadataData
|
||||
data: FragParsingMetadataData,
|
||||
) => void;
|
||||
// [Events.FRAG_PARSING_DATA]: TodoEventType
|
||||
[Events.FRAG_PARSED]: (
|
||||
event: Events.FRAG_PARSED,
|
||||
data: FragParsedData
|
||||
data: FragParsedData,
|
||||
) => void;
|
||||
[Events.FRAG_BUFFERED]: (
|
||||
event: Events.FRAG_BUFFERED,
|
||||
data: FragBufferedData
|
||||
data: FragBufferedData,
|
||||
) => void;
|
||||
[Events.FRAG_CHANGED]: (
|
||||
event: Events.FRAG_CHANGED,
|
||||
data: FragChangedData
|
||||
data: FragChangedData,
|
||||
) => void;
|
||||
[Events.FPS_DROP]: (event: Events.FPS_DROP, data: FPSDropData) => void;
|
||||
[Events.FPS_DROP_LEVEL_CAPPING]: (
|
||||
event: Events.FPS_DROP_LEVEL_CAPPING,
|
||||
data: FPSDropLevelCappingData
|
||||
data: FPSDropLevelCappingData,
|
||||
) => void;
|
||||
[Events.ERROR]: (event: Events.ERROR, data: ErrorData) => void;
|
||||
[Events.DESTROYING]: (event: Events.DESTROYING) => void;
|
||||
[Events.KEY_LOADING]: (
|
||||
event: Events.KEY_LOADING,
|
||||
data: KeyLoadingData
|
||||
data: KeyLoadingData,
|
||||
) => void;
|
||||
[Events.KEY_LOADED]: (event: Events.KEY_LOADED, data: KeyLoadedData) => void;
|
||||
[Events.LIVE_BACK_BUFFER_REACHED]: (
|
||||
event: Events.LIVE_BACK_BUFFER_REACHED,
|
||||
data: LiveBackBufferData
|
||||
data: LiveBackBufferData,
|
||||
) => void;
|
||||
[Events.BACK_BUFFER_REACHED]: (
|
||||
event: Events.BACK_BUFFER_REACHED,
|
||||
data: BackBufferData
|
||||
data: BackBufferData,
|
||||
) => void;
|
||||
[Events.STEERING_MANIFEST_LOADED]: (
|
||||
event: Events.STEERING_MANIFEST_LOADED,
|
||||
data: SteeringManifestLoadedData
|
||||
data: SteeringManifestLoadedData,
|
||||
) => void;
|
||||
}
|
||||
export interface HlsEventEmitter {
|
||||
on<E extends keyof HlsListeners, Context = undefined>(
|
||||
event: E,
|
||||
listener: HlsListeners[E],
|
||||
context?: Context
|
||||
context?: Context,
|
||||
): void;
|
||||
once<E extends keyof HlsListeners, Context = undefined>(
|
||||
event: E,
|
||||
listener: HlsListeners[E],
|
||||
context?: Context
|
||||
context?: Context,
|
||||
): void;
|
||||
|
||||
removeAllListeners<E extends keyof HlsListeners>(event?: E): void;
|
||||
@@ -386,14 +386,14 @@ export interface HlsEventEmitter {
|
||||
event: E,
|
||||
listener?: HlsListeners[E],
|
||||
context?: Context,
|
||||
once?: boolean
|
||||
once?: boolean,
|
||||
): void;
|
||||
|
||||
listeners<E extends keyof HlsListeners>(event: E): HlsListeners[E][];
|
||||
emit<E extends keyof HlsListeners>(
|
||||
event: E,
|
||||
name: E,
|
||||
eventObject: Parameters<HlsListeners[E]>[1]
|
||||
eventObject: Parameters<HlsListeners[E]>[1],
|
||||
): boolean;
|
||||
listenerCount<E extends keyof HlsListeners>(event: E): number;
|
||||
}
|
||||
|
||||
+16
-16
@@ -153,7 +153,7 @@ export default class Hls implements HlsEventEmitter {
|
||||
: null;
|
||||
const levelController = (this.levelController = new LevelController(
|
||||
this,
|
||||
contentSteering
|
||||
contentSteering,
|
||||
));
|
||||
// FragmentTracker must be defined before StreamController because the order of event handling is important
|
||||
const fragmentTracker = new FragmentTracker(this);
|
||||
@@ -161,7 +161,7 @@ export default class Hls implements HlsEventEmitter {
|
||||
const streamController = (this.streamController = new StreamController(
|
||||
this,
|
||||
fragmentTracker,
|
||||
keyLoader
|
||||
keyLoader,
|
||||
));
|
||||
|
||||
// Cap level controller uses streamController to flush the buffer
|
||||
@@ -190,37 +190,37 @@ export default class Hls implements HlsEventEmitter {
|
||||
|
||||
this.audioTrackController = this.createController(
|
||||
config.audioTrackController,
|
||||
networkControllers
|
||||
networkControllers,
|
||||
);
|
||||
const AudioStreamControllerClass = config.audioStreamController;
|
||||
if (AudioStreamControllerClass) {
|
||||
networkControllers.push(
|
||||
new AudioStreamControllerClass(this, fragmentTracker, keyLoader)
|
||||
new AudioStreamControllerClass(this, fragmentTracker, keyLoader),
|
||||
);
|
||||
}
|
||||
// subtitleTrackController must be defined before subtitleStreamController because the order of event handling is important
|
||||
this.subtitleTrackController = this.createController(
|
||||
config.subtitleTrackController,
|
||||
networkControllers
|
||||
networkControllers,
|
||||
);
|
||||
const SubtitleStreamControllerClass = config.subtitleStreamController;
|
||||
if (SubtitleStreamControllerClass) {
|
||||
networkControllers.push(
|
||||
new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader)
|
||||
new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader),
|
||||
);
|
||||
}
|
||||
this.createController(config.timelineController, coreComponents);
|
||||
keyLoader.emeController = this.emeController = this.createController(
|
||||
config.emeController,
|
||||
coreComponents
|
||||
coreComponents,
|
||||
);
|
||||
this.cmcdController = this.createController(
|
||||
config.cmcdController,
|
||||
coreComponents
|
||||
coreComponents,
|
||||
);
|
||||
this.latencyController = this.createController(
|
||||
LatencyController,
|
||||
coreComponents
|
||||
coreComponents,
|
||||
);
|
||||
|
||||
this.coreComponents = coreComponents;
|
||||
@@ -249,7 +249,7 @@ export default class Hls implements HlsEventEmitter {
|
||||
on<E extends keyof HlsListeners, Context = undefined>(
|
||||
event: E,
|
||||
listener: HlsListeners[E],
|
||||
context: Context = this as any
|
||||
context: Context = this as any,
|
||||
) {
|
||||
this._emitter.on(event, listener, context);
|
||||
}
|
||||
@@ -257,7 +257,7 @@ export default class Hls implements HlsEventEmitter {
|
||||
once<E extends keyof HlsListeners, Context = undefined>(
|
||||
event: E,
|
||||
listener: HlsListeners[E],
|
||||
context: Context = this as any
|
||||
context: Context = this as any,
|
||||
) {
|
||||
this._emitter.once(event, listener, context);
|
||||
}
|
||||
@@ -270,7 +270,7 @@ export default class Hls implements HlsEventEmitter {
|
||||
event: E,
|
||||
listener?: HlsListeners[E] | undefined,
|
||||
context: Context = this as any,
|
||||
once?: boolean | undefined
|
||||
once?: boolean | undefined,
|
||||
) {
|
||||
this._emitter.off(event, listener, context, once);
|
||||
}
|
||||
@@ -282,14 +282,14 @@ export default class Hls implements HlsEventEmitter {
|
||||
emit<E extends keyof HlsListeners>(
|
||||
event: E,
|
||||
name: E,
|
||||
eventObject: Parameters<HlsListeners[E]>[1]
|
||||
eventObject: Parameters<HlsListeners[E]>[1],
|
||||
): boolean {
|
||||
return this._emitter.emit(event, name, eventObject);
|
||||
}
|
||||
|
||||
trigger<E extends keyof HlsListeners>(
|
||||
event: E,
|
||||
eventObject: Parameters<HlsListeners[E]>[1]
|
||||
eventObject: Parameters<HlsListeners[E]>[1],
|
||||
): boolean {
|
||||
if (this.config.debug) {
|
||||
return this.emit(event, event, eventObject);
|
||||
@@ -303,7 +303,7 @@ export default class Hls implements HlsEventEmitter {
|
||||
'. Error message: "' +
|
||||
error.message +
|
||||
'". Here is a stacktrace:',
|
||||
error
|
||||
error,
|
||||
);
|
||||
// Prevent recursion in error event handlers that throw #5497
|
||||
if (!this.triggeringException) {
|
||||
@@ -380,7 +380,7 @@ export default class Hls implements HlsEventEmitter {
|
||||
url,
|
||||
{
|
||||
alwaysNormalize: true,
|
||||
}
|
||||
},
|
||||
));
|
||||
logger.log(`loadSource:${loadingSource}`);
|
||||
if (
|
||||
|
||||
@@ -47,7 +47,7 @@ export class DateRange {
|
||||
dateRangeAttr[key] !== previousAttr[key]
|
||||
) {
|
||||
logger.warn(
|
||||
`DATERANGE tag attribute: "${key}" does not match for tags with ID: "${dateRangeAttr.ID}"`
|
||||
`DATERANGE tag attribute: "${key}" does not match for tags with ID: "${dateRangeAttr.ID}"`,
|
||||
);
|
||||
this._badValueForSameId = key;
|
||||
break;
|
||||
@@ -57,7 +57,7 @@ export class DateRange {
|
||||
dateRangeAttr = Object.assign(
|
||||
new AttrList({}),
|
||||
previousAttr,
|
||||
dateRangeAttr
|
||||
dateRangeAttr,
|
||||
);
|
||||
}
|
||||
this.attr = dateRangeAttr;
|
||||
@@ -96,7 +96,7 @@ export class DateRange {
|
||||
get duration(): number | null {
|
||||
if (DateRangeAttribute.DURATION in this.attr) {
|
||||
const duration = this.attr.decimalFloatingPoint(
|
||||
DateRangeAttribute.DURATION
|
||||
DateRangeAttribute.DURATION,
|
||||
);
|
||||
if (Number.isFinite(duration)) {
|
||||
return duration;
|
||||
@@ -110,7 +110,7 @@ export class DateRange {
|
||||
get plannedDuration(): number | null {
|
||||
if (DateRangeAttribute.PLANNED_DURATION in this.attr) {
|
||||
return this.attr.decimalFloatingPoint(
|
||||
DateRangeAttribute.PLANNED_DURATION
|
||||
DateRangeAttribute.PLANNED_DURATION,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -41,7 +41,7 @@ export default class FragmentLoader {
|
||||
|
||||
load(
|
||||
frag: Fragment,
|
||||
onProgress?: FragmentLoadProgressCallback
|
||||
onProgress?: FragmentLoadProgressCallback,
|
||||
): Promise<FragLoadedData> {
|
||||
const url = frag.url;
|
||||
if (!url) {
|
||||
@@ -52,10 +52,10 @@ export default class FragmentLoader {
|
||||
fatal: false,
|
||||
frag,
|
||||
error: new Error(
|
||||
`Fragment does not have a ${url ? 'part list' : 'url'}`
|
||||
`Fragment does not have a ${url ? 'part list' : 'url'}`,
|
||||
),
|
||||
networkDetails: null,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
this.abort();
|
||||
@@ -85,7 +85,7 @@ export default class FragmentLoader {
|
||||
: (new DefaultILoader(config) as Loader<FragmentLoaderContext>));
|
||||
const loaderContext = createLoaderContext(frag);
|
||||
const loadPolicy = getLoaderConfigWithoutReties(
|
||||
config.fragLoadPolicy.default
|
||||
config.fragLoadPolicy.default,
|
||||
);
|
||||
const loaderConfig: LoaderConfiguration = {
|
||||
loadPolicy,
|
||||
@@ -124,7 +124,7 @@ export default class FragmentLoader {
|
||||
error: new Error(`HTTP Error ${response.code} ${response.text}`),
|
||||
networkDetails,
|
||||
stats,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
onAbort: (stats, context, networkDetails) => {
|
||||
@@ -138,7 +138,7 @@ export default class FragmentLoader {
|
||||
error: new Error('Aborted'),
|
||||
networkDetails,
|
||||
stats,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
onTimeout: (stats, context, networkDetails) => {
|
||||
@@ -152,7 +152,7 @@ export default class FragmentLoader {
|
||||
error: new Error(`Timeout after ${loaderConfig.timeout}ms`),
|
||||
networkDetails,
|
||||
stats,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
onProgress: (stats, context, data, networkDetails) => {
|
||||
@@ -172,7 +172,7 @@ export default class FragmentLoader {
|
||||
public loadPart(
|
||||
frag: Fragment,
|
||||
part: Part,
|
||||
onProgress: FragmentLoadProgressCallback
|
||||
onProgress: FragmentLoadProgressCallback,
|
||||
): Promise<FragLoadedData> {
|
||||
this.abort();
|
||||
|
||||
@@ -197,7 +197,7 @@ export default class FragmentLoader {
|
||||
const loaderContext = createLoaderContext(frag, part);
|
||||
// Should we define another load policy for parts?
|
||||
const loadPolicy = getLoaderConfigWithoutReties(
|
||||
config.fragLoadPolicy.default
|
||||
config.fragLoadPolicy.default,
|
||||
);
|
||||
const loaderConfig: LoaderConfiguration = {
|
||||
loadPolicy,
|
||||
@@ -239,7 +239,7 @@ export default class FragmentLoader {
|
||||
error: new Error(`HTTP Error ${response.code} ${response.text}`),
|
||||
networkDetails,
|
||||
stats,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
onAbort: (stats, context, networkDetails) => {
|
||||
@@ -255,7 +255,7 @@ export default class FragmentLoader {
|
||||
error: new Error('Aborted'),
|
||||
networkDetails,
|
||||
stats,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
onTimeout: (stats, context, networkDetails) => {
|
||||
@@ -270,7 +270,7 @@ export default class FragmentLoader {
|
||||
error: new Error(`Timeout after ${loaderConfig.timeout}ms`),
|
||||
networkDetails,
|
||||
stats,
|
||||
})
|
||||
}),
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -286,7 +286,7 @@ export default class FragmentLoader {
|
||||
const estTotalParts = Math.round(frag.duration / part.duration);
|
||||
const estLoadedParts = Math.min(
|
||||
Math.round(fragStats.loaded / partTotal),
|
||||
estTotalParts
|
||||
estTotalParts,
|
||||
);
|
||||
const estRemainingParts = estTotalParts - estLoadedParts;
|
||||
const estRemainingBytes =
|
||||
@@ -319,7 +319,7 @@ export default class FragmentLoader {
|
||||
|
||||
function createLoaderContext(
|
||||
frag: Fragment,
|
||||
part: Part | null = null
|
||||
part: Part | null = null,
|
||||
): FragmentLoaderContext {
|
||||
const segment: BaseSegment = part || frag;
|
||||
const loaderContext: FragmentLoaderContext = {
|
||||
@@ -395,5 +395,5 @@ export interface FragLoadFailResult extends ErrorData {
|
||||
}
|
||||
|
||||
export type FragmentLoadProgressCallback = (
|
||||
result: FragLoadedData | PartsLoadedData
|
||||
result: FragLoadedData | PartsLoadedData,
|
||||
) => void;
|
||||
|
||||
@@ -235,7 +235,7 @@ export class Fragment extends BaseSegment {
|
||||
endPTS: number,
|
||||
startDTS: number,
|
||||
endDTS: number,
|
||||
partial: boolean = false
|
||||
partial: boolean = false,
|
||||
) {
|
||||
const { elementaryStreams } = this;
|
||||
const info = elementaryStreams[type];
|
||||
@@ -282,7 +282,7 @@ export class Part extends BaseSegment {
|
||||
frag: Fragment,
|
||||
baseurl: string,
|
||||
index: number,
|
||||
previous?: Part
|
||||
previous?: Part,
|
||||
) {
|
||||
super(baseurl);
|
||||
this.duration = partAttrs.decimalFloatingPoint('DURATION');
|
||||
|
||||
+25
-25
@@ -74,7 +74,7 @@ export default class KeyLoader implements ComponentAPI {
|
||||
details: ErrorDetails = ErrorDetails.KEY_LOAD_ERROR,
|
||||
error: Error,
|
||||
networkDetails?: any,
|
||||
response?: { url: string; data: undefined; code: number; text: string }
|
||||
response?: { url: string; data: undefined; code: number; text: string },
|
||||
): LoadError {
|
||||
return new LoadError({
|
||||
type: ErrorTypes.NETWORK_ERROR,
|
||||
@@ -89,7 +89,7 @@ export default class KeyLoader implements ComponentAPI {
|
||||
|
||||
loadClear(
|
||||
loadingFrag: Fragment,
|
||||
encryptedFragments: Fragment[]
|
||||
encryptedFragments: Fragment[],
|
||||
): void | Promise<void> {
|
||||
if (this.emeController && this.config.emeEnabled) {
|
||||
// access key-system with nearest key on start (loaidng frag is unencrypted)
|
||||
@@ -126,7 +126,7 @@ export default class KeyLoader implements ComponentAPI {
|
||||
|
||||
loadInternal(
|
||||
frag: Fragment,
|
||||
keySystemFormat?: KeySystemFormats
|
||||
keySystemFormat?: KeySystemFormats,
|
||||
): Promise<KeyLoadedData> {
|
||||
if (keySystemFormat) {
|
||||
frag.setKeyFormat(keySystemFormat);
|
||||
@@ -136,10 +136,10 @@ export default class KeyLoader implements ComponentAPI {
|
||||
const error = new Error(
|
||||
keySystemFormat
|
||||
? `Expected frag.decryptdata to be defined after setting format ${keySystemFormat}`
|
||||
: 'Missing decryption data on fragment in onKeyLoading'
|
||||
: 'Missing decryption data on fragment in onKeyLoading',
|
||||
);
|
||||
return Promise.reject(
|
||||
this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, error)
|
||||
this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, error),
|
||||
);
|
||||
}
|
||||
const uri = decryptdata.uri;
|
||||
@@ -148,8 +148,8 @@ export default class KeyLoader implements ComponentAPI {
|
||||
this.createKeyLoadError(
|
||||
frag,
|
||||
ErrorDetails.KEY_LOAD_ERROR,
|
||||
new Error(`Invalid key URI: "${uri}"`)
|
||||
)
|
||||
new Error(`Invalid key URI: "${uri}"`),
|
||||
),
|
||||
);
|
||||
}
|
||||
let keyInfo = this.keyUriToKeyInfo[uri];
|
||||
@@ -201,9 +201,9 @@ export default class KeyLoader implements ComponentAPI {
|
||||
frag,
|
||||
ErrorDetails.KEY_LOAD_ERROR,
|
||||
new Error(
|
||||
`Key supplied with unsupported METHOD: "${decryptdata.method}"`
|
||||
)
|
||||
)
|
||||
`Key supplied with unsupported METHOD: "${decryptdata.method}"`,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -218,7 +218,7 @@ export default class KeyLoader implements ComponentAPI {
|
||||
(keySessionContext) => {
|
||||
keyInfo.mediaKeySessionContext = keySessionContext;
|
||||
return keyLoadedData;
|
||||
}
|
||||
},
|
||||
)).catch((error) => {
|
||||
// Remove promise for license renewal or retry
|
||||
keyInfo.keyLoadPromise = null;
|
||||
@@ -260,7 +260,7 @@ export default class KeyLoader implements ComponentAPI {
|
||||
response: LoaderResponse,
|
||||
stats: LoaderStats,
|
||||
context: KeyLoaderContext,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => {
|
||||
const { frag, keyInfo, url: uri } = context;
|
||||
if (!frag.decryptdata || keyInfo !== this.keyUriToKeyInfo[uri]) {
|
||||
@@ -269,13 +269,13 @@ export default class KeyLoader implements ComponentAPI {
|
||||
frag,
|
||||
ErrorDetails.KEY_LOAD_ERROR,
|
||||
new Error('after key load, decryptdata unset or changed'),
|
||||
networkDetails
|
||||
)
|
||||
networkDetails,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
keyInfo.decryptdata.key = frag.decryptdata.key = new Uint8Array(
|
||||
response.data as ArrayBuffer
|
||||
response.data as ArrayBuffer,
|
||||
);
|
||||
|
||||
// detach fragment key loader on load success
|
||||
@@ -288,7 +288,7 @@ export default class KeyLoader implements ComponentAPI {
|
||||
response: { code: number; text: string },
|
||||
context: KeyLoaderContext,
|
||||
networkDetails: any,
|
||||
stats: LoaderStats
|
||||
stats: LoaderStats,
|
||||
) => {
|
||||
this.resetLoader(context);
|
||||
reject(
|
||||
@@ -296,18 +296,18 @@ export default class KeyLoader implements ComponentAPI {
|
||||
frag,
|
||||
ErrorDetails.KEY_LOAD_ERROR,
|
||||
new Error(
|
||||
`HTTP Error ${response.code} loading key ${response.text}`
|
||||
`HTTP Error ${response.code} loading key ${response.text}`,
|
||||
),
|
||||
networkDetails,
|
||||
{ url: loaderContext.url, data: undefined, ...response }
|
||||
)
|
||||
{ url: loaderContext.url, data: undefined, ...response },
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
onTimeout: (
|
||||
stats: LoaderStats,
|
||||
context: KeyLoaderContext,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => {
|
||||
this.resetLoader(context);
|
||||
reject(
|
||||
@@ -315,15 +315,15 @@ export default class KeyLoader implements ComponentAPI {
|
||||
frag,
|
||||
ErrorDetails.KEY_LOAD_TIMEOUT,
|
||||
new Error('key loading timed out'),
|
||||
networkDetails
|
||||
)
|
||||
networkDetails,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
onAbort: (
|
||||
stats: LoaderStats,
|
||||
context: KeyLoaderContext,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => {
|
||||
this.resetLoader(context);
|
||||
reject(
|
||||
@@ -331,8 +331,8 @@ export default class KeyLoader implements ComponentAPI {
|
||||
frag,
|
||||
ErrorDetails.INTERNAL_ABORTED,
|
||||
new Error('key loading aborted'),
|
||||
networkDetails
|
||||
)
|
||||
networkDetails,
|
||||
),
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -88,7 +88,7 @@ export class LevelDetails {
|
||||
get hasProgramDateTime(): boolean {
|
||||
if (this.fragments.length) {
|
||||
return Number.isFinite(
|
||||
this.fragments[this.fragments.length - 1].programDateTime as number
|
||||
this.fragments[this.fragments.length - 1].programDateTime as number,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -43,7 +43,7 @@ export class LevelKey implements DecryptData {
|
||||
uri: string,
|
||||
format: string,
|
||||
formatversions: number[] = [1],
|
||||
iv: Uint8Array | null = null
|
||||
iv: Uint8Array | null = null,
|
||||
) {
|
||||
this.method = method;
|
||||
this.uri = uri;
|
||||
@@ -95,7 +95,7 @@ export class LevelKey implements DecryptData {
|
||||
// It must have an IV defined. We cannot substitute the Segment Number in.
|
||||
if (this.method === 'AES-128' && !this.iv) {
|
||||
logger.warn(
|
||||
`missing IV for initialization segment with method="${this.method}" - compliance issue`
|
||||
`missing IV for initialization segment with method="${this.method}" - compliance issue`,
|
||||
);
|
||||
}
|
||||
// Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation.
|
||||
@@ -107,7 +107,7 @@ export class LevelKey implements DecryptData {
|
||||
this.uri,
|
||||
'identity',
|
||||
this.keyFormatVersions,
|
||||
iv
|
||||
iv,
|
||||
);
|
||||
return decryptdata;
|
||||
}
|
||||
@@ -126,7 +126,7 @@ export class LevelKey implements DecryptData {
|
||||
if (keyBytes.length >= 22) {
|
||||
this.keyId = keyBytes.subarray(
|
||||
keyBytes.length - 22,
|
||||
keyBytes.length - 6
|
||||
keyBytes.length - 6,
|
||||
);
|
||||
}
|
||||
break;
|
||||
@@ -141,17 +141,17 @@ export class LevelKey implements DecryptData {
|
||||
const keyBytesUtf16 = new Uint16Array(
|
||||
keyBytes.buffer,
|
||||
keyBytes.byteOffset,
|
||||
keyBytes.byteLength / 2
|
||||
keyBytes.byteLength / 2,
|
||||
);
|
||||
const keyByteStr = String.fromCharCode.apply(
|
||||
null,
|
||||
Array.from(keyBytesUtf16)
|
||||
Array.from(keyBytesUtf16),
|
||||
);
|
||||
|
||||
// Parse Playready WRMHeader XML
|
||||
const xmlKeyBytes = keyByteStr.substring(
|
||||
keyByteStr.indexOf('<'),
|
||||
keyByteStr.length
|
||||
keyByteStr.length,
|
||||
);
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlKeyBytes, 'text/xml');
|
||||
|
||||
+23
-23
@@ -57,7 +57,7 @@ const LEVEL_PLAYLIST_REGEX_FAST = new RegExp(
|
||||
/#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, // next segment's program date/time group 5 => the datetime spec
|
||||
/#.*/.source, // All other non-segment oriented tags will match with all groups empty
|
||||
].join('|'),
|
||||
'g'
|
||||
'g',
|
||||
);
|
||||
|
||||
const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp(
|
||||
@@ -70,13 +70,13 @@ const LEVEL_PLAYLIST_REGEX_SLOW = new RegExp(
|
||||
/#EXT-X-(DISCONTINUITY|ENDLIST|GAP)/.source,
|
||||
/(#)([^:]*):(.*)/.source,
|
||||
/(#)(.*)(?:.*)\r?\n?/.source,
|
||||
].join('|')
|
||||
].join('|'),
|
||||
);
|
||||
|
||||
export default class M3U8Parser {
|
||||
static findGroup(
|
||||
groups: Array<AudioGroup>,
|
||||
mediaGroupId: string
|
||||
mediaGroupId: string,
|
||||
): AudioGroup | undefined {
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const group = groups[i];
|
||||
@@ -108,7 +108,7 @@ export default class M3U8Parser {
|
||||
|
||||
static parseMasterPlaylist(
|
||||
string: string,
|
||||
baseurl: string
|
||||
baseurl: string,
|
||||
): ParsedMultivariantPlaylist {
|
||||
const hasVariableRefs = __USE_VARIABLE_SUBSTITUTION__
|
||||
? hasVariableReferences(string)
|
||||
@@ -166,7 +166,7 @@ export default class M3U8Parser {
|
||||
|
||||
setCodecs(
|
||||
((attrs.CODECS as string) || '').split(/[ ,]+/).filter((c) => c),
|
||||
level
|
||||
level,
|
||||
);
|
||||
|
||||
if (level.videoCodec && level.videoCodec.indexOf('avc1') !== -1) {
|
||||
@@ -212,7 +212,7 @@ export default class M3U8Parser {
|
||||
parsed.sessionKeys.push(sessionKey);
|
||||
} else {
|
||||
logger.warn(
|
||||
`[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "${attributes}"`
|
||||
`[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "${attributes}"`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
@@ -237,13 +237,13 @@ export default class M3U8Parser {
|
||||
substituteVariablesInAttributes(
|
||||
parsed,
|
||||
contentSteeringAttributes,
|
||||
['SERVER-URI', 'PATHWAY-ID']
|
||||
['SERVER-URI', 'PATHWAY-ID'],
|
||||
);
|
||||
}
|
||||
parsed.contentSteering = {
|
||||
uri: M3U8Parser.resolve(
|
||||
contentSteeringAttributes['SERVER-URI'],
|
||||
baseurl
|
||||
baseurl,
|
||||
),
|
||||
pathwayId: contentSteeringAttributes['PATHWAY-ID'] || '.',
|
||||
};
|
||||
@@ -277,7 +277,7 @@ export default class M3U8Parser {
|
||||
static parseMasterPlaylistMedia(
|
||||
string: string,
|
||||
baseurl: string,
|
||||
parsed: ParsedMultivariantPlaylist
|
||||
parsed: ParsedMultivariantPlaylist,
|
||||
): ParsedMultivariantMediaOptions {
|
||||
let result: RegExpExecArray | null;
|
||||
const results: ParsedMultivariantMediaOptions = {};
|
||||
@@ -354,7 +354,7 @@ export default class M3U8Parser {
|
||||
id: number,
|
||||
type: PlaylistLevelType,
|
||||
levelUrlId: number,
|
||||
multivariantVariableList: VariableMap | null
|
||||
multivariantVariableList: VariableMap | null,
|
||||
): LevelDetails {
|
||||
const level = new LevelDetails(baseurl);
|
||||
const fragments: M3U8ParserFragments = level.fragments;
|
||||
@@ -484,7 +484,7 @@ export default class M3U8Parser {
|
||||
currentSN += skippedSegments;
|
||||
}
|
||||
const recentlyRemovedDateranges = skipAttrs.enumeratedString(
|
||||
'RECENTLY-REMOVED-DATERANGES'
|
||||
'RECENTLY-REMOVED-DATERANGES',
|
||||
);
|
||||
if (recentlyRemovedDateranges) {
|
||||
level.recentlyRemovedDateranges =
|
||||
@@ -534,12 +534,12 @@ export default class M3U8Parser {
|
||||
substituteVariablesInAttributes(
|
||||
level,
|
||||
dateRangeAttr,
|
||||
dateRangeAttr.clientAttrs
|
||||
dateRangeAttr.clientAttrs,
|
||||
);
|
||||
}
|
||||
const dateRange = new DateRange(
|
||||
dateRangeAttr,
|
||||
level.dateRanges[dateRangeAttr.ID]
|
||||
level.dateRanges[dateRangeAttr.ID],
|
||||
);
|
||||
if (dateRange.isValid || level.skippedSegments) {
|
||||
level.dateRanges[dateRange.id] = dateRange;
|
||||
@@ -563,7 +563,7 @@ export default class M3U8Parser {
|
||||
importVariableDefinition(
|
||||
level,
|
||||
variableAttributes,
|
||||
multivariantVariableList
|
||||
multivariantVariableList,
|
||||
);
|
||||
} else {
|
||||
addVariableDefinition(level, variableAttributes, baseurl);
|
||||
@@ -632,14 +632,14 @@ export default class M3U8Parser {
|
||||
level.canBlockReload = serverControlAttrs.bool('CAN-BLOCK-RELOAD');
|
||||
level.canSkipUntil = serverControlAttrs.optionalFloat(
|
||||
'CAN-SKIP-UNTIL',
|
||||
0
|
||||
0,
|
||||
);
|
||||
level.canSkipDateRanges =
|
||||
level.canSkipUntil > 0 &&
|
||||
serverControlAttrs.bool('CAN-SKIP-DATERANGES');
|
||||
level.partHoldBack = serverControlAttrs.optionalFloat(
|
||||
'PART-HOLD-BACK',
|
||||
0
|
||||
0,
|
||||
);
|
||||
level.holdBack = serverControlAttrs.optionalFloat('HOLD-BACK', 0);
|
||||
break;
|
||||
@@ -669,7 +669,7 @@ export default class M3U8Parser {
|
||||
frag,
|
||||
baseurl,
|
||||
index,
|
||||
previousFragmentPart
|
||||
previousFragmentPart,
|
||||
);
|
||||
partList.push(part);
|
||||
frag.duration += part.duration;
|
||||
@@ -758,7 +758,7 @@ export default class M3U8Parser {
|
||||
function parseKey(
|
||||
keyTagAttributes: string,
|
||||
baseurl: string,
|
||||
parsed: ParsedMultivariantPlaylist | LevelDetails
|
||||
parsed: ParsedMultivariantPlaylist | LevelDetails,
|
||||
): LevelKey {
|
||||
// https://tools.ietf.org/html/rfc8216#section-4.3.2.4
|
||||
const keyAttrs = new AttrList(keyTagAttributes);
|
||||
@@ -796,7 +796,7 @@ function parseKey(
|
||||
resolvedUri,
|
||||
decryptkeyformat,
|
||||
keyFormatVersions,
|
||||
decryptiv
|
||||
decryptiv,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -832,7 +832,7 @@ function assignCodec(media, groupItem, codecProperty) {
|
||||
|
||||
function backfillProgramDateTimes(
|
||||
fragments: M3U8ParserFragments,
|
||||
firstPdtIndex: number
|
||||
firstPdtIndex: number,
|
||||
) {
|
||||
let fragPrev = fragments[firstPdtIndex] as Fragment;
|
||||
for (let i = firstPdtIndex; i--; ) {
|
||||
@@ -864,7 +864,7 @@ function setInitSegment(
|
||||
frag: Fragment,
|
||||
mapAttrs: AttrList,
|
||||
id: number,
|
||||
levelkeys: { [key: string]: LevelKey } | undefined
|
||||
levelkeys: { [key: string]: LevelKey } | undefined,
|
||||
) {
|
||||
frag.relurl = mapAttrs.URI;
|
||||
if (mapAttrs.BYTERANGE) {
|
||||
@@ -881,7 +881,7 @@ function setInitSegment(
|
||||
function setFragLevelKeys(
|
||||
frag: Fragment,
|
||||
levelkeys: { [key: string]: LevelKey },
|
||||
level: LevelDetails
|
||||
level: LevelDetails,
|
||||
) {
|
||||
frag.levelkeys = levelkeys;
|
||||
const { encryptedFragments } = level;
|
||||
@@ -890,7 +890,7 @@ function setFragLevelKeys(
|
||||
encryptedFragments[encryptedFragments.length - 1].levelkeys !==
|
||||
levelkeys) &&
|
||||
Object.keys(levelkeys).some(
|
||||
(format) => levelkeys![format].isCommonEncryption
|
||||
(format) => levelkeys![format].isCommonEncryption,
|
||||
)
|
||||
) {
|
||||
encryptedFragments.push(frag);
|
||||
|
||||
@@ -35,7 +35,7 @@ import type { MediaAttributes } from '../types/media-playlist';
|
||||
import type { LoaderConfig, RetryConfig } from '../config';
|
||||
|
||||
function mapContextToLevelType(
|
||||
context: PlaylistLoaderContext
|
||||
context: PlaylistLoaderContext,
|
||||
): PlaylistLevelType {
|
||||
const { type } = context;
|
||||
|
||||
@@ -51,7 +51,7 @@ function mapContextToLevelType(
|
||||
|
||||
function getResponseUrl(
|
||||
response: LoaderResponse,
|
||||
context: PlaylistLoaderContext
|
||||
context: PlaylistLoaderContext,
|
||||
): string {
|
||||
let url = response.url;
|
||||
// responseURL not supported on some browsers (it is used to detect URL redirection)
|
||||
@@ -101,7 +101,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
* Returns defaults or configured loader-type overloads (pLoader and loader config params)
|
||||
*/
|
||||
private createInternalLoader(
|
||||
context: PlaylistLoaderContext
|
||||
context: PlaylistLoaderContext,
|
||||
): Loader<LoaderContext> {
|
||||
const config = this.hls.config;
|
||||
const PLoader = config.pLoader;
|
||||
@@ -114,7 +114,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
}
|
||||
|
||||
private getInternalLoader(
|
||||
context: PlaylistLoaderContext
|
||||
context: PlaylistLoaderContext,
|
||||
): Loader<LoaderContext> | undefined {
|
||||
return this.loaders[context.type];
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
|
||||
private onManifestLoading(
|
||||
event: Events.MANIFEST_LOADING,
|
||||
data: ManifestLoadingData
|
||||
data: ManifestLoadingData,
|
||||
) {
|
||||
const { url } = data;
|
||||
this.variableList = null;
|
||||
@@ -175,7 +175,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
|
||||
private onAudioTrackLoading(
|
||||
event: Events.AUDIO_TRACK_LOADING,
|
||||
data: TrackLoadingData
|
||||
data: TrackLoadingData,
|
||||
) {
|
||||
const { id, groupId, url, deliveryDirectives } = data;
|
||||
this.load({
|
||||
@@ -191,7 +191,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
|
||||
private onSubtitleTrackLoading(
|
||||
event: Events.SUBTITLE_TRACK_LOADING,
|
||||
data: TrackLoadingData
|
||||
data: TrackLoadingData,
|
||||
) {
|
||||
const { id, groupId, url, deliveryDirectives } = data;
|
||||
this.load({
|
||||
@@ -220,7 +220,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
return;
|
||||
}
|
||||
logger.log(
|
||||
`[playlist-loader]: aborting previous loader for type: ${context.type}`
|
||||
`[playlist-loader]: aborting previous loader for type: ${context.type}`,
|
||||
);
|
||||
loader.abort();
|
||||
}
|
||||
@@ -267,11 +267,11 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
loadPolicy = Object.assign({}, loadPolicy, {
|
||||
maxTimeToFirstByteMs: Math.min(
|
||||
maxLowLatencyPlaylistRefresh,
|
||||
loadPolicy.maxTimeToFirstByteMs
|
||||
loadPolicy.maxTimeToFirstByteMs,
|
||||
),
|
||||
maxLoadTimeMs: Math.min(
|
||||
maxLowLatencyPlaylistRefresh,
|
||||
loadPolicy.maxTimeToFirstByteMs
|
||||
loadPolicy.maxTimeToFirstByteMs,
|
||||
),
|
||||
});
|
||||
}
|
||||
@@ -304,7 +304,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
context,
|
||||
new Error('no EXTM3U delimiter'),
|
||||
networkDetails || null,
|
||||
stats
|
||||
stats,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -316,7 +316,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
stats,
|
||||
context,
|
||||
networkDetails || null,
|
||||
loader
|
||||
loader,
|
||||
);
|
||||
} else {
|
||||
this.handleMasterPlaylist(response, stats, context, networkDetails);
|
||||
@@ -328,7 +328,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
networkDetails,
|
||||
false,
|
||||
response,
|
||||
stats
|
||||
stats,
|
||||
);
|
||||
},
|
||||
onTimeout: (stats, context, networkDetails) => {
|
||||
@@ -337,7 +337,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
networkDetails,
|
||||
true,
|
||||
undefined,
|
||||
stats
|
||||
stats,
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -351,7 +351,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
response: LoaderResponse,
|
||||
stats: LoaderStats,
|
||||
context: PlaylistLoaderContext,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
): void {
|
||||
const hls = this.hls;
|
||||
const string = response.data as string;
|
||||
@@ -366,7 +366,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
context,
|
||||
parsedResult.playlistParsingError,
|
||||
networkDetails,
|
||||
stats
|
||||
stats,
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -391,7 +391,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
if (audioTracks.length) {
|
||||
// check if we have found an audio track embedded in main playlist (audio track without URI attribute)
|
||||
const embeddedAudioFound: boolean = audioTracks.some(
|
||||
(audioTrack) => !audioTrack.url
|
||||
(audioTrack) => !audioTrack.url,
|
||||
);
|
||||
|
||||
// if no embedded audio track defined, but audio codec signaled in quality level,
|
||||
@@ -404,7 +404,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
!levels[0].attrs.AUDIO
|
||||
) {
|
||||
logger.log(
|
||||
'[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one'
|
||||
'[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one',
|
||||
);
|
||||
audioTracks.unshift({
|
||||
type: 'main',
|
||||
@@ -442,7 +442,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
stats: LoaderStats,
|
||||
context: PlaylistLoaderContext,
|
||||
networkDetails: any,
|
||||
loader: Loader<PlaylistLoaderContext> | undefined
|
||||
loader: Loader<PlaylistLoaderContext> | undefined,
|
||||
): void {
|
||||
const hls = this.hls;
|
||||
const { id, level, type } = context;
|
||||
@@ -459,7 +459,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
levelId,
|
||||
levelType,
|
||||
levelUrlId,
|
||||
this.variableList
|
||||
this.variableList,
|
||||
);
|
||||
|
||||
// We have done our first request (Manifest-type) and receive
|
||||
@@ -501,7 +501,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
stats,
|
||||
context,
|
||||
networkDetails,
|
||||
loader
|
||||
loader,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -510,7 +510,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
context: PlaylistLoaderContext,
|
||||
error: Error,
|
||||
networkDetails: any,
|
||||
stats: LoaderStats
|
||||
stats: LoaderStats,
|
||||
): void {
|
||||
this.hls.trigger(Events.ERROR, {
|
||||
type: ErrorTypes.NETWORK_ERROR,
|
||||
@@ -532,7 +532,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
networkDetails: any,
|
||||
timeout = false,
|
||||
response: { code: number; text: string } | undefined,
|
||||
stats: LoaderStats
|
||||
stats: LoaderStats,
|
||||
): void {
|
||||
let message = `A network ${
|
||||
timeout
|
||||
@@ -611,7 +611,7 @@ class PlaylistLoader implements NetworkComponentAPI {
|
||||
stats: LoaderStats,
|
||||
context: PlaylistLoaderContext,
|
||||
networkDetails: any,
|
||||
loader: Loader<PlaylistLoaderContext> | undefined
|
||||
loader: Loader<PlaylistLoaderContext> | undefined,
|
||||
): void {
|
||||
const hls = this.hls;
|
||||
const { type, level, id, groupId, deliveryDirectives } = context;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
class AAC {
|
||||
static getSilentFrame(
|
||||
codec?: string,
|
||||
channelCount?: number
|
||||
channelCount?: number,
|
||||
): Uint8Array | undefined {
|
||||
switch (codec) {
|
||||
case 'mp4a.40.2':
|
||||
|
||||
+27
-27
@@ -258,7 +258,7 @@ class MP4 {
|
||||
majorBrand,
|
||||
minorVersion,
|
||||
majorBrand,
|
||||
avc1Brand
|
||||
avc1Brand,
|
||||
);
|
||||
MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref));
|
||||
}
|
||||
@@ -338,7 +338,7 @@ class MP4 {
|
||||
0xc4, // 'und' language (undetermined)
|
||||
0x00,
|
||||
0x00,
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ class MP4 {
|
||||
MP4.types.mdia,
|
||||
MP4.mdhd(track.timescale, track.duration),
|
||||
MP4.hdlr(track.type),
|
||||
MP4.minf(track)
|
||||
MP4.minf(track),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -363,7 +363,7 @@ class MP4 {
|
||||
(sequenceNumber >> 16) & 0xff,
|
||||
(sequenceNumber >> 8) & 0xff,
|
||||
sequenceNumber & 0xff, // sequence_number
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -373,14 +373,14 @@ class MP4 {
|
||||
MP4.types.minf,
|
||||
MP4.box(MP4.types.smhd, MP4.SMHD),
|
||||
MP4.DINF,
|
||||
MP4.stbl(track)
|
||||
MP4.stbl(track),
|
||||
);
|
||||
} else {
|
||||
return MP4.box(
|
||||
MP4.types.minf,
|
||||
MP4.box(MP4.types.vmhd, MP4.VMHD),
|
||||
MP4.DINF,
|
||||
MP4.stbl(track)
|
||||
MP4.stbl(track),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -389,7 +389,7 @@ class MP4 {
|
||||
return MP4.box(
|
||||
MP4.types.moof,
|
||||
MP4.mfhd(sn),
|
||||
MP4.traf(track, baseMediaDecodeTime)
|
||||
MP4.traf(track, baseMediaDecodeTime),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -405,7 +405,7 @@ class MP4 {
|
||||
null,
|
||||
[MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)]
|
||||
.concat(boxes)
|
||||
.concat(MP4.mvex(tracks))
|
||||
.concat(MP4.mvex(tracks)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -566,7 +566,7 @@ class MP4 {
|
||||
MP4.box(MP4.types.stts, MP4.STTS),
|
||||
MP4.box(MP4.types.stsc, MP4.STSC),
|
||||
MP4.box(MP4.types.stsz, MP4.STSZ),
|
||||
MP4.box(MP4.types.stco, MP4.STCO)
|
||||
MP4.box(MP4.types.stco, MP4.STCO),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -613,8 +613,8 @@ class MP4 {
|
||||
.concat([
|
||||
track.pps.length, // numOfPictureParameterSets
|
||||
])
|
||||
.concat(pps)
|
||||
)
|
||||
.concat(pps),
|
||||
),
|
||||
); // "PPS"
|
||||
const width = track.width;
|
||||
const height = track.height;
|
||||
@@ -719,7 +719,7 @@ class MP4 {
|
||||
0x2d,
|
||||
0xc6,
|
||||
0xc0,
|
||||
])
|
||||
]),
|
||||
), // avgBitrate
|
||||
MP4.box(
|
||||
MP4.types.pasp,
|
||||
@@ -732,8 +732,8 @@ class MP4 {
|
||||
(vSpacing >> 16) & 0xff,
|
||||
(vSpacing >> 8) & 0xff,
|
||||
vSpacing & 0xff,
|
||||
])
|
||||
)
|
||||
]),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -772,7 +772,7 @@ class MP4 {
|
||||
]
|
||||
.concat([configlen])
|
||||
.concat(track.config)
|
||||
.concat([0x06, 0x01, 0x02])
|
||||
.concat([0x06, 0x01, 0x02]),
|
||||
); // GASpecificConfig)); // length + audio config descriptor
|
||||
}
|
||||
|
||||
@@ -814,7 +814,7 @@ class MP4 {
|
||||
return MP4.box(
|
||||
MP4.types.mp4a,
|
||||
MP4.audioStsd(track),
|
||||
MP4.box(MP4.types.esds, MP4.esds(track))
|
||||
MP4.box(MP4.types.esds, MP4.esds(track)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -826,7 +826,7 @@ class MP4 {
|
||||
return MP4.box(
|
||||
MP4.types['ac-3'],
|
||||
MP4.audioStsd(track),
|
||||
MP4.box(MP4.types.dac3, track.config)
|
||||
MP4.box(MP4.types.dac3, track.config),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -950,7 +950,7 @@ class MP4 {
|
||||
height & 0xff,
|
||||
0x00,
|
||||
0x00, // height
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -958,10 +958,10 @@ class MP4 {
|
||||
const sampleDependencyTable = MP4.sdtp(track);
|
||||
const id = track.id;
|
||||
const upperWordBaseMediaDecodeTime = Math.floor(
|
||||
baseMediaDecodeTime / (UINT32_MAX + 1)
|
||||
baseMediaDecodeTime / (UINT32_MAX + 1),
|
||||
);
|
||||
const lowerWordBaseMediaDecodeTime = Math.floor(
|
||||
baseMediaDecodeTime % (UINT32_MAX + 1)
|
||||
baseMediaDecodeTime % (UINT32_MAX + 1),
|
||||
);
|
||||
return MP4.box(
|
||||
MP4.types.traf,
|
||||
@@ -976,7 +976,7 @@ class MP4 {
|
||||
(id >> 16) & 0xff,
|
||||
(id >> 8) & 0xff,
|
||||
id & 0xff, // track_ID
|
||||
])
|
||||
]),
|
||||
),
|
||||
MP4.box(
|
||||
MP4.types.tfdt,
|
||||
@@ -993,7 +993,7 @@ class MP4 {
|
||||
(lowerWordBaseMediaDecodeTime >> 16) & 0xff,
|
||||
(lowerWordBaseMediaDecodeTime >> 8) & 0xff,
|
||||
lowerWordBaseMediaDecodeTime & 0xff,
|
||||
])
|
||||
]),
|
||||
),
|
||||
MP4.trun(
|
||||
track,
|
||||
@@ -1003,9 +1003,9 @@ class MP4 {
|
||||
8 + // traf header
|
||||
16 + // mfhd
|
||||
8 + // moof header
|
||||
8
|
||||
8,
|
||||
), // mdat header
|
||||
sampleDependencyTable
|
||||
sampleDependencyTable,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1047,7 +1047,7 @@ class MP4 {
|
||||
0x01,
|
||||
0x00,
|
||||
0x01, // default_sample_flags
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1078,7 +1078,7 @@ class MP4 {
|
||||
(offset >>> 8) & 0xff,
|
||||
offset & 0xff, // data_offset
|
||||
],
|
||||
0
|
||||
0,
|
||||
);
|
||||
for (i = 0; i < len; i++) {
|
||||
sample = samples[i];
|
||||
@@ -1108,7 +1108,7 @@ class MP4 {
|
||||
(cts >>> 8) & 0xff,
|
||||
cts & 0xff, // sample_composition_time_offset
|
||||
],
|
||||
12 + 16 * i
|
||||
12 + 16 * i,
|
||||
);
|
||||
}
|
||||
return MP4.box(MP4.types.trun, array);
|
||||
|
||||
+49
-49
@@ -55,7 +55,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
observer: HlsEventEmitter,
|
||||
config: HlsConfig,
|
||||
typeSupported,
|
||||
vendor = ''
|
||||
vendor = '',
|
||||
) {
|
||||
this.observer = observer;
|
||||
this.config = config;
|
||||
@@ -119,7 +119,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
timeOffset: number,
|
||||
accurateTimeOffset: boolean,
|
||||
flush: boolean,
|
||||
playlistType: PlaylistLevelType
|
||||
playlistType: PlaylistLevelType,
|
||||
): RemuxerResult {
|
||||
let video: RemuxedTrack | undefined;
|
||||
let audio: RemuxedTrack | undefined;
|
||||
@@ -152,7 +152,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
audioTrack,
|
||||
videoTrack,
|
||||
timeOffset,
|
||||
accurateTimeOffset
|
||||
accurateTimeOffset,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
independent = true;
|
||||
if (firstKeyFrameIndex > 0) {
|
||||
logger.warn(
|
||||
`[mp4-remuxer]: Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`
|
||||
`[mp4-remuxer]: Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`,
|
||||
);
|
||||
const startPTS = this.getVideoStartPts(videoTrack.samples);
|
||||
videoTrack.samples = videoTrack.samples.slice(firstKeyFrameIndex);
|
||||
@@ -177,7 +177,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
firstKeyFramePTS = videoTimeOffset;
|
||||
} else if (firstKeyFrameIndex === -1) {
|
||||
logger.warn(
|
||||
`[mp4-remuxer]: No keyframe found out of ${length} video samples`
|
||||
`[mp4-remuxer]: No keyframe found out of ${length} video samples`,
|
||||
);
|
||||
independent = false;
|
||||
}
|
||||
@@ -203,13 +203,13 @@ export default class MP4Remuxer implements Remuxer {
|
||||
// if initSegment was generated without audio samples, regenerate it again
|
||||
if (!audioTrack.samplerate) {
|
||||
logger.warn(
|
||||
'[mp4-remuxer]: regenerate InitSegment as audio detected'
|
||||
'[mp4-remuxer]: regenerate InitSegment as audio detected',
|
||||
);
|
||||
initSegment = this.generateIS(
|
||||
audioTrack,
|
||||
videoTrack,
|
||||
timeOffset,
|
||||
accurateTimeOffset
|
||||
accurateTimeOffset,
|
||||
);
|
||||
}
|
||||
audio = this.remuxAudio(
|
||||
@@ -221,27 +221,27 @@ export default class MP4Remuxer implements Remuxer {
|
||||
enoughVideoSamples ||
|
||||
playlistType === PlaylistLevelType.AUDIO
|
||||
? videoTimeOffset
|
||||
: undefined
|
||||
: undefined,
|
||||
);
|
||||
if (enoughVideoSamples) {
|
||||
const audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0;
|
||||
// if initSegment was generated without video samples, regenerate it again
|
||||
if (!videoTrack.inputTimeScale) {
|
||||
logger.warn(
|
||||
'[mp4-remuxer]: regenerate InitSegment as video detected'
|
||||
'[mp4-remuxer]: regenerate InitSegment as video detected',
|
||||
);
|
||||
initSegment = this.generateIS(
|
||||
audioTrack,
|
||||
videoTrack,
|
||||
timeOffset,
|
||||
accurateTimeOffset
|
||||
accurateTimeOffset,
|
||||
);
|
||||
}
|
||||
video = this.remuxVideo(
|
||||
videoTrack,
|
||||
videoTimeOffset,
|
||||
isVideoContiguous,
|
||||
audioTrackLength
|
||||
audioTrackLength,
|
||||
);
|
||||
}
|
||||
} else if (enoughVideoSamples) {
|
||||
@@ -249,7 +249,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
videoTrack,
|
||||
videoTimeOffset,
|
||||
isVideoContiguous,
|
||||
0
|
||||
0,
|
||||
);
|
||||
}
|
||||
if (video) {
|
||||
@@ -267,7 +267,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
id3Track,
|
||||
timeOffset,
|
||||
this._initPTS,
|
||||
this._initDTS
|
||||
this._initDTS,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
text = flushTextTrackUserdataCueSamples(
|
||||
textTrack,
|
||||
timeOffset,
|
||||
this._initPTS
|
||||
this._initPTS,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -294,7 +294,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
audioTrack: DemuxedAudioTrack,
|
||||
videoTrack: DemuxedVideoTrack,
|
||||
timeOffset: number,
|
||||
accurateTimeOffset: boolean
|
||||
accurateTimeOffset: boolean,
|
||||
): InitSegmentData | undefined {
|
||||
const audioSamples = audioTrack.samples;
|
||||
const videoSamples = videoTrack.samples;
|
||||
@@ -378,7 +378,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
const startOffset = Math.round(timescale * timeOffset);
|
||||
initDTS = Math.min(
|
||||
initDTS as number,
|
||||
normalizePts(videoSamples[0].dts, startPTS) - startOffset
|
||||
normalizePts(videoSamples[0].dts, startPTS) - startOffset,
|
||||
);
|
||||
initPTS = Math.min(initPTS as number, startPTS - startOffset);
|
||||
} else {
|
||||
@@ -414,7 +414,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
track: DemuxedVideoTrack,
|
||||
timeOffset: number,
|
||||
contiguous: boolean,
|
||||
audioTrackLength: number
|
||||
audioTrackLength: number,
|
||||
): RemuxedTrack | undefined {
|
||||
const timeScale: number = track.inputTimeScale;
|
||||
const inputSamples: Array<VideoSample> = track.samples;
|
||||
@@ -483,15 +483,15 @@ export default class MP4Remuxer implements Remuxer {
|
||||
logger.warn(
|
||||
`AVC: ${toMsFromMpegTsClock(
|
||||
delta,
|
||||
true
|
||||
)} ms (${delta}dts) hole between fragments detected, filling it`
|
||||
true,
|
||||
)} ms (${delta}dts) hole between fragments detected, filling it`,
|
||||
);
|
||||
} else {
|
||||
logger.warn(
|
||||
`AVC: ${toMsFromMpegTsClock(
|
||||
-delta,
|
||||
true
|
||||
)} ms (${delta}dts) overlapping between fragments detected`
|
||||
true,
|
||||
)} ms (${delta}dts) overlapping between fragments detected`,
|
||||
);
|
||||
}
|
||||
if (!foundOverlap || nextAvcDts >= inputSamples[0].pts) {
|
||||
@@ -502,11 +502,11 @@ export default class MP4Remuxer implements Remuxer {
|
||||
logger.log(
|
||||
`Video: First PTS/DTS adjusted: ${toMsFromMpegTsClock(
|
||||
firstPTS,
|
||||
true
|
||||
true,
|
||||
)}/${toMsFromMpegTsClock(
|
||||
firstDTS,
|
||||
true
|
||||
)}, delta: ${toMsFromMpegTsClock(delta, true)} ms`
|
||||
true,
|
||||
)}, delta: ${toMsFromMpegTsClock(delta, true)} ms`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -621,7 +621,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
deltaToFrameEnd / 90
|
||||
} ms to the next segment; using duration ${
|
||||
mp4SampleDuration / 90
|
||||
} ms for the last video frame.`
|
||||
} ms for the last video frame.`,
|
||||
);
|
||||
} else {
|
||||
mp4SampleDuration = lastFrameDuration;
|
||||
@@ -631,7 +631,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
}
|
||||
}
|
||||
const compositionTimeOffset = Math.round(
|
||||
VideoSample.pts - VideoSample.dts
|
||||
VideoSample.pts - VideoSample.dts,
|
||||
);
|
||||
minDtsDelta = Math.min(minDtsDelta, mp4SampleDuration);
|
||||
maxDtsDelta = Math.max(maxDtsDelta, mp4SampleDuration);
|
||||
@@ -643,8 +643,8 @@ export default class MP4Remuxer implements Remuxer {
|
||||
VideoSample.key,
|
||||
mp4SampleDuration,
|
||||
mp4SampleLength,
|
||||
compositionTimeOffset
|
||||
)
|
||||
compositionTimeOffset,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -666,7 +666,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
outputSamples[0].cts === 0
|
||||
) {
|
||||
logger.warn(
|
||||
'Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.'
|
||||
'Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.',
|
||||
);
|
||||
let dts = firstDTS;
|
||||
for (let i = 0, len = outputSamples.length; i < len; i++) {
|
||||
@@ -699,7 +699,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
firstDTS,
|
||||
Object.assign({}, track, {
|
||||
samples: outputSamples,
|
||||
})
|
||||
}),
|
||||
);
|
||||
const type: SourceBufferName = 'video';
|
||||
const data = {
|
||||
@@ -736,7 +736,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
timeOffset: number,
|
||||
contiguous: boolean,
|
||||
accurateTimeOffset: boolean,
|
||||
videoTimeOffset?: number
|
||||
videoTimeOffset?: number,
|
||||
): RemuxedTrack | undefined {
|
||||
const inputTimeScale: number = track.inputTimeScale;
|
||||
const mp4timeScale: number = track.samplerate
|
||||
@@ -775,7 +775,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
Math.abs(timeOffsetMpegTS - nextAudioPts) < 9000) ||
|
||||
Math.abs(
|
||||
normalizePts(inputSamples[0].pts - initTime, timeOffsetMpegTS) -
|
||||
nextAudioPts
|
||||
nextAudioPts,
|
||||
) <
|
||||
20 * inputSampleDuration)) as boolean);
|
||||
|
||||
@@ -830,10 +830,10 @@ export default class MP4Remuxer implements Remuxer {
|
||||
if (i === 0) {
|
||||
logger.warn(
|
||||
`Audio frame @ ${(pts / inputTimeScale).toFixed(
|
||||
3
|
||||
3,
|
||||
)}s overlaps nextAudioPts by ${Math.round(
|
||||
(1000 * delta) / inputTimeScale
|
||||
)} ms.`
|
||||
(1000 * delta) / inputTimeScale,
|
||||
)} ms.`,
|
||||
);
|
||||
this.nextAudioPts = nextAudioPts = nextPts = pts;
|
||||
}
|
||||
@@ -864,18 +864,18 @@ export default class MP4Remuxer implements Remuxer {
|
||||
`[mp4-remuxer]: Injecting ${missing} audio frame @ ${(
|
||||
nextPts / inputTimeScale
|
||||
).toFixed(3)}s due to ${Math.round(
|
||||
(1000 * delta) / inputTimeScale
|
||||
)} ms gap.`
|
||||
(1000 * delta) / inputTimeScale,
|
||||
)} ms gap.`,
|
||||
);
|
||||
for (let j = 0; j < missing; j++) {
|
||||
const newStamp = Math.max(nextPts as number, 0);
|
||||
let fillFrame = AAC.getSilentFrame(
|
||||
track.manifestCodec || track.codec,
|
||||
track.channelCount
|
||||
track.channelCount,
|
||||
);
|
||||
if (!fillFrame) {
|
||||
logger.log(
|
||||
'[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.'
|
||||
'[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.',
|
||||
);
|
||||
fillFrame = sample.unit.subarray();
|
||||
}
|
||||
@@ -969,7 +969,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
: MP4.moof(
|
||||
track.sequenceNumber++,
|
||||
firstPTS! / scaleFactor,
|
||||
Object.assign({}, track, { samples: outputSamples })
|
||||
Object.assign({}, track, { samples: outputSamples }),
|
||||
);
|
||||
|
||||
// Clear the track samples. This also clears the samples array in the demuxer, since the reference is shared
|
||||
@@ -998,7 +998,7 @@ export default class MP4Remuxer implements Remuxer {
|
||||
track: DemuxedAudioTrack,
|
||||
timeOffset: number,
|
||||
contiguous: boolean,
|
||||
videoData: Fragment
|
||||
videoData: Fragment,
|
||||
): RemuxedTrack | undefined {
|
||||
const inputTimeScale: number = track.inputTimeScale;
|
||||
const mp4timeScale: number = track.samplerate
|
||||
@@ -1021,14 +1021,14 @@ export default class MP4Remuxer implements Remuxer {
|
||||
// silent frame
|
||||
const silentFrame: Uint8Array | undefined = AAC.getSilentFrame(
|
||||
track.manifestCodec || track.codec,
|
||||
track.channelCount
|
||||
track.channelCount,
|
||||
);
|
||||
|
||||
logger.warn('[mp4-remuxer]: remux empty Audio');
|
||||
// Can't remux if we can't generate a silent frame...
|
||||
if (!silentFrame) {
|
||||
logger.trace(
|
||||
'[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec'
|
||||
'[mp4-remuxer]: Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -1080,7 +1080,7 @@ export function flushTextTrackMetadataCueSamples(
|
||||
track: DemuxedMetadataTrack,
|
||||
timeOffset: number,
|
||||
initPTS: RationalTimestamp,
|
||||
initDTS: RationalTimestamp
|
||||
initDTS: RationalTimestamp,
|
||||
): RemuxedMetadata | undefined {
|
||||
const length = track.samples.length;
|
||||
if (!length) {
|
||||
@@ -1094,12 +1094,12 @@ export function flushTextTrackMetadataCueSamples(
|
||||
sample.pts =
|
||||
normalizePts(
|
||||
sample.pts - (initPTS.baseTime * inputTimeScale) / initPTS.timescale,
|
||||
timeOffset * inputTimeScale
|
||||
timeOffset * inputTimeScale,
|
||||
) / inputTimeScale;
|
||||
sample.dts =
|
||||
normalizePts(
|
||||
sample.dts - (initDTS.baseTime * inputTimeScale) / initDTS.timescale,
|
||||
timeOffset * inputTimeScale
|
||||
timeOffset * inputTimeScale,
|
||||
) / inputTimeScale;
|
||||
}
|
||||
const samples = track.samples;
|
||||
@@ -1112,7 +1112,7 @@ export function flushTextTrackMetadataCueSamples(
|
||||
export function flushTextTrackUserdataCueSamples(
|
||||
track: DemuxedUserdataTrack,
|
||||
timeOffset: number,
|
||||
initPTS: RationalTimestamp
|
||||
initPTS: RationalTimestamp,
|
||||
): RemuxedUserdata | undefined {
|
||||
const length = track.samples.length;
|
||||
if (!length) {
|
||||
@@ -1127,7 +1127,7 @@ export function flushTextTrackUserdataCueSamples(
|
||||
sample.pts =
|
||||
normalizePts(
|
||||
sample.pts - (initPTS.baseTime * inputTimeScale) / initPTS.timescale,
|
||||
timeOffset * inputTimeScale
|
||||
timeOffset * inputTimeScale,
|
||||
) / inputTimeScale;
|
||||
}
|
||||
track.samples.sort((a, b) => a.pts - b.pts);
|
||||
@@ -1157,7 +1157,7 @@ class Mp4Sample {
|
||||
isKeyframe: boolean,
|
||||
duration: number,
|
||||
size: number,
|
||||
cts: number
|
||||
cts: number,
|
||||
) {
|
||||
this.duration = duration;
|
||||
this.size = size;
|
||||
|
||||
@@ -56,7 +56,7 @@ class PassThroughRemuxer implements Remuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
decryptdata: DecryptData | null
|
||||
decryptdata: DecryptData | null,
|
||||
) {
|
||||
this.audioCodec = audioCodec;
|
||||
this.videoCodec = videoCodec;
|
||||
@@ -77,14 +77,14 @@ class PassThroughRemuxer implements Remuxer {
|
||||
if (initData.audio) {
|
||||
audioCodec = getParsedTrackCodec(
|
||||
initData.audio,
|
||||
ElementaryStreamTypes.AUDIO
|
||||
ElementaryStreamTypes.AUDIO,
|
||||
);
|
||||
}
|
||||
|
||||
if (initData.video) {
|
||||
videoCodec = getParsedTrackCodec(
|
||||
initData.video,
|
||||
ElementaryStreamTypes.VIDEO
|
||||
ElementaryStreamTypes.VIDEO,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ class PassThroughRemuxer implements Remuxer {
|
||||
};
|
||||
} else {
|
||||
logger.warn(
|
||||
'[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.'
|
||||
'[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.',
|
||||
);
|
||||
}
|
||||
this.initTracks = tracks;
|
||||
@@ -124,7 +124,7 @@ class PassThroughRemuxer implements Remuxer {
|
||||
id3Track: DemuxedMetadataTrack,
|
||||
textTrack: DemuxedUserdataTrack,
|
||||
timeOffset: number,
|
||||
accurateTimeOffset: boolean
|
||||
accurateTimeOffset: boolean,
|
||||
): RemuxerResult {
|
||||
let { initPTS, lastEndTime } = this;
|
||||
const result: RemuxerResult = {
|
||||
@@ -178,7 +178,7 @@ class PassThroughRemuxer implements Remuxer {
|
||||
initSegment.initPTS = decodeTime - timeOffset;
|
||||
if (initPTS && initPTS.timescale === 1) {
|
||||
logger.warn(
|
||||
`Adjusting initPTS by ${initSegment.initPTS - initPTS.baseTime}`
|
||||
`Adjusting initPTS by ${initSegment.initPTS - initPTS.baseTime}`,
|
||||
);
|
||||
}
|
||||
this.initPTS = initPTS = {
|
||||
@@ -232,14 +232,14 @@ class PassThroughRemuxer implements Remuxer {
|
||||
id3Track,
|
||||
timeOffset,
|
||||
initPTS,
|
||||
initPTS
|
||||
initPTS,
|
||||
);
|
||||
|
||||
if (textTrack.samples.length) {
|
||||
result.text = flushTextTrackUserdataCueSamples(
|
||||
textTrack,
|
||||
timeOffset,
|
||||
initPTS
|
||||
initPTS,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ function isInvalidInitPts(
|
||||
initPTS: RationalTimestamp | null,
|
||||
startDTS: number,
|
||||
timeOffset: number,
|
||||
duration: number
|
||||
duration: number,
|
||||
): initPTS is null {
|
||||
if (initPTS === null) {
|
||||
return true;
|
||||
@@ -264,7 +264,7 @@ function isInvalidInitPts(
|
||||
|
||||
function getParsedTrackCodec(
|
||||
track: InitDataTrack,
|
||||
type: ElementaryStreamTypes.AUDIO | ElementaryStreamTypes.VIDEO
|
||||
type: ElementaryStreamTypes.AUDIO | ElementaryStreamTypes.VIDEO,
|
||||
): string {
|
||||
const parsedCodec = track?.codec;
|
||||
if (parsedCodec && parsedCodec.length > 4) {
|
||||
@@ -283,7 +283,7 @@ function getParsedTrackCodec(
|
||||
}
|
||||
const result = 'mp4a.40.5';
|
||||
logger.info(
|
||||
`Parsed audio codec "${parsedCodec}" or audio object type not handled. Using "${result}"`
|
||||
`Parsed audio codec "${parsedCodec}" or audio object type not handled. Using "${result}"`,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -5,12 +5,12 @@ export interface Demuxer {
|
||||
data: Uint8Array,
|
||||
timeOffset: number,
|
||||
isSampleAes?: boolean,
|
||||
flush?: boolean
|
||||
flush?: boolean,
|
||||
): DemuxerResult;
|
||||
demuxSampleAes(
|
||||
data: Uint8Array,
|
||||
keyData: KeyData,
|
||||
timeOffset: number
|
||||
timeOffset: number,
|
||||
): Promise<DemuxerResult>;
|
||||
flush(timeOffset?: number): DemuxerResult | Promise<DemuxerResult>;
|
||||
destroy(): void;
|
||||
@@ -18,7 +18,7 @@ export interface Demuxer {
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
trackDuration: number
|
||||
trackDuration: number,
|
||||
);
|
||||
resetTimeStamp(defaultInitPTS?: RationalTimestamp | null): void;
|
||||
resetContiguity(): void;
|
||||
|
||||
+1
-1
@@ -207,7 +207,7 @@ export class Level {
|
||||
export function addGroupId(
|
||||
level: Level,
|
||||
type: string,
|
||||
id: string | undefined
|
||||
id: string | undefined,
|
||||
): void {
|
||||
if (!id) {
|
||||
return;
|
||||
|
||||
+6
-6
@@ -99,14 +99,14 @@ export type LoaderOnSuccess<T extends LoaderContext> = (
|
||||
response: LoaderResponse,
|
||||
stats: LoaderStats,
|
||||
context: T,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => void;
|
||||
|
||||
export type LoaderOnProgress<T extends LoaderContext> = (
|
||||
stats: LoaderStats,
|
||||
context: T,
|
||||
data: string | ArrayBuffer,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => void;
|
||||
|
||||
export type LoaderOnError<T extends LoaderContext> = (
|
||||
@@ -118,19 +118,19 @@ export type LoaderOnError<T extends LoaderContext> = (
|
||||
},
|
||||
context: T,
|
||||
networkDetails: any,
|
||||
stats: LoaderStats
|
||||
stats: LoaderStats,
|
||||
) => void;
|
||||
|
||||
export type LoaderOnTimeout<T extends LoaderContext> = (
|
||||
stats: LoaderStats,
|
||||
context: T,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => void;
|
||||
|
||||
export type LoaderOnAbort<T extends LoaderContext> = (
|
||||
stats: LoaderStats,
|
||||
context: T,
|
||||
networkDetails: any
|
||||
networkDetails: any,
|
||||
) => void;
|
||||
|
||||
export interface LoaderCallbacks<T extends LoaderContext> {
|
||||
@@ -147,7 +147,7 @@ export interface Loader<T extends LoaderContext> {
|
||||
load(
|
||||
context: T,
|
||||
config: LoaderConfiguration,
|
||||
callbacks: LoaderCallbacks<T>
|
||||
callbacks: LoaderCallbacks<T>,
|
||||
): void;
|
||||
/**
|
||||
* `getCacheAge()` is called by hls.js to get the duration that a given object
|
||||
|
||||
@@ -21,13 +21,13 @@ export interface Remuxer {
|
||||
timeOffset: number,
|
||||
accurateTimeOffset: boolean,
|
||||
flush: boolean,
|
||||
playlistType: PlaylistLevelType
|
||||
playlistType: PlaylistLevelType,
|
||||
): RemuxerResult;
|
||||
resetInitSegment(
|
||||
initSegment: Uint8Array | undefined,
|
||||
audioCodec: string | undefined,
|
||||
videoCodec: string | undefined,
|
||||
decryptdata: DecryptData | null
|
||||
decryptdata: DecryptData | null,
|
||||
): void;
|
||||
resetTimeStamp(defaultInitPTS: RationalTimestamp | null): void;
|
||||
resetNextTimestamp(): void;
|
||||
|
||||
@@ -30,7 +30,7 @@ export class ChunkMetadata {
|
||||
id: number,
|
||||
size = 0,
|
||||
part = -1,
|
||||
partial = false
|
||||
partial = false,
|
||||
) {
|
||||
this.level = level;
|
||||
this.sn = sn;
|
||||
|
||||
@@ -18,7 +18,7 @@ const BinarySearch = {
|
||||
*/
|
||||
search: function <T>(
|
||||
list: T[],
|
||||
comparisonFn: BinarySearchComparison<T>
|
||||
comparisonFn: BinarySearchComparison<T>,
|
||||
): T | null {
|
||||
let minIndex: number = 0;
|
||||
let maxIndex: number = list.length - 1;
|
||||
|
||||
@@ -55,7 +55,7 @@ export class BufferHelper {
|
||||
static bufferInfo(
|
||||
media: Bufferable | null,
|
||||
pos: number,
|
||||
maxHoleDuration: number
|
||||
maxHoleDuration: number,
|
||||
): BufferInfo {
|
||||
try {
|
||||
if (media) {
|
||||
@@ -79,7 +79,7 @@ export class BufferHelper {
|
||||
static bufferedInfo(
|
||||
buffered: BufferTimeRange[],
|
||||
pos: number,
|
||||
maxHoleDuration: number
|
||||
maxHoleDuration: number,
|
||||
): {
|
||||
len: number;
|
||||
start: number;
|
||||
|
||||
+24
-21
@@ -412,13 +412,13 @@ export class Row {
|
||||
if (this.pos < 0) {
|
||||
this.logger.log(
|
||||
VerboseLevel.DEBUG,
|
||||
'Negative cursor position ' + this.pos
|
||||
'Negative cursor position ' + this.pos,
|
||||
);
|
||||
this.pos = 0;
|
||||
} else if (this.pos > NR_COLS) {
|
||||
this.logger.log(
|
||||
VerboseLevel.DEBUG,
|
||||
'Too large cursor position ' + this.pos
|
||||
'Too large cursor position ' + this.pos,
|
||||
);
|
||||
this.pos = NR_COLS;
|
||||
}
|
||||
@@ -461,7 +461,7 @@ export class Row {
|
||||
char +
|
||||
') at position ' +
|
||||
this.pos +
|
||||
'. Skipping it!'
|
||||
'. Skipping it!',
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -601,7 +601,7 @@ export class CaptionScreen {
|
||||
setPAC(pacData: PACData) {
|
||||
this.logger.log(
|
||||
VerboseLevel.INFO,
|
||||
() => 'pacData = ' + JSON.stringify(pacData)
|
||||
() => 'pacData = ' + JSON.stringify(pacData),
|
||||
);
|
||||
let newRow = pacData.row - 1;
|
||||
if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) {
|
||||
@@ -627,7 +627,7 @@ export class CaptionScreen {
|
||||
if (prevLineTime !== null && time !== null && prevLineTime < time) {
|
||||
for (let i = 0; i < this.nrRollUpRows; i++) {
|
||||
this.rows[newRow - this.nrRollUpRows + i + 1].copy(
|
||||
lastOutputScreen.rows[topRowIndex + i]
|
||||
lastOutputScreen.rows[topRowIndex + i],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -658,7 +658,7 @@ export class CaptionScreen {
|
||||
setBkgData(bkgData: Partial<PenStyles>) {
|
||||
this.logger.log(
|
||||
VerboseLevel.INFO,
|
||||
() => 'bkgData = ' + JSON.stringify(bkgData)
|
||||
() => 'bkgData = ' + JSON.stringify(bkgData),
|
||||
);
|
||||
this.backSpace();
|
||||
this.setPen(bkgData);
|
||||
@@ -673,7 +673,7 @@ export class CaptionScreen {
|
||||
if (this.nrRollUpRows === null) {
|
||||
this.logger.log(
|
||||
VerboseLevel.DEBUG,
|
||||
'roll_up but nrRollUpRows not set yet'
|
||||
'roll_up but nrRollUpRows not set yet',
|
||||
);
|
||||
return; // Not properly setup
|
||||
}
|
||||
@@ -745,7 +745,7 @@ class Cea608Channel {
|
||||
constructor(
|
||||
channelNumber: number,
|
||||
outputFilter: OutputFilter,
|
||||
logger: CaptionsLogger
|
||||
logger: CaptionsLogger,
|
||||
) {
|
||||
this.chNr = channelNumber;
|
||||
this.outputFilter = outputFilter;
|
||||
@@ -818,12 +818,12 @@ class Cea608Channel {
|
||||
this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP';
|
||||
this.logger.log(
|
||||
VerboseLevel.INFO,
|
||||
() => screen + ': ' + this.writeScreen.getDisplayText(true)
|
||||
() => screen + ': ' + this.writeScreen.getDisplayText(true),
|
||||
);
|
||||
if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') {
|
||||
this.logger.log(
|
||||
VerboseLevel.TEXT,
|
||||
() => 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true)
|
||||
() => 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true),
|
||||
);
|
||||
this.outputDataUpdate();
|
||||
}
|
||||
@@ -925,7 +925,7 @@ class Cea608Channel {
|
||||
this.writeScreen = this.nonDisplayedMemory;
|
||||
this.logger.log(
|
||||
VerboseLevel.TEXT,
|
||||
() => 'DISP: ' + this.displayedMemory.getDisplayText()
|
||||
() => 'DISP: ' + this.displayedMemory.getDisplayText(),
|
||||
);
|
||||
}
|
||||
this.outputDataUpdate(true);
|
||||
@@ -976,7 +976,7 @@ class Cea608Channel {
|
||||
this.outputFilter.newCue(
|
||||
this.cueStartTime!,
|
||||
time,
|
||||
this.lastOutputScreen
|
||||
this.lastOutputScreen,
|
||||
);
|
||||
if (dispatch && this.outputFilter.dispatchCue) {
|
||||
this.outputFilter.dispatchCue();
|
||||
@@ -1065,7 +1065,7 @@ class Cea608Parser {
|
||||
numArrayToHexArray([byteList[i], byteList[i + 1]]) +
|
||||
'] -> (' +
|
||||
numArrayToHexArray([a, b]) +
|
||||
')'
|
||||
')',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1093,7 +1093,7 @@ class Cea608Parser {
|
||||
} else {
|
||||
this.logger.log(
|
||||
VerboseLevel.WARNING,
|
||||
'No channel found yet. TEXT-MODE?'
|
||||
'No channel found yet. TEXT-MODE?',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1104,7 +1104,7 @@ class Cea608Parser {
|
||||
"Couldn't parse cleaned data " +
|
||||
numArrayToHexArray([a, b]) +
|
||||
' orig: ' +
|
||||
numArrayToHexArray([byteList[i], byteList[i + 1]])
|
||||
numArrayToHexArray([byteList[i], byteList[i + 1]]),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1129,7 +1129,7 @@ class Cea608Parser {
|
||||
setLastCmd(null, null, cmdHistory);
|
||||
this.logger.log(
|
||||
VerboseLevel.DEBUG,
|
||||
'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped'
|
||||
'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -1196,7 +1196,7 @@ class Cea608Parser {
|
||||
if (chNr !== this.currentChannel) {
|
||||
this.logger.log(
|
||||
VerboseLevel.ERROR,
|
||||
'Mismatch channel in midrow parsing'
|
||||
'Mismatch channel in midrow parsing',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -1207,7 +1207,7 @@ class Cea608Parser {
|
||||
channel.ccMIDROW(b);
|
||||
this.logger.log(
|
||||
VerboseLevel.DEBUG,
|
||||
'MIDROW (' + numArrayToHexArray([a, b]) + ')'
|
||||
'MIDROW (' + numArrayToHexArray([a, b]) + ')',
|
||||
);
|
||||
return true;
|
||||
}
|
||||
@@ -1324,7 +1324,10 @@ class Cea608Parser {
|
||||
|
||||
this.logger.log(
|
||||
VerboseLevel.INFO,
|
||||
"Special char '" + getCharForByte(oneCode) + "' in channel " + channelNr
|
||||
"Special char '" +
|
||||
getCharForByte(oneCode) +
|
||||
"' in channel " +
|
||||
channelNr,
|
||||
);
|
||||
charCodes = [oneCode];
|
||||
} else if (a >= 0x20 && a <= 0x7f) {
|
||||
@@ -1334,7 +1337,7 @@ class Cea608Parser {
|
||||
const hexCodes = numArrayToHexArray(charCodes);
|
||||
this.logger.log(
|
||||
VerboseLevel.DEBUG,
|
||||
'Char codes = ' + hexCodes.join(',')
|
||||
'Char codes = ' + hexCodes.join(','),
|
||||
);
|
||||
setLastCmd(a, b, this.cmdHistory);
|
||||
}
|
||||
@@ -1403,7 +1406,7 @@ class Cea608Parser {
|
||||
function setLastCmd(
|
||||
a: number | null,
|
||||
b: number | null,
|
||||
cmdHistory: CmdHistory
|
||||
cmdHistory: CmdHistory,
|
||||
) {
|
||||
cmdHistory.a = a;
|
||||
cmdHistory.b = b;
|
||||
|
||||
+4
-4
@@ -88,7 +88,7 @@ export function isCodecType(codec: string, type: CodecType): boolean {
|
||||
|
||||
export function areCodecsMediaSourceSupported(
|
||||
codecs: string,
|
||||
type: CodecType
|
||||
type: CodecType,
|
||||
): boolean {
|
||||
return !codecs
|
||||
.split(',')
|
||||
@@ -123,7 +123,7 @@ const CODEC_COMPATIBLE_NAMES: CodecNameCache = {};
|
||||
type LowerCaseCodecType = 'flac' | 'opus';
|
||||
|
||||
function getCodecCompatibleNameLower(
|
||||
lowerCaseCodec: LowerCaseCodecType
|
||||
lowerCaseCodec: LowerCaseCodecType,
|
||||
): string {
|
||||
if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) {
|
||||
return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]!;
|
||||
@@ -150,13 +150,13 @@ function getCodecCompatibleNameLower(
|
||||
const AUDIO_CODEC_REGEXP = /flac|opus/i;
|
||||
export function getCodecCompatibleName(codec: string): string {
|
||||
return codec.replace(AUDIO_CODEC_REGEXP, (m) =>
|
||||
getCodecCompatibleNameLower(m.toLowerCase() as LowerCaseCodecType)
|
||||
getCodecCompatibleNameLower(m.toLowerCase() as LowerCaseCodecType),
|
||||
);
|
||||
}
|
||||
|
||||
export function pickMostCompleteCodecName(
|
||||
parsedCodec: string,
|
||||
levelCodec: string
|
||||
levelCodec: string,
|
||||
): string {
|
||||
// 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
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ export interface CuesInterface {
|
||||
track: TextTrack | null,
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
captionScreen: CaptionScreen
|
||||
captionScreen: CaptionScreen,
|
||||
): VTTCue[];
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const Cues: CuesInterface = {
|
||||
track: TextTrack | null,
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
captionScreen: CaptionScreen
|
||||
captionScreen: CaptionScreen,
|
||||
): VTTCue[] {
|
||||
const result: VTTCue[] = [];
|
||||
let row: Row;
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { RequiredProperties } from '../types/general';
|
||||
|
||||
export function findFirstFragWithCC(
|
||||
fragments: Fragment[],
|
||||
cc: number
|
||||
cc: number,
|
||||
): Fragment | null {
|
||||
for (let i = 0, len = fragments.length; i < len; i++) {
|
||||
if (fragments[i]?.cc === cc) {
|
||||
@@ -21,7 +21,7 @@ export function findFirstFragWithCC(
|
||||
export function shouldAlignOnDiscontinuities(
|
||||
lastFrag: Fragment | null,
|
||||
lastLevel: Level,
|
||||
details: LevelDetails
|
||||
details: LevelDetails,
|
||||
): lastLevel is RequiredProperties<Level, 'details'> {
|
||||
if (lastLevel.details) {
|
||||
if (
|
||||
@@ -37,7 +37,7 @@ export function shouldAlignOnDiscontinuities(
|
||||
// Find the first frag in the previous level which matches the CC of the first frag of the new level
|
||||
export function findDiscontinuousReferenceFrag(
|
||||
prevDetails: LevelDetails,
|
||||
curDetails: LevelDetails
|
||||
curDetails: LevelDetails,
|
||||
) {
|
||||
const prevFrags = prevDetails.fragments;
|
||||
const curFrags = curDetails.fragments;
|
||||
@@ -91,7 +91,7 @@ export function adjustSlidingStart(sliding: number, details: LevelDetails) {
|
||||
export function alignStream(
|
||||
lastFrag: Fragment | null,
|
||||
lastLevel: Level | null,
|
||||
details: LevelDetails
|
||||
details: LevelDetails,
|
||||
) {
|
||||
if (!lastLevel) {
|
||||
return;
|
||||
@@ -125,16 +125,16 @@ export function alignStream(
|
||||
function alignDiscontinuities(
|
||||
lastFrag: Fragment | null,
|
||||
details: LevelDetails,
|
||||
lastLevel: Level
|
||||
lastLevel: Level,
|
||||
) {
|
||||
if (shouldAlignOnDiscontinuities(lastFrag, lastLevel, details)) {
|
||||
const referenceFrag = findDiscontinuousReferenceFrag(
|
||||
lastLevel.details,
|
||||
details
|
||||
details,
|
||||
);
|
||||
if (referenceFrag && Number.isFinite(referenceFrag.start)) {
|
||||
logger.log(
|
||||
`Adjusting PTS using last level due to CC increase within current level ${details.url}`
|
||||
`Adjusting PTS using last level due to CC increase within current level ${details.url}`,
|
||||
);
|
||||
adjustSlidingStart(referenceFrag.start, details);
|
||||
}
|
||||
@@ -156,7 +156,7 @@ function alignDiscontinuities(
|
||||
*/
|
||||
export function alignMediaPlaylistByPDT(
|
||||
details: LevelDetails,
|
||||
refDetails: LevelDetails
|
||||
refDetails: LevelDetails,
|
||||
) {
|
||||
if (!details.hasProgramDateTime || !refDetails.hasProgramDateTime) {
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,7 @@ export function isTimeoutError(error: ErrorData): boolean {
|
||||
|
||||
export function getRetryConfig(
|
||||
loadPolicy: LoadPolicy,
|
||||
error: ErrorData
|
||||
error: ErrorData,
|
||||
): RetryConfig | null {
|
||||
const isTimeout = isTimeoutError(error);
|
||||
return loadPolicy.default[`${isTimeout ? 'timeout' : 'error'}Retry`];
|
||||
@@ -23,19 +23,19 @@ export function getRetryConfig(
|
||||
|
||||
export function getRetryDelay(
|
||||
retryConfig: RetryConfig,
|
||||
retryCount: number
|
||||
retryCount: number,
|
||||
): number {
|
||||
// exponential backoff capped to max retry delay
|
||||
const backoffFactor =
|
||||
retryConfig.backoff === 'linear' ? 1 : Math.pow(2, retryCount);
|
||||
return Math.min(
|
||||
backoffFactor * retryConfig.retryDelayMs,
|
||||
retryConfig.maxRetryDelayMs
|
||||
retryConfig.maxRetryDelayMs,
|
||||
);
|
||||
}
|
||||
|
||||
export function getLoaderConfigWithoutReties(
|
||||
loderConfig: LoaderConfig
|
||||
loderConfig: LoaderConfig,
|
||||
): LoaderConfig {
|
||||
return {
|
||||
...loderConfig,
|
||||
@@ -50,7 +50,7 @@ export function shouldRetry(
|
||||
retryConfig: RetryConfig | null | undefined,
|
||||
retryCount: number,
|
||||
isTimeout: boolean,
|
||||
httpStatus?: number | undefined
|
||||
httpStatus?: number | undefined,
|
||||
): retryConfig is RetryConfig & boolean {
|
||||
return (
|
||||
!!retryConfig &&
|
||||
|
||||
@@ -21,7 +21,7 @@ class EwmaBandWidthEstimator {
|
||||
slow: number,
|
||||
fast: number,
|
||||
defaultEstimate: number,
|
||||
defaultTTFB: number = 100
|
||||
defaultTTFB: number = 100,
|
||||
) {
|
||||
this.defaultEstimate_ = defaultEstimate;
|
||||
this.minWeight_ = 0.001;
|
||||
|
||||
+15
-12
@@ -75,7 +75,7 @@ class FetchLoader implements Loader<LoaderContext> {
|
||||
this.callbacks.onAbort(
|
||||
this.stats,
|
||||
this.context as LoaderContext,
|
||||
this.response
|
||||
this.response,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ class FetchLoader implements Loader<LoaderContext> {
|
||||
load(
|
||||
context: LoaderContext,
|
||||
config: LoaderConfiguration,
|
||||
callbacks: LoaderCallbacks<LoaderContext>
|
||||
callbacks: LoaderCallbacks<LoaderContext>,
|
||||
): void {
|
||||
const stats = this.stats;
|
||||
if (stats.loading.start) {
|
||||
@@ -121,17 +121,20 @@ class FetchLoader implements Loader<LoaderContext> {
|
||||
|
||||
self.clearTimeout(this.requestTimeout);
|
||||
config.timeout = maxLoadTimeMs;
|
||||
this.requestTimeout = self.setTimeout(() => {
|
||||
this.abortInternal();
|
||||
callbacks.onTimeout(stats, context, this.response);
|
||||
}, maxLoadTimeMs - (first - stats.loading.start));
|
||||
this.requestTimeout = self.setTimeout(
|
||||
() => {
|
||||
this.abortInternal();
|
||||
callbacks.onTimeout(stats, context, this.response);
|
||||
},
|
||||
maxLoadTimeMs - (first - stats.loading.start),
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const { status, statusText } = response;
|
||||
throw new FetchError(
|
||||
statusText || 'fetch, bad network response',
|
||||
status,
|
||||
response
|
||||
response,
|
||||
);
|
||||
}
|
||||
stats.loading.first = first;
|
||||
@@ -144,7 +147,7 @@ class FetchLoader implements Loader<LoaderContext> {
|
||||
stats,
|
||||
context,
|
||||
config.highWaterMark,
|
||||
onProgress
|
||||
onProgress,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -164,7 +167,7 @@ class FetchLoader implements Loader<LoaderContext> {
|
||||
self.clearTimeout(this.requestTimeout);
|
||||
stats.loading.end = Math.max(
|
||||
self.performance.now(),
|
||||
stats.loading.first
|
||||
stats.loading.first,
|
||||
);
|
||||
const total = responseData[LENGTH];
|
||||
if (total) {
|
||||
@@ -196,7 +199,7 @@ class FetchLoader implements Loader<LoaderContext> {
|
||||
{ code, text },
|
||||
context,
|
||||
error ? error.details : null,
|
||||
stats
|
||||
stats,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -219,7 +222,7 @@ class FetchLoader implements Loader<LoaderContext> {
|
||||
stats: LoaderStats,
|
||||
context: LoaderContext,
|
||||
highWaterMark: number = 0,
|
||||
onProgress: LoaderOnProgress<LoaderContext>
|
||||
onProgress: LoaderOnProgress<LoaderContext>,
|
||||
): Promise<ArrayBuffer> {
|
||||
const chunkCache = new ChunkCache();
|
||||
const reader = (response.body as ReadableStream).getReader();
|
||||
@@ -275,7 +278,7 @@ function getRequestParameters(context: LoaderContext, signal): any {
|
||||
if (context.rangeEnd) {
|
||||
initParams.headers.set(
|
||||
'Range',
|
||||
'bytes=' + context.rangeStart + '-' + String(context.rangeEnd - 1)
|
||||
'bytes=' + context.rangeStart + '-' + String(context.rangeEnd - 1),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ export function parseIMSC1(
|
||||
payload: ArrayBuffer,
|
||||
initPTS: RationalTimestamp,
|
||||
callBack: (cues: Array<VTTCue>) => any,
|
||||
errorCallBack: (error: Error) => any
|
||||
errorCallBack: (error: Error) => any,
|
||||
) {
|
||||
const results = findBox(new Uint8Array(payload), ['mdat']);
|
||||
if (results.length === 0) {
|
||||
@@ -65,16 +65,16 @@ function parseTTML(ttml: string, syncTime: number): Array<VTTCue> {
|
||||
result[key] = tt.getAttribute(`ttp:${key}`) || defaultRateInfo[key];
|
||||
return result;
|
||||
},
|
||||
{}
|
||||
{},
|
||||
);
|
||||
|
||||
const trim = tt.getAttribute('xml:space') !== 'preserve';
|
||||
|
||||
const styleElements = collectionToDictionary(
|
||||
getElementCollection(tt, 'styling', 'style')
|
||||
getElementCollection(tt, 'styling', 'style'),
|
||||
);
|
||||
const regionElements = collectionToDictionary(
|
||||
getElementCollection(tt, 'layout', 'region')
|
||||
getElementCollection(tt, 'layout', 'region'),
|
||||
);
|
||||
const cueElements = getElementCollection(tt, 'body', '[begin]');
|
||||
|
||||
@@ -87,7 +87,7 @@ function parseTTML(ttml: string, syncTime: number): Array<VTTCue> {
|
||||
}
|
||||
const startTime = parseTtmlTime(
|
||||
cueElement.getAttribute('begin'),
|
||||
rateInfo
|
||||
rateInfo,
|
||||
);
|
||||
const duration = parseTtmlTime(cueElement.getAttribute('dur'), rateInfo);
|
||||
let endTime = parseTtmlTime(cueElement.getAttribute('end'), rateInfo);
|
||||
@@ -127,7 +127,7 @@ function parseTTML(ttml: string, syncTime: number): Array<VTTCue> {
|
||||
function getElementCollection(
|
||||
fromElement,
|
||||
parentName,
|
||||
childName
|
||||
childName,
|
||||
): Array<HTMLElement> {
|
||||
const parent = fromElement.getElementsByTagName(parentName)[0];
|
||||
if (parent) {
|
||||
@@ -165,7 +165,7 @@ function getTextContent(element, trim): string {
|
||||
function getTtmlStyles(
|
||||
region,
|
||||
style,
|
||||
styleElements
|
||||
styleElements,
|
||||
): { [style: string]: string } {
|
||||
const ttsNs = 'http://www.w3.org/ns/ttml#styling';
|
||||
let regionStyle = null;
|
||||
|
||||
@@ -43,6 +43,6 @@ export function convertDataUriToArrayBytes(uri: string): Uint8Array | null {
|
||||
|
||||
export function strToUtf8array(str: string): Uint8Array {
|
||||
return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
|
||||
c.charCodeAt(0)
|
||||
c.charCodeAt(0),
|
||||
);
|
||||
}
|
||||
|
||||
+18
-18
@@ -14,7 +14,7 @@ type PartIntersection = (oldPart: Part, newPart: Part) => void;
|
||||
export function updatePTS(
|
||||
fragments: Fragment[],
|
||||
fromIdx: number,
|
||||
toIdx: number
|
||||
toIdx: number,
|
||||
): void {
|
||||
const fragFrom = fragments[fromIdx];
|
||||
const fragTo = fragments[toIdx];
|
||||
@@ -59,7 +59,7 @@ export function updateFragPTSDTS(
|
||||
startPTS: number,
|
||||
endPTS: number,
|
||||
startDTS: number,
|
||||
endDTS: number
|
||||
endDTS: number,
|
||||
): number {
|
||||
const parsedMediaDuration = endPTS - startPTS;
|
||||
if (parsedMediaDuration <= 0) {
|
||||
@@ -134,7 +134,7 @@ export function updateFragPTSDTS(
|
||||
|
||||
export function mergeDetails(
|
||||
oldDetails: LevelDetails,
|
||||
newDetails: LevelDetails
|
||||
newDetails: LevelDetails,
|
||||
): void {
|
||||
// Track the last initSegment processed. Initialize it to the last one on the timeline.
|
||||
let currentInitSegment: Fragment | null = null;
|
||||
@@ -194,7 +194,7 @@ export function mergeDetails(
|
||||
newFrag.initSegment = oldFrag.initSegment;
|
||||
currentInitSegment = oldFrag.initSegment;
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
if (currentInitSegment) {
|
||||
@@ -215,7 +215,7 @@ export function mergeDetails(
|
||||
newDetails.deltaUpdateFailed = newDetails.fragments.some((frag) => !frag);
|
||||
if (newDetails.deltaUpdateFailed) {
|
||||
logger.warn(
|
||||
'[level-helper] Previous playlist missing segments skipped in delta playlist'
|
||||
'[level-helper] Previous playlist missing segments skipped in delta playlist',
|
||||
);
|
||||
for (let i = newDetails.skippedSegments; i--; ) {
|
||||
newDetails.fragments.shift();
|
||||
@@ -226,7 +226,7 @@ export function mergeDetails(
|
||||
newDetails.dateRanges = mergeDateRanges(
|
||||
oldDetails.dateRanges,
|
||||
newDetails.dateRanges,
|
||||
newDetails.recentlyRemovedDateranges
|
||||
newDetails.recentlyRemovedDateranges,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -249,7 +249,7 @@ export function mergeDetails(
|
||||
(oldPart: Part, newPart: Part) => {
|
||||
newPart.elementaryStreams = oldPart.elementaryStreams;
|
||||
newPart.stats = oldPart.stats;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// if at least one fragment contains PTS info, recompute PTS information for all fragments
|
||||
@@ -260,7 +260,7 @@ export function mergeDetails(
|
||||
PTSFrag.startPTS,
|
||||
PTSFrag.endPTS,
|
||||
PTSFrag.startDTS,
|
||||
PTSFrag.endDTS
|
||||
PTSFrag.endDTS,
|
||||
);
|
||||
} else {
|
||||
// ensure that delta is within oldFragments range
|
||||
@@ -294,7 +294,7 @@ export function mergeDetails(
|
||||
function mergeDateRanges(
|
||||
oldDateRanges: Record<string, DateRange>,
|
||||
deltaDateRanges: Record<string, DateRange>,
|
||||
recentlyRemovedDateranges: string[] | undefined
|
||||
recentlyRemovedDateranges: string[] | undefined,
|
||||
): Record<string, DateRange> {
|
||||
const dateRanges = Object.assign({}, oldDateRanges);
|
||||
if (recentlyRemovedDateranges) {
|
||||
@@ -309,8 +309,8 @@ function mergeDateRanges(
|
||||
} else {
|
||||
logger.warn(
|
||||
`Ignoring invalid Playlist Delta Update DATERANGE tag: "${JSON.stringify(
|
||||
deltaDateRanges[id].attr
|
||||
)}"`
|
||||
deltaDateRanges[id].attr,
|
||||
)}"`,
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -320,7 +320,7 @@ function mergeDateRanges(
|
||||
export function mapPartIntersection(
|
||||
oldParts: Part[] | null,
|
||||
newParts: Part[] | null,
|
||||
intersectionFn: PartIntersection
|
||||
intersectionFn: PartIntersection,
|
||||
) {
|
||||
if (oldParts && newParts) {
|
||||
let delta = 0;
|
||||
@@ -344,7 +344,7 @@ export function mapPartIntersection(
|
||||
export function mapFragmentIntersection(
|
||||
oldDetails: LevelDetails,
|
||||
newDetails: LevelDetails,
|
||||
intersectionFn: FragmentIntersection
|
||||
intersectionFn: FragmentIntersection,
|
||||
): void {
|
||||
const skippedSegments = newDetails.skippedSegments;
|
||||
const start =
|
||||
@@ -378,7 +378,7 @@ export function mapFragmentIntersection(
|
||||
|
||||
export function adjustSliding(
|
||||
oldDetails: LevelDetails,
|
||||
newDetails: LevelDetails
|
||||
newDetails: LevelDetails,
|
||||
): void {
|
||||
const delta =
|
||||
newDetails.startSN + newDetails.skippedSegments - oldDetails.startSN;
|
||||
@@ -403,7 +403,7 @@ export function addSliding(details: LevelDetails, start: number) {
|
||||
|
||||
export function computeReloadInterval(
|
||||
newDetails: LevelDetails,
|
||||
distanceToLiveEdgeMs: number = Infinity
|
||||
distanceToLiveEdgeMs: number = Infinity,
|
||||
): number {
|
||||
let reloadInterval = 1000 * newDetails.targetduration;
|
||||
|
||||
@@ -435,7 +435,7 @@ export function computeReloadInterval(
|
||||
export function getFragmentWithSN(
|
||||
level: Level,
|
||||
sn: number,
|
||||
fragCurrent: Fragment | null
|
||||
fragCurrent: Fragment | null,
|
||||
): Fragment | null {
|
||||
if (!level?.details) {
|
||||
return null;
|
||||
@@ -459,7 +459,7 @@ export function getFragmentWithSN(
|
||||
export function getPartWith(
|
||||
level: Level,
|
||||
sn: number,
|
||||
partIndex: number
|
||||
partIndex: number,
|
||||
): Part | null {
|
||||
if (!level?.details) {
|
||||
return null;
|
||||
@@ -470,7 +470,7 @@ export function getPartWith(
|
||||
export function findPart(
|
||||
partList: Part[] | null | undefined,
|
||||
sn: number,
|
||||
partIndex: number
|
||||
partIndex: number,
|
||||
): Part | null {
|
||||
if (partList) {
|
||||
for (let i = partList.length; i--; ) {
|
||||
|
||||
+2
-2
@@ -66,13 +66,13 @@ export function enableLogs(debugConfig: boolean | ILogger, id: string): void {
|
||||
'log',
|
||||
'info',
|
||||
'warn',
|
||||
'error'
|
||||
'error',
|
||||
);
|
||||
// Some browsers don't allow to use bind on console object anyway
|
||||
// fallback to default if needed
|
||||
try {
|
||||
exportedLogger.log(
|
||||
`Debug logs enabled for "${id}" in hls.js version ${__VERSION__}`
|
||||
`Debug logs enabled for "${id}" in hls.js version ${__VERSION__}`,
|
||||
);
|
||||
} catch (e) {
|
||||
exportedLogger = fakeLogger;
|
||||
|
||||
@@ -3,7 +3,7 @@ import type { MediaAttributes, MediaPlaylist } from '../types/media-playlist';
|
||||
|
||||
export function subtitleOptionsIdentical(
|
||||
trackList1: MediaPlaylist[] | Level[],
|
||||
trackList2: MediaPlaylist[]
|
||||
trackList2: MediaPlaylist[],
|
||||
): boolean {
|
||||
if (trackList1.length !== trackList2.length) {
|
||||
return false;
|
||||
@@ -12,7 +12,7 @@ export function subtitleOptionsIdentical(
|
||||
if (
|
||||
!subtitleAttributesIdentical(
|
||||
trackList1[i].attrs as MediaAttributes,
|
||||
trackList2[i].attrs
|
||||
trackList2[i].attrs,
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
@@ -23,7 +23,7 @@ export function subtitleOptionsIdentical(
|
||||
|
||||
export function subtitleAttributesIdentical(
|
||||
attrs1: MediaAttributes,
|
||||
attrs2: MediaAttributes
|
||||
attrs2: MediaAttributes,
|
||||
): boolean {
|
||||
// Media options with the same rendition ID must be bit identical
|
||||
const stableRenditionId = attrs1['STABLE-RENDITION-ID'];
|
||||
@@ -40,6 +40,6 @@ export function subtitleAttributesIdentical(
|
||||
'FORCED',
|
||||
].some(
|
||||
(subtitleAttribute) =>
|
||||
attrs1[subtitleAttribute] !== attrs2[subtitleAttribute]
|
||||
attrs1[subtitleAttribute] !== attrs2[subtitleAttribute],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function requiresMediaCapabilitiesDecodingInfo(
|
||||
mediaCapabilities: MediaCapabilities | undefined,
|
||||
currentVideoRange: VideoRange | undefined,
|
||||
currentFrameRate: number,
|
||||
currentBw: number
|
||||
currentBw: number,
|
||||
): boolean {
|
||||
// Only test support when configuration is exceeds minimum options
|
||||
const audioGroupId = level.audioCodec ? level.audioGroupId : null;
|
||||
@@ -57,7 +57,7 @@ export function requiresMediaCapabilitiesDecodingInfo(
|
||||
export function getMediaDecodingInfoPromise(
|
||||
level: Level,
|
||||
audioTracksByGroup: AudioTracksByGroup,
|
||||
mediaCapabilities: MediaCapabilities
|
||||
mediaCapabilities: MediaCapabilities,
|
||||
): Promise<MediaDecodingInfo> {
|
||||
const videoCodecs = level.videoCodec;
|
||||
const audioCodecs = level.audioCodec;
|
||||
@@ -106,7 +106,7 @@ export function getMediaDecodingInfoPromise(
|
||||
// spatialRendering:
|
||||
// audioCodec === 'ec-3' && channels.indexOf('JOC'),
|
||||
},
|
||||
}))
|
||||
})),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ export function getMediaDecodingInfoPromise(
|
||||
(SUPPORTED_INFO_CACHE[decodingInfoKey] =
|
||||
mediaCapabilities.decodingInfo(configuration))
|
||||
);
|
||||
})
|
||||
}),
|
||||
)
|
||||
.then((decodingInfoResults) => ({
|
||||
supported: !decodingInfoResults.some((info) => !info.supported),
|
||||
|
||||
@@ -19,7 +19,7 @@ export const enum KeySystemFormats {
|
||||
}
|
||||
|
||||
export function keySystemFormatToKeySystemDomain(
|
||||
format: KeySystemFormats
|
||||
format: KeySystemFormats,
|
||||
): KeySystems | undefined {
|
||||
switch (format) {
|
||||
case KeySystemFormats.FAIRPLAY:
|
||||
@@ -43,7 +43,7 @@ export const enum KeySystemIds {
|
||||
}
|
||||
|
||||
export function keySystemIdToKeySystemDomain(
|
||||
systemId: KeySystemIds
|
||||
systemId: KeySystemIds,
|
||||
): KeySystems | undefined {
|
||||
if (systemId === KeySystemIds.WIDEVINE) {
|
||||
return KeySystems.WIDEVINE;
|
||||
@@ -55,7 +55,7 @@ export function keySystemIdToKeySystemDomain(
|
||||
}
|
||||
|
||||
export function keySystemDomainToKeySystemFormat(
|
||||
keySystem: KeySystems
|
||||
keySystem: KeySystems,
|
||||
): KeySystemFormats | undefined {
|
||||
switch (keySystem) {
|
||||
case KeySystems.FAIRPLAY:
|
||||
@@ -70,7 +70,7 @@ export function keySystemDomainToKeySystemFormat(
|
||||
}
|
||||
|
||||
export function getKeySystemsForConfig(
|
||||
config: EMEControllerConfig
|
||||
config: EMEControllerConfig,
|
||||
): KeySystems[] {
|
||||
const { drmSystems, widevineLicenseUrl } = config;
|
||||
const keySystemsToAttempt: KeySystems[] = drmSystems
|
||||
@@ -89,7 +89,7 @@ export function getKeySystemsForConfig(
|
||||
|
||||
export type MediaKeyFunc = (
|
||||
keySystem: KeySystems,
|
||||
supportedConfigurations: MediaKeySystemConfiguration[]
|
||||
supportedConfigurations: MediaKeySystemConfiguration[],
|
||||
) => Promise<MediaKeySystemAccess>;
|
||||
|
||||
export const requestMediaKeySystemAccess = (function (): MediaKeyFunc | null {
|
||||
@@ -111,7 +111,7 @@ export function getSupportedMediaKeySystemConfigurations(
|
||||
keySystem: KeySystems,
|
||||
audioCodecs: string[],
|
||||
videoCodecs: string[],
|
||||
drmSystemOptions: DRMSystemOptions
|
||||
drmSystemOptions: DRMSystemOptions,
|
||||
): MediaKeySystemConfiguration[] {
|
||||
let initDataTypes: string[];
|
||||
switch (keySystem) {
|
||||
@@ -132,7 +132,7 @@ export function getSupportedMediaKeySystemConfigurations(
|
||||
initDataTypes,
|
||||
audioCodecs,
|
||||
videoCodecs,
|
||||
drmSystemOptions
|
||||
drmSystemOptions,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ function createMediaKeySystemConfigurations(
|
||||
initDataTypes: string[],
|
||||
audioCodecs: string[],
|
||||
videoCodecs: string[],
|
||||
drmSystemOptions: DRMSystemOptions
|
||||
drmSystemOptions: DRMSystemOptions,
|
||||
): MediaKeySystemConfiguration[] {
|
||||
const baseConfig: MediaKeySystemConfiguration = {
|
||||
initDataTypes: initDataTypes,
|
||||
|
||||
+17
-17
@@ -406,7 +406,7 @@ function addLeadingZero(num: number): string {
|
||||
|
||||
export function patchEncyptionData(
|
||||
initSegment: Uint8Array | undefined,
|
||||
decryptdata: DecryptData | null
|
||||
decryptdata: DecryptData | null,
|
||||
): Uint8Array | undefined {
|
||||
if (!initSegment || !decryptdata) {
|
||||
return initSegment;
|
||||
@@ -437,8 +437,8 @@ export function patchEncyptionData(
|
||||
`[eme] Patching keyId in 'enc${
|
||||
isAudio ? 'a' : 'v'
|
||||
}>sinf>>tenc' box: ${Hex.hexDump(tencKeyId)} -> ${Hex.hexDump(
|
||||
keyId
|
||||
)}`
|
||||
keyId,
|
||||
)}`,
|
||||
);
|
||||
tenc.set(keyId, 8);
|
||||
}
|
||||
@@ -482,7 +482,7 @@ export function parseSinf(sinf: Uint8Array): Uint8Array | null {
|
||||
*/
|
||||
export function getStartDTS(
|
||||
initData: InitData,
|
||||
fmp4: Uint8Array
|
||||
fmp4: Uint8Array,
|
||||
): number | null {
|
||||
// we need info from two children of each track fragment box
|
||||
return findBox(fmp4, ['moof', 'traf']).reduce(
|
||||
@@ -502,7 +502,7 @@ export function getStartDTS(
|
||||
// https://github.com/video-dev/hls.js/issues/5303
|
||||
if (baseTime === UINT32_MAX) {
|
||||
logger.warn(
|
||||
`[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time`
|
||||
`[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time`,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
@@ -522,7 +522,7 @@ export function getStartDTS(
|
||||
}
|
||||
return result;
|
||||
},
|
||||
null
|
||||
null,
|
||||
);
|
||||
if (
|
||||
start !== null &&
|
||||
@@ -533,7 +533,7 @@ export function getStartDTS(
|
||||
}
|
||||
return result;
|
||||
},
|
||||
null
|
||||
null,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ export function getDuration(data: Uint8Array, initData: InitData) {
|
||||
if (sidx?.references) {
|
||||
sidxDuration += sidx.references.reduce(
|
||||
(dur, ref) => dur + ref.info.duration || 0,
|
||||
0
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -682,7 +682,7 @@ export function computeRawDurationFromSamples(trun): number {
|
||||
export function offsetStartDTS(
|
||||
initData: InitData,
|
||||
fmp4: Uint8Array,
|
||||
timeOffset: number
|
||||
timeOffset: number,
|
||||
) {
|
||||
findBox(fmp4, ['moof', 'traf']).forEach((traf) => {
|
||||
findBox(traf, ['tfhd']).forEach((tfhd) => {
|
||||
@@ -746,7 +746,7 @@ export interface SegmentedRange {
|
||||
|
||||
export function appendUint8Array(
|
||||
data1: Uint8Array,
|
||||
data2: Uint8Array
|
||||
data2: Uint8Array,
|
||||
): Uint8Array {
|
||||
const temp = new Uint8Array(data1.length + data2.length);
|
||||
temp.set(data1);
|
||||
@@ -768,7 +768,7 @@ export interface IEmsgParsingData {
|
||||
|
||||
export function parseSamples(
|
||||
timeOffset: number,
|
||||
track: PassthroughTrack
|
||||
track: PassthroughTrack,
|
||||
): UserdataSample[] {
|
||||
const seiSamples = [] as UserdataSample[];
|
||||
const videoData = track.samples;
|
||||
@@ -888,13 +888,13 @@ export function parseSamples(
|
||||
if (isSEIMessage(isHEVCFlavor, videoData[sampleOffset])) {
|
||||
const data = videoData.subarray(
|
||||
sampleOffset,
|
||||
sampleOffset + naluSize
|
||||
sampleOffset + naluSize,
|
||||
);
|
||||
parseSEIMessageFromNALu(
|
||||
data,
|
||||
isHEVCFlavor ? 2 : 1,
|
||||
timeOffset + compositionOffset / timescale,
|
||||
seiSamples
|
||||
seiSamples,
|
||||
);
|
||||
}
|
||||
sampleOffset += naluSize;
|
||||
@@ -941,7 +941,7 @@ export function parseSEIMessageFromNALu(
|
||||
unescapedData: Uint8Array,
|
||||
headerSize: number,
|
||||
pts: number,
|
||||
samples: UserdataSample[]
|
||||
samples: UserdataSample[],
|
||||
) {
|
||||
const data = discardEPB(unescapedData);
|
||||
let seiPtr = 0;
|
||||
@@ -1134,7 +1134,7 @@ export function parseEmsg(data: Uint8Array): IEmsgParsingData {
|
||||
if (!Number.isSafeInteger(presentationTime)) {
|
||||
presentationTime = Number.MAX_SAFE_INTEGER;
|
||||
logger.warn(
|
||||
'Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box'
|
||||
'Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1196,7 +1196,7 @@ export function mp4Box(type: ArrayLike<number>, ...payload: Uint8Array[]) {
|
||||
export function mp4pssh(
|
||||
systemId: Uint8Array,
|
||||
keyids: Array<Uint8Array> | null,
|
||||
data: Uint8Array
|
||||
data: Uint8Array,
|
||||
) {
|
||||
if (systemId.byteLength !== 16) {
|
||||
throw new RangeError('Invalid system id');
|
||||
@@ -1242,7 +1242,7 @@ export function mp4pssh(
|
||||
kidCount,
|
||||
kids,
|
||||
dataSize,
|
||||
data || new Uint8Array()
|
||||
data || new Uint8Array(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class OutputFilter {
|
||||
this.startTime,
|
||||
this.endTime as number,
|
||||
this.screen as CaptionScreen,
|
||||
this.cueRanges
|
||||
this.cueRanges,
|
||||
);
|
||||
this.startTime = null;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ type StartParameters = {
|
||||
export function getStartCodecTier(
|
||||
codecTiers: Record<string, CodecSetTier>,
|
||||
videoRange: VideoRange | undefined,
|
||||
currentBw: number
|
||||
currentBw: number,
|
||||
): StartParameters {
|
||||
const codecSets = Object.keys(codecTiers);
|
||||
// Use first level set to determine stereo, and minimum resolution and framerate
|
||||
@@ -70,49 +70,49 @@ export function getStartCodecTier(
|
||||
if (candidateTier.minBitrate > currentBw) {
|
||||
logStartCodecCandidateIgnored(
|
||||
candidate,
|
||||
`min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`
|
||||
`min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
if (!candidateTier.hasDefaultAudio) {
|
||||
logStartCodecCandidateIgnored(
|
||||
candidate,
|
||||
`no renditions with default or auto-select sound found`
|
||||
`no renditions with default or auto-select sound found`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
if (hasStereo && candidateTier.channels['2'] === 0) {
|
||||
logStartCodecCandidateIgnored(
|
||||
candidate,
|
||||
`no renditions with stereo sound found`
|
||||
`no renditions with stereo sound found`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
if (candidateTier.minHeight > maxHeight) {
|
||||
logStartCodecCandidateIgnored(
|
||||
candidate,
|
||||
`min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`
|
||||
`min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
if (candidateTier.minFramerate > maxFramerate) {
|
||||
logStartCodecCandidateIgnored(
|
||||
candidate,
|
||||
`min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`
|
||||
`min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
if (videoRange && candidateTier.videoRanges[videoRange] === 0) {
|
||||
logStartCodecCandidateIgnored(
|
||||
candidate,
|
||||
`no variants with VIDEO-RANGE of ${videoRange} found`
|
||||
`no variants with VIDEO-RANGE of ${videoRange} found`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
if (candidateTier.maxScore < selectedScore) {
|
||||
logStartCodecCandidateIgnored(
|
||||
candidate,
|
||||
`max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`
|
||||
`max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`,
|
||||
);
|
||||
return selected;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ export function getStartCodecTier(
|
||||
selectedScore = candidateTier.maxScore;
|
||||
return candidate;
|
||||
},
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
return {
|
||||
codecSet,
|
||||
@@ -140,7 +140,7 @@ export function getStartCodecTier(
|
||||
|
||||
function logStartCodecCandidateIgnored(codeSet: string, reason: string) {
|
||||
logger.log(
|
||||
`[abr] start candidates with "${codeSet}" ignored because ${reason}`
|
||||
`[abr] start candidates with "${codeSet}" ignored because ${reason}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ export function getAudioTracksByGroup(allAudioTracks: MediaPlaylist[]) {
|
||||
hasDefaultAudio: false,
|
||||
hasAutoSelectAudio: false,
|
||||
groups: {},
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -188,7 +188,7 @@ export function getCodecTiers(
|
||||
levels: Level[],
|
||||
audioTracksByGroup: AudioTracksByGroup,
|
||||
minAutoLevel: number,
|
||||
maxAutoLevel: number
|
||||
maxAutoLevel: number,
|
||||
): Record<string, CodecSetTier> {
|
||||
return levels
|
||||
.slice(minAutoLevel, maxAutoLevel + 1)
|
||||
|
||||
@@ -33,13 +33,13 @@ export function addCueToTrack(track: TextTrack, cue: VTTCue) {
|
||||
const textTrackCue = new (self.TextTrackCue as any)(
|
||||
cue.startTime,
|
||||
cue.endTime,
|
||||
cue.text
|
||||
cue.text,
|
||||
);
|
||||
textTrackCue.id = cue.id;
|
||||
track.addCue(textTrackCue);
|
||||
} catch (err2) {
|
||||
logger.debug(
|
||||
`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${err2}`
|
||||
`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${err2}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +71,7 @@ export function removeCuesInRange(
|
||||
track: TextTrack,
|
||||
start: number,
|
||||
end: number,
|
||||
predicate?: (cue: TextTrackCue) => boolean
|
||||
predicate?: (cue: TextTrackCue) => boolean,
|
||||
) {
|
||||
const mode = track.mode;
|
||||
if (mode === 'disabled') {
|
||||
@@ -95,7 +95,7 @@ export function removeCuesInRange(
|
||||
// Modified version of binary search O(log(n)).
|
||||
function getFirstCueIndexAfterTime(
|
||||
cues: TextTrackCueList | TextTrackCue[],
|
||||
time: number
|
||||
time: number,
|
||||
): number {
|
||||
// If first cue starts after time, start there
|
||||
if (time < cues[0].startTime) {
|
||||
@@ -132,7 +132,7 @@ function getFirstCueIndexAfterTime(
|
||||
export function getCuesInRange(
|
||||
cues: TextTrackCueList | TextTrackCue[],
|
||||
start: number,
|
||||
end: number
|
||||
end: number,
|
||||
): TextTrackCue[] {
|
||||
const cuesFound: TextTrackCue[] = [];
|
||||
const firstCueInRange = getFirstCueIndexAfterTime(cues, start);
|
||||
|
||||
@@ -9,7 +9,7 @@ export function toTimescaleFromBase(
|
||||
baseTime: number,
|
||||
destScale: number,
|
||||
srcBase: number = 1,
|
||||
round: boolean = false
|
||||
round: boolean = false,
|
||||
): number {
|
||||
const result = baseTime * destScale * srcBase; // equivalent to `(value * scale) / (1 / base)`
|
||||
return round ? Math.round(result) : result;
|
||||
@@ -19,21 +19,21 @@ export function toTimescaleFromScale(
|
||||
baseTime: number,
|
||||
destScale: number,
|
||||
srcScale: number = 1,
|
||||
round: boolean = false
|
||||
round: boolean = false,
|
||||
): number {
|
||||
return toTimescaleFromBase(baseTime, destScale, 1 / srcScale, round);
|
||||
}
|
||||
|
||||
export function toMsFromMpegTsClock(
|
||||
baseTime: number,
|
||||
round: boolean = false
|
||||
round: boolean = false,
|
||||
): number {
|
||||
return toTimescaleFromBase(baseTime, 1000, 1 / MPEG_TS_CLOCK_FREQ_HZ, round);
|
||||
}
|
||||
|
||||
export function toMpegTsClockFromTimescale(
|
||||
baseTime: number,
|
||||
srcScale: number = 1
|
||||
srcScale: number = 1,
|
||||
): number {
|
||||
return toTimescaleFromBase(baseTime, MPEG_TS_CLOCK_FREQ_HZ, 1 / srcScale);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export function sliceUint8(
|
||||
array: Uint8Array,
|
||||
start?: number,
|
||||
end?: number
|
||||
end?: number,
|
||||
): Uint8Array {
|
||||
// @ts-expect-error This polyfills IE11 usage of Uint8Array slice.
|
||||
// It always exists in the TypeScript definition so fails, but it fails at runtime on IE11.
|
||||
|
||||
@@ -15,7 +15,7 @@ export function substituteVariablesInAttributes(
|
||||
'variableList' | 'hasVariableRefs' | 'playlistParsingError'
|
||||
>,
|
||||
attr: AttrList,
|
||||
attributeNames: string[]
|
||||
attributeNames: string[],
|
||||
) {
|
||||
if (parsed.variableList !== null || parsed.hasVariableRefs) {
|
||||
for (let i = attributeNames.length; i--; ) {
|
||||
@@ -33,7 +33,7 @@ export function substituteVariables(
|
||||
ParsedMultivariantPlaylist | LevelDetails,
|
||||
'variableList' | 'hasVariableRefs' | 'playlistParsingError'
|
||||
>,
|
||||
value: string
|
||||
value: string,
|
||||
): string {
|
||||
if (parsed.variableList !== null || parsed.hasVariableRefs) {
|
||||
const variableList = parsed.variableList;
|
||||
@@ -42,17 +42,17 @@ export function substituteVariables(
|
||||
(variableReference: string) => {
|
||||
const variableName = variableReference.substring(
|
||||
2,
|
||||
variableReference.length - 1
|
||||
variableReference.length - 1,
|
||||
);
|
||||
const variableValue = variableList?.[variableName];
|
||||
if (variableValue === undefined) {
|
||||
parsed.playlistParsingError ||= new Error(
|
||||
`Missing preceding EXT-X-DEFINE tag for Variable Reference: "${variableName}"`
|
||||
`Missing preceding EXT-X-DEFINE tag for Variable Reference: "${variableName}"`,
|
||||
);
|
||||
return variableReference;
|
||||
}
|
||||
return variableValue;
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
return value;
|
||||
@@ -64,7 +64,7 @@ export function addVariableDefinition(
|
||||
'variableList' | 'playlistParsingError'
|
||||
>,
|
||||
attr: AttrList,
|
||||
parentUrl: string
|
||||
parentUrl: string,
|
||||
) {
|
||||
let variableList = parsed.variableList;
|
||||
if (!variableList) {
|
||||
@@ -80,12 +80,12 @@ export function addVariableDefinition(
|
||||
VALUE = searchParams.get(NAME);
|
||||
} else {
|
||||
throw new Error(
|
||||
`"${NAME}" does not match any query parameter in URI: "${parentUrl}"`
|
||||
`"${NAME}" does not match any query parameter in URI: "${parentUrl}"`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
parsed.playlistParsingError ||= new Error(
|
||||
`EXT-X-DEFINE QUERYPARAM: ${error.message}`
|
||||
`EXT-X-DEFINE QUERYPARAM: ${error.message}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -94,7 +94,7 @@ export function addVariableDefinition(
|
||||
}
|
||||
if (NAME in variableList) {
|
||||
parsed.playlistParsingError ||= new Error(
|
||||
`EXT-X-DEFINE duplicate Variable Name declarations: "${NAME}"`
|
||||
`EXT-X-DEFINE duplicate Variable Name declarations: "${NAME}"`,
|
||||
);
|
||||
} else {
|
||||
variableList[NAME] = VALUE || '';
|
||||
@@ -107,7 +107,7 @@ export function importVariableDefinition(
|
||||
'variableList' | 'playlistParsingError'
|
||||
>,
|
||||
attr: AttrList,
|
||||
sourceVariableList: VariableMap | null
|
||||
sourceVariableList: VariableMap | null,
|
||||
) {
|
||||
const IMPORT = attr.IMPORT;
|
||||
if (sourceVariableList && IMPORT in sourceVariableList) {
|
||||
@@ -118,7 +118,7 @@ export function importVariableDefinition(
|
||||
variableList[IMPORT] = sourceVariableList[IMPORT];
|
||||
} else {
|
||||
parsed.playlistParsingError ||= new Error(
|
||||
`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${IMPORT}"`
|
||||
`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${IMPORT}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-21
@@ -58,14 +58,14 @@ export default (function () {
|
||||
function findDirectionSetting(value: string) {
|
||||
return isAllowedValue<typeof AllowedDirections, Direction>(
|
||||
AllowedDirections,
|
||||
value
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
function findAlignSetting(value: string) {
|
||||
return isAllowedValue<typeof AllowedAlignments, Alignment>(
|
||||
AllowedAlignments,
|
||||
value
|
||||
value,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ export default (function () {
|
||||
set: function (value: string) {
|
||||
_id = '' + value;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -137,7 +137,7 @@ export default (function () {
|
||||
set: function (value: boolean) {
|
||||
_pauseOnExit = !!value;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -155,7 +155,7 @@ export default (function () {
|
||||
_startTime = value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -173,7 +173,7 @@ export default (function () {
|
||||
_endTime = value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -187,7 +187,7 @@ export default (function () {
|
||||
_text = '' + value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
// todo: implement VTTRegion polyfill?
|
||||
@@ -202,7 +202,7 @@ export default (function () {
|
||||
_region = value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -217,14 +217,14 @@ export default (function () {
|
||||
// Have to check for false because the setting an be an empty string.
|
||||
if (setting === false) {
|
||||
throw new SyntaxError(
|
||||
'An invalid or illegal string was specified.'
|
||||
'An invalid or illegal string was specified.',
|
||||
);
|
||||
}
|
||||
|
||||
_vertical = setting;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -238,7 +238,7 @@ export default (function () {
|
||||
_snapToLines = !!value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -251,14 +251,14 @@ export default (function () {
|
||||
set: function (value: number | 'auto') {
|
||||
if (typeof value !== 'number' && value !== 'auto') {
|
||||
throw new SyntaxError(
|
||||
'An invalid number or illegal string was specified.'
|
||||
'An invalid number or illegal string was specified.',
|
||||
);
|
||||
}
|
||||
|
||||
_line = value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -272,14 +272,14 @@ export default (function () {
|
||||
const setting = findAlignSetting(value);
|
||||
if (!setting) {
|
||||
throw new SyntaxError(
|
||||
'An invalid or illegal string was specified.'
|
||||
'An invalid or illegal string was specified.',
|
||||
);
|
||||
}
|
||||
|
||||
_lineAlign = setting;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -297,7 +297,7 @@ export default (function () {
|
||||
_position = value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -311,14 +311,14 @@ export default (function () {
|
||||
const setting = findAlignSetting(value);
|
||||
if (!setting) {
|
||||
throw new SyntaxError(
|
||||
'An invalid or illegal string was specified.'
|
||||
'An invalid or illegal string was specified.',
|
||||
);
|
||||
}
|
||||
|
||||
_positionAlign = setting;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -336,7 +336,7 @@ export default (function () {
|
||||
_size = value;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
Object.defineProperty(
|
||||
@@ -350,14 +350,14 @@ export default (function () {
|
||||
const setting = findAlignSetting(value);
|
||||
if (!setting) {
|
||||
throw new SyntaxError(
|
||||
'An invalid or illegal string was specified.'
|
||||
'An invalid or illegal string was specified.',
|
||||
);
|
||||
}
|
||||
|
||||
_align = setting;
|
||||
this.hasBeenReset = true;
|
||||
},
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,7 +101,7 @@ function parseOptions(
|
||||
input: string,
|
||||
callback: (k: string, v: any) => void,
|
||||
keyValueDelim: RegExp,
|
||||
groupDelim?: RegExp
|
||||
groupDelim?: RegExp,
|
||||
) {
|
||||
const groups = groupDelim ? input.split(groupDelim) : [input];
|
||||
for (const i in groups) {
|
||||
@@ -198,7 +198,7 @@ function parseCue(input: string, cue: VTTCue, regionList: Region[]) {
|
||||
}
|
||||
},
|
||||
/:/,
|
||||
/\s/
|
||||
/\s/,
|
||||
);
|
||||
|
||||
// Apply default values for any missing fields.
|
||||
@@ -238,7 +238,8 @@ function parseCue(input: string, cue: VTTCue, regionList: Region[]) {
|
||||
if (input.slice(0, 3) !== '-->') {
|
||||
// (3) next characters must match '-->'
|
||||
throw new Error(
|
||||
"Malformed time stamp (time stamps must be separated by '-->'): " + oInput
|
||||
"Malformed time stamp (time stamps must be separated by '-->'): " +
|
||||
oInput,
|
||||
);
|
||||
}
|
||||
input = input.slice(3);
|
||||
@@ -329,7 +330,7 @@ export class VTTParser {
|
||||
// break;
|
||||
// }
|
||||
},
|
||||
/:/
|
||||
/:/,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ const LINEBREAKS = /\r\n|\n\r|\n|\r/g;
|
||||
const startsWith = function (
|
||||
inputString: string,
|
||||
searchString: string,
|
||||
position: number = 0
|
||||
position: number = 0,
|
||||
) {
|
||||
return (
|
||||
inputString.slice(position, position + searchString.length) === searchString
|
||||
@@ -61,7 +61,7 @@ const hash = function (text: string) {
|
||||
export function generateCueId(
|
||||
startTime: number,
|
||||
endTime: number,
|
||||
text: string
|
||||
text: string,
|
||||
) {
|
||||
return hash(startTime.toString()) + hash(endTime.toString()) + hash(text);
|
||||
}
|
||||
@@ -97,7 +97,7 @@ export function parseWebVTT(
|
||||
cc: number,
|
||||
timeOffset: number,
|
||||
callBack: (cues: VTTCue[]) => void,
|
||||
errorCallBack: (error: Error) => void
|
||||
errorCallBack: (error: Error) => void,
|
||||
) {
|
||||
const parser = new VTTParser();
|
||||
// Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character.
|
||||
@@ -146,7 +146,7 @@ export function parseWebVTT(
|
||||
const startTime =
|
||||
normalizePts(
|
||||
(cue.startTime + cueOffset - timestampMapLOCAL) * 90000,
|
||||
timeOffset * 90000
|
||||
timeOffset * 90000,
|
||||
) / 90000;
|
||||
cue.startTime = Math.max(startTime, 0);
|
||||
cue.endTime = Math.max(startTime + duration, 0);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user