Skip to content

Commit d2991f1

Browse files
authored
Merge branch 'master' into refactor/nullable-network-details
2 parents f9f1108 + 178333c commit d2991f1

34 files changed

+1102
-518
lines changed

.eslintrc.js

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
const asyncKeywordConstraintMsg =
2+
'The async keyword adds a `regenerator` dependency in the hls.js ES5 output not allowed in v1 due to bundle size constraints.';
3+
const selfVsWindowGlobalMsg =
4+
'Use `self` instead of `window` to access the global context everywhere (including workers).';
5+
const arrayFindCompatibilityMsg =
6+
'Usage of Array find methods is restricted for compatibility.';
7+
const arrayFindIndexCompatibilityMsg =
8+
'Usage of Array findIndex methods is restricted for compatibility.';
9+
110
module.exports = {
211
env: { browser: true, commonjs: true, es6: true },
312
globals: {
@@ -27,20 +36,13 @@ module.exports = {
2736
2,
2837
{
2938
name: 'window',
30-
message:
31-
'Use `self` instead of `window` to access the global context everywhere (including workers).',
39+
message: selfVsWindowGlobalMsg,
3240
},
3341
{ name: 'SourceBuffer', message: 'Use `self.SourceBuffer`' },
3442
{ name: 'setTimeout', message: 'Use `self.setTimeout`' },
3543
{ name: 'setInterval', message: 'Use `self.setInterval`' },
3644
],
3745

38-
'no-restricted-properties': [
39-
2,
40-
{ property: 'findIndex' }, // Intended to block usage of Array.prototype.findIndex
41-
{ property: 'find' }, // Intended to block usage of Array.prototype.find
42-
],
43-
4446
'import/first': 1,
4547
'no-var': 1,
4648
'no-empty': 1,
@@ -67,6 +69,31 @@ module.exports = {
6769
'no-unused-vars': 0,
6870
'no-undef': 0,
6971
'no-use-before-define': 'off',
72+
'no-restricted-syntax': [
73+
'error',
74+
{
75+
selector: 'FunctionDeclaration[async=true]',
76+
message: asyncKeywordConstraintMsg,
77+
},
78+
{
79+
selector: 'ArrowFunctionExpression[async=true]',
80+
message: asyncKeywordConstraintMsg,
81+
},
82+
{
83+
selector: 'MethodDefinition[value.async=true]',
84+
message: asyncKeywordConstraintMsg,
85+
},
86+
{
87+
selector:
88+
'MemberExpression[property.name="find"][object.type="Identifier"]',
89+
message: arrayFindCompatibilityMsg,
90+
},
91+
{
92+
selector:
93+
'MemberExpression[property.name="findIndex"][object.type="Identifier"]',
94+
message: arrayFindIndexCompatibilityMsg,
95+
},
96+
],
7097
'import/order': [
7198
'warn',
7299
{
@@ -98,6 +125,8 @@ module.exports = {
98125
'@typescript-eslint/consistent-type-imports': 'error',
99126
'@typescript-eslint/no-import-type-side-effects': 'error',
100127
'@typescript-eslint/no-restricted-imports': 'error',
128+
'@typescript-eslint/no-floating-promises': 'error',
129+
'@typescript-eslint/no-misused-promises': 'error',
101130
},
102131
},
103132
],

api-extractor/report/hls.js.api.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,8 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP
394394
// (undocumented)
395395
protected checkLiveUpdate(details: LevelDetails): void;
396396
// (undocumented)
397+
protected checkRetryDate(): void;
398+
// (undocumented)
397399
protected clearTrackerIfNeeded(frag: Fragment): void;
398400
// (undocumented)
399401
protected config: HlsConfig;
@@ -530,8 +532,6 @@ export class BaseStreamController extends TaskLoop implements NetworkComponentAP
530532
// (undocumented)
531533
protected resetLoadingState(): void;
532534
// (undocumented)
533-
protected resetStartWhenNotLoaded(level: Level | null): void;
534-
// (undocumented)
535535
protected resetTransmuxer(): void;
536536
// (undocumented)
537537
protected resetWhenMissingContext(chunkMeta: ChunkMetadata | Fragment): void;
@@ -1191,8 +1191,8 @@ export type EMEControllerConfig = {
11911191
licenseResponseCallback?: (this: Hls, xhr: XMLHttpRequest, url: string, keyContext: MediaKeySessionContext) => ArrayBuffer;
11921192
emeEnabled: boolean;
11931193
widevineLicenseUrl?: string;
1194-
drmSystems: DRMSystemsConfiguration;
1195-
drmSystemOptions: DRMSystemOptions;
1194+
drmSystems: DRMSystemsConfiguration | undefined;
1195+
drmSystemOptions: DRMSystemOptions | undefined;
11961196
requestMediaKeySystemAccessFunc: MediaKeyFunc | null;
11971197
requireKeySystemAccessOnStart: boolean;
11981198
};
@@ -1206,9 +1206,11 @@ export const enum ErrorActionFlags {
12061206
// (undocumented)
12071207
MoveAllAlternatesMatchingHost = 1,
12081208
// (undocumented)
1209+
MoveAllAlternatesMatchingKey = 4,
1210+
// (undocumented)
12091211
None = 0,
12101212
// (undocumented)
1211-
SwitchToSDR = 4
1213+
SwitchToSDR = 8
12121214
}
12131215

12141216
// Warning: (ae-missing-release-tag) "ErrorController" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -1241,6 +1243,8 @@ export interface ErrorData {
12411243
// (undocumented)
12421244
context?: PlaylistLoaderContext;
12431245
// (undocumented)
1246+
decryptdata?: LevelKey;
1247+
// (undocumented)
12441248
details: ErrorDetails;
12451249
// @deprecated (undocumented)
12461250
err?: {
@@ -2983,8 +2987,8 @@ export interface KeyLoadedData {
29832987
// Warning: (ae-missing-release-tag) "KeyLoader" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
29842988
//
29852989
// @public (undocumented)
2986-
export class KeyLoader implements ComponentAPI {
2987-
constructor(config: HlsConfig);
2990+
export class KeyLoader extends Logger implements ComponentAPI {
2991+
constructor(config: HlsConfig, logger: ILogger);
29882992
// (undocumented)
29892993
abort(type?: PlaylistLevelType): void;
29902994
// (undocumented)
@@ -3001,10 +3005,6 @@ export class KeyLoader implements ComponentAPI {
30013005
// (undocumented)
30023006
emeController: EMEController | null;
30033007
// (undocumented)
3004-
keyUriToKeyInfo: {
3005-
[keyuri: string]: KeyLoaderInfo;
3006-
};
3007-
// (undocumented)
30083008
load(frag: Fragment): Promise<KeyLoadedData>;
30093009
// (undocumented)
30103010
loadClear(loadingFrag: Fragment, encryptedFragments: Fragment[], startFragRequested: boolean): null | Promise<void>;
@@ -3280,6 +3280,8 @@ export class LevelDetails {
32803280
// (undocumented)
32813281
get fragmentStart(): number;
32823282
// (undocumented)
3283+
hasKey(levelKey: LevelKey): boolean;
3284+
// (undocumented)
32833285
get hasProgramDateTime(): boolean;
32843286
// (undocumented)
32853287
hasVariableRefs: boolean;

docs/API.md

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ It should not be used in response to non-fatal hls.js error events.
362362
```js
363363
let attemptedErrorRecovery = null;
364364

365-
video.addEventListener('error', (event) {
365+
video.addEventListener('error', (event) => {
366366
const mediaError = event.currentTarget.error;
367367
if (mediaError.code === mediaError.MEDIA_ERR_DECODE) {
368368
const now = Date.now();
@@ -380,11 +380,19 @@ hls.on(Hls.Events.ERROR, function (name, data) {
380380
case Hls.ErrorTypes.MEDIA_ERROR: {
381381
const now = Date.now();
382382
if (!attemptedErrorRecovery || now - attemptedErrorRecovery > 5000) {
383-
console.log('Fatal media error encountered (' + video.error + + '), attempting to recover');
383+
console.log(
384+
'Fatal media error encountered (' +
385+
video.error +
386+
+'), attempting to recover',
387+
);
384388
attemptedErrorRecovery = now;
385389
hls.recoverMediaError();
386390
} else {
387-
console.log('Skipping media error recovery (only ' + (now - attemptedErrorRecovery) + 'ms since last error)');
391+
console.log(
392+
'Skipping media error recovery (only ' +
393+
(now - attemptedErrorRecovery) +
394+
'ms since last error)',
395+
);
388396
}
389397
break;
390398
}
@@ -561,7 +569,9 @@ This configuration will be applied by default to all instances.
561569

562570
(default: `false`)
563571

564-
Setting `config.debug = true;` will turn on debug logs on JS console.
572+
Setting `config.debug = true` enables JavaScript debug console logs. Debug mode also disables catching exceptions in even handler callbacks.
573+
In debug mode, when an event listener throws, the exception is not caught. This allows uncaught exeptions to trigger the JavaScript debugger.
574+
In production mode (`config.debug = false`), exceptions that are caught in event handlers are redispatched as errors with `type: OTHER_ERROR, details: INTERNAL_EXCEPTION, error: <caught exception>`.
565575

566576
A logger object could also be provided for custom logging: `config.debug = customLogger;`.
567577

@@ -756,10 +766,10 @@ Decreasing this value will mean that each stall will have less affect on `hls.ta
756766

757767
(default: `Infinity`)
758768

759-
maximum delay allowed from edge of live, expressed in multiple of `EXT-X-TARGETDURATION`.
760-
if set to 10, the player will seek back to `liveSyncDurationCount` whenever the next fragment to be loaded is older than N-10, N being the last fragment of the live playlist.
761-
If set, this value must be stricly superior to `liveSyncDurationCount`
762-
a value too close from `liveSyncDurationCount` is likely to cause playback stalls.
769+
Maximum delay allowed from edge of live, expressed in multiple of `EXT-X-TARGETDURATION`.
770+
If set to 10, the player will seek back to `liveSyncDurationCount` whenever the next fragment to be loaded is older than N-10, N being the last fragment of the live playlist.
771+
If set, this value must be strictly superior to `liveSyncDurationCount`.
772+
A value too close from `liveSyncDurationCount` is likely to cause playback stalls.
763773

764774
### `liveSyncDuration`
765775

@@ -776,7 +786,7 @@ A value too low (inferior to ~3 segment durations) is likely to cause playback s
776786

777787
Alternative parameter to `liveMaxLatencyDurationCount`, expressed in seconds vs number of segments.
778788
If defined in the configuration object, `liveMaxLatencyDuration` will take precedence over the default `liveMaxLatencyDurationCount`.
779-
If set, this value must be stricly superior to `liveSyncDuration` which must be defined as well.
789+
If set, this value must be strictly superior to `liveSyncDuration` which must be defined as well.
780790
You can't define this parameter and either `liveSyncDurationCount` or `liveMaxLatencyDurationCount` in your configuration object at the same time.
781791
A value too close from `liveSyncDuration` is likely to cause playback stalls.
782792

src/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ export type EMEControllerConfig = {
119119
) => ArrayBuffer;
120120
emeEnabled: boolean;
121121
widevineLicenseUrl?: string;
122-
drmSystems: DRMSystemsConfiguration;
123-
drmSystemOptions: DRMSystemOptions;
122+
drmSystems: DRMSystemsConfiguration | undefined;
123+
drmSystemOptions: DRMSystemOptions | undefined;
124124
requestMediaKeySystemAccessFunc: MediaKeyFunc | null;
125125
requireKeySystemAccessOnStart: boolean;
126126
};

src/controller/abr-controller.ts

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -845,43 +845,49 @@ class AbrController extends Logger implements AbrComponentAPI {
845845
mediaCapabilities,
846846
this.supportedCache,
847847
);
848-
levelInfo.supportedPromise.then((decodingInfo) => {
849-
if (!this.hls) {
850-
return;
851-
}
852-
levelInfo.supportedResult = decodingInfo;
853-
const levels = this.hls.levels;
854-
const index = levels.indexOf(levelInfo);
855-
if (decodingInfo.error) {
856-
this.warn(
857-
`MediaCapabilities decodingInfo error: "${
858-
decodingInfo.error
859-
}" for level ${index} ${stringify(decodingInfo)}`,
860-
);
861-
} else if (!decodingInfo.supported) {
862-
this.warn(
863-
`Unsupported MediaCapabilities decodingInfo result for level ${index} ${stringify(
864-
decodingInfo,
865-
)}`,
866-
);
867-
if (index > -1 && levels.length > 1) {
868-
this.log(`Removing unsupported level ${index}`);
869-
this.hls.removeLevel(index);
870-
if (this.hls.loadLevel === -1) {
871-
this.hls.nextLoadLevel = 0;
848+
levelInfo.supportedPromise
849+
.then((decodingInfo) => {
850+
if (!this.hls) {
851+
return;
852+
}
853+
levelInfo.supportedResult = decodingInfo;
854+
const levels = this.hls.levels;
855+
const index = levels.indexOf(levelInfo);
856+
if (decodingInfo.error) {
857+
this.warn(
858+
`MediaCapabilities decodingInfo error: "${
859+
decodingInfo.error
860+
}" for level ${index} ${stringify(decodingInfo)}`,
861+
);
862+
} else if (!decodingInfo.supported) {
863+
this.warn(
864+
`Unsupported MediaCapabilities decodingInfo result for level ${index} ${stringify(
865+
decodingInfo,
866+
)}`,
867+
);
868+
if (index > -1 && levels.length > 1) {
869+
this.log(`Removing unsupported level ${index}`);
870+
this.hls.removeLevel(index);
871+
if (this.hls.loadLevel === -1) {
872+
this.hls.nextLoadLevel = 0;
873+
}
872874
}
875+
} else if (
876+
decodingInfo.decodingInfoResults.some(
877+
(info) =>
878+
info.smooth === false || info.powerEfficient === false,
879+
)
880+
) {
881+
this.log(
882+
`MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`,
883+
);
873884
}
874-
} else if (
875-
decodingInfo.decodingInfoResults.some(
876-
(info) =>
877-
info.smooth === false || info.powerEfficient === false,
878-
)
879-
) {
880-
this.log(
881-
`MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`,
885+
})
886+
.catch((error) => {
887+
this.warn(
888+
`Error handling MediaCapabilities decodingInfo: ${error}`,
882889
);
883-
}
884-
});
890+
});
885891
} else {
886892
levelInfo.supportedResult = SUPPORTED_INFO_DEFAULT;
887893
}

src/controller/audio-stream-controller.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -255,15 +255,7 @@ class AudioStreamController
255255
break;
256256
}
257257
case State.FRAG_LOADING_WAITING_RETRY: {
258-
const now = performance.now();
259-
const retryDate = this.retryDate;
260-
// if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading
261-
if (!retryDate || now >= retryDate || this.media?.seeking) {
262-
const { levels, trackId } = this;
263-
this.log('RetryDate reached, switch back to IDLE state');
264-
this.resetStartWhenNotLoaded(levels?.[trackId] || null);
265-
this.state = State.IDLE;
266-
}
258+
this.checkRetryDate();
267259
break;
268260
}
269261
case State.WAITING_INIT_PTS: {

0 commit comments

Comments
 (0)