hls.js 1.5.8-0.canary.10170 → 1.5.8

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 (87) hide show
  1. package/README.md +3 -4
  2. package/dist/hls-demo.js +3 -12
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +2366 -3626
  5. package/dist/hls.js.d.ts +84 -98
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1643 -2278
  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 +1258 -1903
  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 +1531 -2794
  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 +30 -30
  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 +10 -27
  25. package/src/controller/base-stream-controller.ts +38 -160
  26. package/src/controller/buffer-controller.ts +92 -230
  27. package/src/controller/buffer-operation-queue.ts +19 -16
  28. package/src/controller/cap-level-controller.ts +2 -3
  29. package/src/controller/cmcd-controller.ts +9 -30
  30. package/src/controller/content-steering-controller.ts +6 -8
  31. package/src/controller/eme-controller.ts +23 -10
  32. package/src/controller/error-controller.ts +8 -6
  33. package/src/controller/fps-controller.ts +3 -8
  34. package/src/controller/fragment-tracker.ts +11 -15
  35. package/src/controller/gap-controller.ts +16 -43
  36. package/src/controller/id3-track-controller.ts +7 -7
  37. package/src/controller/latency-controller.ts +11 -9
  38. package/src/controller/level-controller.ts +19 -13
  39. package/src/controller/stream-controller.ts +32 -37
  40. package/src/controller/subtitle-stream-controller.ts +40 -28
  41. package/src/controller/subtitle-track-controller.ts +3 -5
  42. package/src/controller/timeline-controller.ts +31 -25
  43. package/src/crypt/aes-crypto.ts +2 -21
  44. package/src/crypt/decrypter.ts +18 -32
  45. package/src/crypt/fast-aes-key.ts +5 -24
  46. package/src/demux/audio/aacdemuxer.ts +2 -2
  47. package/src/demux/audio/ac3-demuxer.ts +3 -4
  48. package/src/demux/audio/adts.ts +4 -9
  49. package/src/demux/audio/base-audio-demuxer.ts +14 -16
  50. package/src/demux/audio/mp3demuxer.ts +3 -4
  51. package/src/demux/audio/mpegaudio.ts +1 -1
  52. package/src/demux/id3.ts +411 -0
  53. package/src/demux/mp4demuxer.ts +7 -7
  54. package/src/demux/sample-aes.ts +0 -2
  55. package/src/demux/transmuxer-interface.ts +12 -4
  56. package/src/demux/transmuxer-worker.ts +4 -4
  57. package/src/demux/transmuxer.ts +3 -16
  58. package/src/demux/tsdemuxer.ts +37 -71
  59. package/src/demux/video/avc-video-parser.ts +119 -208
  60. package/src/demux/video/base-video-parser.ts +2 -134
  61. package/src/demux/video/exp-golomb.ts +208 -0
  62. package/src/events.ts +1 -8
  63. package/src/exports-named.ts +1 -1
  64. package/src/hls.ts +37 -49
  65. package/src/loader/fragment-loader.ts +3 -10
  66. package/src/loader/key-loader.ts +1 -3
  67. package/src/loader/level-key.ts +9 -10
  68. package/src/loader/playlist-loader.ts +5 -4
  69. package/src/remux/mp4-generator.ts +1 -196
  70. package/src/remux/mp4-remuxer.ts +8 -24
  71. package/src/task-loop.ts +2 -5
  72. package/src/types/component-api.ts +1 -3
  73. package/src/types/demuxer.ts +0 -3
  74. package/src/types/events.ts +0 -4
  75. package/src/types/remuxer.ts +1 -1
  76. package/src/utils/buffer-helper.ts +31 -12
  77. package/src/utils/codecs.ts +5 -34
  78. package/src/utils/fetch-loader.ts +1 -1
  79. package/src/utils/imsc1-ttml-parser.ts +1 -1
  80. package/src/utils/keysystem-util.ts +6 -1
  81. package/src/utils/logger.ts +23 -58
  82. package/src/utils/mp4-tools.ts +3 -5
  83. package/src/utils/webvtt-parser.ts +1 -1
  84. package/src/crypt/decrypter-aes-mode.ts +0 -4
  85. package/src/demux/video/hevc-video-parser.ts +0 -749
  86. package/src/utils/encryption-methods-util.ts +0 -21
  87. package/src/utils/utf8-utils.ts +0 -18
@@ -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,
@@ -350,10 +308,11 @@ export default class BaseStreamController
350
308
  }
351
309
 
352
310
  protected onHandlerDestroying() {
311
+ this.hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
353
312
  this.stopLoad();
354
313
  super.onHandlerDestroying();
355
314
  // @ts-ignore
356
- this.hls = this.onMediaSeeking = this.onMediaEnded = null;
315
+ this.hls = null;
357
316
  }
