hls.js 1.5.7-0.canary.10042 → 1.5.7
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 -2
- package/dist/hls-demo.js +0 -10
- package/dist/hls-demo.js.map +1 -1
- package/dist/hls.js +1283 -2293
- package/dist/hls.js.d.ts +84 -97
- package/dist/hls.js.map +1 -1
- package/dist/hls.light.js +1030 -1435
- 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 +809 -1209
- 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 +1039 -2030
- 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 +22 -22
- package/src/config.ts +2 -3
- package/src/controller/abr-controller.ts +20 -24
- package/src/controller/audio-stream-controller.ts +74 -68
- package/src/controller/audio-track-controller.ts +1 -1
- package/src/controller/base-playlist-controller.ts +8 -20
- package/src/controller/base-stream-controller.ts +36 -157
- package/src/controller/buffer-controller.ts +99 -226
- package/src/controller/buffer-operation-queue.ts +19 -16
- package/src/controller/cap-level-controller.ts +2 -2
- package/src/controller/cmcd-controller.ts +6 -27
- package/src/controller/content-steering-controller.ts +6 -8
- package/src/controller/eme-controller.ts +22 -9
- package/src/controller/error-controller.ts +8 -6
- package/src/controller/fps-controller.ts +3 -2
- package/src/controller/fragment-tracker.ts +11 -15
- package/src/controller/gap-controller.ts +16 -43
- package/src/controller/latency-controller.ts +11 -9
- package/src/controller/level-controller.ts +18 -12
- package/src/controller/stream-controller.ts +31 -36
- package/src/controller/subtitle-stream-controller.ts +40 -28
- package/src/controller/subtitle-track-controller.ts +3 -5
- package/src/controller/timeline-controller.ts +30 -23
- package/src/crypt/aes-crypto.ts +2 -21
- package/src/crypt/decrypter.ts +18 -32
- package/src/crypt/fast-aes-key.ts +5 -24
- package/src/demux/audio/adts.ts +4 -9
- package/src/demux/sample-aes.ts +0 -2
- package/src/demux/transmuxer-interface.ts +12 -4
- package/src/demux/transmuxer-worker.ts +4 -4
- package/src/demux/transmuxer.ts +3 -16
- package/src/demux/tsdemuxer.ts +37 -71
- package/src/demux/video/avc-video-parser.ts +119 -208
- package/src/demux/video/base-video-parser.ts +2 -134
- package/src/demux/video/exp-golomb.ts +208 -0
- package/src/events.ts +0 -7
- package/src/hls.ts +37 -49
- package/src/loader/fragment-loader.ts +2 -9
- package/src/loader/key-loader.ts +0 -2
- package/src/loader/level-key.ts +9 -10
- package/src/loader/playlist-loader.ts +5 -4
- package/src/remux/mp4-generator.ts +1 -196
- package/src/remux/mp4-remuxer.ts +7 -23
- package/src/task-loop.ts +2 -5
- package/src/types/component-api.ts +0 -2
- package/src/types/demuxer.ts +0 -3
- package/src/types/events.ts +0 -4
- package/src/utils/buffer-helper.ts +31 -12
- package/src/utils/codecs.ts +5 -34
- package/src/utils/logger.ts +24 -54
- package/src/utils/mp4-tools.ts +2 -4
- package/src/crypt/decrypter-aes-mode.ts +0 -4
- package/src/demux/video/hevc-video-parser.ts +0 -746
- package/src/utils/encryption-methods-util.ts +0 -21
@@ -1,15 +1,12 @@
|
|
1
1
|
import TaskLoop from '../task-loop';
|
2
2
|
import { FragmentState } from './fragment-tracker';
|
3
3
|
import { Bufferable, BufferHelper, BufferInfo } from '../utils/buffer-helper';
|
4
|
+
import { logger } from '../utils/logger';
|
4
5
|
import { Events } from '../events';
|
5
6
|
import { ErrorDetails, ErrorTypes } from '../errors';
|
6
7
|
import { ChunkMetadata } from '../types/transmuxer';
|
7
8
|
import { appendUint8Array } from '../utils/mp4-tools';
|
8
9
|
import { alignStream } from '../utils/discontinuities';
|
9
|
-
import {
|
10
|
-
isFullSegmentEncryption,
|
11
|
-
getAesModeFromFullSegmentMethod,
|
12
|
-
} from '../utils/encryption-methods-util';
|
13
10
|
import {
|
14
11
|
findFragmentByPDT,
|
15
12
|
findFragmentByPTS,
|
@@ -100,8 +97,12 @@ export default class BaseStreamController
|
|
100
97
|
protected startFragRequested: boolean = false;
|
101
98
|
protected decrypter: Decrypter;
|
102
99
|
protected initPTS: RationalTimestamp[] = [];
|
103
|
-
protected
|
104
|
-
|
100
|
+
protected onvseeking: EventListener | null = null;
|
101
|
+
protected onvended: EventListener | null = null;
|
102
|
+
|
103
|
+
private readonly logPrefix: string = '';
|
104
|
+
protected log: (msg: any) => void;
|
105
|
+
protected warn: (msg: any) => void;
|
105
106
|
|
106
107
|
constructor(
|
107
108
|
hls: Hls,
|
@@ -110,32 +111,18 @@ export default class BaseStreamController
|
|
110
111
|
logPrefix: string,
|
111
112
|
playlistType: PlaylistLevelType,
|
112
113
|
) {
|
113
|
-
super(
|
114
|
+
super();
|
114
115
|
this.playlistType = playlistType;
|
116
|
+
this.logPrefix = logPrefix;
|
117
|
+
this.log = logger.log.bind(logger, `${logPrefix}:`);
|
118
|
+
this.warn = logger.warn.bind(logger, `${logPrefix}:`);
|
115
119
|
this.hls = hls;
|
116
120
|
this.fragmentLoader = new FragmentLoader(hls.config);
|
117
121
|
this.keyLoader = keyLoader;
|
118
122
|
this.fragmentTracker = fragmentTracker;
|
119
123
|
this.config = hls.config;
|
120
124
|
this.decrypter = new Decrypter(hls.config);
|
121
|
-
}
|
122
|
-
|
123
|
-
protected registerListeners() {
|
124
|
-
const { hls } = this;
|
125
|
-
hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
126
|
-
hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
127
|
-
hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
128
125
|
hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
|
129
|
-
hls.on(Events.ERROR, this.onError, this);
|
130
|
-
}
|
131
|
-
|
132
|
-
protected unregisterListeners() {
|
133
|
-
const { hls } = this;
|
134
|
-
hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
|
135
|
-
hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
|
136
|
-
hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
|
137
|
-
hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
|
138
|
-
hls.off(Events.ERROR, this.onError, this);
|
139
126
|
}
|
140
127
|
|
141
128
|
protected doTick() {
|
@@ -163,14 +150,6 @@ export default class BaseStreamController
|
|
163
150
|
this.state = State.STOPPED;
|
164
151
|
}
|
165
152
|
|
166
|
-
public pauseBuffering() {
|
167
|
-
this.buffering = false;
|
168
|
-
}
|
169
|
-
|
170
|
-
public resumeBuffering() {
|
171
|
-
this.buffering = true;
|
172
|
-
}
|
173
|
-
|
174
153
|
protected _streamEnded(
|
175
154
|
bufferInfo: BufferInfo,
|
176
155
|
levelDetails: LevelDetails,
|
@@ -218,8 +197,10 @@ export default class BaseStreamController
|
|
218
197
|
data: MediaAttachedData,
|
219
198
|
) {
|
220
199
|
const media = (this.media = this.mediaBuffer = data.media);
|
221
|
-
|
222
|
-
|
200
|
+
this.onvseeking = this.onMediaSeeking.bind(this) as EventListener;
|
201
|
+
this.onvended = this.onMediaEnded.bind(this) as EventListener;
|
202
|
+
media.addEventListener('seeking', this.onvseeking);
|
203
|
+
media.addEventListener('ended', this.onvended);
|
223
204
|
const config = this.config;
|
224
205
|
if (this.levels && config.autoStartLoad && this.state === State.STOPPED) {
|
225
206
|
this.startLoad(config.startPosition);
|
@@ -234,9 +215,10 @@ export default class BaseStreamController
|
|
234
215
|
}
|
235
216
|
|
236
217
|
// remove video listeners
|
237
|
-
if (media) {
|
238
|
-
media.removeEventListener('seeking', this.
|
239
|
-
media.removeEventListener('ended', this.
|
218
|
+
if (media && this.onvseeking && this.onvended) {
|
219
|
+
media.removeEventListener('seeking', this.onvseeking);
|
220
|
+
media.removeEventListener('ended', this.onvended);
|
221
|
+
this.onvseeking = this.onvended = null;
|
240
222
|
}
|
241
223
|
if (this.keyLoader) {
|
242
224
|
this.keyLoader.detach();
|
@@ -247,11 +229,7 @@ export default class BaseStreamController
|
|
247
229
|
this.stopLoad();
|
248
230
|
}
|
249
231
|
|
250
|
-
protected
|
251
|
-
|
252
|
-
protected onError(event: Events.ERROR, data: ErrorData) {}
|
253
|
-
|
254
|
-
protected onMediaSeeking = () => {
|
232
|
+
protected onMediaSeeking() {
|
255
233
|
const { config, fragCurrent, media, mediaBuffer, state } = this;
|
256
234
|
const currentTime: number = media ? media.currentTime : 0;
|
257
235
|
const bufferInfo = BufferHelper.bufferInfo(
|
@@ -305,21 +283,6 @@ export default class BaseStreamController
|
|
305
283
|
);
|
306
284
|
|
307
285
|
this.lastCurrentTime = currentTime;
|
308
|
-
if (!this.loadingParts) {
|
309
|
-
const bufferEnd = Math.max(bufferInfo.end, currentTime);
|
310
|
-
const shouldLoadParts = this.shouldLoadParts(
|
311
|
-
this.getLevelDetails(),
|
312
|
-
bufferEnd,
|
313
|
-
);
|
314
|
-
if (shouldLoadParts) {
|
315
|
-
this.log(
|
316
|
-
`LL-Part loading ON after seeking to ${currentTime.toFixed(
|
317
|
-
2,
|
318
|
-
)} with buffer @${bufferEnd.toFixed(2)}`,
|
319
|
-
);
|
320
|
-
this.loadingParts = shouldLoadParts;
|
321
|
-
}
|
322
|
-
}
|
323
286
|
}
|
324
287
|
|
325
288
|
// in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
|
@@ -329,17 +292,12 @@ export default class BaseStreamController
|
|
329
292
|
|
330
293
|
// Async tick to speed up processing
|
331
294
|
this.tickImmediate();
|
332
|
-
}
|
295
|
+
}
|
333
296
|
|
334
|
-
protected onMediaEnded
|
297
|
+
protected onMediaEnded() {
|
335
298
|
// reset startPosition and lastCurrentTime to restart playback @ stream beginning
|
336
299
|
this.startPosition = this.lastCurrentTime = 0;
|
337
|
-
|
338
|
-
this.hls.trigger(Events.MEDIA_ENDED, {
|
339
|
-
stalled: false,
|
340
|
-
});
|
341
|
-
}
|
342
|
-
};
|
300
|
+
}
|
343
301
|
|
344
302
|
protected onManifestLoaded(
|
345
303
|
event: Events.MANIFEST_LOADED,
|
@@ -354,7 +312,7 @@ export default class BaseStreamController
|
|
354
312
|
this.stopLoad();
|
355
313
|
super.onHandlerDestroying();
|
356
314
|
// @ts-ignore
|
357
|
-
this.hls =
|
315
|
+
this.hls = null;
|
358
316
|
}
|
359
317
|
|
360
318
|
protected onHandlerDestroyed() {
|
@@ -528,7 +486,7 @@ export default class BaseStreamController
|
|
528
486
|
payload.byteLength > 0 &&
|
529
487
|
decryptData?.key &&
|
530
488
|
decryptData.iv &&
|
531
|
-
|
489
|
+
decryptData.method === 'AES-128'
|
532
490
|
) {
|
533
491
|
const startTime = self.performance.now();
|
534
492
|
// decrypt init segment data
|
@@ -537,7 +495,6 @@ export default class BaseStreamController
|
|
537
495
|
new Uint8Array(payload),
|
538
496
|
decryptData.key.buffer,
|
539
497
|
decryptData.iv.buffer,
|
540
|
-
getAesModeFromFullSegmentMethod(decryptData.method),
|
541
498
|
)
|
542
499
|
.catch((err) => {
|
543
500
|
hls.trigger(Events.ERROR, {
|
@@ -692,7 +649,7 @@ export default class BaseStreamController
|
|
692
649
|
if (frag.encrypted && !frag.decryptdata?.key) {
|
693
650
|
this.log(
|
694
651
|
`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${
|
695
|
-
this.
|
652
|
+
this.logPrefix === '[stream-controller]' ? 'level' : 'track'
|
696
653
|
} ${frag.level}`,
|
697
654
|
);
|
698
655
|
this.state = State.KEY_LOADING;
|
@@ -716,23 +673,8 @@ export default class BaseStreamController
|
|
716
673
|
this.keyLoader.loadClear(frag, details.encryptedFragments);
|
717
674
|
}
|
718
675
|
|
719
|
-
const fragPrevious = this.fragPrevious;
|
720
|
-
if (
|
721
|
-
frag.sn !== 'initSegment' &&
|
722
|
-
(!fragPrevious || frag.sn !== fragPrevious.sn)
|
723
|
-
) {
|
724
|
-
const shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
|
725
|
-
if (shouldLoadParts !== this.loadingParts) {
|
726
|
-
this.log(
|
727
|
-
`LL-Part loading ${
|
728
|
-
shouldLoadParts ? 'ON' : 'OFF'
|
729
|
-
} loading sn ${fragPrevious?.sn}->${frag.sn}`,
|
730
|
-
);
|
731
|
-
this.loadingParts = shouldLoadParts;
|
732
|
-
}
|
733
|
-
}
|
734
676
|
targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
|
735
|
-
if (this.
|
677
|
+
if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
|
736
678
|
const partList = details.partList;
|
737
679
|
if (partList && progressCallback) {
|
738
680
|
if (targetBufferTime > frag.end && details.fragmentHint) {
|
@@ -747,7 +689,7 @@ export default class BaseStreamController
|
|
747
689
|
} of playlist [${details.startSN}-${
|
748
690
|
details.endSN
|
749
691
|
}] parts [0-${partIndex}-${partList.length - 1}] ${
|
750
|
-
this.
|
692
|
+
this.logPrefix === '[stream-controller]' ? 'level' : 'track'
|
751
693
|
}: ${frag.level}, target: ${parseFloat(
|
752
694
|
targetBufferTime.toFixed(3),
|
753
695
|
)}`,
|
@@ -803,22 +745,10 @@ export default class BaseStreamController
|
|
803
745
|
}
|
804
746
|
}
|
805
747
|
|
806
|
-
if (frag.sn !== 'initSegment' && this.loadingParts) {
|
807
|
-
this.log(
|
808
|
-
`LL-Part loading OFF after next part miss @${targetBufferTime.toFixed(
|
809
|
-
2,
|
810
|
-
)}`,
|
811
|
-
);
|
812
|
-
this.loadingParts = false;
|
813
|
-
} else if (!frag.url) {
|
814
|
-
// Selected fragment hint for part but not loading parts
|
815
|
-
return Promise.resolve(null);
|
816
|
-
}
|
817
|
-
|
818
748
|
this.log(
|
819
749
|
`Loading fragment ${frag.sn} cc: ${frag.cc} ${
|
820
750
|
details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''
|
821
|
-
}${this.
|
751
|
+
}${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${
|
822
752
|
frag.level
|
823
753
|
}, target: ${parseFloat(targetBufferTime.toFixed(3))}`,
|
824
754
|
);
|
@@ -942,50 +872,9 @@ export default class BaseStreamController
|
|
942
872
|
if (part) {
|
943
873
|
part.stats.parsing.end = now;
|
944
874
|
}
|
945
|
-
// See if part loading should be disabled/enabled based on buffer and playback position.
|
946
|
-
if (frag.sn !== 'initSegment') {
|
947
|
-
const levelDetails = this.getLevelDetails();
|
948
|
-
const loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
|
949
|
-
const shouldLoadParts =
|
950
|
-
loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
|
951
|
-
if (shouldLoadParts !== this.loadingParts) {
|
952
|
-
this.log(
|
953
|
-
`LL-Part loading ${
|
954
|
-
shouldLoadParts ? 'ON' : 'OFF'
|
955
|
-
} after parsing segment ending @${frag.end.toFixed(2)}`,
|
956
|
-
);
|
957
|
-
this.loadingParts = shouldLoadParts;
|
958
|
-
}
|
959
|
-
}
|
960
|
-
|
961
875
|
this.updateLevelTiming(frag, part, level, chunkMeta.partial);
|
962
876
|
}
|
963
877
|
|
964
|
-
private shouldLoadParts(
|
965
|
-
details: LevelDetails | undefined,
|
966
|
-
bufferEnd: number,
|
967
|
-
): boolean {
|
968
|
-
if (this.config.lowLatencyMode) {
|
969
|
-
if (!details) {
|
970
|
-
return this.loadingParts;
|
971
|
-
}
|
972
|
-
if (details?.partList) {
|
973
|
-
// Buffer must be ahead of first part + duration of parts after last segment
|
974
|
-
// and playback must be at or past segment adjacent to part list
|
975
|
-
const firstPart = details.partList[0];
|
976
|
-
const safePartStart =
|
977
|
-
firstPart.end + (details.fragmentHint?.duration || 0);
|
978
|
-
if (
|
979
|
-
bufferEnd >= safePartStart &&
|
980
|
-
this.lastCurrentTime > firstPart.start - firstPart.fragment.duration
|
981
|
-
) {
|
982
|
-
return true;
|
983
|
-
}
|
984
|
-
}
|
985
|
-
}
|
986
|
-
return false;
|
987
|
-
}
|
988
|
-
|
989
878
|
protected getCurrentContext(
|
990
879
|
chunkMeta: ChunkMetadata,
|
991
880
|
): { frag: Fragment; part: Part | null; level: Level } | null {
|
@@ -1102,10 +991,7 @@ export default class BaseStreamController
|
|
1102
991
|
// Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
|
1103
992
|
if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
|
1104
993
|
const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
|
1105
|
-
if (
|
1106
|
-
bufferedFragAtPos &&
|
1107
|
-
(bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)
|
1108
|
-
) {
|
994
|
+
if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {
|
1109
995
|
return BufferHelper.bufferInfo(
|
1110
996
|
bufferable,
|
1111
997
|
pos,
|
@@ -1118,7 +1004,7 @@ export default class BaseStreamController
|
|
1118
1004
|
|
1119
1005
|
protected getMaxBufferLength(levelBitrate?: number): number {
|
1120
1006
|
const { config } = this;
|
1121
|
-
let maxBufLen
|
1007
|
+
let maxBufLen;
|
1122
1008
|
if (levelBitrate) {
|
1123
1009
|
maxBufLen = Math.max(
|
1124
1010
|
(8 * config.maxBufferSize) / levelBitrate,
|
@@ -1170,8 +1056,7 @@ export default class BaseStreamController
|
|
1170
1056
|
// find fragment index, contiguous with end of buffer position
|
1171
1057
|
const { config } = this;
|
1172
1058
|
const start = fragments[0].start;
|
1173
|
-
|
1174
|
-
let frag: Fragment | null = null;
|
1059
|
+
let frag;
|
1175
1060
|
|
1176
1061
|
if (levelDetails.live) {
|
1177
1062
|
const initialLiveManifestSize = config.initialLiveManifestSize;
|
@@ -1191,10 +1076,6 @@ export default class BaseStreamController
|
|
1191
1076
|
this.startPosition === -1) ||
|
1192
1077
|
pos < start
|
1193
1078
|
) {
|
1194
|
-
if (canLoadParts && !this.loadingParts) {
|
1195
|
-
this.log(`LL-Part loading ON for initial live fragment`);
|
1196
|
-
this.loadingParts = true;
|
1197
|
-
}
|
1198
1079
|
frag = this.getInitialLiveFragment(levelDetails, fragments);
|
1199
1080
|
this.startPosition = this.nextLoadPosition = frag
|
1200
1081
|
? this.hls.liveSyncPosition || frag.start
|
@@ -1207,7 +1088,7 @@ export default class BaseStreamController
|
|
1207
1088
|
|
1208
1089
|
// If we haven't run into any special cases already, just load the fragment most closely matching the requested position
|
1209
1090
|
if (!frag) {
|
1210
|
-
const end =
|
1091
|
+
const end = config.lowLatencyMode
|
1211
1092
|
? levelDetails.partEnd
|
1212
1093
|
: levelDetails.fragmentEnd;
|
1213
1094
|
frag = this.getFragmentAtPosition(pos, end, levelDetails);
|
@@ -1390,7 +1271,7 @@ export default class BaseStreamController
|
|
1390
1271
|
const partList = levelDetails.partList;
|
1391
1272
|
|
1392
1273
|
const loadingParts = !!(
|
1393
|
-
|
1274
|
+
config.lowLatencyMode &&
|
1394
1275
|
partList?.length &&
|
1395
1276
|
fragmentHint
|
1396
1277
|
);
|
@@ -1669,7 +1550,7 @@ export default class BaseStreamController
|
|
1669
1550
|
errorAction.resolved = true;
|
1670
1551
|
}
|
1671
1552
|
} else {
|
1672
|
-
|
1553
|
+
logger.warn(
|
1673
1554
|
`${data.details} reached or exceeded max retry (${retryCount})`,
|
1674
1555
|
);
|
1675
1556
|
return;
|
@@ -1758,9 +1639,7 @@ export default class BaseStreamController
|
|
1758
1639
|
this.log('Reset loading state');
|
1759
1640
|
this.fragCurrent = null;
|
1760
1641
|
this.fragPrevious = null;
|
1761
|
-
|
1762
|
-
this.state = State.IDLE;
|
1763
|
-
}
|
1642
|
+
this.state = State.IDLE;
|
1764
1643
|
}
|
1765
1644
|
|
1766
1645
|
protected resetStartWhenNotLoaded(level: Level | null): void {
|