hls.js 1.5.9-0.canary.10310 → 1.5.9

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 (89) hide show
  1. package/README.md +3 -4
  2. package/dist/hls-demo.js +38 -41
  3. package/dist/hls-demo.js.map +1 -1
  4. package/dist/hls.js +2191 -3474
  5. package/dist/hls.js.d.ts +85 -108
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +3136 -3783
  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 +1260 -1934
  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 +4182 -5488
  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 +35 -35
  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 +14 -51
  30. package/src/controller/content-steering-controller.ts +15 -29
  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 -37
  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 +21 -19
  43. package/src/crypt/aes-crypto.ts +2 -21
  44. package/src/crypt/decrypter.ts +16 -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 +18 -147
  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 +38 -61
  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 -4
  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/cea-608-parser.ts +3 -1
  78. package/src/utils/codecs.ts +5 -34
  79. package/src/utils/fetch-loader.ts +1 -1
  80. package/src/utils/imsc1-ttml-parser.ts +1 -1
  81. package/src/utils/keysystem-util.ts +6 -1
  82. package/src/utils/logger.ts +23 -58
  83. package/src/utils/mp4-tools.ts +3 -5
  84. package/src/utils/webvtt-parser.ts +1 -1
  85. package/src/crypt/decrypter-aes-mode.ts +0 -4
  86. package/src/demux/video/hevc-video-parser.ts +0 -749
  87. package/src/empty-es.js +0 -5
  88. package/src/utils/encryption-methods-util.ts +0 -21
  89. 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() {
@@ -537,7 +496,7 @@ export default class BaseStreamController
537
496
  payload.byteLength > 0 &&
538
497
  decryptData?.key &&
539
498
  decryptData.iv &&
540
- isFullSegmentEncryption(decryptData.method)
499
+ decryptData.method === 'AES-128'
541
500
  ) {
542
501
  const startTime = self.performance.now();
543
502
  // decrypt init segment data
@@ -546,7 +505,6 @@ export default class BaseStreamController
546
505
  new Uint8Array(payload),
547
506
  decryptData.key.buffer,
548
507
  decryptData.iv.buffer,
549
- getAesModeFromFullSegmentMethod(decryptData.method),
550
508
  )
