@zenvor/hls.js 1.0.0

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 (159) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +472 -0
  3. package/dist/hls-demo.js +26995 -0
  4. package/dist/hls-demo.js.map +1 -0
  5. package/dist/hls.d.mts +4204 -0
  6. package/dist/hls.d.ts +4204 -0
  7. package/dist/hls.js +40050 -0
  8. package/dist/hls.js.d.ts +4204 -0
  9. package/dist/hls.js.map +1 -0
  10. package/dist/hls.light.js +27145 -0
  11. package/dist/hls.light.js.map +1 -0
  12. package/dist/hls.light.min.js +2 -0
  13. package/dist/hls.light.min.js.map +1 -0
  14. package/dist/hls.light.mjs +26392 -0
  15. package/dist/hls.light.mjs.map +1 -0
  16. package/dist/hls.min.js +2 -0
  17. package/dist/hls.min.js.map +1 -0
  18. package/dist/hls.mjs +38956 -0
  19. package/dist/hls.mjs.map +1 -0
  20. package/dist/hls.worker.js +2 -0
  21. package/dist/hls.worker.js.map +1 -0
  22. package/package.json +143 -0
  23. package/src/config.ts +794 -0
  24. package/src/controller/abr-controller.ts +1019 -0
  25. package/src/controller/algo-data-controller.ts +794 -0
  26. package/src/controller/audio-stream-controller.ts +1099 -0
  27. package/src/controller/audio-track-controller.ts +454 -0
  28. package/src/controller/base-playlist-controller.ts +438 -0
  29. package/src/controller/base-stream-controller.ts +2526 -0
  30. package/src/controller/buffer-controller.ts +2015 -0
  31. package/src/controller/buffer-operation-queue.ts +159 -0
  32. package/src/controller/cap-level-controller.ts +367 -0
  33. package/src/controller/cmcd-controller.ts +422 -0
  34. package/src/controller/content-steering-controller.ts +622 -0
  35. package/src/controller/eme-controller.ts +1617 -0
  36. package/src/controller/error-controller.ts +627 -0
  37. package/src/controller/fps-controller.ts +146 -0
  38. package/src/controller/fragment-finders.ts +256 -0
  39. package/src/controller/fragment-tracker.ts +567 -0
  40. package/src/controller/gap-controller.ts +719 -0
  41. package/src/controller/id3-track-controller.ts +488 -0
  42. package/src/controller/interstitial-player.ts +302 -0
  43. package/src/controller/interstitials-controller.ts +2895 -0
  44. package/src/controller/interstitials-schedule.ts +698 -0
  45. package/src/controller/latency-controller.ts +294 -0
  46. package/src/controller/level-controller.ts +776 -0
  47. package/src/controller/stream-controller.ts +1597 -0
  48. package/src/controller/subtitle-stream-controller.ts +508 -0
  49. package/src/controller/subtitle-track-controller.ts +617 -0
  50. package/src/controller/timeline-controller.ts +677 -0
  51. package/src/crypt/aes-crypto.ts +36 -0
  52. package/src/crypt/aes-decryptor.ts +339 -0
  53. package/src/crypt/decrypter-aes-mode.ts +4 -0
  54. package/src/crypt/decrypter.ts +225 -0
  55. package/src/crypt/fast-aes-key.ts +39 -0
  56. package/src/define-plugin.d.ts +17 -0
  57. package/src/demux/audio/aacdemuxer.ts +126 -0
  58. package/src/demux/audio/ac3-demuxer.ts +170 -0
  59. package/src/demux/audio/adts.ts +249 -0
  60. package/src/demux/audio/base-audio-demuxer.ts +205 -0
  61. package/src/demux/audio/dolby.ts +21 -0
  62. package/src/demux/audio/mp3demuxer.ts +85 -0
  63. package/src/demux/audio/mpegaudio.ts +177 -0
  64. package/src/demux/chunk-cache.ts +42 -0
  65. package/src/demux/dummy-demuxed-track.ts +13 -0
  66. package/src/demux/inject-worker.ts +75 -0
  67. package/src/demux/mp4demuxer.ts +234 -0
  68. package/src/demux/sample-aes.ts +198 -0
  69. package/src/demux/transmuxer-interface.ts +449 -0
  70. package/src/demux/transmuxer-worker.ts +221 -0
  71. package/src/demux/transmuxer.ts +560 -0
  72. package/src/demux/tsdemuxer.ts +1256 -0
  73. package/src/demux/video/avc-video-parser.ts +401 -0
  74. package/src/demux/video/base-video-parser.ts +198 -0
  75. package/src/demux/video/exp-golomb.ts +153 -0
  76. package/src/demux/video/hevc-video-parser.ts +736 -0
  77. package/src/empty-es.js +5 -0
  78. package/src/empty.js +3 -0
  79. package/src/errors.ts +107 -0
  80. package/src/events.ts +548 -0
  81. package/src/exports-default.ts +3 -0
  82. package/src/exports-named.ts +81 -0
  83. package/src/hls.ts +1613 -0
  84. package/src/is-supported.ts +54 -0
  85. package/src/loader/date-range.ts +207 -0
  86. package/src/loader/fragment-loader.ts +403 -0
  87. package/src/loader/fragment.ts +487 -0
  88. package/src/loader/interstitial-asset-list.ts +162 -0
  89. package/src/loader/interstitial-event.ts +337 -0
  90. package/src/loader/key-loader.ts +439 -0
  91. package/src/loader/level-details.ts +203 -0
  92. package/src/loader/level-key.ts +259 -0
  93. package/src/loader/load-stats.ts +17 -0
  94. package/src/loader/m3u8-parser.ts +1072 -0
  95. package/src/loader/playlist-loader.ts +839 -0
  96. package/src/polyfills/number.ts +15 -0
  97. package/src/remux/aac-helper.ts +81 -0
  98. package/src/remux/mp4-generator.ts +1380 -0
  99. package/src/remux/mp4-remuxer.ts +1261 -0
  100. package/src/remux/passthrough-remuxer.ts +434 -0
  101. package/src/task-loop.ts +130 -0
  102. package/src/types/algo.ts +44 -0
  103. package/src/types/buffer.ts +105 -0
  104. package/src/types/component-api.ts +20 -0
  105. package/src/types/demuxer.ts +208 -0
  106. package/src/types/events.ts +574 -0
  107. package/src/types/fragment-tracker.ts +23 -0
  108. package/src/types/level.ts +268 -0
  109. package/src/types/loader.ts +198 -0
  110. package/src/types/media-playlist.ts +92 -0
  111. package/src/types/network-details.ts +3 -0
  112. package/src/types/remuxer.ts +104 -0
  113. package/src/types/track.ts +12 -0
  114. package/src/types/transmuxer.ts +46 -0
  115. package/src/types/tuples.ts +6 -0
  116. package/src/types/vtt.ts +11 -0
  117. package/src/utils/arrays.ts +22 -0
  118. package/src/utils/attr-list.ts +192 -0
  119. package/src/utils/binary-search.ts +46 -0
  120. package/src/utils/buffer-helper.ts +173 -0
  121. package/src/utils/cea-608-parser.ts +1413 -0
  122. package/src/utils/chunker.ts +41 -0
  123. package/src/utils/codecs.ts +314 -0
  124. package/src/utils/cues.ts +96 -0
  125. package/src/utils/discontinuities.ts +174 -0
  126. package/src/utils/encryption-methods-util.ts +21 -0
  127. package/src/utils/error-helper.ts +95 -0
  128. package/src/utils/event-listener-helper.ts +16 -0
  129. package/src/utils/ewma-bandwidth-estimator.ts +97 -0
  130. package/src/utils/ewma.ts +43 -0
  131. package/src/utils/fetch-loader.ts +331 -0
  132. package/src/utils/global.ts +2 -0
  133. package/src/utils/hash.ts +10 -0
  134. package/src/utils/hdr.ts +67 -0
  135. package/src/utils/hex.ts +32 -0
  136. package/src/utils/imsc1-ttml-parser.ts +261 -0
  137. package/src/utils/keysystem-util.ts +45 -0
  138. package/src/utils/level-helper.ts +629 -0
  139. package/src/utils/logger.ts +120 -0
  140. package/src/utils/media-option-attributes.ts +49 -0
  141. package/src/utils/mediacapabilities-helper.ts +301 -0
  142. package/src/utils/mediakeys-helper.ts +210 -0
  143. package/src/utils/mediasource-helper.ts +37 -0
  144. package/src/utils/mp4-tools.ts +1473 -0
  145. package/src/utils/number.ts +3 -0
  146. package/src/utils/numeric-encoding-utils.ts +26 -0
  147. package/src/utils/output-filter.ts +46 -0
  148. package/src/utils/rendition-helper.ts +505 -0
  149. package/src/utils/safe-json-stringify.ts +22 -0
  150. package/src/utils/texttrack-utils.ts +164 -0
  151. package/src/utils/time-ranges.ts +17 -0
  152. package/src/utils/timescale-conversion.ts +46 -0
  153. package/src/utils/utf8-utils.ts +18 -0
  154. package/src/utils/variable-substitution.ts +105 -0
  155. package/src/utils/vttcue.ts +384 -0
  156. package/src/utils/vttparser.ts +497 -0
  157. package/src/utils/webvtt-parser.ts +166 -0
  158. package/src/utils/xhr-loader.ts +337 -0
  159. package/src/version.ts +1 -0
