@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,164 @@
1
+ import { logger } from './logger';
2
+ import type { MediaPlaylist } from '../hls';
3
+
4
+ // This is a replacement of the native addTextTrack method.
5
+ // TextTracks created by the native method are unremovable since their livecycles
6
+ // are neither associated with the current media nor managed by user code.
7
+ export function createTrackNode(
8
+ videoEl: HTMLMediaElement,
9
+ kind: TextTrackKind | 'forced',
10
+ label: string,
11
+ lang: string = '',
12
+ mode: TextTrackMode = 'disabled',
13
+ ): HTMLTrackElement {
14
+ const el = videoEl.ownerDocument.createElement('track');
15
+ el.kind = kind;
16
+ el.label = label;
17
+ if (lang) {
18
+ el.srclang = lang;
19
+ }
20
+ // To avoid an issue of the built-in captions menu in Chrome
21
+ // https://github.com/video-dev/hls.js/issues/2198#issuecomment-761614248
22
+ // It also prevents Safari from removing cues after setting track.mode to `hidden` or `disabled`
23
+ // https://github.com/video-dev/hls.js/pull/7515#issuecomment-3251091932
24
+ el.src = 'data:,WEBVTT';
25
+ el.track.mode = mode;
26
+ videoEl.appendChild(el);
27
+ return el;
28
+ }
29
+
30
+ export function getTrackKind(track: MediaPlaylist): TextTrackKind | 'forced' {
31
+ if (
32
+ track.forced &&
33
+ (navigator.vendor.includes('Apple') ||
34
+ /iPhone|iPad|iPod/.test(navigator.userAgent))
35
+ ) {
36
+ return 'forced';
37
+ } else if (
38
+ track.characteristics &&
39
+ /transcribes-spoken-dialog/gi.test(track.characteristics) &&
40
+ /describes-music-and-sound/gi.test(track.characteristics)
41
+ ) {
42
+ return 'captions';
43
+ }
44
+
45
+ return 'subtitles';
46
+ }
47
+
48
+ export function addCueToTrack(track: TextTrack, cue: VTTCue) {
49
+ // Sometimes there are cue overlaps on segmented vtts so the same
50
+ // cue can appear more than once in different vtt files.
51
+ // This avoid showing duplicated cues with same timecode and text.
52
+ const mode = track.mode;
53
+ if (mode === 'disabled') {
54
+ track.mode = 'hidden';
55
+ }
56
+ if (track.cues && !track.cues.getCueById(cue.id)) {
57
+ try {
58
+ track.addCue(cue);
59
+ if (!track.cues.getCueById(cue.id)) {
60
+ throw new Error(`addCue is failed for: ${cue}`);
61
+ }
62
+ } catch (err) {
63
+ logger.debug(`[texttrack-utils]: ${err}`);
64
+ try {
65
+ const textTrackCue = new (self.TextTrackCue as any)(
66
+ cue.startTime,
67
+ cue.endTime,
68
+ cue.text,
69
+ );
70
+ textTrackCue.id = cue.id;
71
+ track.addCue(textTrackCue);
72
+ } catch (err2) {
73
+ logger.debug(
74
+ `[texttrack-utils]: Legacy TextTrackCue fallback failed: ${err2}`,
75
+ );
76
+ }
77
+ }
78
+ }
79
+ if (mode === 'disabled') {
80
+ track.mode = mode;
81
+ }
82
+ }
83
+
84
+ export function removeCuesInRange(
85
+ track: TextTrack,
86
+ start: number,
87
+ end: number,
88
+ predicate?: (cue: TextTrackCue) => boolean,
89
+ ) {
90
+ const mode = track.mode;
91
+ if (mode === 'disabled') {
92
+ track.mode = 'hidden';
93
+ }
94
+
95
+ if (track.cues && track.cues.length > 0) {
96
+ const cues = getCuesInRange(track.cues, start, end);
97
+ for (let i = 0; i < cues.length; i++) {
98
+ if (!predicate || predicate(cues[i])) {
99
+ track.removeCue(cues[i]);
100
+ }
101
+ }
102
+ }
103
+ if (mode === 'disabled') {
104
+ track.mode = mode;
105
+ }
106
+ }
107
+
108
+ // Find first cue starting at or after given time.
109
+ // Modified version of binary search O(log(n)).
110
+ function getFirstCueIndexFromTime(
111
+ cues: TextTrackCueList | TextTrackCue[],
112
+ time: number,
113
+ ): number {
114
+ // If first cue starts at or after time, start there
115
+ if (time <= cues[0].startTime) {
116
+ return 0;
117
+ }
118
+ // If the last cue ends before time there is no overlap
119
+ const len = cues.length - 1;
120
+ if (time > cues[len].endTime) {
121
+ return -1;
122
+ }
123
+
124
+ let left = 0;
125
+ let right = len;
126
+ let mid;
127
+ while (left <= right) {
128
+ mid = Math.floor((right + left) / 2);
129
+
130
+ if (time < cues[mid].startTime) {
131
+ right = mid - 1;
132
+ } else if (time > cues[mid].startTime && left < len) {
133
+ left = mid + 1;
134
+ } else {
135
+ // If it's not lower or higher, it must be equal.
136
+ return mid;
137
+ }
138
+ }
139
+ // At this point, left and right have swapped.
140
+ // No direct match was found, left or right element must be the closest. Check which one has the smallest diff.
141
+ return cues[left].startTime - time < time - cues[right].startTime
142
+ ? left
143
+ : right;
144
+ }
145
+
146
+ export function getCuesInRange(
147
+ cues: TextTrackCueList | TextTrackCue[],
148
+ start: number,
149
+ end: number,
150
+ ): TextTrackCue[] {
151
+ const cuesFound: TextTrackCue[] = [];
152
+ const firstCueInRange = getFirstCueIndexFromTime(cues, start);
153
+ if (firstCueInRange > -1) {
154
+ for (let i = firstCueInRange, len = cues.length; i < len; i++) {
155
+ const cue = cues[i];
156
+ if (cue.startTime >= start && cue.endTime <= end) {
157
+ cuesFound.push(cue);
158
+ } else if (cue.startTime > end) {
159
+ return cuesFound;
160
+ }
161
+ }
162
+ }
163
+ return cuesFound;
164
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * TimeRanges to string helper
3
+ */
4
+
5
+ const TimeRanges = {
6
+ toString: function (r: TimeRanges) {
7
+ let log = '';
8
+ const len = r.length;
9
+ for (let i = 0; i < len; i++) {
10
+ log += `[${r.start(i).toFixed(3)}-${r.end(i).toFixed(3)}]`;
11
+ }
12
+
13
+ return log;
14
+ },
15
+ };
16
+
17
+ export default TimeRanges;
@@ -0,0 +1,46 @@
1
+ const MPEG_TS_CLOCK_FREQ_HZ = 90000;
2
+
3
+ export type RationalTimestamp = {
4
+ baseTime: number; // ticks
5
+ timescale: number; // ticks per second
6
+ };
7
+
8
+ export type TimestampOffset = RationalTimestamp & { trackId: number };
9
+
10
+ export function toTimescaleFromBase(
11
+ baseTime: number,
12
+ destScale: number,
13
+ srcBase: number = 1,
14
+ round: boolean = false,
15
+ ): number {
16
+ const result = baseTime * destScale * srcBase; // equivalent to `(value * scale) / (1 / base)`
17
+ return round ? Math.round(result) : result;
18
+ }
19
+
20
+ export function toTimescaleFromScale(
21
+ baseTime: number,
22
+ destScale: number,
23
+ srcScale: number = 1,
24
+ round: boolean = false,
25
+ ): number {
26
+ return toTimescaleFromBase(baseTime, destScale, 1 / srcScale, round);
27
+ }
28
+
29
+ export function toMsFromMpegTsClock(
30
+ baseTime: number,
31
+ round: boolean = false,
32
+ ): number {
33
+ return toTimescaleFromBase(baseTime, 1000, 1 / MPEG_TS_CLOCK_FREQ_HZ, round);
34
+ }
35
+
36
+ export function toMpegTsClockFromTimescale(
37
+ baseTime: number,
38
+ srcScale: number = 1,
39
+ ): number {
40
+ return toTimescaleFromBase(baseTime, MPEG_TS_CLOCK_FREQ_HZ, 1 / srcScale);
41
+ }
42
+
43
+ export function timestampToString(timestamp: TimestampOffset): string {
44
+ const { baseTime, timescale, trackId } = timestamp;
45
+ return `${baseTime / timescale} (${baseTime}/${timescale}) trackId: ${trackId}`;
46
+ }
@@ -0,0 +1,18 @@
1
+ // breaking up those two types in order to clarify what is happening in the decoding path.
2
+ type DecodedFrame<T> = { key: string; data: T; info?: any };
3
+ export type Frame = DecodedFrame<ArrayBuffer | string>;
4
+ // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197
5
+ // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
6
+ /* utf.js - UTF-8 <=> UTF-16 convertion
7
+ *
8
+ * Copyright (C) 1999 Masanao Izumo <iz@onicos.co.jp>
9
+ * Version: 1.0
10
+ * LastModified: Dec 25 1999
11
+ * This library is free. You can redistribute it and/or modify it.
12
+ */
13
+
14
+ export function strToUtf8array(str: string) {
15
+ return Uint8Array.from(unescape(encodeURIComponent(str)), (c) =>
16
+ c.charCodeAt(0),
17
+ );
18
+ }
@@ -0,0 +1,105 @@
1
+ import type { AttrList } from './attr-list';
2
+ import type { LevelDetails } from '../loader/level-details';
3
+ import type { ParsedMultivariantPlaylist } from '../loader/m3u8-parser';
4
+ import type { VariableMap } from '../types/level';
5
+
6
+ const VARIABLE_REPLACEMENT_REGEX = /\{\$([a-zA-Z0-9-_]+)\}/g;
7
+
8
+ export function hasVariableReferences(str: string): boolean {
9
+ return VARIABLE_REPLACEMENT_REGEX.test(str);
10
+ }
11
+
12
+ export function substituteVariables(
13
+ parsed: Pick<
14
+ ParsedMultivariantPlaylist | LevelDetails,
15
+ 'variableList' | 'hasVariableRefs' | 'playlistParsingError'
16
+ >,
17
+ value: string,
18
+ ): string {
19
+ if (parsed.variableList !== null || parsed.hasVariableRefs) {
20
+ const variableList = parsed.variableList;
21
+ return value.replace(
22
+ VARIABLE_REPLACEMENT_REGEX,
23
+ (variableReference: string) => {
24
+ const variableName = variableReference.substring(
25
+ 2,
26
+ variableReference.length - 1,
27
+ );
28
+ const variableValue = variableList?.[variableName];
29
+ if (variableValue === undefined) {
30
+ parsed.playlistParsingError ||= new Error(
31
+ `Missing preceding EXT-X-DEFINE tag for Variable Reference: "${variableName}"`,
32
+ );
33
+ return variableReference;
34
+ }
35
+ return variableValue;
36
+ },
37
+ );
38
+ }
39
+ return value;
40
+ }
41
+
42
+ export function addVariableDefinition(
43
+ parsed: Pick<
44
+ ParsedMultivariantPlaylist | LevelDetails,
45
+ 'variableList' | 'playlistParsingError'
46
+ >,
47
+ attr: AttrList,
48
+ parentUrl: string,
49
+ ) {
50
+ let variableList = parsed.variableList;
51
+ if (!variableList) {
52
+ parsed.variableList = variableList = {};
53
+ }
54
+ let NAME: string;
55
+ let VALUE;
56
+ if ('QUERYPARAM' in attr) {
57
+ NAME = attr.QUERYPARAM;
58
+ try {
59
+ const searchParams = new self.URL(parentUrl).searchParams;
60
+ if (searchParams.has(NAME)) {
61
+ VALUE = searchParams.get(NAME);
62
+ } else {
63
+ throw new Error(
64
+ `"${NAME}" does not match any query parameter in URI: "${parentUrl}"`,
65
+ );
66
+ }
67
+ } catch (error) {
68
+ parsed.playlistParsingError ||= new Error(
69
+ `EXT-X-DEFINE QUERYPARAM: ${error.message}`,
70
+ );
71
+ }
72
+ } else {
73
+ NAME = attr.NAME;
74
+ VALUE = attr.VALUE;
75
+ }
76
+ if (NAME in variableList) {
77
+ parsed.playlistParsingError ||= new Error(
78
+ `EXT-X-DEFINE duplicate Variable Name declarations: "${NAME}"`,
79
+ );
80
+ } else {
81
+ variableList[NAME] = VALUE || '';
82
+ }
83
+ }
84
+
85
+ export function importVariableDefinition(
86
+ parsed: Pick<
87
+ ParsedMultivariantPlaylist | LevelDetails,
88
+ 'variableList' | 'playlistParsingError'
89
+ >,
90
+ attr: AttrList,
91
+ sourceVariableList: VariableMap | null,
92
+ ) {
93
+ const IMPORT = attr.IMPORT;
94
+ if (sourceVariableList && IMPORT in sourceVariableList) {
95
+ let variableList = parsed.variableList;
96
+ if (!variableList) {
97
+ parsed.variableList = variableList = {};
98
+ }
99
+ variableList[IMPORT] = sourceVariableList[IMPORT];
100
+ } else {
101
+ parsed.playlistParsingError ||= new Error(
102
+ `EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${IMPORT}"`,
103
+ );
104
+ }
105
+ }