@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,560 @@
1
+ import AACDemuxer from './audio/aacdemuxer';
2
+ import { AC3Demuxer } from './audio/ac3-demuxer';
3
+ import MP3Demuxer from './audio/mp3demuxer';
4
+ import Decrypter from '../crypt/decrypter';
5
+ import MP4Demuxer from '../demux/mp4demuxer';
6
+ import TSDemuxer from '../demux/tsdemuxer';
7
+ import { ErrorDetails, ErrorTypes } from '../errors';
8
+ import { Events } from '../events';
9
+ import MP4Remuxer from '../remux/mp4-remuxer';
10
+ import PassThroughRemuxer from '../remux/passthrough-remuxer';
11
+ import { PlaylistLevelType } from '../types/loader';
12
+ import {
13
+ getAesModeFromFullSegmentMethod,
14
+ isFullSegmentEncryption,
15
+ } from '../utils/encryption-methods-util';
16
+ import type { HlsConfig } from '../config';
17
+ import type { HlsEventEmitter } from '../events';
18
+ import type { DecryptData } from '../loader/level-key';
19
+ import type { Demuxer, DemuxerResult, KeyData } from '../types/demuxer';
20
+ import type { Remuxer } from '../types/remuxer';
21
+ import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer';
22
+ import type { TypeSupported } from '../utils/codecs';
23
+ import type { ILogger } from '../utils/logger';
24
+ import type { TimestampOffset } from '../utils/timescale-conversion';
25
+
26
+ let now: () => number;
27
+ // performance.now() not available on WebWorker, at least on Safari Desktop
28
+ try {
29
+ now = self.performance.now.bind(self.performance);
30
+ } catch (err) {
31
+ now = Date.now;
32
+ }
33
+
34
+ type MuxConfig =
35
+ | { demux: typeof MP4Demuxer; remux: typeof PassThroughRemuxer }
36
+ | { demux: typeof TSDemuxer; remux: typeof MP4Remuxer }
37
+ | { demux: typeof AC3Demuxer; remux: typeof MP4Remuxer }
38
+ | { demux: typeof AACDemuxer; remux: typeof MP4Remuxer }
39
+ | { demux: typeof MP3Demuxer; remux: typeof MP4Remuxer };
40
+
41
+ const muxConfig: MuxConfig[] = [
42
+ { demux: MP4Demuxer, remux: PassThroughRemuxer },
43
+ { demux: TSDemuxer, remux: MP4Remuxer },
44
+ { demux: AACDemuxer, remux: MP4Remuxer },
45
+ { demux: MP3Demuxer, remux: MP4Remuxer },
46
+ ];
47
+
48
+ if (__USE_M2TS_ADVANCED_CODECS__) {
49
+ muxConfig.splice(2, 0, { demux: AC3Demuxer, remux: MP4Remuxer });
50
+ }
51
+
52
+ export default class Transmuxer {
53
+ private asyncResult: boolean = false;
54
+ private logger: ILogger;
55
+ private observer: HlsEventEmitter;
56
+ private typeSupported: TypeSupported;
57
+ private config: HlsConfig;
58
+ private id: PlaylistLevelType;
59
+ private demuxer?: Demuxer;
60
+ private remuxer?: Remuxer;
61
+ private decrypter?: Decrypter;
62
+ private probe!: Function;
63
+ private decryptionPromise: Promise<TransmuxerResult> | null = null;
64
+ private transmuxConfig!: TransmuxConfig;
65
+ private currentTransmuxState!: TransmuxState;
66
+
67
+ constructor(
68
+ observer: HlsEventEmitter,
69
+ typeSupported: TypeSupported,
70
+ config: HlsConfig,
71
+ vendor: string,
72
+ id: PlaylistLevelType,
73
+ logger: ILogger,
74
+ ) {
75
+ this.observer = observer;
76
+ this.typeSupported = typeSupported;
77
+ this.config = config;
78
+ this.id = id;
79
+ this.logger = logger;
80
+ }
81
+
82
+ configure(transmuxConfig: TransmuxConfig) {
83
+ this.transmuxConfig = transmuxConfig;
84
+ if (this.decrypter) {
85
+ this.decrypter.reset();
86
+ }
87
+ }
88
+
89
+ push(
90
+ data: ArrayBuffer,
91
+ decryptdata: DecryptData | null,
92
+ chunkMeta: ChunkMetadata,
93
+ state?: TransmuxState,
94
+ ): TransmuxerResult | Promise<TransmuxerResult> {
95
+ const stats = chunkMeta.transmuxing;
96
+ stats.executeStart = now();
97
+
98
+ let uintData: Uint8Array<ArrayBuffer> = new Uint8Array(data);
99
+ const { currentTransmuxState, transmuxConfig } = this;
100
+ if (state) {
101
+ this.currentTransmuxState = state;
102
+ }
103
+
104
+ const {
105
+ contiguous,
106
+ discontinuity,
107
+ trackSwitch,
108
+ accurateTimeOffset,
109
+ timeOffset,
110
+ initSegmentChange,
111
+ } = state || currentTransmuxState;
112
+ const {
113
+ audioCodec,
114
+ videoCodec,
115
+ defaultInitPts,
116
+ duration,
117
+ initSegmentData,
118
+ } = transmuxConfig;
119
+
120
+ const keyData = getEncryptionType(uintData, decryptdata);
121
+ if (keyData && isFullSegmentEncryption(keyData.method)) {
122
+ const decrypter = this.getDecrypter();
123
+ const aesMode = getAesModeFromFullSegmentMethod(keyData.method);
124
+
125
+ // Software decryption is synchronous; webCrypto is not
126
+ if (decrypter.isSync()) {
127
+ // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached
128
+ // data is handled in the flush() call
129
+ let decryptedData = decrypter.softwareDecrypt(
130
+ uintData,
131
+ keyData.key.buffer,
132
+ keyData.iv.buffer,
133
+ aesMode,
134
+ );
135
+ // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress
136
+ const loadingParts = chunkMeta.part > -1;
137
+ if (loadingParts) {
138
+ const data = decrypter.flush();
139
+ decryptedData = data ? data.buffer : data;
140
+ }
141
+ if (!decryptedData) {
142
+ stats.executeEnd = now();
143
+ return emptyResult(chunkMeta);
144
+ }
145
+ uintData = new Uint8Array(decryptedData);
146
+ } else {
147
+ this.asyncResult = true;
148
+ this.decryptionPromise = decrypter
149
+ .webCryptoDecrypt(
150
+ uintData,
151
+ keyData.key.buffer,
152
+ keyData.iv.buffer,
153
+ aesMode,
154
+ )
155
+ .then((decryptedData): TransmuxerResult => {
156
+ // Calling push here is important; if flush() is called while this is still resolving, this ensures that
157
+ // the decrypted data has been transmuxed
158
+ const result = this.push(
159
+ decryptedData,
160
+ null,
161
+ chunkMeta,
162
+ ) as TransmuxerResult;
163
+ this.decryptionPromise = null;
164
+ return result;
165
+ });
166
+ return this.decryptionPromise;
167
+ }
168
+ }
169
+
170
+ const resetMuxers = this.needsProbing(discontinuity, trackSwitch);
171
+ if (resetMuxers) {
172
+ const error = this.configureTransmuxer(uintData);
173
+ if (error) {
174
+ this.logger.warn(`[transmuxer] ${error.message}`);
175
+ this.observer.emit(Events.ERROR, Events.ERROR, {
176
+ type: ErrorTypes.MEDIA_ERROR,
177
+ details: ErrorDetails.FRAG_PARSING_ERROR,
178
+ fatal: false,
179
+ error,
180
+ reason: error.message,
181
+ });
182
+ stats.executeEnd = now();
183
+ return emptyResult(chunkMeta);
184
+ }
185
+ }
186
+
187
+ if (discontinuity || trackSwitch || initSegmentChange || resetMuxers) {
188
+ this.resetInitSegment(
189
+ initSegmentData,
190
+ audioCodec,
191
+ videoCodec,
192
+ duration,
193
+ decryptdata,
194
+ );
195
+ }
196
+
197
+ if (discontinuity || initSegmentChange || resetMuxers) {
198
+ this.resetInitialTimestamp(defaultInitPts);
199
+ }
200
+
201
+ if (!contiguous) {
202
+ this.resetContiguity();
203
+ }
204
+
205
+ const result = this.transmux(
206
+ uintData,
207
+ keyData,
208
+ timeOffset,
209
+ accurateTimeOffset,
210
+ chunkMeta,
211
+ );
212
+ this.asyncResult = isPromise(result);
213
+
214
+ const currentState = this.currentTransmuxState;
215
+
216
+ currentState.contiguous = true;
217
+ currentState.discontinuity = false;
218
+ currentState.trackSwitch = false;
219
+
220
+ stats.executeEnd = now();
221
+ return result;
222
+ }
223
+
224
+ // Due to data caching, flush calls can produce more than one TransmuxerResult (hence the Array type)
225
+ flush(
226
+ chunkMeta: ChunkMetadata,
227
+ ): TransmuxerResult[] | Promise<TransmuxerResult[]> {
228
+ const stats = chunkMeta.transmuxing;
229
+ stats.executeStart = now();
230
+
231
+ const { decrypter, currentTransmuxState, decryptionPromise } = this;
232
+
233
+ if (decryptionPromise) {
234
+ this.asyncResult = true;
235
+ // Upon resolution, the decryption promise calls push() and returns its TransmuxerResult up the stack. Therefore
236
+ // only flushing is required for async decryption
237
+ return decryptionPromise.then(() => {
238
+ return this.flush(chunkMeta);
239
+ });
240
+ }
241
+
242
+ const transmuxResults: TransmuxerResult[] = [];
243
+ const { timeOffset } = currentTransmuxState;
244
+ if (decrypter) {
245
+ // The decrypter may have data cached, which needs to be demuxed. In this case we'll have two TransmuxResults
246
+ // This happens in the case that we receive only 1 push call for a segment (either for non-progressive downloads,
247
+ // or for progressive downloads with small segments)
248
+ const decryptedData = decrypter.flush();
249
+ if (decryptedData) {
250
+ // Push always returns a TransmuxerResult if decryptdata is null
251
+ transmuxResults.push(
252
+ this.push(decryptedData.buffer, null, chunkMeta) as TransmuxerResult,
253
+ );
254
+ }
255
+ }
256
+
257
+ const { demuxer, remuxer } = this;
258
+ if (!demuxer || !remuxer) {
259
+ // If probing failed, then Hls.js has been given content its not able to handle
260
+ stats.executeEnd = now();
261
+ const emptyResults = [emptyResult(chunkMeta)];
262
+ if (this.asyncResult) {
263
+ return Promise.resolve(emptyResults);
264
+ }
265
+ return emptyResults;
266
+ }
267
+
268
+ const demuxResultOrPromise = demuxer.flush(timeOffset);
269
+ if (isPromise(demuxResultOrPromise)) {
270
+ this.asyncResult = true;
271
+ // Decrypt final SAMPLE-AES samples
272
+ return demuxResultOrPromise.then((demuxResult) => {
273
+ this.flushRemux(transmuxResults, demuxResult, chunkMeta);
274
+ return transmuxResults;
275
+ });
276
+ }
277
+
278
+ this.flushRemux(transmuxResults, demuxResultOrPromise, chunkMeta);
279
+ if (this.asyncResult) {
280
+ return Promise.resolve(transmuxResults);
281
+ }
282
+ return transmuxResults;
283
+ }
284
+
285
+ private flushRemux(
286
+ transmuxResults: TransmuxerResult[],
287
+ demuxResult: DemuxerResult,
288
+ chunkMeta: ChunkMetadata,
289
+ ) {
290
+ const { audioTrack, videoTrack, id3Track, textTrack } = demuxResult;
291
+ const { accurateTimeOffset, timeOffset } = this.currentTransmuxState;
292
+ this.logger.log(
293
+ `[transmuxer.ts]: Flushed ${this.id} sn: ${chunkMeta.sn}${
294
+ chunkMeta.part > -1 ? ' part: ' + chunkMeta.part : ''
295
+ } of ${this.id === PlaylistLevelType.MAIN ? 'level' : 'track'} ${chunkMeta.level}`,
296
+ );
297
+ const remuxResult = this.remuxer!.remux(
298
+ audioTrack,
299
+ videoTrack,
300
+ id3Track,
301
+ textTrack,
302
+ timeOffset,
303
+ accurateTimeOffset,
304
+ true,
305
+ this.id,
306
+ );
307
+ transmuxResults.push({
308
+ remuxResult,
309
+ chunkMeta,
310
+ });
311
+
312
+ chunkMeta.transmuxing.executeEnd = now();
313
+ }
314
+
315
+ resetInitialTimestamp(defaultInitPts: TimestampOffset | null) {
316
+ const { demuxer, remuxer } = this;
317
+ if (!demuxer || !remuxer) {
318
+ return;
319
+ }
320
+ demuxer.resetTimeStamp(defaultInitPts);
321
+ remuxer.resetTimeStamp(defaultInitPts);
322
+ }
323
+
324
+ resetContiguity() {
325
+ const { demuxer, remuxer } = this;
326
+ if (!demuxer || !remuxer) {
327
+ return;
328
+ }
329
+ demuxer.resetContiguity();
330
+ remuxer.resetNextTimestamp();
331
+ }
332
+
333
+ resetInitSegment(
334
+ initSegmentData: Uint8Array | undefined,
335
+ audioCodec: string | undefined,
336
+ videoCodec: string | undefined,
337
+ trackDuration: number,
338
+ decryptdata: DecryptData | null,
339
+ ) {
340
+ const { demuxer, remuxer } = this;
341
+ if (!demuxer || !remuxer) {
342
+ return;
343
+ }
344
+ demuxer.resetInitSegment(
345
+ initSegmentData,
346
+ audioCodec,
347
+ videoCodec,
348
+ trackDuration,
349
+ );
350
+ remuxer.resetInitSegment(
351
+ initSegmentData,
352
+ audioCodec,
353
+ videoCodec,
354
+ decryptdata,
355
+ );
356
+ }
357
+
358
+ destroy(): void {
359
+ if (this.demuxer) {
360
+ this.demuxer.destroy();
361
+ this.demuxer = undefined;
362
+ }
363
+ if (this.remuxer) {
364
+ this.remuxer.destroy();
365
+ this.remuxer = undefined;
366
+ }
367
+ }
368
+
369
+ private transmux(
370
+ data: Uint8Array,
371
+ keyData: KeyData | null,
372
+ timeOffset: number,
373
+ accurateTimeOffset: boolean,
374
+ chunkMeta: ChunkMetadata,
375
+ ): TransmuxerResult | Promise<TransmuxerResult> {
376
+ let result: TransmuxerResult | Promise<TransmuxerResult>;
377
+ if (keyData?.method === 'SAMPLE-AES') {
378
+ result = this.transmuxSampleAes(
379
+ data,
380
+ keyData,
381
+ timeOffset,
382
+ accurateTimeOffset,
383
+ chunkMeta,
384
+ );
385
+ } else {
386
+ result = this.transmuxUnencrypted(
387
+ data,
388
+ timeOffset,
389
+ accurateTimeOffset,
390
+ chunkMeta,
391
+ );
392
+ }
393
+ return result;
394
+ }
395
+
396
+ private transmuxUnencrypted(
397
+ data: Uint8Array,
398
+ timeOffset: number,
399
+ accurateTimeOffset: boolean,
400
+ chunkMeta: ChunkMetadata,
401
+ ): TransmuxerResult {
402
+ const { audioTrack, videoTrack, id3Track, textTrack } = (
403
+ this.demuxer as Demuxer
404
+ ).demux(data, timeOffset, false, !this.config.progressive);
405
+ const remuxResult = this.remuxer!.remux(
406
+ audioTrack,
407
+ videoTrack,
408
+ id3Track,
409
+ textTrack,
410
+ timeOffset,
411
+ accurateTimeOffset,
412
+ false,
413
+ this.id,
414
+ );
415
+ return {
416
+ remuxResult,
417
+ chunkMeta,
418
+ };
419
+ }
420
+
421
+ private transmuxSampleAes(
422
+ data: Uint8Array,
423
+ decryptData: KeyData,
424
+ timeOffset: number,
425
+ accurateTimeOffset: boolean,
426
+ chunkMeta: ChunkMetadata,
427
+ ): Promise<TransmuxerResult> {
428
+ return (this.demuxer as Demuxer)
429
+ .demuxSampleAes(data, decryptData, timeOffset)
430
+ .then((demuxResult) => {
431
+ const remuxResult = this.remuxer!.remux(
432
+ demuxResult.audioTrack,
433
+ demuxResult.videoTrack,
434
+ demuxResult.id3Track,
435
+ demuxResult.textTrack,
436
+ timeOffset,
437
+ accurateTimeOffset,
438
+ false,
439
+ this.id,
440
+ );
441
+ return {
442
+ remuxResult,
443
+ chunkMeta,
444
+ };
445
+ });
446
+ }
447
+
448
+ private configureTransmuxer(data: Uint8Array): void | Error {
449
+ const { config, observer, typeSupported } = this;
450
+ // probe for content type
451
+ let mux;
452
+ for (let i = 0, len = muxConfig.length; i < len; i++) {
453
+ if (muxConfig[i].demux?.probe(data, this.logger)) {
454
+ mux = muxConfig[i];
455
+ break;
456
+ }
457
+ }
458
+ if (!mux) {
459
+ return new Error('Failed to find demuxer by probing fragment data');
460
+ }
461
+ // so let's check that current remuxer and demuxer are still valid
462
+ const demuxer = this.demuxer;
463
+ const remuxer = this.remuxer;
464
+ const Remuxer: MuxConfig['remux'] = mux.remux;
465
+ const Demuxer: MuxConfig['demux'] = mux.demux;
466
+ if (!remuxer || !(remuxer instanceof Remuxer)) {
467
+ this.remuxer = new Remuxer(observer, config, typeSupported, this.logger);
468
+ }
469
+ if (!demuxer || !(demuxer instanceof Demuxer)) {
470
+ this.demuxer = new Demuxer(observer, config, typeSupported, this.logger);
471
+ this.probe = Demuxer.probe;
472
+ }
473
+ }
474
+
475
+ private needsProbing(discontinuity: boolean, trackSwitch: boolean): boolean {
476
+ // in case of continuity change, or track switch
477
+ // we might switch from content type (AAC container to TS container, or TS to fmp4 for example)
478
+ return !this.demuxer || !this.remuxer || discontinuity || trackSwitch;
479
+ }
480
+
481
+ private getDecrypter(): Decrypter {
482
+ let decrypter = this.decrypter;
483
+ if (!decrypter) {
484
+ decrypter = this.decrypter = new Decrypter(this.config);
485
+ }
486
+ return decrypter;
487
+ }
488
+ }
489
+
490
+ function getEncryptionType(
491
+ data: Uint8Array,
492
+ decryptData: DecryptData | null,
493
+ ): KeyData | null {
494
+ let encryptionType: KeyData | null = null;
495
+ if (
496
+ data.byteLength > 0 &&
497
+ decryptData?.key != null &&
498
+ decryptData.iv !== null &&
499
+ decryptData.method != null
500
+ ) {
501
+ encryptionType = decryptData as KeyData;
502
+ }
503
+ return encryptionType;
504
+ }
505
+
506
+ const emptyResult = (chunkMeta): TransmuxerResult => ({
507
+ remuxResult: {},
508
+ chunkMeta,
509
+ });
510
+
511
+ export function isPromise<T>(p: Promise<T> | any): p is Promise<T> {
512
+ return 'then' in p && p.then instanceof Function;
513
+ }
514
+
515
+ export class TransmuxConfig {
516
+ public audioCodec?: string;
517
+ public videoCodec?: string;
518
+ public initSegmentData?: Uint8Array;
519
+ public duration: number;
520
+ public defaultInitPts: TimestampOffset | null;
521
+
522
+ constructor(
523
+ audioCodec: string | undefined,
524
+ videoCodec: string | undefined,
525
+ initSegmentData: Uint8Array | undefined,
526
+ duration: number,
527
+ defaultInitPts?: TimestampOffset,
528
+ ) {
529
+ this.audioCodec = audioCodec;
530
+ this.videoCodec = videoCodec;
531
+ this.initSegmentData = initSegmentData;
532
+ this.duration = duration;
533
+ this.defaultInitPts = defaultInitPts || null;
534
+ }
535
+ }
536
+
537
+ export class TransmuxState {
538
+ public discontinuity: boolean;
539
+ public contiguous: boolean;
540
+ public accurateTimeOffset: boolean;
541
+ public trackSwitch: boolean;
542
+ public timeOffset: number;
543
+ public initSegmentChange: boolean;
544
+
545
+ constructor(
546
+ discontinuity: boolean,
547
+ contiguous: boolean,
548
+ accurateTimeOffset: boolean,
549
+ trackSwitch: boolean,
550
+ timeOffset: number,
551
+ initSegmentChange: boolean,
552
+ ) {
553
+ this.discontinuity = discontinuity;
554
+ this.contiguous = contiguous;
555
+ this.accurateTimeOffset = accurateTimeOffset;
556
+ this.trackSwitch = trackSwitch;
557
+ this.timeOffset = timeOffset;
558
+ this.initSegmentChange = initSegmentChange;
559
+ }
560
+ }