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.
Files changed (72) hide show
  1. package/README.md +1 -2
  2. package/dist/hls-demo.js +0 -10
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +1283 -2293
  5. package/dist/hls.js.d.ts +84 -97
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1030 -1435
  8. package/dist/hls.light.js.map +1 -1
  9. package/dist/hls.light.min.js +1 -1
  10. package/dist/hls.light.min.js.map +1 -1
  11. package/dist/hls.light.mjs +809 -1209
  12. package/dist/hls.light.mjs.map +1 -1
  13. package/dist/hls.min.js +1 -1
  14. package/dist/hls.min.js.map +1 -1
  15. package/dist/hls.mjs +1039 -2030
  16. package/dist/hls.mjs.map +1 -1
  17. package/dist/hls.worker.js +1 -1
  18. package/dist/hls.worker.js.map +1 -1
  19. package/package.json +22 -22
  20. package/src/config.ts +2 -3
  21. package/src/controller/abr-controller.ts +20 -24
  22. package/src/controller/audio-stream-controller.ts +74 -68
  23. package/src/controller/audio-track-controller.ts +1 -1
  24. package/src/controller/base-playlist-controller.ts +8 -20
  25. package/src/controller/base-stream-controller.ts +36 -157
  26. package/src/controller/buffer-controller.ts +99 -226
  27. package/src/controller/buffer-operation-queue.ts +19 -16
  28. package/src/controller/cap-level-controller.ts +2 -2
  29. package/src/controller/cmcd-controller.ts +6 -27
  30. package/src/controller/content-steering-controller.ts +6 -8
  31. package/src/controller/eme-controller.ts +22 -9
  32. package/src/controller/error-controller.ts +8 -6
  33. package/src/controller/fps-controller.ts +3 -2
  34. package/src/controller/fragment-tracker.ts +11 -15
  35. package/src/controller/gap-controller.ts +16 -43
  36. package/src/controller/latency-controller.ts +11 -9
  37. package/src/controller/level-controller.ts +18 -12
  38. package/src/controller/stream-controller.ts +31 -36
  39. package/src/controller/subtitle-stream-controller.ts +40 -28
  40. package/src/controller/subtitle-track-controller.ts +3 -5
  41. package/src/controller/timeline-controller.ts +30 -23
  42. package/src/crypt/aes-crypto.ts +2 -21
  43. package/src/crypt/decrypter.ts +18 -32
  44. package/src/crypt/fast-aes-key.ts +5 -24
  45. package/src/demux/audio/adts.ts +4 -9
  46. package/src/demux/sample-aes.ts +0 -2
  47. package/src/demux/transmuxer-interface.ts +12 -4
  48. package/src/demux/transmuxer-worker.ts +4 -4
  49. package/src/demux/transmuxer.ts +3 -16
  50. package/src/demux/tsdemuxer.ts +37 -71
  51. package/src/demux/video/avc-video-parser.ts +119 -208
  52. package/src/demux/video/base-video-parser.ts +2 -134
  53. package/src/demux/video/exp-golomb.ts +208 -0
  54. package/src/events.ts +0 -7
  55. package/src/hls.ts +37 -49
  56. package/src/loader/fragment-loader.ts +2 -9
  57. package/src/loader/key-loader.ts +0 -2
  58. package/src/loader/level-key.ts +9 -10
  59. package/src/loader/playlist-loader.ts +5 -4
  60. package/src/remux/mp4-generator.ts +1 -196
  61. package/src/remux/mp4-remuxer.ts +7 -23
  62. package/src/task-loop.ts +2 -5
  63. package/src/types/component-api.ts +0 -2
  64. package/src/types/demuxer.ts +0 -3
  65. package/src/types/events.ts +0 -4
  66. package/src/utils/buffer-helper.ts +31 -12
  67. package/src/utils/codecs.ts +5 -34
  68. package/src/utils/logger.ts +24 -54
  69. package/src/utils/mp4-tools.ts +2 -4
  70. package/src/crypt/decrypter-aes-mode.ts +0 -4
  71. package/src/demux/video/hevc-video-parser.ts +0 -746
  72. 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 buffering: boolean = true;
104
- private loadingParts: boolean = false;
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(logPrefix, hls.logger);
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
- media.addEventListener('seeking', this.onMediaSeeking);
222
- media.addEventListener('ended', this.onMediaEnded);
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.onMediaSeeking);
239
- media.removeEventListener('ended', this.onMediaEnded);
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 onManifestLoading() {}
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
- if (this.playlistType === PlaylistLevelType.MAIN) {
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 = this.onMediaSeeking = this.onMediaEnded = null;
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
- isFullSegmentEncryption(decryptData.method)
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.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'
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.loadingParts && frag.sn !== 'initSegment') {
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.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'
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.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${
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: number;
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
- const canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
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 = this.loadingParts
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
- this.loadingParts &&
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
- this.warn(
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
- if (this.state !== State.STOPPED) {
1762
- this.state = State.IDLE;
1763
- }
1642
+ this.state = State.IDLE;
1764
1643
  }
1765
1644
 
1766
1645
  protected resetStartWhenNotLoaded(level: Level | null): void {