hls.js 1.5.13-0.canary.10411 → 1.5.13

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 (103) 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 +2702 -4247
  5. package/dist/hls.js.d.ts +110 -179
  6. package/dist/hls.js.map +1 -1
  7. package/dist/hls.light.js +1994 -2914
  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 +3458 -4388
  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 +4504 -6059
  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 +38 -38
  20. package/src/config.ts +2 -5
  21. package/src/controller/abr-controller.ts +25 -39
  22. package/src/controller/audio-stream-controller.ts +136 -156
  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 +91 -235
  26. package/src/controller/buffer-controller.ts +97 -250
  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 +22 -28
  33. package/src/controller/fps-controller.ts +3 -8
  34. package/src/controller/fragment-finders.ts +16 -44
  35. package/src/controller/fragment-tracker.ts +25 -58
  36. package/src/controller/gap-controller.ts +16 -43
  37. package/src/controller/id3-track-controller.ts +35 -45
  38. package/src/controller/latency-controller.ts +13 -18
  39. package/src/controller/level-controller.ts +19 -37
  40. package/src/controller/stream-controller.ts +83 -100
  41. package/src/controller/subtitle-stream-controller.ts +47 -35
  42. package/src/controller/subtitle-track-controller.ts +3 -5
  43. package/src/controller/timeline-controller.ts +22 -20
  44. package/src/crypt/aes-crypto.ts +2 -21
  45. package/src/crypt/decrypter.ts +16 -32
  46. package/src/crypt/fast-aes-key.ts +5 -28
  47. package/src/demux/audio/aacdemuxer.ts +2 -2
  48. package/src/demux/audio/ac3-demuxer.ts +3 -4
  49. package/src/demux/audio/adts.ts +4 -9
  50. package/src/demux/audio/base-audio-demuxer.ts +14 -16
  51. package/src/demux/audio/mp3demuxer.ts +3 -4
  52. package/src/demux/audio/mpegaudio.ts +1 -1
  53. package/src/demux/id3.ts +411 -0
  54. package/src/demux/mp4demuxer.ts +7 -7
  55. package/src/demux/sample-aes.ts +0 -2
  56. package/src/demux/transmuxer-interface.ts +16 -8
  57. package/src/demux/transmuxer-worker.ts +4 -4
  58. package/src/demux/transmuxer.ts +3 -16
  59. package/src/demux/tsdemuxer.ts +38 -75
  60. package/src/demux/video/avc-video-parser.ts +121 -210
  61. package/src/demux/video/base-video-parser.ts +2 -135
  62. package/src/demux/video/exp-golomb.ts +208 -0
  63. package/src/events.ts +1 -8
  64. package/src/exports-named.ts +1 -1
  65. package/src/hls.ts +47 -84
  66. package/src/loader/date-range.ts +5 -71
  67. package/src/loader/fragment-loader.ts +21 -23
  68. package/src/loader/fragment.ts +4 -8
  69. package/src/loader/key-loader.ts +1 -3
  70. package/src/loader/level-details.ts +6 -6
  71. package/src/loader/level-key.ts +9 -10
  72. package/src/loader/m3u8-parser.ts +144 -138
  73. package/src/loader/playlist-loader.ts +7 -5
  74. package/src/remux/mp4-generator.ts +1 -196
  75. package/src/remux/mp4-remuxer.ts +62 -32
  76. package/src/remux/passthrough-remuxer.ts +1 -1
  77. package/src/task-loop.ts +2 -5
  78. package/src/types/component-api.ts +1 -3
  79. package/src/types/demuxer.ts +0 -3
  80. package/src/types/events.ts +6 -19
  81. package/src/types/fragment-tracker.ts +2 -2
  82. package/src/types/general.ts +6 -0
  83. package/src/types/media-playlist.ts +1 -9
  84. package/src/types/remuxer.ts +1 -1
  85. package/src/utils/attr-list.ts +9 -96
  86. package/src/utils/buffer-helper.ts +31 -12
  87. package/src/utils/cea-608-parser.ts +3 -1
  88. package/src/utils/codecs.ts +5 -34
  89. package/src/utils/fetch-loader.ts +1 -1
  90. package/src/utils/hdr.ts +7 -4
  91. package/src/utils/imsc1-ttml-parser.ts +1 -1
  92. package/src/utils/keysystem-util.ts +6 -1
  93. package/src/utils/level-helper.ts +44 -71
  94. package/src/utils/logger.ts +23 -58
  95. package/src/utils/mp4-tools.ts +3 -5
  96. package/src/utils/rendition-helper.ts +74 -100
  97. package/src/utils/variable-substitution.ts +19 -0
  98. package/src/utils/webvtt-parser.ts +12 -2
  99. package/src/crypt/decrypter-aes-mode.ts +0 -4
  100. package/src/demux/video/hevc-video-parser.ts +0 -749
  101. package/src/utils/encryption-methods-util.ts +0 -21
  102. package/src/utils/hash.ts +0 -10
  103. package/src/utils/utf8-utils.ts +0 -18