358
317
 
359
318
  protected onHandlerDestroyed() {
@@ -527,7 +486,7 @@ export default class BaseStreamController
527
486
  payload.byteLength > 0 &&
528
487
  decryptData?.key &&
529
488
  decryptData.iv &&
530
- isFullSegmentEncryption(decryptData.method)
489
+ decryptData.method === 'AES-128'
531
490
  ) {
532
491
  const startTime = self.performance.now();
533
492
  // decrypt init segment data
@@ -536,7 +495,6 @@ export default class BaseStreamController
536
495
  new Uint8Array(payload),
537
496
  decryptData.key.buffer,
538
497
  decryptData.iv.buffer,
539
- getAesModeFromFullSegmentMethod(decryptData.method),
540
498
  )
541
499
  .catch((err) => {
542
500
  hls.trigger(Events.ERROR, {
@@ -580,9 +538,7 @@ export default class BaseStreamController
580
538
  throw new Error('init load aborted, missing levels');
581
539
  }
582
540
  const stats = data.frag.stats;
583
- if (this.state !== State.STOPPED) {
584
- this.state = State.IDLE;
585
- }
541
+ this.state = State.IDLE;
586
542
  data.frag.data = new Uint8Array(data.payload);
587
543
  stats.parsing.start = stats.buffering.start = self.performance.now();
588
544
  stats.parsing.end = stats.buffering.end = self.performance.now();
@@ -693,7 +649,7 @@ export default class BaseStreamController
693
649
  if (frag.encrypted && !frag.decryptdata?.key) {
694
650
  this.log(
695
651
  `Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${
696
- this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'
652
+ this.logPrefix === '[stream-controller]' ? 'level' : 'track'
697
653
  } ${frag.level}`,
698
654
  );
699
655
  this.state = State.KEY_LOADING;
@@ -717,23 +673,8 @@ export default class BaseStreamController
717
673
  this.keyLoader.loadClear(frag, details.encryptedFragments);
718
674
  }
719
675
 
720
- const fragPrevious = this.fragPrevious;
721
- if (
722
- frag.sn !== 'initSegment' &&
723
- (!fragPrevious || frag.sn !== fragPrevious.sn)
724
- ) {
725
- const shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
726
- if (shouldLoadParts !== this.loadingParts) {
727
- this.log(
728
- `LL-Part loading ${
729
- shouldLoadParts ? 'ON' : 'OFF'
730
- } loading sn ${fragPrevious?.sn}->${frag.sn}`,
731
- );
732
- this.loadingParts = shouldLoadParts;
733
- }
734
- }
735
676
  targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
736
- if (this.loadingParts && frag.sn !== 'initSegment') {
677
+ if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
737
678
  const partList = details.partList;
738
679
  if (partList && progressCallback) {
739
680
  if (targetBufferTime > frag.end && details.fragmentHint) {
@@ -748,7 +689,7 @@ export default class BaseStreamController
748
689
  } of playlist [${details.startSN}-${
749
690
  details.endSN
750
691
  }] parts [0-${partIndex}-${partList.length - 1}] ${
751
- this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'
692
+ this.logPrefix === '[stream-controller]' ? 'level' : 'track'
752
693
  }: ${frag.level}, target: ${parseFloat(
753
694
  targetBufferTime.toFixed(3),
754
695
  )}`,
@@ -804,22 +745,10 @@ export default class BaseStreamController
804
745
  }
805
746
  }
806
747
 
807
- if (frag.sn !== 'initSegment' && this.loadingParts) {
808
- this.log(
809
- `LL-Part loading OFF after next part miss @${targetBufferTime.toFixed(
810
- 2,
811
- )}`,
812
- );
813
- this.loadingParts = false;
814
- } else if (!frag.url) {
815
- // Selected fragment hint for part but not loading parts
816
- return Promise.resolve(null);
817
- }
818
-
819
748
  this.log(
820
749
  `Loading fragment ${frag.sn} cc: ${frag.cc} ${
821
750
  details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''
822
- }${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${
751
+ }${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${
823
752
  frag.level
824
753
  }, target: ${parseFloat(targetBufferTime.toFixed(3))}`,
825
754
  );
@@ -943,50 +872,9 @@ export default class BaseStreamController
943
872
  if (part) {
944
873
  part.stats.parsing.end = now;
945
874
  }
946
- // See if part loading should be disabled/enabled based on buffer and playback position.
947
- if (frag.sn !== 'initSegment') {
948
- const levelDetails = this.getLevelDetails();
949
- const loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
950
- const shouldLoadParts =
951
- loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
952
- if (shouldLoadParts !== this.loadingParts) {
953
- this.log(
954
- `LL-Part loading ${
955
- shouldLoadParts ? 'ON' : 'OFF'
956
- } after parsing segment ending @${frag.end.toFixed(2)}`,
957
- );
958
- this.loadingParts = shouldLoadParts;
959
- }
960
- }
961
-
962
875
  this.updateLevelTiming(frag, part, level, chunkMeta.partial);
963
876
  }
964
877
 
965
- private shouldLoadParts(
966
- details: LevelDetails | undefined,
967
- bufferEnd: number,
968
- ): boolean {
969
- if (this.config.lowLatencyMode) {
970
- if (!details) {
971
- return this.loadingParts;
972
- }
973
- if (details?.partList) {
974
- // Buffer must be ahead of first part + duration of parts after last segment
975
- // and playback must be at or past segment adjacent to part list
976
- const firstPart = details.partList[0];
977
- const safePartStart =
978
- firstPart.end + (details.fragmentHint?.duration || 0);
979
- if (
980
- bufferEnd >= safePartStart &&
981
- this.lastCurrentTime > firstPart.start - firstPart.fragment.duration
982
- ) {
983
- return true;
984
- }
985
- }
986
- }
987
- return false;
988
- }
989
-
990
878
  protected getCurrentContext(
991
879
  chunkMeta: ChunkMetadata,
992
880
  ): { frag: Fragment; part: Part | null; level: Level } | null {
@@ -1103,10 +991,7 @@ export default class BaseStreamController
1103
991
  // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
1104
992
  if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
1105
993
  const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
1106
- if (
1107
- bufferedFragAtPos &&
1108
- (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)
1109
- ) {
994
+ if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {
1110
995
  return BufferHelper.bufferInfo(
1111
996
  bufferable,
1112
997
  pos,
@@ -1119,7 +1004,7 @@ export default class BaseStreamController
1119
1004
 
1120
1005
  protected getMaxBufferLength(levelBitrate?: number): number {
1121
1006
  const { config } = this;
1122
- let maxBufLen: number;
1007
+ let maxBufLen;
1123
1008
  if (levelBitrate) {
1124
1009
  maxBufLen = Math.max(
1125
1010
  (8 * config.maxBufferSize) / levelBitrate,
@@ -1171,8 +1056,7 @@ export default class BaseStreamController
1171
1056
  // find fragment index, contiguous with end of buffer position
1172
1057
  const { config } = this;
1173
1058
  const start = fragments[0].start;
1174
- const canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
1175
- let frag: Fragment | null = null;
1059
+ let frag;
1176
1060
 
1177
1061
  if (levelDetails.live) {
1178
1062
  const initialLiveManifestSize = config.initialLiveManifestSize;
@@ -1192,10 +1076,6 @@ export default class BaseStreamController
1192
1076
  this.startPosition === -1) ||
1193
1077
  pos < start
1194
1078
  ) {
1195
- if (canLoadParts && !this.loadingParts) {
1196
- this.log(`LL-Part loading ON for initial live fragment`);
1197
- this.loadingParts = true;
1198
- }
1199
1079
  frag = this.getInitialLiveFragment(levelDetails, fragments);
1200
1080
  this.startPosition = this.nextLoadPosition = frag
1201
1081
  ? this.hls.liveSyncPosition || frag.start
@@ -1208,7 +1088,7 @@ export default class BaseStreamController
1208
1088
 
1209
1089
  // If we haven't run into any special cases already, just load the fragment most closely matching the requested position
1210
1090
  if (!frag) {
1211
- const end = this.loadingParts
1091
+ const end = config.lowLatencyMode
1212
1092
  ? levelDetails.partEnd
1213
1093
  : levelDetails.fragmentEnd;
1214
1094
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
@@ -1391,7 +1271,7 @@ export default class BaseStreamController
1391
1271
  const partList = levelDetails.partList;
1392
1272
 
1393
1273
  const loadingParts = !!(
1394
- this.loadingParts &&
1274
+ config.lowLatencyMode &&
1395
1275
  partList?.length &&
1396
1276
  fragmentHint
1397
1277
  );
@@ -1670,7 +1550,7 @@ export default class BaseStreamController
1670
1550
  errorAction.resolved = true;
1671
1551
  }
1672
1552
  } else {
1673
- this.warn(
1553
+ logger.warn(
1674
1554
  `${data.details} reached or exceeded max retry (${retryCount})`,
1675
1555
  );
1676
1556
  return;
@@ -1759,9 +1639,7 @@ export default class BaseStreamController
1759
1639
  this.log('Reset loading state');
1760
1640
  this.fragCurrent = null;
1761
1641
  this.fragPrevious = null;
1762
- if (this.state !== State.STOPPED) {
1763
- this.state = State.IDLE;
1764
- }
1642
+ this.state = State.IDLE;
1765
1643
  }
1766
1644
 
1767
1645
  protected resetStartWhenNotLoaded(level: Level | null): void {