@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,422 @@
1
+ import { CmcdObjectType } from '@svta/common-media-library/cmcd/CmcdObjectType';
2
+ import { CmcdStreamingFormat } from '@svta/common-media-library/cmcd/CmcdStreamingFormat';
3
+ import { appendCmcdHeaders } from '@svta/common-media-library/cmcd/appendCmcdHeaders';
4
+ import { appendCmcdQuery } from '@svta/common-media-library/cmcd/appendCmcdQuery';
5
+ import { Events } from '../events';
6
+ import { BufferHelper } from '../utils/buffer-helper';
7
+ import type {
8
+ FragmentLoaderConstructor,
9
+ HlsConfig,
10
+ PlaylistLoaderConstructor,
11
+ } from '../config';
12
+ import type Hls from '../hls';
13
+ import type { Fragment, Part } from '../loader/fragment';
14
+ import type { ExtendedSourceBuffer } from '../types/buffer';
15
+ import type { ComponentAPI } from '../types/component-api';
16
+ import type { BufferCreatedData, MediaAttachedData } from '../types/events';
17
+ import type {
18
+ FragmentLoaderContext,
19
+ Loader,
20
+ LoaderCallbacks,
21
+ LoaderConfiguration,
22
+ LoaderContext,
23
+ PlaylistLoaderContext,
24
+ } from '../types/loader';
25
+ import type { Cmcd } from '@svta/common-media-library/cmcd/Cmcd';
26
+ import type { CmcdEncodeOptions } from '@svta/common-media-library/cmcd/CmcdEncodeOptions';
27
+
28
+ /**
29
+ * Controller to deal with Common Media Client Data (CMCD)
30
+ * @see https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf
31
+ */
32
+ export default class CMCDController implements ComponentAPI {
33
+ private hls: Hls;
34
+ private config: HlsConfig;
35
+ private media?: HTMLMediaElement;
36
+ private sid?: string;
37
+ private cid?: string;
38
+ private useHeaders: boolean = false;
39
+ private includeKeys?: string[];
40
+ private initialized: boolean = false;
41
+ private starved: boolean = false;
42
+ private buffering: boolean = true;
43
+ private audioBuffer?: ExtendedSourceBuffer;
44
+ private videoBuffer?: ExtendedSourceBuffer;
45
+
46
+ constructor(hls: Hls) {
47
+ this.hls = hls;
48
+ const config = (this.config = hls.config);
49
+ const { cmcd } = config;
50
+
51
+ if (cmcd != null) {
52
+ config.pLoader = this.createPlaylistLoader();
53
+ config.fLoader = this.createFragmentLoader();
54
+
55
+ this.sid = cmcd.sessionId || hls.sessionId;
56
+ this.cid = cmcd.contentId;
57
+ this.useHeaders = cmcd.useHeaders === true;
58
+ this.includeKeys = cmcd.includeKeys;
59
+ this.registerListeners();
60
+ }
61
+ }
62
+
63
+ private registerListeners() {
64
+ const hls = this.hls;
65
+ hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
66
+ hls.on(Events.MEDIA_DETACHED, this.onMediaDetached, this);
67
+ hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
68
+ }
69
+
70
+ private unregisterListeners() {
71
+ const hls = this.hls;
72
+ hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
73
+ hls.off(Events.MEDIA_DETACHED, this.onMediaDetached, this);
74
+ hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
75
+ }
76
+
77
+ destroy() {
78
+ this.unregisterListeners();
79
+ this.onMediaDetached();
80
+
81
+ // @ts-ignore
82
+ this.hls = this.config = this.audioBuffer = this.videoBuffer = null;
83
+ // @ts-ignore
84
+ this.onWaiting = this.onPlaying = this.media = null;
85
+ }
86
+
87
+ private onMediaAttached(
88
+ event: Events.MEDIA_ATTACHED,
89
+ data: MediaAttachedData,
90
+ ) {
91
+ this.media = data.media;
92
+ this.media.addEventListener('waiting', this.onWaiting);
93
+ this.media.addEventListener('playing', this.onPlaying);
94
+ }
95
+
96
+ private onMediaDetached() {
97
+ if (!this.media) {
98
+ return;
99
+ }
100
+
101
+ this.media.removeEventListener('waiting', this.onWaiting);
102
+ this.media.removeEventListener('playing', this.onPlaying);
103
+
104
+ // @ts-ignore
105
+ this.media = null;
106
+ }
107
+
108
+ private onBufferCreated(
109
+ event: Events.BUFFER_CREATED,
110
+ data: BufferCreatedData,
111
+ ) {
112
+ this.audioBuffer = data.tracks.audio?.buffer;
113
+ this.videoBuffer = data.tracks.video?.buffer;
114
+ }
115
+
116
+ private onWaiting = () => {
117
+ if (this.initialized) {
118
+ this.starved = true;
119
+ }
120
+
121
+ this.buffering = true;
122
+ };
123
+
124
+ private onPlaying = () => {
125
+ if (!this.initialized) {
126
+ this.initialized = true;
127
+ }
128
+
129
+ this.buffering = false;
130
+ };
131
+
132
+ /**
133
+ * Create baseline CMCD data
134
+ */
135
+ private createData(): Cmcd {
136
+ return {
137
+ v: 1,
138
+ sf: CmcdStreamingFormat.HLS,
139
+ sid: this.sid,
140
+ cid: this.cid,
141
+ pr: this.media?.playbackRate,
142
+ mtp: this.hls.bandwidthEstimate / 1000,
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Apply CMCD data to a request.
148
+ */
149
+ private apply(context: LoaderContext, data: Cmcd = {}) {
150
+ // apply baseline data
151
+ Object.assign(data, this.createData());
152
+
153
+ const isVideo =
154
+ data.ot === CmcdObjectType.INIT ||
155
+ data.ot === CmcdObjectType.VIDEO ||
156
+ data.ot === CmcdObjectType.MUXED;
157
+
158
+ if (this.starved && isVideo) {
159
+ data.bs = true;
160
+ data.su = true;
161
+ this.starved = false;
162
+ }
163
+
164
+ if (data.su == null) {
165
+ data.su = this.buffering;
166
+ }
167
+
168
+ // TODO: Implement rtp, nrr, dl
169
+
170
+ const { includeKeys } = this;
171
+ if (includeKeys) {
172
+ data = Object.keys(data).reduce((acc, key) => {
173
+ includeKeys.includes(key) && (acc[key] = data[key]);
174
+ return acc;
175
+ }, {});
176
+ }
177
+
178
+ const options: CmcdEncodeOptions = { baseUrl: context.url };
179
+
180
+ if (this.useHeaders) {
181
+ if (!context.headers) {
182
+ context.headers = {};
183
+ }
184
+
185
+ appendCmcdHeaders(context.headers, data, options);
186
+ } else {
187
+ context.url = appendCmcdQuery(context.url, data, options);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Apply CMCD data to a manifest request.
193
+ */
194
+ private applyPlaylistData = (context: PlaylistLoaderContext) => {
195
+ try {
196
+ this.apply(context, {
197
+ ot: CmcdObjectType.MANIFEST,
198
+ su: !this.initialized,
199
+ });
200
+ } catch (error) {
201
+ this.hls.logger.warn('Could not generate manifest CMCD data.', error);
202
+ }
203
+ };
204
+
205
+ /**
206
+ * Apply CMCD data to a segment request
207
+ */
208
+ private applyFragmentData = (context: FragmentLoaderContext) => {
209
+ try {
210
+ const { frag, part } = context;
211
+ const level = this.hls.levels[frag.level];
212
+ const ot = this.getObjectType(frag);
213
+ const data: Cmcd = { d: (part || frag).duration * 1000, ot };
214
+
215
+ if (
216
+ ot === CmcdObjectType.VIDEO ||
217
+ ot === CmcdObjectType.AUDIO ||
218
+ ot == CmcdObjectType.MUXED
219
+ ) {
220
+ data.br = level.bitrate / 1000;
221
+ data.tb = this.getTopBandwidth(ot) / 1000;
222
+ data.bl = this.getBufferLength(ot);
223
+ }
224
+
225
+ const next = part ? this.getNextPart(part) : this.getNextFrag(frag);
226
+
227
+ if (next?.url && next.url !== frag.url) {
228
+ data.nor = next.url;
229
+ }
230
+
231
+ this.apply(context, data);
232
+ } catch (error) {
233
+ this.hls.logger.warn('Could not generate segment CMCD data.', error);
234
+ }
235
+ };
236
+
237
+ private getNextFrag(fragment: Fragment): Fragment | undefined {
238
+ const levelDetails = this.hls.levels[fragment.level]?.details;
239
+ if (levelDetails) {
240
+ const index = (fragment.sn as number) - levelDetails.startSN;
241
+ return levelDetails.fragments[index + 1];
242
+ }
243
+
244
+ return undefined;
245
+ }
246
+
247
+ private getNextPart(part: Part): Part | undefined {
248
+ const { index, fragment } = part;
249
+ const partList = this.hls.levels[fragment.level]?.details?.partList;
250
+
251
+ if (partList) {
252
+ const { sn } = fragment;
253
+ for (let i = partList.length - 1; i >= 0; i--) {
254
+ const p = partList[i];
255
+ if (p.index === index && p.fragment.sn === sn) {
256
+ return partList[i + 1];
257
+ }
258
+ }
259
+ }
260
+
261
+ return undefined;
262
+ }
263
+
264
+ /**
265
+ * The CMCD object type.
266
+ */
267
+ private getObjectType(fragment: Fragment): CmcdObjectType | undefined {
268
+ const { type } = fragment;
269
+
270
+ if (type === 'subtitle') {
271
+ return CmcdObjectType.TIMED_TEXT;
272
+ }
273
+
274
+ if (fragment.sn === 'initSegment') {
275
+ return CmcdObjectType.INIT;
276
+ }
277
+
278
+ if (type === 'audio') {
279
+ return CmcdObjectType.AUDIO;
280
+ }
281
+
282
+ if (type === 'main') {
283
+ if (!this.hls.audioTracks.length) {
284
+ return CmcdObjectType.MUXED;
285
+ }
286
+
287
+ return CmcdObjectType.VIDEO;
288
+ }
289
+
290
+ return undefined;
291
+ }
292
+
293
+ /**
294
+ * Get the highest bitrate.
295
+ */
296
+ private getTopBandwidth(type: CmcdObjectType) {
297
+ let bitrate: number = 0;
298
+ let levels;
299
+ const hls = this.hls;
300
+
301
+ if (type === CmcdObjectType.AUDIO) {
302
+ levels = hls.audioTracks;
303
+ } else {
304
+ const max = hls.maxAutoLevel;
305
+ const len = max > -1 ? max + 1 : hls.levels.length;
306
+ levels = hls.levels.slice(0, len);
307
+ }
308
+
309
+ levels.forEach((level) => {
310
+ if (level.bitrate > bitrate) {
311
+ bitrate = level.bitrate;
312
+ }
313
+ });
314
+
315
+ return bitrate > 0 ? bitrate : NaN;
316
+ }
317
+
318
+ /**
319
+ * Get the buffer length for a media type in milliseconds
320
+ */
321
+ private getBufferLength(type: CmcdObjectType) {
322
+ const media = this.media;
323
+ const buffer =
324
+ type === CmcdObjectType.AUDIO ? this.audioBuffer : this.videoBuffer;
325
+
326
+ if (!buffer || !media) {
327
+ return NaN;
328
+ }
329
+
330
+ const info = BufferHelper.bufferInfo(
331
+ buffer,
332
+ media.currentTime,
333
+ this.config.maxBufferHole,
334
+ );
335
+
336
+ return info.len * 1000;
337
+ }
338
+
339
+ /**
340
+ * Create a playlist loader
341
+ */
342
+ private createPlaylistLoader(): PlaylistLoaderConstructor | undefined {
343
+ const { pLoader } = this.config;
344
+ const apply = this.applyPlaylistData;
345
+ const Ctor = pLoader || (this.config.loader as PlaylistLoaderConstructor);
346
+
347
+ return class CmcdPlaylistLoader {
348
+ private loader: Loader<PlaylistLoaderContext>;
349
+
350
+ constructor(config: HlsConfig) {
351
+ this.loader = new Ctor(config);
352
+ }
353
+
354
+ get stats() {
355
+ return this.loader.stats;
356
+ }
357
+
358
+ get context() {
359
+ return this.loader.context;
360
+ }
361
+
362
+ destroy() {
363
+ this.loader.destroy();
364
+ }
365
+
366
+ abort() {
367
+ this.loader.abort();
368
+ }
369
+
370
+ load(
371
+ context: PlaylistLoaderContext,
372
+ config: LoaderConfiguration,
373
+ callbacks: LoaderCallbacks<PlaylistLoaderContext>,
374
+ ) {
375
+ apply(context);
376
+ this.loader.load(context, config, callbacks);
377
+ }
378
+ };
379
+ }
380
+
381
+ /**
382
+ * Create a playlist loader
383
+ */
384
+ private createFragmentLoader(): FragmentLoaderConstructor | undefined {
385
+ const { fLoader } = this.config;
386
+ const apply = this.applyFragmentData;
387
+ const Ctor = fLoader || (this.config.loader as FragmentLoaderConstructor);
388
+
389
+ return class CmcdFragmentLoader {
390
+ private loader: Loader<FragmentLoaderContext>;
391
+
392
+ constructor(config: HlsConfig) {
393
+ this.loader = new Ctor(config);
394
+ }
395
+
396
+ get stats() {
397
+ return this.loader.stats;
398
+ }
399
+
400
+ get context() {
401
+ return this.loader.context;
402
+ }
403
+
404
+ destroy() {
405
+ this.loader.destroy();
406
+ }
407
+
408
+ abort() {
409
+ this.loader.abort();
410
+ }
411
+
412
+ load(
413
+ context: FragmentLoaderContext,
414
+ config: LoaderConfiguration,
415
+ callbacks: LoaderCallbacks<FragmentLoaderContext>,
416
+ ) {
417
+ apply(context);
418
+ this.loader.load(context, config, callbacks);
419
+ }
420
+ };
421
+ }
422
+ }