551
509
  .catch((err) => {
552
510
  hls.trigger(Events.ERROR, {
@@ -590,9 +548,7 @@ export default class BaseStreamController
590
548
  throw new Error('init load aborted, missing levels');
591
549
  }
592
550
  const stats = data.frag.stats;
593
- if (this.state !== State.STOPPED) {
594
- this.state = State.IDLE;
595
- }
551
+ this.state = State.IDLE;
596
552
  data.frag.data = new Uint8Array(data.payload);
597
553
  stats.parsing.start = stats.buffering.start = self.performance.now();
598
554
  stats.parsing.end = stats.buffering.end = self.performance.now();
@@ -703,7 +659,7 @@ export default class BaseStreamController
703
659
  if (frag.encrypted && !frag.decryptdata?.key) {
704
660
  this.log(
705
661
  `Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${
706
- this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'
662
+ this.logPrefix === '[stream-controller]' ? 'level' : 'track'
707
663
  } ${frag.level}`,
708
664
  );
709
665
  this.state = State.KEY_LOADING;
@@ -727,23 +683,8 @@ export default class BaseStreamController
727
683
  this.keyLoader.loadClear(frag, details.encryptedFragments);
728
684
  }
729
685
 
730
- const fragPrevious = this.fragPrevious;
731
- if (
732
- frag.sn !== 'initSegment' &&
733
- (!fragPrevious || frag.sn !== fragPrevious.sn)
734
- ) {
735
- const shouldLoadParts = this.shouldLoadParts(level.details, frag.end);
736
- if (shouldLoadParts !== this.loadingParts) {
737
- this.log(
738
- `LL-Part loading ${
739
- shouldLoadParts ? 'ON' : 'OFF'
740
- } loading sn ${fragPrevious?.sn}->${frag.sn}`,
741
- );
742
- this.loadingParts = shouldLoadParts;
743
- }
744
- }
745
686
  targetBufferTime = Math.max(frag.start, targetBufferTime || 0);
746
- if (this.loadingParts && frag.sn !== 'initSegment') {
687
+ if (this.config.lowLatencyMode && frag.sn !== 'initSegment') {
747
688
  const partList = details.partList;
748
689
  if (partList && progressCallback) {
749
690
  if (targetBufferTime > frag.end && details.fragmentHint) {
@@ -758,7 +699,7 @@ export default class BaseStreamController
758
699
  } of playlist [${details.startSN}-${
759
700
  details.endSN
760
701
  }] parts [0-${partIndex}-${partList.length - 1}] ${
761
- this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'
702
+ this.logPrefix === '[stream-controller]' ? 'level' : 'track'
762
703
  }: ${frag.level}, target: ${parseFloat(
763
704
  targetBufferTime.toFixed(3),
764
705
  )}`,
@@ -814,22 +755,10 @@ export default class BaseStreamController
814
755
  }
815
756
  }
816
757
 
817
- if (frag.sn !== 'initSegment' && this.loadingParts) {
818
- this.log(
819
- `LL-Part loading OFF after next part miss @${targetBufferTime.toFixed(
820
- 2,
821
- )}`,
822
- );
823
- this.loadingParts = false;
824
- } else if (!frag.url) {
825
- // Selected fragment hint for part but not loading parts
826
- return Promise.resolve(null);
827
- }
828
-
829
758
  this.log(
830
759
  `Loading fragment ${frag.sn} cc: ${frag.cc} ${
831
760
  details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''
832
- }${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${
761
+ }${this.logPrefix === '[stream-controller]' ? 'level' : 'track'}: ${
833
762
  frag.level
834
763
  }, target: ${parseFloat(targetBufferTime.toFixed(3))}`,
835
764
  );
@@ -953,50 +882,9 @@ export default class BaseStreamController
953
882
  if (part) {
954
883
  part.stats.parsing.end = now;
955
884
  }
956
- // See if part loading should be disabled/enabled based on buffer and playback position.
957
- if (frag.sn !== 'initSegment') {
958
- const levelDetails = this.getLevelDetails();
959
- const loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN;
960
- const shouldLoadParts =
961
- loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end);
962
- if (shouldLoadParts !== this.loadingParts) {
963
- this.log(
964
- `LL-Part loading ${
965
- shouldLoadParts ? 'ON' : 'OFF'
966
- } after parsing segment ending @${frag.end.toFixed(2)}`,
967
- );
968
- this.loadingParts = shouldLoadParts;
969
- }
970
- }
971
-
972
885
  this.updateLevelTiming(frag, part, level, chunkMeta.partial);
973
886
  }
974
887
 
975
- private shouldLoadParts(
976
- details: LevelDetails | undefined,
977
- bufferEnd: number,
978
- ): boolean {
979
- if (this.config.lowLatencyMode) {
980
- if (!details) {
981
- return this.loadingParts;
982
- }
983
- if (details?.partList) {
984
- // Buffer must be ahead of first part + duration of parts after last segment
985
- // and playback must be at or past segment adjacent to part list
986
- const firstPart = details.partList[0];
987
- const safePartStart =
988
- firstPart.end + (details.fragmentHint?.duration || 0);
989
- if (
990
- bufferEnd >= safePartStart &&
991
- this.lastCurrentTime > firstPart.start - firstPart.fragment.duration
992
- ) {
993
- return true;
994
- }
995
- }
996
- }
997
- return false;
998
- }
999
-
1000
888
  protected getCurrentContext(
1001
889
  chunkMeta: ChunkMetadata,
1002
890
  ): { frag: Fragment; part: Part | null; level: Level } | null {
@@ -1113,10 +1001,7 @@ export default class BaseStreamController
1113
1001
  // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos
1114
1002
  if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) {
1115
1003
  const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type);
1116
- if (
1117
- bufferedFragAtPos &&
1118
- (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)
1119
- ) {
1004
+ if (bufferedFragAtPos && bufferInfo.nextStart < bufferedFragAtPos.end) {
1120
1005
  return BufferHelper.bufferInfo(
1121
1006
  bufferable,
1122
1007
  pos,
@@ -1129,7 +1014,7 @@ export default class BaseStreamController
1129
1014
 
1130
1015
  protected getMaxBufferLength(levelBitrate?: number): number {
1131
1016
  const { config } = this;
1132
- let maxBufLen: number;
1017
+ let maxBufLen;
1133
1018
  if (levelBitrate) {
1134
1019
  maxBufLen = Math.max(
1135
1020
  (8 * config.maxBufferSize) / levelBitrate,
@@ -1182,8 +1067,7 @@ export default class BaseStreamController
1182
1067
  // find fragment index, contiguous with end of buffer position
1183
1068
  const { config } = this;
1184
1069
  const start = fragments[0].start;
1185
- const canLoadParts = config.lowLatencyMode && !!levelDetails.partList;
1186
- let frag: Fragment | null = null;
1070
+ let frag;
1187
1071
 
1188
1072
  if (levelDetails.live) {
1189
1073
  const initialLiveManifestSize = config.initialLiveManifestSize;
@@ -1203,10 +1087,6 @@ export default class BaseStreamController
1203
1087
  this.startPosition === -1) ||
1204
1088
  pos < start
1205
1089
  ) {
1206
- if (canLoadParts && !this.loadingParts) {
1207
- this.log(`LL-Part loading ON for initial live fragment`);
1208
- this.loadingParts = true;
1209
- }
1210
1090
  frag = this.getInitialLiveFragment(levelDetails, fragments);
1211
1091
  this.startPosition = this.nextLoadPosition = frag
1212
1092
  ? this.hls.liveSyncPosition || frag.start
@@ -1219,7 +1099,7 @@ export default class BaseStreamController
1219
1099
 
1220
1100
  // If we haven't run into any special cases already, just load the fragment most closely matching the requested position
1221
1101
  if (!frag) {
1222
- const end = this.loadingParts
1102
+ const end = config.lowLatencyMode
1223
1103
  ? levelDetails.partEnd
1224
1104
  : levelDetails.fragmentEnd;
1225
1105
  frag = this.getFragmentAtPosition(pos, end, levelDetails);
@@ -1402,7 +1282,7 @@ export default class BaseStreamController
1402
1282
  const partList = levelDetails.partList;
1403
1283
 
1404
1284
  const loadingParts = !!(
1405
- this.loadingParts &&
1285
+ config.lowLatencyMode &&
1406
1286
  partList?.length &&
1407
1287
  fragmentHint
1408
1288
  );
@@ -1681,7 +1561,7 @@ export default class BaseStreamController
1681
1561
  errorAction.resolved = true;
1682
1562
  }
1683
1563
  } else {
1684
- this.warn(
1564
+ logger.warn(
1685
1565
  `${data.details} reached or exceeded max retry (${retryCount})`,
1686
1566
  );
1687
1567
  return;
@@ -1770,9 +1650,7 @@ export default class BaseStreamController
1770
1650
  this.log('Reset loading state');
1771
1651
  this.fragCurrent = null;
1772
1652
  this.fragPrevious = null;
1773
- if (this.state !== State.STOPPED) {
1774
- this.state = State.IDLE;
1775
- }
1653
+ this.state = State.IDLE;
1776
1654
  }
1777
1655
 
1778
1656
  protected resetStartWhenNotLoaded(level: Level | null): void {