@@ -11,25 +11,6 @@ export interface ILogger {
11
11
  error: ILogFunction;
12
12
  }
13
13
 
14
- export class Logger implements ILogger {
15
- trace: ILogFunction;
16
- debug: ILogFunction;
17
- log: ILogFunction;
18
- warn: ILogFunction;
19
- info: ILogFunction;
20
- error: ILogFunction;
21
-
22
- constructor(label: string, logger: ILogger) {
23
- const lb = `[${label}]:`;
24
- this.trace = noop;
25
- this.debug = logger.debug.bind(null, lb);
26
- this.log = logger.log.bind(null, lb);
27
- this.warn = logger.warn.bind(null, lb);
28
- this.info = logger.info.bind(null, lb);
29
- this.error = logger.error.bind(null, lb);
30
- }
31
- }
32
-
33
14
  const noop: ILogFunction = function () {};
34
15
 
35
16
  const fakeLogger: ILogger = {
@@ -41,9 +22,7 @@ const fakeLogger: ILogger = {
41
22
  error: noop,
42
23
  };
43
24
 
44
- function createLogger() {
45
- return Object.assign({}, fakeLogger);
46
- }
25
+ let exportedLogger: ILogger = fakeLogger;
47
26
 
48
27
  // let lastCallTime;
49
28
  // function formatMsgWithTimeInfo(type, msg) {
@@ -54,37 +33,33 @@ function createLogger() {
54
33
  // return msg;
55
34
  // }
56
35
 
57
- function consolePrintFn(type: string, id: string | undefined): ILogFunction {
36
+ function consolePrintFn(type: string): ILogFunction {
58
37
  const func: ILogFunction = self.console[type];
59
- return func
60
- ? func.bind(self.console, `${id ? '[' + id + '] ' : ''}[${type}] >`)
61
- : noop;
38
+ if (func) {
39
+ return func.bind(self.console, `[${type}] >`);
40
+ }
41
+ return noop;
62
42
  }
63
43
 
64
- function getLoggerFn(
65
- key: string,
66
- debugConfig: boolean | Partial<ILogger>,
67
- id?: string,
68
- ): ILogFunction {
69
- return debugConfig[key]
70
- ? debugConfig[key].bind(debugConfig)
71
- : consolePrintFn(key, id);
44
+ function exportLoggerFunctions(
45
+ debugConfig: boolean | ILogger,
46
+ ...functions: string[]
47
+ ): void {
48
+ functions.forEach(function (type) {
49
+ exportedLogger[type] = debugConfig[type]
50
+ ? debugConfig[type].bind(debugConfig)
51
+ : consolePrintFn(type);
52
+ });
72
53
  }
73
54
 
74
- const exportedLogger: ILogger = createLogger();
75
-
76
- export function enableLogs(
77
- debugConfig: boolean | ILogger,
78
- context: string,
79
- id?: string | undefined,
80
- ): ILogger {
55
+ export function enableLogs(debugConfig: boolean | ILogger, id: string): void {
81
56
  // check that console is available
82
- const newLogger = createLogger();
83
57
  if (
84
58
  (typeof console === 'object' && debugConfig === true) ||
85
59
  typeof debugConfig === 'object'
86
60
  ) {
87
- const keys: (keyof ILogger)[] = [
61
+ exportLoggerFunctions(
62
+ debugConfig,
88
63
  // Remove out from list here to hard-disable a log-level
89
64
  // 'trace',
90
65
  'debug',
@@ -92,29 +67,19 @@ export function enableLogs(
92
67
  'info',
93
68
  'warn',
94
69
  'error',
95
- ];
96
- keys.forEach((key) => {
97
- newLogger[key] = getLoggerFn(key, debugConfig, id);
98
- });
70
+ );
99
71
  // Some browsers don't allow to use bind on console object anyway
100
72
  // fallback to default if needed
101
73
  try {
102
- newLogger.log(
103
- `Debug logs enabled for "${context}" in hls.js version ${__VERSION__}`,
74
+ exportedLogger.log(
75
+ `Debug logs enabled for "${id}" in hls.js version ${__VERSION__}`,
104
76
  );
105
77
  } catch (e) {
106
- /* log fn threw an exception. All logger methods are no-ops. */
107
- return createLogger();
78
+ exportedLogger = fakeLogger;
108
79
  }
109
- // global exported logger uses the same functions as new logger without `id`
110
- keys.forEach((key) => {
111
- exportedLogger[key] = getLoggerFn(key, debugConfig);
112
- });
113
80
  } else {
114
- // Reset global exported logger
115
- Object.assign(exportedLogger, newLogger);
81
+ exportedLogger = fakeLogger;
116
82
  }
117
- return newLogger;
118
83
  }
119
84
 
120
85
  export const logger: ILogger = exportedLogger;
@@ -1,6 +1,6 @@
1
1
  import { ElementaryStreamTypes } from '../loader/fragment';
2
2
  import { sliceUint8 } from './typed-array';
3
- import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
3
+ import { utf8ArrayToStr } from '../demux/id3';
4
4
  import { logger } from '../utils/logger';
5
5
  import Hex from './hex';
6
6
  import type { PassthroughTrack, UserdataSample } from '../types/demuxer';
@@ -327,7 +327,7 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
327
327
  case 'mp4a': {
328
328
  const codecBox = findBox(sampleEntries, [fourCC])[0];
329
329
  const esdsBox = findBox(codecBox.subarray(28), ['esds'])[0];
330
- if (esdsBox && esdsBox.length > 7) {
330
+ if (esdsBox && esdsBox.length > 12) {
331
331
  let i = 4;
332
332
  // ES Descriptor tag
333
333
  if (esdsBox[i++] !== 0x03) {
@@ -478,9 +478,7 @@ function parseStsd(stsd: Uint8Array): { codec: string; encrypted: boolean } {
478
478
 
479
479
  function skipBERInteger(bytes: Uint8Array, i: number): number {
480
480
  const limit = i + 5;
481
- while (bytes[i++] & 0x80 && i < limit) {
482
- /* do nothing */
483
- }
481
+ while (bytes[i++] & 0x80 && i < limit) {}
484
482
  return i;
485
483
  }
486
484
 
@@ -13,7 +13,6 @@ export type CodecSetTier = {
13
13
  minBitrate: number;
14
14
  minHeight: number;
15
15
  minFramerate: number;
16
- minIndex: number;
17
16
  maxScore: number;
18
17
  videoRanges: Record<string, number>;
19
18
  channels: Record<string, number>;
@@ -33,7 +32,6 @@ type StartParameters = {
33
32
  preferHDR: boolean;
34
33
  minFramerate: number;
35
34
  minBitrate: number;
36
- minIndex: number;
37
35
  };
38
36
 
39
37
  export function getStartCodecTier(
@@ -46,15 +44,13 @@ export function getStartCodecTier(
46
44
  const codecSets = Object.keys(codecTiers);
47
45
  const channelsPreference = audioPreference?.channels;
48
46
  const audioCodecPreference = audioPreference?.audioCodec;
49
- const videoCodecPreference = videoPreference?.videoCodec;
50
47
  const preferStereo = channelsPreference && parseInt(channelsPreference) === 2;
51
48
  // Use first level set to determine stereo, and minimum resolution and framerate
52
- let hasStereo = false;
49
+ let hasStereo = true;
53
50
  let hasCurrentVideoRange = false;
54
51
  let minHeight = Infinity;
55
52
  let minFramerate = Infinity;
56
53
  let minBitrate = Infinity;
57
- let minIndex = Infinity;
58
54
  let selectedScore = 0;
59
55
  let videoRanges: Array<VideoRange> = [];
60
56
 
@@ -65,7 +61,7 @@ export function getStartCodecTier(
65
61
 
66
62
  for (let i = codecSets.length; i--; ) {
67
63
  const tier = codecTiers[codecSets[i]];
68
- hasStereo ||= tier.channels[2] > 0;
64
+ hasStereo = tier.channels[2] > 0;
69
65
  minHeight = Math.min(minHeight, tier.minHeight);
70
66
  minFramerate = Math.min(minFramerate, tier.minFramerate);
71
67
  minBitrate = Math.min(minBitrate, tier.minBitrate);
@@ -74,6 +70,7 @@ export function getStartCodecTier(
74
70
  );
75
71
  if (matchingVideoRanges.length > 0) {
76
72
  hasCurrentVideoRange = true;
73
+ videoRanges = matchingVideoRanges;
77
74
  }
78
75
  }
79
76
  minHeight = Number.isFinite(minHeight) ? minHeight : 0;
@@ -85,8 +82,8 @@ export function getStartCodecTier(
85
82
  // If there are no variants with matching preference, set currentVideoRange to undefined
86
83
  if (!hasCurrentVideoRange) {
87
84
  currentVideoRange = undefined;
85
+ videoRanges = [];
88
86
  }
89
- const hasMultipleSets = codecSets.length > 1;
90
87
  const codecSet = codecSets.reduce(
91
88
  (selected: string | undefined, candidate: string) => {
92
89
  // Remove candiates which do not meet bitrate, default audio, stereo or channels preference, 1080p or lower, 30fps or lower, or SDR/HDR selection if present
@@ -94,99 +91,80 @@ export function getStartCodecTier(
94
91
  if (candidate === selected) {
95
92
  return selected;
96
93
  }
97
- videoRanges = hasCurrentVideoRange
98
- ? allowedVideoRanges.filter(
99
- (range) => candidateTier.videoRanges[range] > 0,
100
- )
101
- : [];
102
- if (hasMultipleSets) {
103
- if (candidateTier.minBitrate > currentBw) {
104
- logStartCodecCandidateIgnored(
105
- candidate,
106
- `min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`,
107
- );
108
- return selected;
109
- }
110
- if (!candidateTier.hasDefaultAudio) {
111
- logStartCodecCandidateIgnored(
112
- candidate,
113
- `no renditions with default or auto-select sound found`,
114
- );
115
- return selected;
116
- }
117
- if (
118
- audioCodecPreference &&
119
- candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0
120
- ) {
121
- logStartCodecCandidateIgnored(
122
- candidate,
123
- `audio codec preference "${audioCodecPreference}" not found`,
124
- );
125
- return selected;
126
- }
127
- if (channelsPreference && !preferStereo) {
128
- if (!candidateTier.channels[channelsPreference]) {
129
- logStartCodecCandidateIgnored(
130
- candidate,
131
- `no renditions with ${channelsPreference} channel sound found (channels options: ${Object.keys(
132
- candidateTier.channels,
133
- )})`,
134
- );
135
- return selected;
136
- }
137
- } else if (
138
- (!audioCodecPreference || preferStereo) &&
139
- hasStereo &&
140
- candidateTier.channels['2'] === 0
141
- ) {
142
- logStartCodecCandidateIgnored(
143
- candidate,
144
- `no renditions with stereo sound found`,
145
- );
146
- return selected;
147
- }
148
- if (candidateTier.minHeight > maxHeight) {
149
- logStartCodecCandidateIgnored(
150
- candidate,
151
- `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`,
152
- );
153
- return selected;
154
- }
155
- if (candidateTier.minFramerate > maxFramerate) {
156
- logStartCodecCandidateIgnored(
157
- candidate,
158
- `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`,
159
- );
160
- return selected;
161
- }
162
- if (
163
- !videoRanges.some((range) => candidateTier.videoRanges[range] > 0)
164
- ) {
165
- logStartCodecCandidateIgnored(
166
- candidate,
167
- `no variants with VIDEO-RANGE of ${JSON.stringify(
168
- videoRanges,
169
- )} found`,
170
- );
171
- return selected;
172
- }
173
- if (
174
- videoCodecPreference &&
175
- candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0
176
- ) {
177
- logStartCodecCandidateIgnored(
178
- candidate,
179
- `video codec preference "${videoCodecPreference}" not found`,
180
- );
181
- return selected;
182
- }
183
- if (candidateTier.maxScore < selectedScore) {
94
+ if (candidateTier.minBitrate > currentBw) {
95
+ logStartCodecCandidateIgnored(
96
+ candidate,
97
+ `min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`,
98
+ );
99
+ return selected;
100
+ }
101
+ if (!candidateTier.hasDefaultAudio) {
102
+ logStartCodecCandidateIgnored(
103
+ candidate,
104
+ `no renditions with default or auto-select sound found`,
105
+ );
106
+ return selected;
107
+ }
108
+ if (
109
+ audioCodecPreference &&
110
+ candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0
111
+ ) {
112
+ logStartCodecCandidateIgnored(
113
+ candidate,
114
+ `audio codec preference "${audioCodecPreference}" not found`,
115
+ );
116
+ return selected;
117
+ }
118
+ if (channelsPreference && !preferStereo) {
119
+ if (!candidateTier.channels[channelsPreference]) {
184
120
  logStartCodecCandidateIgnored(
185
121
  candidate,
186
- `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`,
122
+ `no renditions with ${channelsPreference} channel sound found (channels options: ${Object.keys(
123
+ candidateTier.channels,
124
+ )})`,
187
125
  );
188
126
  return selected;
189
127
  }
128
+ } else if (
129
+ (!audioCodecPreference || preferStereo) &&
130
+ hasStereo &&
131
+ candidateTier.channels['2'] === 0
132
+ ) {
133
+ logStartCodecCandidateIgnored(
134
+ candidate,
135
+ `no renditions with stereo sound found`,
136
+ );
137
+ return selected;
138
+ }
139
+ if (candidateTier.minHeight > maxHeight) {
140
+ logStartCodecCandidateIgnored(
141
+ candidate,
142
+ `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`,
143
+ );
144
+ return selected;
145
+ }
146
+ if (candidateTier.minFramerate > maxFramerate) {
147
+ logStartCodecCandidateIgnored(
148
+ candidate,
149
+ `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`,
150
+ );
151
+ return selected;
152
+ }
153
+ if (!videoRanges.some((range) => candidateTier.videoRanges[range] > 0)) {
154
+ logStartCodecCandidateIgnored(
155
+ candidate,
156
+ `no variants with VIDEO-RANGE of ${JSON.stringify(
157
+ videoRanges,
158
+ )} found`,
159
+ );
160
+ return selected;
161
+ }
162
+ if (candidateTier.maxScore < selectedScore) {
163
+ logStartCodecCandidateIgnored(
164
+ candidate,
165
+ `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`,
166
+ );
167
+ return selected;
190
168
  }
191
169
  // Remove candiates with less preferred codecs or more errors
192
170
  if (
@@ -197,7 +175,6 @@ export function getStartCodecTier(
197
175
  ) {
198
176
  return selected;
199
177
  }
200
- minIndex = candidateTier.minIndex;
201
178
  selectedScore = candidateTier.maxScore;
202
179
  return candidate;
203
180
  },
@@ -209,7 +186,6 @@ export function getStartCodecTier(
209
186
  preferHDR,
210
187
  minFramerate,
211
188
  minBitrate,
212
- minIndex,
213
189
  };
214
190
  }
215
191
 
@@ -267,7 +243,7 @@ export function getCodecTiers(
267
243
  ): Record<string, CodecSetTier> {
268
244
  return levels
269
245
  .slice(minAutoLevel, maxAutoLevel + 1)
270
- .reduce((tiers: Record<string, CodecSetTier>, level, index) => {
246
+ .reduce((tiers: Record<string, CodecSetTier>, level) => {
271
247
  if (!level.codecSet) {
272
248
  return tiers;
273
249
  }
@@ -278,7 +254,6 @@ export function getCodecTiers(
278
254
  minBitrate: Infinity,
279
255
  minHeight: Infinity,
280
256
  minFramerate: Infinity,
281
- minIndex: index,
282
257
  maxScore: 0,
283
258
  videoRanges: { SDR: 0 },
284
259
  channels: { '2': 0 },
@@ -290,7 +265,6 @@ export function getCodecTiers(
290
265
  const lesserWidthOrHeight = Math.min(level.height, level.width);
291
266
  tier.minHeight = Math.min(tier.minHeight, lesserWidthOrHeight);
292
267
  tier.minFramerate = Math.min(tier.minFramerate, level.frameRate);
293
- tier.minIndex = Math.min(tier.minIndex, index);
294
268
  tier.maxScore = Math.max(tier.maxScore, level.score);
295
269
  tier.fragmentError += level.fragmentError;
296
270
  tier.videoRanges[level.videoRange] =
@@ -9,6 +9,25 @@ export function hasVariableReferences(str: string): boolean {
9
9
  return VARIABLE_REPLACEMENT_REGEX.test(str);
10
10
  }
11
11
 
12
+ export function substituteVariablesInAttributes(
13
+ parsed: Pick<
14
+ ParsedMultivariantPlaylist | LevelDetails,
15
+ 'variableList' | 'hasVariableRefs' | 'playlistParsingError'
16
+ >,
17
+ attr: AttrList,
18
+ attributeNames: string[],
19
+ ) {
20
+ if (parsed.variableList !== null || parsed.hasVariableRefs) {
21
+ for (let i = attributeNames.length; i--; ) {
22
+ const name = attributeNames[i];
23
+ const value = attr[name];
24
+ if (value) {
25
+ attr[name] = substituteVariables(parsed, value);
26
+ }
27
+ }
28
+ }
29
+ }
30
+
12
31
  export function substituteVariables(
13
32
  parsed: Pick<
14
33
  ParsedMultivariantPlaylist | LevelDetails,
@@ -1,6 +1,5 @@
1
1
  import { VTTParser } from './vttparser';
2
- import { utf8ArrayToStr } from '@svta/common-media-library/utils/utf8ArrayToStr';
3
- import { hash } from './hash';
2
+ import { utf8ArrayToStr } from '../demux/id3';
4
3
  import {
5
4
  RationalTimestamp,
6
5
  toMpegTsClockFromTimescale,
@@ -46,6 +45,17 @@ const cueString2millis = function (timeString: string) {
46
45
  return ts;
47
46
  };
48
47
 
48
+ // From https://github.com/darkskyapp/string-hash
49
+ const hash = function (text: string) {
50
+ let hash = 5381;
51
+ let i = text.length;
52
+ while (i) {
53
+ hash = (hash * 33) ^ text.charCodeAt(--i);
54
+ }
55
+
56
+ return (hash >>> 0).toString();
57
+ };
58
+
49
59
  // Create a unique hash id for a cue based on start/end times and text.
50
60
  // This helps timeline-controller to avoid showing repeated captions.
51
61
  export function generateCueId(
@@ -1,4 +0,0 @@
1
- export const enum DecrypterAesMode {
2
- cbc = 0,
3
- ctr = 1,
4
- }