hls.js 1.5.6 → 1.5.7-0.canary.10014
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 +2074 -1165
- package/dist/hls.js.d.ts +65 -50
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1147 -858
- 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 +983 -695
- 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 +1756 -862
- 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 +21 -21
- 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 +24 -31
- 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
|
-
"mocha": "10.
|
117
|
+
"mocha": "10.3.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.
|
122
|
+
"rollup": "4.12.0",
|
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.7-0.canary.10014"
|
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
|
}
|
@@ -584,8 +585,8 @@ class AbrController implements AbrComponentAPI {
|
|
584
585
|
? Math.min(currentFragDuration, config.maxLoadingDelay)
|
585
586
|
: config.maxLoadingDelay;
|
586
587
|
maxStarvationDelay = maxLoadingDelay - bitrateTestDelay;
|
587
|
-
|
588
|
-
`
|
588
|
+
this.info(
|
589
|
+
`bitrate test took ${Math.round(
|
589
590
|
1000 * bitrateTestDelay,
|
590
591
|
)}ms, set first fragment max fetchDuration to ${Math.round(
|
591
592
|
1000 * maxStarvationDelay,
|
@@ -604,8 +605,8 @@ class AbrController implements AbrComponentAPI {
|
|
604
605
|
bwFactor,
|
605
606
|
bwUpFactor,
|
606
607
|
);
|
607
|
-
|
608
|
-
|
608
|
+
this.info(
|
609
|
+
`${
|
609
610
|
bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty'
|
610
611
|
}, optimal quality level ${bestLevel}`,
|
611
612
|
);
|
@@ -698,7 +699,7 @@ class AbrController implements AbrComponentAPI {
|
|
698
699
|
: videoRanges[0];
|
699
700
|
currentFrameRate = minFramerate;
|
700
701
|
currentBw = Math.max(currentBw, minBitrate);
|
701
|
-
|
702
|
+
this.log(`picked start tier ${JSON.stringify(startTier)}`);
|
702
703
|
} else {
|
703
704
|
currentCodecSet = level?.codecSet;
|
704
705
|
currentVideoRange = level?.videoRange;
|
@@ -751,19 +752,19 @@ class AbrController implements AbrComponentAPI {
|
|
751
752
|
const levels = this.hls.levels;
|
752
753
|
const index = levels.indexOf(levelInfo);
|
753
754
|
if (decodingInfo.error) {
|
754
|
-
|
755
|
-
`
|
755
|
+
this.warn(
|
756
|
+
`MediaCapabilities decodingInfo error: "${
|
756
757
|
decodingInfo.error
|
757
758
|
}" for level ${index} ${JSON.stringify(decodingInfo)}`,
|
758
759
|
);
|
759
760
|
} else if (!decodingInfo.supported) {
|
760
|
-
|
761
|
-
`
|
761
|
+
this.warn(
|
762
|
+
`Unsupported MediaCapabilities decodingInfo result for level ${index} ${JSON.stringify(
|
762
763
|
decodingInfo,
|
763
764
|
)}`,
|
764
765
|
);
|
765
766
|
if (index > -1 && levels.length > 1) {
|
766
|
-
|
767
|
+
this.log(`Removing unsupported level ${index}`);
|
767
768
|
this.hls.removeLevel(index);
|
768
769
|
}
|
769
770
|
}
|
@@ -842,8 +843,8 @@ class AbrController implements AbrComponentAPI {
|
|
842
843
|
(forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)
|
843
844
|
) {
|
844
845
|
if (levelsSkipped.length) {
|
845
|
-
|
846
|
-
`
|
846
|
+
this.trace(
|
847
|
+
`Skipped level(s) ${levelsSkipped.join(
|
847
848
|
',',
|
848
849
|
)} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${
|
849
850
|
levels[levelsSkipped[0]].codecs
|
@@ -852,8 +853,8 @@ class AbrController implements AbrComponentAPI {
|
|
852
853
|
}" ${currentVideoRange}`,
|
853
854
|
);
|
854
855
|
}
|
855
|
-
|
856
|
-
`
|
856
|
+
this.info(
|
857
|
+
`switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(
|
857
858
|
adjustedbw,
|
858
859
|
)})-bitrate=${Math.round(
|
859
860
|
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(
|