@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,1380 @@
1
+ /**
2
+ * Generate MP4 Box
3
+ */
4
+
5
+ import { appendUint8Array } from '../utils/mp4-tools';
6
+ import type {
7
+ DemuxedAC3,
8
+ DemuxedAudioTrack,
9
+ DemuxedAVC1,
10
+ DemuxedHEVC,
11
+ DemuxedVideoTrack,
12
+ } from '../types/demuxer';
13
+ import type {
14
+ Mp4SampleFlags,
15
+ RemuxedAudioTrackSamples,
16
+ RemuxedVideoTrackSamples,
17
+ } from '../types/remuxer';
18
+
19
+ type MediaTrackType = DemuxedAudioTrack | DemuxedVideoTrack;
20
+ type RemuxedTrackType = RemuxedAudioTrackSamples | RemuxedVideoTrackSamples;
21
+
22
+ type HdlrTypes = {
23
+ video: Uint8Array;
24
+ audio: Uint8Array;
25
+ };
26
+
27
+ const UINT32_MAX = Math.pow(2, 32) - 1;
28
+
29
+ class MP4 {
30
+ public static types: Record<string, number[]>;
31
+ private static HDLR_TYPES: HdlrTypes;
32
+ private static STTS: Uint8Array;
33
+ private static STSC: Uint8Array;
34
+ private static STCO: Uint8Array;
35
+ private static STSZ: Uint8Array;
36
+ private static VMHD: Uint8Array;
37
+ private static SMHD: Uint8Array;
38
+ private static STSD: Uint8Array;
39
+ private static FTYP: Uint8Array;
40
+ private static DINF: Uint8Array;
41
+
42
+ static init() {
43
+ MP4.types = {
44
+ avc1: [], // codingname
45
+ avcC: [],
46
+ hvc1: [],
47
+ hvcC: [],
48
+ btrt: [],
49
+ dinf: [],
50
+ dref: [],
51
+ esds: [],
52
+ ftyp: [],
53
+ hdlr: [],
54
+ mdat: [],
55
+ mdhd: [],
56
+ mdia: [],
57
+ mfhd: [],
58
+ minf: [],
59
+ moof: [],
60
+ moov: [],
61
+ mp4a: [],
62
+ '.mp3': [],
63
+ dac3: [],
64
+ 'ac-3': [],
65
+ mvex: [],
66
+ mvhd: [],
67
+ pasp: [],
68
+ sdtp: [],
69
+ stbl: [],
70
+ stco: [],
71
+ stsc: [],
72
+ stsd: [],
73
+ stsz: [],
74
+ stts: [],
75
+ tfdt: [],
76
+ tfhd: [],
77
+ traf: [],
78
+ trak: [],
79
+ trun: [],
80
+ trex: [],
81
+ tkhd: [],
82
+ vmhd: [],
83
+ smhd: [],
84
+ };
85
+
86
+ let i: string;
87
+ for (i in MP4.types) {
88
+ if (MP4.types.hasOwnProperty(i)) {
89
+ MP4.types[i] = [
90
+ i.charCodeAt(0),
91
+ i.charCodeAt(1),
92
+ i.charCodeAt(2),
93
+ i.charCodeAt(3),
94
+ ];
95
+ }
96
+ }
97
+
98
+ const videoHdlr = new Uint8Array([
99
+ 0x00, // version 0
100
+ 0x00,
101
+ 0x00,
102
+ 0x00, // flags
103
+ 0x00,
104
+ 0x00,
105
+ 0x00,
106
+ 0x00, // pre_defined
107
+ 0x76,
108
+ 0x69,
109
+ 0x64,
110
+ 0x65, // handler_type: 'vide'
111
+ 0x00,
112
+ 0x00,
113
+ 0x00,
114
+ 0x00, // reserved
115
+ 0x00,
116
+ 0x00,
117
+ 0x00,
118
+ 0x00, // reserved
119
+ 0x00,
120
+ 0x00,
121
+ 0x00,
122
+ 0x00, // reserved
123
+ 0x56,
124
+ 0x69,
125
+ 0x64,
126
+ 0x65,
127
+ 0x6f,
128
+ 0x48,
129
+ 0x61,
130
+ 0x6e,
131
+ 0x64,
132
+ 0x6c,
133
+ 0x65,
134
+ 0x72,
135
+ 0x00, // name: 'VideoHandler'
136
+ ]);
137
+
138
+ const audioHdlr = new Uint8Array([
139
+ 0x00, // version 0
140
+ 0x00,
141
+ 0x00,
142
+ 0x00, // flags
143
+ 0x00,
144
+ 0x00,
145
+ 0x00,
146
+ 0x00, // pre_defined
147
+ 0x73,
148
+ 0x6f,
149
+ 0x75,
150
+ 0x6e, // handler_type: 'soun'
151
+ 0x00,
152
+ 0x00,
153
+ 0x00,
154
+ 0x00, // reserved
155
+ 0x00,
156
+ 0x00,
157
+ 0x00,
158
+ 0x00, // reserved
159
+ 0x00,
160
+ 0x00,
161
+ 0x00,
162
+ 0x00, // reserved
163
+ 0x53,
164
+ 0x6f,
165
+ 0x75,
166
+ 0x6e,
167
+ 0x64,
168
+ 0x48,
169
+ 0x61,
170
+ 0x6e,
171
+ 0x64,
172
+ 0x6c,
173
+ 0x65,
174
+ 0x72,
175
+ 0x00, // name: 'SoundHandler'
176
+ ]);
177
+
178
+ MP4.HDLR_TYPES = {
179
+ video: videoHdlr,
180
+ audio: audioHdlr,
181
+ };
182
+
183
+ const dref = new Uint8Array([
184
+ 0x00, // version 0
185
+ 0x00,
186
+ 0x00,
187
+ 0x00, // flags
188
+ 0x00,
189
+ 0x00,
190
+ 0x00,
191
+ 0x01, // entry_count
192
+ 0x00,
193
+ 0x00,
194
+ 0x00,
195
+ 0x0c, // entry_size
196
+ 0x75,
197
+ 0x72,
198
+ 0x6c,
199
+ 0x20, // 'url' type
200
+ 0x00, // version 0
201
+ 0x00,
202
+ 0x00,
203
+ 0x01, // entry_flags
204
+ ]);
205
+
206
+ const stco = new Uint8Array([
207
+ 0x00, // version
208
+ 0x00,
209
+ 0x00,
210
+ 0x00, // flags
211
+ 0x00,
212
+ 0x00,
213
+ 0x00,
214
+ 0x00, // entry_count
215
+ ]);
216
+
217
+ MP4.STTS = MP4.STSC = MP4.STCO = stco;
218
+
219
+ MP4.STSZ = new Uint8Array([
220
+ 0x00, // version
221
+ 0x00,
222
+ 0x00,
223
+ 0x00, // flags
224
+ 0x00,
225
+ 0x00,
226
+ 0x00,
227
+ 0x00, // sample_size
228
+ 0x00,
229
+ 0x00,
230
+ 0x00,
231
+ 0x00, // sample_count
232
+ ]);
233
+ MP4.VMHD = new Uint8Array([
234
+ 0x00, // version
235
+ 0x00,
236
+ 0x00,
237
+ 0x01, // flags
238
+ 0x00,
239
+ 0x00, // graphicsmode
240
+ 0x00,
241
+ 0x00,
242
+ 0x00,
243
+ 0x00,
244
+ 0x00,
245
+ 0x00, // opcolor
246
+ ]);
247
+ MP4.SMHD = new Uint8Array([
248
+ 0x00, // version
249
+ 0x00,
250
+ 0x00,
251
+ 0x00, // flags
252
+ 0x00,
253
+ 0x00, // balance
254
+ 0x00,
255
+ 0x00, // reserved
256
+ ]);
257
+
258
+ MP4.STSD = new Uint8Array([
259
+ 0x00, // version 0
260
+ 0x00,
261
+ 0x00,
262
+ 0x00, // flags
263
+ 0x00,
264
+ 0x00,
265
+ 0x00,
266
+ 0x01,
267
+ ]); // entry_count
268
+
269
+ const majorBrand = new Uint8Array([105, 115, 111, 109]); // isom
270
+ const avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1
271
+ const minorVersion = new Uint8Array([0, 0, 0, 1]);
272
+
273
+ MP4.FTYP = MP4.box(
274
+ MP4.types.ftyp,
275
+ majorBrand,
276
+ minorVersion,
277
+ majorBrand,
278
+ avc1Brand,
279
+ );
280
+ MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref));
281
+ }
282
+
283
+ static box(type: number[], ...payload: Uint8Array[]) {
284
+ let size = 8;
285
+ let i = payload.length;
286
+ const len = i;
287
+ // calculate the total size we need to allocate
288
+ while (i--) {
289
+ size += payload[i].byteLength;
290
+ }
291
+
292
+ const result = new Uint8Array(size);
293
+ result[0] = (size >> 24) & 0xff;
294
+ result[1] = (size >> 16) & 0xff;
295
+ result[2] = (size >> 8) & 0xff;
296
+ result[3] = size & 0xff;
297
+ result.set(type, 4);
298
+ // copy the payload into the result
299
+ for (i = 0, size = 8; i < len; i++) {
300
+ // copy payload[i] array @ offset size
301
+ result.set(payload[i], size);
302
+ size += payload[i].byteLength;
303
+ }
304
+ return result;
305
+ }
306
+
307
+ static hdlr(type: keyof HdlrTypes) {
308
+ return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]);
309
+ }
310
+
311
+ static mdat(data: Uint8Array) {
312
+ return MP4.box(MP4.types.mdat, data);
313
+ }
314
+
315
+ static mdhd(timescale: number, duration: number) {
316
+ duration *= timescale;
317
+ const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));
318
+ const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
319
+ return MP4.box(
320
+ MP4.types.mdhd,
321
+ new Uint8Array([
322
+ 0x01, // version 1
323
+ 0x00,
324
+ 0x00,
325
+ 0x00, // flags
326
+ 0x00,
327
+ 0x00,
328
+ 0x00,
329
+ 0x00,
330
+ 0x00,
331
+ 0x00,
332
+ 0x00,
333
+ 0x02, // creation_time
334
+ 0x00,
335
+ 0x00,
336
+ 0x00,
337
+ 0x00,
338
+ 0x00,
339
+ 0x00,
340
+ 0x00,
341
+ 0x03, // modification_time
342
+ (timescale >> 24) & 0xff,
343
+ (timescale >> 16) & 0xff,
344
+ (timescale >> 8) & 0xff,
345
+ timescale & 0xff, // timescale
346
+ upperWordDuration >> 24,
347
+ (upperWordDuration >> 16) & 0xff,
348
+ (upperWordDuration >> 8) & 0xff,
349
+ upperWordDuration & 0xff,
350
+ lowerWordDuration >> 24,
351
+ (lowerWordDuration >> 16) & 0xff,
352
+ (lowerWordDuration >> 8) & 0xff,
353
+ lowerWordDuration & 0xff,
354
+ 0x55,
355
+ 0xc4, // 'und' language (undetermined)
356
+ 0x00,
357
+ 0x00,
358
+ ]),
359
+ );
360
+ }
361
+
362
+ static mdia(track: MediaTrackType) {
363
+ return MP4.box(
364
+ MP4.types.mdia,
365
+ MP4.mdhd(track.timescale || 0, track.duration || 0),
366
+ MP4.hdlr(track.type),
367
+ MP4.minf(track),
368
+ );
369
+ }
370
+
371
+ static mfhd(sequenceNumber: number) {
372
+ return MP4.box(
373
+ MP4.types.mfhd,
374
+ new Uint8Array([
375
+ 0x00,
376
+ 0x00,
377
+ 0x00,
378
+ 0x00, // flags
379
+ sequenceNumber >> 24,
380
+ (sequenceNumber >> 16) & 0xff,
381
+ (sequenceNumber >> 8) & 0xff,
382
+ sequenceNumber & 0xff, // sequence_number
383
+ ]),
384
+ );
385
+ }
386
+
387
+ static minf(track: MediaTrackType) {
388
+ if (track.type === 'audio') {
389
+ return MP4.box(
390
+ MP4.types.minf,
391
+ MP4.box(MP4.types.smhd, MP4.SMHD),
392
+ MP4.DINF,
393
+ MP4.stbl(track),
394
+ );
395
+ } else {
396
+ return MP4.box(
397
+ MP4.types.minf,
398
+ MP4.box(MP4.types.vmhd, MP4.VMHD),
399
+ MP4.DINF,
400
+ MP4.stbl(track),
401
+ );
402
+ }
403
+ }
404
+
405
+ static moof(
406
+ sn: number,
407
+ baseMediaDecodeTime: number,
408
+ track: RemuxedTrackType,
409
+ ) {
410
+ return MP4.box(
411
+ MP4.types.moof,
412
+ MP4.mfhd(sn),
413
+ MP4.traf(track, baseMediaDecodeTime),
414
+ );
415
+ }
416
+
417
+ static moov(tracks: MediaTrackType[]) {
418
+ let i = tracks.length;
419
+ const boxes: Uint8Array<ArrayBuffer>[] = [];
420
+
421
+ while (i--) {
422
+ boxes[i] = MP4.trak(tracks[i]);
423
+ }
424
+
425
+ return MP4.box.apply(
426
+ null,
427
+ [
428
+ MP4.types.moov,
429
+ MP4.mvhd(tracks[0].timescale || 0, tracks[0].duration || 0),
430
+ ]
431
+ .concat(boxes)
432
+ .concat(MP4.mvex(tracks)),
433
+ );
434
+ }
435
+
436
+ static mvex(tracks: MediaTrackType[]) {
437
+ let i = tracks.length;
438
+ const boxes: Uint8Array[] = [];
439
+
440
+ while (i--) {
441
+ boxes[i] = MP4.trex(tracks[i]);
442
+ }
443
+
444
+ return MP4.box.apply(null, [MP4.types.mvex, ...boxes]);
445
+ }
446
+
447
+ static mvhd(timescale: number, duration: number) {
448
+ duration *= timescale;
449
+ const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));
450
+ const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
451
+ const bytes = new Uint8Array([
452
+ 0x01, // version 1
453
+ 0x00,
454
+ 0x00,
455
+ 0x00, // flags
456
+ 0x00,
457
+ 0x00,
458
+ 0x00,
459
+ 0x00,
460
+ 0x00,
461
+ 0x00,
462
+ 0x00,
463
+ 0x02, // creation_time
464
+ 0x00,
465
+ 0x00,
466
+ 0x00,
467
+ 0x00,
468
+ 0x00,
469
+ 0x00,
470
+ 0x00,
471
+ 0x03, // modification_time
472
+ (timescale >> 24) & 0xff,
473
+ (timescale >> 16) & 0xff,
474
+ (timescale >> 8) & 0xff,
475
+ timescale & 0xff, // timescale
476
+ upperWordDuration >> 24,
477
+ (upperWordDuration >> 16) & 0xff,
478
+ (upperWordDuration >> 8) & 0xff,
479
+ upperWordDuration & 0xff,
480
+ lowerWordDuration >> 24,
481
+ (lowerWordDuration >> 16) & 0xff,
482
+ (lowerWordDuration >> 8) & 0xff,
483
+ lowerWordDuration & 0xff,
484
+ 0x00,
485
+ 0x01,
486
+ 0x00,
487
+ 0x00, // 1.0 rate
488
+ 0x01,
489
+ 0x00, // 1.0 volume
490
+ 0x00,
491
+ 0x00, // reserved
492
+ 0x00,
493
+ 0x00,
494
+ 0x00,
495
+ 0x00, // reserved
496
+ 0x00,
497
+ 0x00,
498
+ 0x00,
499
+ 0x00, // reserved
500
+ 0x00,
501
+ 0x01,
502
+ 0x00,
503
+ 0x00,
504
+ 0x00,
505
+ 0x00,
506
+ 0x00,
507
+ 0x00,
508
+ 0x00,
509
+ 0x00,
510
+ 0x00,
511
+ 0x00,
512
+ 0x00,
513
+ 0x00,
514
+ 0x00,
515
+ 0x00,
516
+ 0x00,
517
+ 0x01,
518
+ 0x00,
519
+ 0x00,
520
+ 0x00,
521
+ 0x00,
522
+ 0x00,
523
+ 0x00,
524
+ 0x00,
525
+ 0x00,
526
+ 0x00,
527
+ 0x00,
528
+ 0x00,
529
+ 0x00,
530
+ 0x00,
531
+ 0x00,
532
+ 0x40,
533
+ 0x00,
534
+ 0x00,
535
+ 0x00, // transformation: unity matrix
536
+ 0x00,
537
+ 0x00,
538
+ 0x00,
539
+ 0x00,
540
+ 0x00,
541
+ 0x00,
542
+ 0x00,
543
+ 0x00,
544
+ 0x00,
545
+ 0x00,
546
+ 0x00,
547
+ 0x00,
548
+ 0x00,
549
+ 0x00,
550
+ 0x00,
551
+ 0x00,
552
+ 0x00,
553
+ 0x00,
554
+ 0x00,
555
+ 0x00,
556
+ 0x00,
557
+ 0x00,
558
+ 0x00,
559
+ 0x00, // pre_defined
560
+ 0xff,
561
+ 0xff,
562
+ 0xff,
563
+ 0xff, // next_track_ID
564
+ ]);
565
+ return MP4.box(MP4.types.mvhd, bytes);
566
+ }
567
+
568
+ static sdtp(track: RemuxedTrackType) {
569
+ const samples = track.samples || [];
570
+ const bytes = new Uint8Array(4 + samples.length);
571
+ let i: number;
572
+ let flags: Mp4SampleFlags;
573
+ // leave the full box header (4 bytes) all zero
574
+ // write the sample table
575
+ for (i = 0; i < samples.length; i++) {
576
+ flags = samples[i].flags;
577
+ bytes[i + 4] =
578
+ (flags.dependsOn << 4) |
579
+ (flags.isDependedOn << 2) |
580
+ flags.hasRedundancy;
581
+ }
582
+
583
+ return MP4.box(MP4.types.sdtp, bytes);
584
+ }
585
+
586
+ static stbl(track: MediaTrackType) {
587
+ return MP4.box(
588
+ MP4.types.stbl,
589
+ MP4.stsd(track),
590
+ MP4.box(MP4.types.stts, MP4.STTS),
591
+ MP4.box(MP4.types.stsc, MP4.STSC),
592
+ MP4.box(MP4.types.stsz, MP4.STSZ),
593
+ MP4.box(MP4.types.stco, MP4.STCO),
594
+ );
595
+ }
596
+
597
+ static avc1(track: DemuxedAVC1) {
598
+ let sps: number[] = [];
599
+ let pps: number[] = [];
600
+ let i;
601
+ let data;
602
+ let len;
603
+ // assemble the SPSs
604
+
605
+ for (i = 0; i < track.sps.length; i++) {
606
+ data = track.sps[i];
607
+ len = data.byteLength;
608
+ sps.push((len >>> 8) & 0xff);
609
+ sps.push(len & 0xff);
610
+
611
+ // SPS
612
+ sps = sps.concat(Array.prototype.slice.call(data));
613
+ }
614
+
615
+ // assemble the PPSs
616
+ for (i = 0; i < track.pps.length; i++) {
617
+ data = track.pps[i];
618
+ len = data.byteLength;
619
+ pps.push((len >>> 8) & 0xff);
620
+ pps.push(len & 0xff);
621
+
622
+ pps = pps.concat(Array.prototype.slice.call(data));
623
+ }
624
+
625
+ const avcc = MP4.box(
626
+ MP4.types.avcC,
627
+ new Uint8Array(
628
+ [
629
+ 0x01, // version
630
+ sps[3], // profile
631
+ sps[4], // profile compat
632
+ sps[5], // level
633
+ 0xfc | 3, // lengthSizeMinusOne, hard-coded to 4 bytes
634
+ 0xe0 | track.sps.length, // 3bit reserved (111) + numOfSequenceParameterSets
635
+ ]
636
+ .concat(sps)
637
+ .concat([
638
+ track.pps.length, // numOfPictureParameterSets
639
+ ])
640
+ .concat(pps),
641
+ ),
642
+ ); // "PPS"
643
+ const width = track.width;
644
+ const height = track.height;
645
+ const hSpacing = track.pixelRatio[0];
646
+ const vSpacing = track.pixelRatio[1];
647
+
648
+ return MP4.box(
649
+ MP4.types.avc1,
650
+ new Uint8Array([
651
+ 0x00,
652
+ 0x00,
653
+ 0x00, // reserved
654
+ 0x00,
655
+ 0x00,
656
+ 0x00, // reserved
657
+ 0x00,
658
+ 0x01, // data_reference_index
659
+ 0x00,
660
+ 0x00, // pre_defined
661
+ 0x00,
662
+ 0x00, // reserved
663
+ 0x00,
664
+ 0x00,
665
+ 0x00,
666
+ 0x00,
667
+ 0x00,
668
+ 0x00,
669
+ 0x00,
670
+ 0x00,
671
+ 0x00,
672
+ 0x00,
673
+ 0x00,
674
+ 0x00, // pre_defined
675
+ (width >> 8) & 0xff,
676
+ width & 0xff, // width
677
+ (height >> 8) & 0xff,
678
+ height & 0xff, // height
679
+ 0x00,
680
+ 0x48,
681
+ 0x00,
682
+ 0x00, // horizresolution
683
+ 0x00,
684
+ 0x48,
685
+ 0x00,
686
+ 0x00, // vertresolution
687
+ 0x00,
688
+ 0x00,
689
+ 0x00,
690
+ 0x00, // reserved
691
+ 0x00,
692
+ 0x01, // frame_count
693
+ 0x12,
694
+ 0x64,
695
+ 0x61,
696
+ 0x69,
697
+ 0x6c, // dailymotion/hls.js
698
+ 0x79,
699
+ 0x6d,
700
+ 0x6f,
701
+ 0x74,
702
+ 0x69,
703
+ 0x6f,
704
+ 0x6e,
705
+ 0x2f,
706
+ 0x68,
707
+ 0x6c,
708
+ 0x73,
709
+ 0x2e,
710
+ 0x6a,
711
+ 0x73,
712
+ 0x00,
713
+ 0x00,
714
+ 0x00,
715
+ 0x00,
716
+ 0x00,
717
+ 0x00,
718
+ 0x00,
719
+ 0x00,
720
+ 0x00,
721
+ 0x00,
722
+ 0x00,
723
+ 0x00,
724
+ 0x00, // compressorname
725
+ 0x00,
726
+ 0x18, // depth = 24
727
+ 0x11,
728
+ 0x11,
729
+ ]), // pre_defined = -1
730
+ avcc,
731
+ MP4.box(
732
+ MP4.types.btrt,
733
+ new Uint8Array([
734
+ 0x00,
735
+ 0x1c,
736
+ 0x9c,
737
+ 0x80, // bufferSizeDB
738
+ 0x00,
739
+ 0x2d,
740
+ 0xc6,
741
+ 0xc0, // maxBitrate
742
+ 0x00,
743
+ 0x2d,
744
+ 0xc6,
745
+ 0xc0,
746
+ ]),
747
+ ), // avgBitrate
748
+ MP4.box(
749
+ MP4.types.pasp,
750
+ new Uint8Array([
751
+ hSpacing >> 24, // hSpacing
752
+ (hSpacing >> 16) & 0xff,
753
+ (hSpacing >> 8) & 0xff,
754
+ hSpacing & 0xff,
755
+ vSpacing >> 24, // vSpacing
756
+ (vSpacing >> 16) & 0xff,
757
+ (vSpacing >> 8) & 0xff,
758
+ vSpacing & 0xff,
759
+ ]),
760
+ ),
761
+ );
762
+ }
763
+
764
+ static esds(track: DemuxedAudioTrack) {
765
+ const config = track.config as [number, number];
766
+ return new Uint8Array([
767
+ 0x00, // version 0
768
+ 0x00,
769
+ 0x00,
770
+ 0x00, // flags
771
+
772
+ 0x03, // descriptor_type
773
+ 0x19, // length
774
+
775
+ 0x00,
776
+ 0x01, // es_id
777
+
778
+ 0x00, // stream_priority
779
+
780
+ 0x04, // descriptor_type
781
+ 0x11, // length
782
+ 0x40, // codec : mpeg4_audio
783
+ 0x15, // stream_type
784
+ 0x00,
785
+ 0x00,
786
+ 0x00, // buffer_size
787
+ 0x00,
788
+ 0x00,
789
+ 0x00,
790
+ 0x00, // maxBitrate
791
+ 0x00,
792
+ 0x00,
793
+ 0x00,
794
+ 0x00, // avgBitrate
795
+
796
+ 0x05, // descriptor_type
797
+ 0x02, // length
798
+ ...config,
799
+ 0x06,
800
+ 0x01,
801
+ 0x02, // GASpecificConfig)); // length + audio config descriptor
802
+ ]);
803
+ }
804
+
805
+ static audioStsd(track: DemuxedAudioTrack) {
806
+ const samplerate = track.samplerate || 0;
807
+ return new Uint8Array([
808
+ 0x00,
809
+ 0x00,
810
+ 0x00, // reserved
811
+ 0x00,
812
+ 0x00,
813
+ 0x00, // reserved
814
+ 0x00,
815
+ 0x01, // data_reference_index
816
+ 0x00,
817
+ 0x00,
818
+ 0x00,
819
+ 0x00,
820
+ 0x00,
821
+ 0x00,
822
+ 0x00,
823
+ 0x00, // reserved
824
+ 0x00,
825
+ track.channelCount || 0, // channelcount
826
+ 0x00,
827
+ 0x10, // sampleSize:16bits
828
+ 0x00,
829
+ 0x00,
830
+ 0x00,
831
+ 0x00, // reserved2
832
+ (samplerate >> 8) & 0xff,
833
+ samplerate & 0xff, //
834
+ 0x00,
835
+ 0x00,
836
+ ]);
837
+ }
838
+
839
+ static mp4a(track: DemuxedAudioTrack) {
840
+ return MP4.box(
841
+ MP4.types.mp4a,
842
+ MP4.audioStsd(track),
843
+ MP4.box(MP4.types.esds, MP4.esds(track)),
844
+ );
845
+ }
846
+
847
+ static mp3(track: DemuxedAudioTrack) {
848
+ return MP4.box(MP4.types['.mp3'], MP4.audioStsd(track));
849
+ }
850
+
851
+ static ac3(track: DemuxedAudioTrack) {
852
+ return MP4.box(
853
+ MP4.types['ac-3'],
854
+ MP4.audioStsd(track),
855
+ MP4.box(MP4.types.dac3, track.config as Uint8Array),
856
+ );
857
+ }
858
+
859
+ static stsd(track: MediaTrackType | DemuxedAC3): Uint8Array {
860
+ const { segmentCodec } = track;
861
+ if (track.type === 'audio') {
862
+ if (segmentCodec === 'aac') {
863
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
864
+ }
865
+ if (
866
+ __USE_M2TS_ADVANCED_CODECS__ &&
867
+ segmentCodec === 'ac3' &&
868
+ track.config
869
+ ) {
870
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
871
+ }
872
+ if (segmentCodec === 'mp3' && track.codec === 'mp3') {
873
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track));
874
+ }
875
+ } else {
876
+ if (track.pps && track.sps) {
877
+ if (segmentCodec === 'avc') {
878
+ return MP4.box(
879
+ MP4.types.stsd,
880
+ MP4.STSD,
881
+ MP4.avc1(track as DemuxedAVC1),
882
+ );
883
+ }
884
+ if (
885
+ __USE_M2TS_ADVANCED_CODECS__ &&
886
+ segmentCodec === 'hevc' &&
887
+ track.vps
888
+ ) {
889
+ return MP4.box(
890
+ MP4.types.stsd,
891
+ MP4.STSD,
892
+ MP4.hvc1(track as DemuxedHEVC),
893
+ );
894
+ }
895
+ } else {
896
+ throw new Error(`video track missing pps or sps`);
897
+ }
898
+ }
899
+
900
+ throw new Error(
901
+ `unsupported ${track.type} segment codec (${segmentCodec}/${track.codec})`,
902
+ );
903
+ }
904
+
905
+ static tkhd(track: MediaTrackType) {
906
+ const id = track.id;
907
+ const duration = (track.duration || 0) * (track.timescale || 0);
908
+ const width = (track as any).width || 0;
909
+ const height = (track as any).height || 0;
910
+ const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1));
911
+ const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1));
912
+ return MP4.box(
913
+ MP4.types.tkhd,
914
+ new Uint8Array([
915
+ 0x01, // version 1
916
+ 0x00,
917
+ 0x00,
918
+ 0x07, // flags
919
+ 0x00,
920
+ 0x00,
921
+ 0x00,
922
+ 0x00,
923
+ 0x00,
924
+ 0x00,
925
+ 0x00,
926
+ 0x02, // creation_time
927
+ 0x00,
928
+ 0x00,
929
+ 0x00,
930
+ 0x00,
931
+ 0x00,
932
+ 0x00,
933
+ 0x00,
934
+ 0x03, // modification_time
935
+ (id >> 24) & 0xff,
936
+ (id >> 16) & 0xff,
937
+ (id >> 8) & 0xff,
938
+ id & 0xff, // track_ID
939
+ 0x00,
940
+ 0x00,
941
+ 0x00,
942
+ 0x00, // reserved
943
+ upperWordDuration >> 24,
944
+ (upperWordDuration >> 16) & 0xff,
945
+ (upperWordDuration >> 8) & 0xff,
946
+ upperWordDuration & 0xff,
947
+ lowerWordDuration >> 24,
948
+ (lowerWordDuration >> 16) & 0xff,
949
+ (lowerWordDuration >> 8) & 0xff,
950
+ lowerWordDuration & 0xff,
951
+ 0x00,
952
+ 0x00,
953
+ 0x00,
954
+ 0x00,
955
+ 0x00,
956
+ 0x00,
957
+ 0x00,
958
+ 0x00, // reserved
959
+ 0x00,
960
+ 0x00, // layer
961
+ 0x00,
962
+ 0x00, // alternate_group
963
+ 0x00,
964
+ 0x00, // non-audio track volume
965
+ 0x00,
966
+ 0x00, // reserved
967
+ 0x00,
968
+ 0x01,
969
+ 0x00,
970
+ 0x00,
971
+ 0x00,
972
+ 0x00,
973
+ 0x00,
974
+ 0x00,
975
+ 0x00,
976
+ 0x00,
977
+ 0x00,
978
+ 0x00,
979
+ 0x00,
980
+ 0x00,
981
+ 0x00,
982
+ 0x00,
983
+ 0x00,
984
+ 0x01,
985
+ 0x00,
986
+ 0x00,
987
+ 0x00,
988
+ 0x00,
989
+ 0x00,
990
+ 0x00,
991
+ 0x00,
992
+ 0x00,
993
+ 0x00,
994
+ 0x00,
995
+ 0x00,
996
+ 0x00,
997
+ 0x00,
998
+ 0x00,
999
+ 0x40,
1000
+ 0x00,
1001
+ 0x00,
1002
+ 0x00, // transformation: unity matrix
1003
+ (width >> 8) & 0xff,
1004
+ width & 0xff,
1005
+ 0x00,
1006
+ 0x00, // width
1007
+ (height >> 8) & 0xff,
1008
+ height & 0xff,
1009
+ 0x00,
1010
+ 0x00, // height
1011
+ ]),
1012
+ );
1013
+ }
1014
+
1015
+ static traf(track: RemuxedTrackType, baseMediaDecodeTime: number) {
1016
+ const sampleDependencyTable = MP4.sdtp(track);
1017
+ const id = track.id;
1018
+ const upperWordBaseMediaDecodeTime = Math.floor(
1019
+ baseMediaDecodeTime / (UINT32_MAX + 1),
1020
+ );
1021
+ const lowerWordBaseMediaDecodeTime = Math.floor(
1022
+ baseMediaDecodeTime % (UINT32_MAX + 1),
1023
+ );
1024
+ return MP4.box(
1025
+ MP4.types.traf,
1026
+ MP4.box(
1027
+ MP4.types.tfhd,
1028
+ new Uint8Array([
1029
+ 0x00, // version 0
1030
+ 0x00,
1031
+ 0x00,
1032
+ 0x00, // flags
1033
+ id >> 24,
1034
+ (id >> 16) & 0xff,
1035
+ (id >> 8) & 0xff,
1036
+ id & 0xff, // track_ID
1037
+ ]),
1038
+ ),
1039
+ MP4.box(
1040
+ MP4.types.tfdt,
1041
+ new Uint8Array([
1042
+ 0x01, // version 1
1043
+ 0x00,
1044
+ 0x00,
1045
+ 0x00, // flags
1046
+ upperWordBaseMediaDecodeTime >> 24,
1047
+ (upperWordBaseMediaDecodeTime >> 16) & 0xff,
1048
+ (upperWordBaseMediaDecodeTime >> 8) & 0xff,
1049
+ upperWordBaseMediaDecodeTime & 0xff,
1050
+ lowerWordBaseMediaDecodeTime >> 24,
1051
+ (lowerWordBaseMediaDecodeTime >> 16) & 0xff,
1052
+ (lowerWordBaseMediaDecodeTime >> 8) & 0xff,
1053
+ lowerWordBaseMediaDecodeTime & 0xff,
1054
+ ]),
1055
+ ),
1056
+ MP4.trun(
1057
+ track,
1058
+ sampleDependencyTable.length +
1059
+ 16 + // tfhd
1060
+ 20 + // tfdt
1061
+ 8 + // traf header
1062
+ 16 + // mfhd
1063
+ 8 + // moof header
1064
+ 8,
1065
+ ), // mdat header
1066
+ sampleDependencyTable,
1067
+ );
1068
+ }
1069
+
1070
+ /**
1071
+ * Generate a track box.
1072
+ * @param track a track definition
1073
+ */
1074
+ static trak(track: MediaTrackType) {
1075
+ track.duration = track.duration || 0xffffffff;
1076
+ return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track));
1077
+ }
1078
+
1079
+ static trex(track: MediaTrackType) {
1080
+ const id = track.id;
1081
+ return MP4.box(
1082
+ MP4.types.trex,
1083
+ new Uint8Array([
1084
+ 0x00, // version 0
1085
+ 0x00,
1086
+ 0x00,
1087
+ 0x00, // flags
1088
+ id >> 24,
1089
+ (id >> 16) & 0xff,
1090
+ (id >> 8) & 0xff,
1091
+ id & 0xff, // track_ID
1092
+ 0x00,
1093
+ 0x00,
1094
+ 0x00,
1095
+ 0x01, // default_sample_description_index
1096
+ 0x00,
1097
+ 0x00,
1098
+ 0x00,
1099
+ 0x00, // default_sample_duration
1100
+ 0x00,
1101
+ 0x00,
1102
+ 0x00,
1103
+ 0x00, // default_sample_size
1104
+ 0x00,
1105
+ 0x01,
1106
+ 0x00,
1107
+ 0x01, // default_sample_flags
1108
+ ]),
1109
+ );
1110
+ }
1111
+
1112
+ static trun(track: MediaTrackType, offset: number) {
1113
+ const samples = track.samples || [];
1114
+ const len = samples.length;
1115
+ const arraylen = 12 + 16 * len;
1116
+ const array = new Uint8Array(arraylen);
1117
+ let i;
1118
+ let sample;
1119
+ let duration;
1120
+ let size;
1121
+ let flags;
1122
+ let cts;
1123
+ offset += 8 + arraylen;
1124
+ array.set(
1125
+ [
1126
+ track.type === 'video' ? 0x01 : 0x00, // version 1 for video with signed-int sample_composition_time_offset
1127
+ 0x00,
1128
+ 0x0f,
1129
+ 0x01, // flags
1130
+ (len >>> 24) & 0xff,
1131
+ (len >>> 16) & 0xff,
1132
+ (len >>> 8) & 0xff,
1133
+ len & 0xff, // sample_count
1134
+ (offset >>> 24) & 0xff,
1135
+ (offset >>> 16) & 0xff,
1136
+ (offset >>> 8) & 0xff,
1137
+ offset & 0xff, // data_offset
1138
+ ],
1139
+ 0,
1140
+ );
1141
+ for (i = 0; i < len; i++) {
1142
+ sample = samples[i];
1143
+ duration = sample.duration;
1144
+ size = sample.size;
1145
+ flags = sample.flags;
1146
+ cts = sample.cts;
1147
+ array.set(
1148
+ [
1149
+ (duration >>> 24) & 0xff,
1150
+ (duration >>> 16) & 0xff,
1151
+ (duration >>> 8) & 0xff,
1152
+ duration & 0xff, // sample_duration
1153
+ (size >>> 24) & 0xff,
1154
+ (size >>> 16) & 0xff,
1155
+ (size >>> 8) & 0xff,
1156
+ size & 0xff, // sample_size
1157
+ (flags.isLeading << 2) | flags.dependsOn,
1158
+ (flags.isDependedOn << 6) |
1159
+ (flags.hasRedundancy << 4) |
1160
+ (flags.paddingValue << 1) |
1161
+ flags.isNonSync,
1162
+ flags.degradPrio & (0xf0 << 8),
1163
+ flags.degradPrio & 0x0f, // sample_flags
1164
+ (cts >>> 24) & 0xff,
1165
+ (cts >>> 16) & 0xff,
1166
+ (cts >>> 8) & 0xff,
1167
+ cts & 0xff, // sample_composition_time_offset
1168
+ ],
1169
+ 12 + 16 * i,
1170
+ );
1171
+ }
1172
+ return MP4.box(MP4.types.trun, array);
1173
+ }
1174
+
1175
+ static initSegment(tracks: MediaTrackType[]) {
1176
+ if (!MP4.types) {
1177
+ MP4.init();
1178
+ }
1179
+
1180
+ const movie = MP4.moov(tracks);
1181
+ const result = appendUint8Array(MP4.FTYP, movie);
1182
+ return result;
1183
+ }
1184
+
1185
+ static hvc1(track: DemuxedHEVC) {
1186
+ if (!__USE_M2TS_ADVANCED_CODECS__) {
1187
+ return new Uint8Array();
1188
+ }
1189
+ const ps = track.params;
1190
+ const units: Uint8Array[][] = [track.vps, track.sps, track.pps];
1191
+ const NALuLengthSize = 4;
1192
+ const config = new Uint8Array([
1193
+ 0x01,
1194
+ (ps.general_profile_space << 6) |
1195
+ (ps.general_tier_flag ? 32 : 0) |
1196
+ ps.general_profile_idc,
1197
+ ps.general_profile_compatibility_flags[0],
1198
+ ps.general_profile_compatibility_flags[1],
1199
+ ps.general_profile_compatibility_flags[2],
1200
+ ps.general_profile_compatibility_flags[3],
1201
+ ps.general_constraint_indicator_flags[0],
1202
+ ps.general_constraint_indicator_flags[1],
1203
+ ps.general_constraint_indicator_flags[2],
1204
+ ps.general_constraint_indicator_flags[3],
1205
+ ps.general_constraint_indicator_flags[4],
1206
+ ps.general_constraint_indicator_flags[5],
1207
+ ps.general_level_idc,
1208
+ 240 | (ps.min_spatial_segmentation_idc >> 8),
1209
+ 255 & ps.min_spatial_segmentation_idc,
1210
+ 252 | ps.parallelismType,
1211
+ 252 | ps.chroma_format_idc,
1212
+ 248 | ps.bit_depth_luma_minus8,
1213
+ 248 | ps.bit_depth_chroma_minus8,
1214
+ 0x00,
1215
+ parseInt(ps.frame_rate.fps),
1216
+ (NALuLengthSize - 1) |
1217
+ (ps.temporal_id_nested << 2) |
1218
+ (ps.num_temporal_layers << 3) |
1219
+ (ps.frame_rate.fixed ? 64 : 0),
1220
+ units.length,
1221
+ ]);
1222
+
1223
+ // compute hvcC size in bytes
1224
+ let length = config.length;
1225
+ for (let i = 0; i < units.length; i += 1) {
1226
+ length += 3;
1227
+ for (let j = 0; j < units[i].length; j += 1) {
1228
+ length += 2 + units[i][j].length;
1229
+ }
1230
+ }
1231
+
1232
+ const hvcC = new Uint8Array(length);
1233
+ hvcC.set(config, 0);
1234
+ length = config.length;
1235
+ // append parameter set units: one vps, one or more sps and pps
1236
+ const iMax = units.length - 1;
1237
+ for (let i = 0; i < units.length; i += 1) {
1238
+ hvcC.set(
1239
+ new Uint8Array([
1240
+ (32 + i) | (i === iMax ? 128 : 0),
1241
+ 0x00,
1242
+ units[i].length,
1243
+ ]),
1244
+ length,
1245
+ );
1246
+ length += 3;
1247
+ for (let j = 0; j < units[i].length; j += 1) {
1248
+ hvcC.set(
1249
+ new Uint8Array([units[i][j].length >> 8, units[i][j].length & 255]),
1250
+ length,
1251
+ );
1252
+ length += 2;
1253
+ hvcC.set(units[i][j], length);
1254
+ length += units[i][j].length;
1255
+ }
1256
+ }
1257
+ const hvcc = MP4.box(MP4.types.hvcC, hvcC);
1258
+ const width = track.width;
1259
+ const height = track.height;
1260
+ const hSpacing = track.pixelRatio[0];
1261
+ const vSpacing = track.pixelRatio[1];
1262
+
1263
+ return MP4.box(
1264
+ MP4.types.hvc1,
1265
+ new Uint8Array([
1266
+ 0x00,
1267
+ 0x00,
1268
+ 0x00, // reserved
1269
+ 0x00,
1270
+ 0x00,
1271
+ 0x00, // reserved
1272
+ 0x00,
1273
+ 0x01, // data_reference_index
1274
+ 0x00,
1275
+ 0x00, // pre_defined
1276
+ 0x00,
1277
+ 0x00, // reserved
1278
+ 0x00,
1279
+ 0x00,
1280
+ 0x00,
1281
+ 0x00,
1282
+ 0x00,
1283
+ 0x00,
1284
+ 0x00,
1285
+ 0x00,
1286
+ 0x00,
1287
+ 0x00,
1288
+ 0x00,
1289
+ 0x00, // pre_defined
1290
+ (width >> 8) & 0xff,
1291
+ width & 0xff, // width
1292
+ (height >> 8) & 0xff,
1293
+ height & 0xff, // height
1294
+ 0x00,
1295
+ 0x48,
1296
+ 0x00,
1297
+ 0x00, // horizresolution
1298
+ 0x00,
1299
+ 0x48,
1300
+ 0x00,
1301
+ 0x00, // vertresolution
1302
+ 0x00,
1303
+ 0x00,
1304
+ 0x00,
1305
+ 0x00, // reserved
1306
+ 0x00,
1307
+ 0x01, // frame_count
1308
+ 0x12,
1309
+ 0x64,
1310
+ 0x61,
1311
+ 0x69,
1312
+ 0x6c, // dailymotion/hls.js
1313
+ 0x79,
1314
+ 0x6d,
1315
+ 0x6f,
1316
+ 0x74,
1317
+ 0x69,
1318
+ 0x6f,
1319
+ 0x6e,
1320
+ 0x2f,
1321
+ 0x68,
1322
+ 0x6c,
1323
+ 0x73,
1324
+ 0x2e,
1325
+ 0x6a,
1326
+ 0x73,
1327
+ 0x00,
1328
+ 0x00,
1329
+ 0x00,
1330
+ 0x00,
1331
+ 0x00,
1332
+ 0x00,
1333
+ 0x00,
1334
+ 0x00,
1335
+ 0x00,
1336
+ 0x00,
1337
+ 0x00,
1338
+ 0x00,
1339
+ 0x00, // compressorname
1340
+ 0x00,
1341
+ 0x18, // depth = 24
1342
+ 0x11,
1343
+ 0x11,
1344
+ ]), // pre_defined = -1
1345
+ hvcc,
1346
+ MP4.box(
1347
+ MP4.types.btrt,
1348
+ new Uint8Array([
1349
+ 0x00,
1350
+ 0x1c,
1351
+ 0x9c,
1352
+ 0x80, // bufferSizeDB
1353
+ 0x00,
1354
+ 0x2d,
1355
+ 0xc6,
1356
+ 0xc0, // maxBitrate
1357
+ 0x00,
1358
+ 0x2d,
1359
+ 0xc6,
1360
+ 0xc0,
1361
+ ]),
1362
+ ), // avgBitrate
1363
+ MP4.box(
1364
+ MP4.types.pasp,
1365
+ new Uint8Array([
1366
+ hSpacing >> 24, // hSpacing
1367
+ (hSpacing >> 16) & 0xff,
1368
+ (hSpacing >> 8) & 0xff,
1369
+ hSpacing & 0xff,
1370
+ vSpacing >> 24, // vSpacing
1371
+ (vSpacing >> 16) & 0xff,
1372
+ (vSpacing >> 8) & 0xff,
1373
+ vSpacing & 0xff,
1374
+ ]),
1375
+ ),
1376
+ );
1377
+ }
1378
+ }
1379
+
1380
+ export default MP4;