hls.js 1.5.5 → 1.5.6-0.canary.10003
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/hls-demo.js +10 -0
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +2075 -1166
- package/dist/hls.js.d.ts +65 -50
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1148 -859
- package/dist/hls.light.js.map +1 -1
- package/dist/hls.light.min.js +1 -1
- package/dist/hls.light.min.js.map +1 -1
- package/dist/hls.light.mjs +984 -696
- package/dist/hls.light.mjs.map +1 -1
- package/dist/hls.min.js +1 -1
- package/dist/hls.min.js.map +1 -1
- package/dist/hls.mjs +1757 -863
- package/dist/hls.mjs.map +1 -1
- package/dist/hls.worker.js +1 -1
- package/dist/hls.worker.js.map +1 -1
- package/package.json +20 -20
- package/src/config.ts +3 -2
- package/src/controller/abr-controller.ts +21 -20
- package/src/controller/audio-stream-controller.ts +15 -16
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +20 -8
- package/src/controller/base-stream-controller.ts +149 -33
- package/src/controller/buffer-controller.ts +11 -11
- package/src/controller/cap-level-controller.ts +1 -2
- package/src/controller/cmcd-controller.ts +27 -6
- package/src/controller/content-steering-controller.ts +8 -6
- package/src/controller/eme-controller.ts +9 -22
- package/src/controller/error-controller.ts +6 -8
- package/src/controller/fps-controller.ts +2 -3
- package/src/controller/gap-controller.ts +43 -16
- package/src/controller/latency-controller.ts +9 -11
- package/src/controller/level-controller.ts +12 -18
- package/src/controller/stream-controller.ts +25 -32
- package/src/controller/subtitle-stream-controller.ts +13 -14
- package/src/controller/subtitle-track-controller.ts +5 -3
- package/src/controller/timeline-controller.ts +23 -30
- package/src/crypt/aes-crypto.ts +21 -2
- package/src/crypt/decrypter-aes-mode.ts +4 -0
- package/src/crypt/decrypter.ts +32 -18
- package/src/crypt/fast-aes-key.ts +24 -5
- package/src/demux/audio/adts.ts +9 -4
- package/src/demux/sample-aes.ts +2 -0
- package/src/demux/transmuxer-interface.ts +4 -12
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +16 -3
- package/src/demux/tsdemuxer.ts +71 -37
- package/src/demux/video/avc-video-parser.ts +208 -119
- package/src/demux/video/base-video-parser.ts +134 -2
- package/src/demux/video/exp-golomb.ts +0 -208
- package/src/demux/video/hevc-video-parser.ts +746 -0
- package/src/events.ts +7 -0
- package/src/hls.ts +42 -34
- package/src/loader/fragment-loader.ts +9 -2
- package/src/loader/key-loader.ts +2 -0
- package/src/loader/level-key.ts +10 -9
- package/src/loader/playlist-loader.ts +4 -5
- package/src/remux/mp4-generator.ts +196 -1
- package/src/remux/mp4-remuxer.ts +23 -7
- package/src/task-loop.ts +5 -2
- package/src/types/component-api.ts +2 -0
- package/src/types/demuxer.ts +3 -0
- package/src/types/events.ts +4 -0
- package/src/utils/codecs.ts +33 -4
- package/src/utils/encryption-methods-util.ts +21 -0
- package/src/utils/logger.ts +54 -24
package/package.json
CHANGED
@@ -58,39 +58,39 @@
|
|
58
58
|
"test:func:sauce": "SAUCE=1 UA=safari OS='OS X 10.15' BABEL_ENV=development mocha --require @babel/register tests/functional/auto/setup.js --timeout 40000 --exit",
|
59
59
|
"type-check": "tsc --noEmit",
|
60
60
|
"type-check:watch": "npm run type-check -- --watch",
|
61
|
-
"prepare": "husky
|
61
|
+
"prepare": "husky"
|
62
62
|
},
|
63
63
|
"devDependencies": {
|
64
|
-
"@babel/core": "7.23.
|
64
|
+
"@babel/core": "7.23.9",
|
65
65
|
"@babel/helper-module-imports": "7.22.15",
|
66
66
|
"@babel/plugin-proposal-class-properties": "7.18.6",
|
67
67
|
"@babel/plugin-proposal-object-rest-spread": "7.20.7",
|
68
68
|
"@babel/plugin-proposal-optional-chaining": "7.21.0",
|
69
69
|
"@babel/plugin-transform-object-assign": "7.23.3",
|
70
|
-
"@babel/preset-env": "7.23.
|
70
|
+
"@babel/preset-env": "7.23.9",
|
71
71
|
"@babel/preset-typescript": "7.23.3",
|
72
72
|
"@babel/register": "7.23.7",
|
73
|
-
"@microsoft/api-documenter": "7.23.
|
74
|
-
"@microsoft/api-extractor": "7.
|
73
|
+
"@microsoft/api-documenter": "7.23.23",
|
74
|
+
"@microsoft/api-extractor": "7.40.1",
|
75
75
|
"@rollup/plugin-alias": "5.1.0",
|
76
76
|
"@rollup/plugin-babel": "6.0.4",
|
77
77
|
"@rollup/plugin-commonjs": "25.0.7",
|
78
78
|
"@rollup/plugin-node-resolve": "15.2.3",
|
79
79
|
"@rollup/plugin-replace": "5.0.5",
|
80
80
|
"@rollup/plugin-terser": "0.4.4",
|
81
|
-
"@rollup/plugin-typescript": "11.1.
|
82
|
-
"@svta/common-media-library": "0.6.
|
81
|
+
"@rollup/plugin-typescript": "11.1.6",
|
82
|
+
"@svta/common-media-library": "0.6.2",
|
83
83
|
"@types/chai": "4.3.11",
|
84
84
|
"@types/chart.js": "2.9.41",
|
85
85
|
"@types/mocha": "10.0.6",
|
86
86
|
"@types/sinon-chai": "3.2.12",
|
87
|
-
"@typescript-eslint/eslint-plugin": "6.
|
88
|
-
"@typescript-eslint/parser": "6.
|
87
|
+
"@typescript-eslint/eslint-plugin": "6.21.0",
|
88
|
+
"@typescript-eslint/parser": "6.21.0",
|
89
89
|
"babel-loader": "9.1.3",
|
90
90
|
"babel-plugin-transform-remove-console": "6.9.4",
|
91
|
-
"chai": "4.
|
91
|
+
"chai": "4.4.1",
|
92
92
|
"chart.js": "2.9.4",
|
93
|
-
"chromedriver": "
|
93
|
+
"chromedriver": "121.0.0",
|
94
94
|
"doctoc": "2.2.1",
|
95
95
|
"es-check": "7.1.1",
|
96
96
|
"eslint": "8.56.0",
|
@@ -101,7 +101,7 @@
|
|
101
101
|
"eslint-plugin-promise": "6.1.1",
|
102
102
|
"eventemitter3": "5.0.1",
|
103
103
|
"http-server": "14.1.1",
|
104
|
-
"husky": "
|
104
|
+
"husky": "9.0.10",
|
105
105
|
"jsonpack": "1.1.5",
|
106
106
|
"karma": "6.4.2",
|
107
107
|
"karma-chrome-launcher": "3.2.0",
|
@@ -111,24 +111,24 @@
|
|
111
111
|
"karma-rollup-preprocessor": "github:jlmakes/karma-rollup-preprocessor#7a7268d91149307b3cf2888ee4e65ccd079955a3",
|
112
112
|
"karma-sinon-chai": "2.0.2",
|
113
113
|
"karma-sourcemap-loader": "0.4.0",
|
114
|
-
"lint-staged": "15.2.
|
114
|
+
"lint-staged": "15.2.2",
|
115
115
|
"markdown-styles": "3.2.0",
|
116
116
|
"micromatch": "4.0.5",
|
117
117
|
"mocha": "10.2.0",
|
118
118
|
"node-fetch": "3.3.2",
|
119
|
-
"npm-run-
|
120
|
-
"prettier": "3.
|
119
|
+
"npm-run-all2": "5.0.2",
|
120
|
+
"prettier": "3.2.4",
|
121
121
|
"promise-polyfill": "8.3.0",
|
122
|
-
"rollup": "4.9.
|
122
|
+
"rollup": "4.9.6",
|
123
123
|
"rollup-plugin-istanbul": "5.0.0",
|
124
124
|
"sauce-connect-launcher": "1.3.2",
|
125
|
-
"selenium-webdriver": "4.
|
126
|
-
"semver": "7.
|
125
|
+
"selenium-webdriver": "4.17.0",
|
126
|
+
"semver": "7.6.0",
|
127
127
|
"sinon": "17.0.1",
|
128
128
|
"sinon-chai": "3.7.0",
|
129
129
|
"typescript": "5.3.3",
|
130
130
|
"url-toolkit": "2.2.5",
|
131
|
-
"wrangler": "3.
|
131
|
+
"wrangler": "3.28.2"
|
132
132
|
},
|
133
|
-
"version": "1.5.
|
133
|
+
"version": "1.5.6-0.canary.10003"
|
134
134
|
}
|
package/src/config.ts
CHANGED
@@ -17,10 +17,10 @@ import XhrLoader from './utils/xhr-loader';
|
|
17
17
|
import FetchLoader, { fetchSupported } from './utils/fetch-loader';
|
18
18
|
import Cues from './utils/cues';
|
19
19
|
import { requestMediaKeySystemAccess } from './utils/mediakeys-helper';
|
20
|
-
import { ILogger, logger } from './utils/logger';
|
21
20
|
|
22
21
|
import type Hls from './hls';
|
23
22
|
import type { CuesInterface } from './utils/cues';
|
23
|
+
import type { ILogger } from './utils/logger';
|
24
24
|
import type { MediaKeyFunc, KeySystems } from './utils/mediakeys-helper';
|
25
25
|
import type {
|
26
26
|
FragmentLoaderContext,
|
@@ -558,6 +558,7 @@ function timelineConfig(): TimelineControllerConfig {
|
|
558
558
|
export function mergeConfig(
|
559
559
|
defaultConfig: HlsConfig,
|
560
560
|
userConfig: Partial<HlsConfig>,
|
561
|
+
logger: ILogger,
|
561
562
|
): HlsConfig {
|
562
563
|
if (
|
563
564
|
(userConfig.liveSyncDurationCount ||
|
@@ -664,7 +665,7 @@ function deepCpy(obj: any): any {
|
|
664
665
|
/**
|
665
666
|
* @ignore
|
666
667
|
*/
|
667
|
-
export function enableStreamingMode(config) {
|
668
|
+
export function enableStreamingMode(config: HlsConfig, logger: ILogger) {
|
668
669
|
const currentLoader = config.loader;
|
669
670
|
if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
|
670
671
|
// If a developer has configured their own loader, respect that choice
|
@@ -2,7 +2,7 @@ import EwmaBandWidthEstimator from '../utils/ewma-bandwidth-estimator';
|
|
2
2
|
import { Events } from '../events';
|
3
3
|
import { ErrorDetails } from '../errors';
|
4
4
|
import { PlaylistLevelType } from '../types/loader';
|
5
|
-
import {
|
5
|
+
import { Logger } from '../utils/logger';
|
6
6
|
import {
|
7
7
|
SUPPORTED_INFO_DEFAULT,
|
8
8
|
getMediaDecodingInfoPromise,
|
@@ -31,7 +31,7 @@ import type {
|
|
31
31
|
} from '../types/events';
|
32
32
|
import type { AbrComponentAPI } from '../types/component-api';
|
33
33
|
|
34
|
-
class AbrController implements AbrComponentAPI {
|
34
|
+
class AbrController extends Logger implements AbrComponentAPI {
|
35
35
|
protected hls: Hls;
|
36
36
|
private lastLevelLoadSec: number = 0;
|
37
37
|
private lastLoadedFragLevel: number = -1;
|
@@ -48,6 +48,7 @@ class AbrController implements AbrComponentAPI {
|
|
48
48
|
public bwEstimator: EwmaBandWidthEstimator;
|
49
49
|
|
50
50
|
constructor(hls: Hls) {
|
51
|
+
super('abr', hls.logger);
|
51
52
|
this.hls = hls;
|
52
53
|
this.bwEstimator = this.initEstimator();
|
53
54
|
this.registerListeners();
|
@@ -55,7 +56,7 @@ class AbrController implements AbrComponentAPI {
|
|
55
56
|
|
56
57
|
public resetEstimator(abrEwmaDefaultEstimate?: number) {
|
57
58
|
if (abrEwmaDefaultEstimate) {
|
58
|
-
|
59
|
+
this.log(`setting initial bwe to ${abrEwmaDefaultEstimate}`);
|
59
60
|
this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate;
|
60
61
|
}
|
61
62
|
this.firstSelection = -1;
|
@@ -355,7 +356,7 @@ class AbrController implements AbrComponentAPI {
|
|
355
356
|
}
|
356
357
|
|
357
358
|
this.clearTimer();
|
358
|
-
|
359
|
+
this.warn(`Fragment ${frag.sn}${
|
359
360
|
part ? ' part ' + part.index : ''
|
360
361
|
} of level ${frag.level} is loading too slowly;
|
361
362
|
Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s
|
@@ -479,8 +480,8 @@ class AbrController implements AbrComponentAPI {
|
|
479
480
|
}
|
480
481
|
const firstLevel = this.hls.firstLevel;
|
481
482
|
const clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel);
|
482
|
-
|
483
|
-
`
|
483
|
+
this.warn(
|
484
|
+
`Could not find best starting auto level. Defaulting to first in playlist ${firstLevel} clamped to ${clamped}`,
|
484
485
|
);
|
485
486
|
return clamped;
|
486
487
|
}
|
@@ -591,8 +592,8 @@ class AbrController implements AbrComponentAPI {
|
|
591
592
|
? Math.min(currentFragDuration, config.maxLoadingDelay)
|
592
593
|
: config.maxLoadingDelay;
|
593
594
|
maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
|
594
|
-
|
595
|
-
`
|
595
|
+
this.info(
|
596
|
+
`bitrate test took ${Math.round(
|
596
597
|
1000 * bitrateTestDelay,
|
597
598
|
)}ms, set first fragment max fetchDuration to ${Math.round(
|
598
599
|
1000 * maxStarvationDelay,
|
@@ -611,8 +612,8 @@ class AbrController implements AbrComponentAPI {
|
|
611
612
|
bwFactor,
|
612
613
|
bwUpFactor,
|
613
614
|
);
|
614
|
-
|
615
|
-
|
615
|
+
this.info(
|
616
|
+
`${
|
616
617
|
bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'
|
617
618
|
}, optimal quality level ${bestLevel}`,
|
618
619
|
);
|
@@ -691,7 +692,7 @@ class AbrController implements AbrComponentAPI {
|
|
691
692
|
: videoRanges[0];
|
692
693
|
currentFrameRate = minFramerate;
|
693
694
|
currentBw = Math.max(currentBw, minBitrate);
|
694
|
-
|
695
|
+
this.log(`picked start tier ${JSON.stringify(startTier)}`);
|
695
696
|
} else {
|
696
697
|
currentCodecSet = level?.codecSet;
|
697
698
|
currentVideoRange = level?.videoRange;
|
@@ -741,19 +742,19 @@ class AbrController implements AbrComponentAPI {
|
|
741
742
|
const levels = this.hls.levels;
|
742
743
|
const index = levels.indexOf(levelInfo);
|
743
744
|
if (decodingInfo.error) {
|
744
|
-
|
745
|
-
`
|
745
|
+
this.warn(
|
746
|
+
`MediaCapabilities decodingInfo error: "${
|
746
747
|
decodingInfo.error
|
747
748
|
}" for level ${index} ${JSON.stringify(decodingInfo)}`,
|
748
749
|
);
|
749
750
|
} else if (!decodingInfo.supported) {
|
750
|
-
|
751
|
-
`
|
751
|
+
this.warn(
|
752
|
+
`Unsupported MediaCapabilities decodingInfo result for level ${index} ${JSON.stringify(
|
752
753
|
decodingInfo,
|
753
754
|
)}`,
|
754
755
|
);
|
755
756
|
if (index > -1 && levels.length > 1) {
|
756
|
-
|
757
|
+
this.log(`Removing unsupported level ${index}`);
|
757
758
|
this.hls.removeLevel(index);
|
758
759
|
}
|
759
760
|
}
|
@@ -832,8 +833,8 @@ class AbrController implements AbrComponentAPI {
|
|
832
833
|
(forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)
|
833
834
|
) {
|
834
835
|
if (levelsSkipped.length) {
|
835
|
-
|
836
|
-
`
|
836
|
+
this.trace(
|
837
|
+
`Skipped level(s) ${levelsSkipped.join(
|
837
838
|
',',
|
838
839
|
)} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${
|
839
840
|
levels[levelsSkipped[0]].codecs
|
@@ -842,8 +843,8 @@ class AbrController implements AbrComponentAPI {
|
|
842
843
|
}" ${currentVideoRange}`,
|
843
844
|
);
|
844
845
|
}
|
845
|
-
|
846
|
-
`
|
846
|
+
this.info(
|
847
|
+
`switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(
|
847
848
|
adjustedbw,
|
848
849
|
)})-bitrate=${Math.round(
|
849
850
|
adjustedbw - bitrate,
|
@@ -71,30 +71,27 @@ class AudioStreamController
|
|
71
71
|
hls,
|
72
72
|
fragmentTracker,
|
73
73
|
keyLoader,
|
74
|
-
'
|
74
|
+
'audio-stream-controller',
|
75
75
|
PlaylistLevelType.AUDIO,
|
76
76
|
);
|
77
|
-
this.
|
77
|
+
this.registerListeners();
|
78
78
|
}
|
79
79
|
|
80
80
|
protected onHandlerDestroying() {
|
81
|
-
this.
|
81
|
+
this.unregisterListeners();
|
82
82
|
super.onHandlerDestroying();
|
83
83
|
this.mainDetails = null;
|
84
84
|
this.bufferedTrack = null;
|
85
85
|
this.switchingTrack = null;
|
86
86
|
}
|
87
87
|
|
88
|
-
|
88
|
+
protected registerListeners() {
|
89
|
+
super.registerListeners();
|
89
90
|
const { hls } = this;
|
90
|
-
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
91
|
-
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
92
|
-
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
93
91
|
hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
94
92
|
hls.on(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this);
|
95
93
|
hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
96
94
|
hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);
|
97
|
-
hls.on(Events.ERROR, this.onError, this);
|
98
95
|
hls.on(Events.BUFFER_RESET, this.onBufferReset, this);
|
99
96
|
hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
100
97
|
hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
|
@@ -103,16 +100,16 @@ class AudioStreamController
|
|
103
100
|
hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
|
104
101
|
}
|
105
102
|
|
106
|
-
|
103
|
+
protected unregisterListeners() {
|
107
104
|
const { hls } = this;
|
108
|
-
hls
|
109
|
-
|
110
|
-
|
105
|
+
if (!hls) {
|
106
|
+
return;
|
107
|
+
}
|
108
|
+
super.unregisterListeners();
|
111
109
|
hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
|
112
110
|
hls.off(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this);
|
113
111
|
hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
|
114
112
|
hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this);
|
115
|
-
hls.off(Events.ERROR, this.onError, this);
|
116
113
|
hls.off(Events.BUFFER_RESET, this.onBufferReset, this);
|
117
114
|
hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
|
118
115
|
hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
|
@@ -281,12 +278,14 @@ class AudioStreamController
|
|
281
278
|
const { hls, levels, media, trackId } = this;
|
282
279
|
const config = hls.config;
|
283
280
|
|
284
|
-
// 1. if
|
281
|
+
// 1. if buffering is suspended
|
282
|
+
// 2. if video not attached AND
|
285
283
|
// start fragment already requested OR start frag prefetch not enabled
|
286
|
-
//
|
284
|
+
// 3. if tracks or track not loaded and selected
|
287
285
|
// then exit loop
|
288
286
|
// => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop
|
289
287
|
if (
|
288
|
+
!this.buffering ||
|
290
289
|
(!media && (this.startFragRequested || !config.startFragPrefetch)) ||
|
291
290
|
!levels?.[trackId]
|
292
291
|
) {
|
@@ -713,7 +712,7 @@ class AudioStreamController
|
|
713
712
|
this.fragBufferedComplete(frag, part);
|
714
713
|
}
|
715
714
|
|
716
|
-
|
715
|
+
protected onError(event: Events.ERROR, data: ErrorData) {
|
717
716
|
if (data.fatal) {
|
718
717
|
this.state = State.ERROR;
|
719
718
|
return;
|
@@ -5,7 +5,7 @@ import { computeReloadInterval, mergeDetails } from '../utils/level-helper';
|
|
5
5
|
import { ErrorData } from '../types/events';
|
6
6
|
import { getRetryDelay, isTimeoutError } from '../utils/error-helper';
|
7
7
|
import { NetworkErrorAction } from './error-controller';
|
8
|
-
import {
|
8
|
+
import { Logger } from '../utils/logger';
|
9
9
|
import type { LevelDetails } from '../loader/level-details';
|
10
10
|
import type { MediaPlaylist } from '../types/media-playlist';
|
11
11
|
import type {
|
@@ -14,17 +14,17 @@ import type {
|
|
14
14
|
TrackLoadedData,
|
15
15
|
} from '../types/events';
|
16
16
|
|
17
|
-
export default class BasePlaylistController
|
17
|
+
export default class BasePlaylistController
|
18
|
+
extends Logger
|
19
|
+
implements NetworkComponentAPI
|
20
|
+
{
|
18
21
|
protected hls: Hls;
|
19
22
|
protected timer: number = -1;
|
20
23
|
protected requestScheduled: number = -1;
|
21
24
|
protected canLoad: boolean = false;
|
22
|
-
protected log: (msg: any) => void;
|
23
|
-
protected warn: (msg: any) => void;
|
24
25
|
|
25
26
|
constructor(hls: Hls, logPrefix: string) {
|
26
|
-
|
27
|
-
this.warn = logger.warn.bind(logger, `${logPrefix}:`);
|
27
|
+
super(logPrefix, hls.logger);
|
28
28
|
this.hls = hls;
|
29
29
|
}
|
30
30
|
|
@@ -65,7 +65,7 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
|
65
65
|
try {
|
66
66
|
uri = new self.URL(attr.URI, previous.url).href;
|
67
67
|
} catch (error) {
|
68
|
-
|
68
|
+
this.warn(
|
69
69
|
`Could not construct new URL for Rendition Report: ${error}`,
|
70
70
|
);
|
71
71
|
uri = attr.URI || '';
|
@@ -192,7 +192,19 @@ export default class BasePlaylistController implements NetworkComponentAPI {
|
|
192
192
|
details.targetduration * 1.5,
|
193
193
|
);
|
194
194
|
if (currentGoal > 0) {
|
195
|
-
if (
|
195
|
+
if (cdnAge > details.targetduration * 3) {
|
196
|
+
// Omit segment and part directives when the last response was more than 3 target durations ago,
|
197
|
+
this.log(
|
198
|
+
`Playlist last advanced ${lastAdvanced.toFixed(
|
199
|
+
2,
|
200
|
+
)}s ago. Omitting segment and part directives.`,
|
201
|
+
);
|
202
|
+
msn = undefined;
|
203
|
+
part = undefined;
|
204
|
+
} else if (
|
205
|
+
previousDetails?.tuneInGoal &&
|
206
|
+
cdnAge - details.partTarget > previousDetails.tuneInGoal
|
207
|
+
) {
|
196
208
|
// If we attempted to get the next or latest playlist update, but currentGoal increased,
|
197
209
|
// then we either can't catchup, or the "age" header cannot be trusted.
|
198
210
|
this.warn(
|