@@ -0,0 +1,337 @@
1
+ import { hash } from '../utils/hash';
2
+ import type { DateRange, DateRangeCue } from './date-range';
3
+ import type { MediaFragmentRef } from './fragment';
4
+ import type { Loader, LoaderContext } from '../types/loader';
5
+
6
+ export const ALIGNED_END_THRESHOLD_SECONDS = 0.025;
7
+
8
+ export type PlaybackRestrictions = {
9
+ skip: boolean;
10
+ jump: boolean;
11
+ };
12
+
13
+ export type SnapOptions = {
14
+ out: boolean;
15
+ in: boolean;
16
+ };
17
+
18
+ export enum TimelineOccupancy {
19
+ Point,
20
+ Range,
21
+ }
22
+
23
+ export type AssetListJSON = {
24
+ ASSETS: Array<{ URI: string; DURATION: string }>;
25
+ };
26
+ export interface InterstitialEventWithAssetList extends InterstitialEvent {
27
+ assetListUrl: string;
28
+ }
29
+
30
+ export type BaseData = {
31
+ url: string;
32
+ };
33
+
34
+ export type InterstitialId = string;
35
+ export type InterstitialAssetId = string;
36
+
37
+ export type InterstitialAssetItem = {
38
+ parentIdentifier: InterstitialId;
39
+ identifier: InterstitialAssetId;
40
+ duration: number | null;
41
+ startOffset: number; // asset start offset from start of interstitial event
42
+ timelineStart: number; // asset start on media element timeline
43
+ uri: string;
44
+ error?: Error;
45
+ };
46
+
47
+ export function generateAssetIdentifier(
48
+ interstitial: InterstitialEvent,
49
+ uri: string,
50
+ assetListIndex: number,
51
+ ): string {
52
+ return `${interstitial.identifier}-${assetListIndex + 1}-${hash(uri)}`;
53
+ }
54
+
55
+ export class InterstitialEvent {
56
+ private base: BaseData;
57
+ private _duration: number | null = null;
58
+ private _timelineStart: number | null = null;
59
+ private appendInPlaceDisabled?: boolean;
60
+ public appendInPlaceStarted?: boolean;
61
+ public dateRange: DateRange;
62
+ public hasPlayed: boolean = false;
63
+ public cumulativeDuration: number = 0;
64
+ public resumeOffset: number = NaN;
65
+ public playoutLimit: number = NaN;
66
+ public restrictions: PlaybackRestrictions = {
67
+ skip: false,
68
+ jump: false,
69
+ };
70
+ public snapOptions: SnapOptions = {
71
+ out: false,
72
+ in: false,
73
+ };
74
+ public assetList: InterstitialAssetItem[] = [];
75
+ public assetListLoader?: Loader<LoaderContext>;
76
+ public assetListResponse: AssetListJSON | null = null;
77
+ public resumeAnchor?: MediaFragmentRef;
78
+ public error?: Error;
79
+ public resetOnResume?: boolean;
80
+
81
+ constructor(dateRange: DateRange, base: BaseData) {
82
+ this.base = base;
83
+ this.dateRange = dateRange;
84
+ this.setDateRange(dateRange);
85
+ }
86
+
87
+ public setDateRange(dateRange: DateRange) {
88
+ this.dateRange = dateRange;
89
+ this.resumeOffset = dateRange.attr.optionalFloat(
90
+ 'X-RESUME-OFFSET',
91
+ this.resumeOffset,
92
+ );
93
+ this.playoutLimit = dateRange.attr.optionalFloat(
94
+ 'X-PLAYOUT-LIMIT',
95
+ this.playoutLimit,
96
+ );
97
+ this.restrictions = dateRange.attr.enumeratedStringList(
98
+ 'X-RESTRICT',
99
+ this.restrictions,
100
+ );
101
+ this.snapOptions = dateRange.attr.enumeratedStringList(
102
+ 'X-SNAP',
103
+ this.snapOptions,
104
+ );
105
+ }
106
+
107
+ public reset() {
108
+ this.appendInPlaceStarted = false;
109
+ this.assetListLoader?.destroy();
110
+ this.assetListLoader = undefined;
111
+ if (!this.supplementsPrimary) {
112
+ this.assetListResponse = null;
113
+ this.assetList = [];
114
+ this._duration = null;
115
+ }
116
+ // `error?` is reset when seeking back over interstitial `startOffset`
117
+ // using `schedule.resetErrorsInRange(start, end)`.
118
+ }
119
+
120
+ public isAssetPastPlayoutLimit(assetIndex: number): boolean {
121
+ if (assetIndex > 0 && assetIndex >= this.assetList.length) {
122
+ return true;
123
+ }
124
+ const playoutLimit = this.playoutLimit;
125
+ if (assetIndex <= 0 || isNaN(playoutLimit)) {
126
+ return false;
127
+ }
128
+ if (playoutLimit === 0) {
129
+ return true;
130
+ }
131
+ const assetOffset = this.assetList[assetIndex]?.startOffset || 0;
132
+ return assetOffset > playoutLimit;
133
+ }
134
+
135
+ public findAssetIndex(asset: InterstitialAssetItem): number {
136
+ const index = this.assetList.indexOf(asset);
137
+ return index;
138
+ }
139
+
140
+ get identifier(): InterstitialId {
141
+ return this.dateRange.id;
142
+ }
143
+
144
+ get startDate(): Date {
145
+ return this.dateRange.startDate;
146
+ }
147
+
148
+ get startTime(): number {
149
+ // Primary media timeline start time
150
+ const startTime = this.dateRange.startTime;
151
+ if (this.snapOptions.out) {
152
+ const frag = this.dateRange.tagAnchor;
153
+ if (frag) {
154
+ return getSnapToFragmentTime(startTime, frag);
155
+ }
156
+ }
157
+ return startTime;
158
+ }
159
+
160
+ get startOffset(): number {
161
+ return this.cue.pre ? 0 : this.startTime;
162
+ }
163
+
164
+ get startIsAligned(): boolean {
165
+ if (this.startTime === 0 || this.snapOptions.out) {
166
+ return true;
167
+ }
168
+ const frag = this.dateRange.tagAnchor;
169
+ if (frag) {
170
+ const startTime = this.dateRange.startTime;
171
+ const snappedStart = getSnapToFragmentTime(startTime, frag);
172
+ return startTime - snappedStart < 0.1;
173
+ }
174
+ return false;
175
+ }
176
+
177
+ get resumptionOffset(): number {
178
+ const resumeOffset = this.resumeOffset;
179
+ const offset = Number.isFinite(resumeOffset) ? resumeOffset : this.duration;
180
+ return this.cumulativeDuration + offset;
181
+ }
182
+
183
+ get resumeTime(): number {
184
+ // Primary media timeline resumption time
185
+ const resumeTime = this.startOffset + this.resumptionOffset;
186
+ if (this.snapOptions.in) {
187
+ const frag = this.resumeAnchor;
188
+ if (frag) {
189
+ return getSnapToFragmentTime(resumeTime, frag);
190
+ }
191
+ }
192
+ return resumeTime;
193
+ }
194
+
195
+ get appendInPlace(): boolean {
196
+ if (this.appendInPlaceStarted) {
197
+ return true;
198
+ }
199
+ if (this.appendInPlaceDisabled) {
200
+ return false;
201
+ }
202
+ if (
203
+ !this.cue.once &&
204
+ !this.cue.pre && // preroll starts at startPosition before startPosition is known (live)
205
+ this.startIsAligned &&
206
+ ((isNaN(this.playoutLimit) && isNaN(this.resumeOffset)) ||
207
+ (this.resumeOffset &&
208
+ this.duration &&
209
+ Math.abs(this.resumeOffset - this.duration) <
210
+ ALIGNED_END_THRESHOLD_SECONDS))
211
+ ) {
212
+ return true;
213
+ }
214
+ return false;
215
+ }
216
+
217
+ set appendInPlace(value: boolean) {
218
+ if (this.appendInPlaceStarted) {
219
+ this.resetOnResume = !value;
220
+ return;
221
+ }
222
+ this.appendInPlaceDisabled = !value;
223
+ }
224
+
225
+ // Extended timeline start time
226
+ get timelineStart(): number {
227
+ if (this._timelineStart !== null) {
228
+ return this._timelineStart;
229
+ }
230
+ return this.startTime;
231
+ }
232
+
233
+ set timelineStart(value: number) {
234
+ this._timelineStart = value;
235
+ }
236
+
237
+ get duration(): number {
238
+ const playoutLimit = this.playoutLimit;
239
+ let duration: number;
240
+ if (this._duration !== null) {
241
+ duration = this._duration;
242
+ } else if (this.dateRange.duration) {
243
+ duration = this.dateRange.duration;
244
+ } else {
245
+ duration = this.dateRange.plannedDuration || 0;
246
+ }
247
+ if (!isNaN(playoutLimit) && playoutLimit < duration) {
248
+ duration = playoutLimit;
249
+ }
250
+ return duration;
251
+ }
252
+
253
+ set duration(value: number) {
254
+ this._duration = value;
255
+ }
256
+
257
+ get cue(): DateRangeCue {
258
+ return this.dateRange.cue;
259
+ }
260
+
261
+ get timelineOccupancy() {
262
+ if (this.dateRange.attr['X-TIMELINE-OCCUPIES'] === 'RANGE') {
263
+ return TimelineOccupancy.Range;
264
+ }
265
+ return TimelineOccupancy.Point;
266
+ }
267
+
268
+ get supplementsPrimary(): boolean {
269
+ return this.dateRange.attr['X-TIMELINE-STYLE'] === 'PRIMARY';
270
+ }
271
+
272
+ get contentMayVary(): boolean {
273
+ return this.dateRange.attr['X-CONTENT-MAY-VARY'] !== 'NO';
274
+ }
275
+
276
+ get assetUrl(): string | undefined {
277
+ return this.dateRange.attr['X-ASSET-URI'];
278
+ }
279
+
280
+ get assetListUrl(): string | undefined {
281
+ return this.dateRange.attr['X-ASSET-LIST'];
282
+ }
283
+
284
+ get baseUrl(): string {
285
+ return this.base.url;
286
+ }
287
+
288
+ get assetListLoaded(): boolean {
289
+ return this.assetList.length > 0 || this.assetListResponse !== null;
290
+ }
291
+
292
+ toString(): string {
293
+ return eventToString(this);
294
+ }
295
+ }
296
+
297
+ function getSnapToFragmentTime(time: number, frag: MediaFragmentRef) {
298
+ return time - frag.start < frag.duration / 2 &&
299
+ !(
300
+ Math.abs(time - (frag.start + frag.duration)) <
301
+ ALIGNED_END_THRESHOLD_SECONDS
302
+ )
303
+ ? frag.start
304
+ : frag.start + frag.duration;
305
+ }
306
+
307
+ export function getInterstitialUrl(
308
+ uri: string,
309
+ sessionId: string,
310
+ baseUrl?: string,
311
+ ): URL | never {
312
+ const url = new self.URL(uri, baseUrl);
313
+ if (url.protocol !== 'data:') {
314
+ url.searchParams.set('_HLS_primary_id', sessionId);
315
+ }
316
+ return url;
317
+ }
318
+
319
+ export function getNextAssetIndex(
320
+ interstitial: InterstitialEvent,
321
+ assetListIndex: number,
322
+ ): number {
323
+ while (interstitial.assetList[++assetListIndex]?.error) {
324
+ /* no-op */
325
+ }
326
+ return assetListIndex;
327
+ }
328
+
329
+ function eventToString(interstitial: InterstitialEvent): string {
330
+ return `["${interstitial.identifier}" ${interstitial.cue.pre ? '<pre>' : interstitial.cue.post ? '<post>' : ''}${interstitial.timelineStart.toFixed(2)}-${interstitial.resumeTime.toFixed(2)}]`;
331
+ }
332
+
333
+ export function eventAssetToString(asset: InterstitialAssetItem): string {
334
+ const start = asset.timelineStart;
335
+ const duration = asset.duration || 0;
336
+ return `["${asset.identifier}" ${start.toFixed(2)}-${(start + duration).toFixed(2)}]`;
337
+ }