jmuxer 2.0.7 → 2.1.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.
@@ -1,11 +1,13 @@
1
1
  import * as debug from '../util/debug';
2
- import { H264Parser } from '../parsers/h264.js';
2
+ import { H264Parser, NALU264 } from '../parsers/h264.js';
3
3
  import { BaseRemuxer } from './base.js';
4
+ import { appendByteArray } from '../util/utils.js';
4
5
 
5
6
  export class H264Remuxer extends BaseRemuxer {
6
7
 
7
- constructor(timescale) {
8
- super();
8
+ constructor(timescale, duration, frameDuration) {
9
+ super('H264Remuxer');
10
+ this.frameDuration = frameDuration;
9
11
  this.readyToDecode = false;
10
12
  this.nextDts = 0;
11
13
  this.dts = 0;
@@ -20,11 +22,13 @@ export class H264Remuxer extends BaseRemuxer {
20
22
  width: 0,
21
23
  height: 0,
22
24
  timescale: timescale,
23
- duration: timescale,
25
+ duration: duration,
24
26
  samples: [],
25
27
  };
26
28
  this.samples = [];
27
- this.h264 = new H264Parser(this);
29
+ this.remainingData = new Uint8Array();
30
+ this.kfCounter = 0;
31
+ this.pendingUnits = {};
28
32
  }
29
33
 
30
34
  resetTrack() {
@@ -33,6 +37,99 @@ export class H264Remuxer extends BaseRemuxer {
33
37
  this.mp4track.pps = '';
34
38
  this.nextDts = 0;
35
39
  this.dts = 0;
40
+ this.remainingData = new Uint8Array();
41
+ this.kfCounter = 0;
42
+ this.pendingUnits = {};
43
+ }
44
+
45
+ feed(data, duration, compositionTimeOffset) {
46
+ let slices = [];
47
+ let left;
48
+ data = appendByteArray(this.remainingData, data);
49
+ [slices, left] = H264Parser.extractNALu(data);
50
+ this.remainingData = left || new Uint8Array();
51
+
52
+ if (slices.length > 0) {
53
+ this.remux(this.getVideoFrames(slices, duration, compositionTimeOffset));
54
+ return true;
55
+ } else {
56
+ debug.error('Failed to extract any NAL units from video data:', left);
57
+ this.dispatch('outOfData');
58
+ return false;
59
+ }
60
+ }
61
+
62
+ getVideoFrames(nalus, duration, compositionTimeOffset) {
63
+ let units = [],
64
+ frames = [],
65
+ fd = 0, // frame duration
66
+ tt = 0, // time ticks (remainder adjustment counter)
67
+ keyFrame = false,
68
+ vcl = false; // Video Coding Layer data (i.e., a "real" frame)
69
+ if (this.pendingUnits.units) {
70
+ units = this.pendingUnits.units;
71
+ vcl = this.pendingUnits.vcl;
72
+ keyFrame = this.pendingUnits.keyFrame;
73
+ this.pendingUnits = {};
74
+ }
75
+
76
+ for (let nalu of nalus) {
77
+ let unit = new NALU264(nalu);
78
+
79
+ // frame boundary detection
80
+ if (units.length && vcl && (unit.isFirstSlice || !unit.isVCL)) {
81
+ frames.push({
82
+ units,
83
+ keyFrame
84
+ });
85
+ units = [];
86
+ keyFrame = false;
87
+ vcl = false;
88
+ }
89
+
90
+ units.push(unit);
91
+ keyFrame = keyFrame || unit.isKeyframe;
92
+ vcl = vcl || unit.isVCL;
93
+ }
94
+
95
+ if (units.length) {
96
+ // lets keep indecisive nalus as pending in case of fixed fps
97
+ if (!duration) {
98
+ this.pendingUnits = {
99
+ units,
100
+ keyFrame,
101
+ vcl
102
+ };
103
+ } else if (vcl) {
104
+ frames.push({
105
+ units,
106
+ keyFrame
107
+ });
108
+ } else {
109
+ let last = frames.length - 1;
110
+ if (last >= 0) {
111
+ frames[last].units = frames[last].units.concat(units);
112
+ }
113
+ }
114
+ }
115
+
116
+ fd = duration ? duration / frames.length | 0 : this.frameDuration;
117
+ tt = duration ? (duration - (fd * frames.length)) : 0;
118
+
119
+ frames.map((frame) => {
120
+ frame.duration = fd;
121
+ frame.compositionTimeOffset = compositionTimeOffset;
122
+ if (tt > 0) {
123
+ frame.duration++;
124
+ tt--;
125
+ }
126
+ this.kfCounter++;
127
+ if (frame.keyFrame) {
128
+ this.dispatch('keyframePosition', (this.kfCounter * fd) / 1000);
129
+ }
130
+ });
131
+ debug.log(`jmuxer: No. of H264 frames of the last chunk: ${frames.length}`);
132
+ return frames;
36
133
  }
37
134
 
38
135
  remux(frames) {
@@ -40,7 +137,7 @@ export class H264Remuxer extends BaseRemuxer {
40
137
  let units = [];
41
138
  let size = 0;
42
139
  for (let unit of frame.units) {
43
- if (this.h264.parseNAL(unit)) {
140
+ if (this.parseNAL(unit)) {
44
141
  units.push(unit);
45
142
  size += unit.getSize();
46
143
  }
@@ -67,7 +164,6 @@ export class H264Remuxer extends BaseRemuxer {
67
164
  let samples = this.mp4track.samples;
68
165
  let mp4Sample,
69
166
  duration;
70
-
71
167
  this.dts = this.nextDts;
72
168
  while (this.samples.length) {
73
169
  let sample = this.samples.shift(),
@@ -105,4 +201,64 @@ export class H264Remuxer extends BaseRemuxer {
105
201
 
106
202
  return new Uint8Array(payload.buffer, 0, this.mp4track.len);
107
203
  }
204
+
205
+ parseSPS(sps) {
206
+ var config = H264Parser.readSPS(new Uint8Array(sps));
207
+
208
+ this.mp4track.fps = config.fps || this.mp4track.fps;
209
+ this.mp4track.width = config.width;
210
+ this.mp4track.height = config.height;
211
+ this.mp4track.sps = [new Uint8Array(sps)];
212
+ this.mp4track.codec = 'avc1.';
213
+
214
+ let codecarray = new DataView(sps.buffer, sps.byteOffset + 1, 4);
215
+ for (let i = 0; i < 3; ++i) {
216
+ var h = codecarray.getUint8(i).toString(16);
217
+ if (h.length < 2) {
218
+ h = '0' + h;
219
+ }
220
+ this.mp4track.codec += h;
221
+ }
222
+ }
223
+
224
+ parsePPS(pps) {
225
+ this.mp4track.pps = [new Uint8Array(pps)];
226
+ }
227
+
228
+ parseNAL(unit) {
229
+ if (!unit) return false;
230
+
231
+ if (unit.isVCL) {
232
+ return true;
233
+ }
234
+
235
+ let push = false;
236
+ switch (unit.type()) {
237
+ case NALU264.PPS:
238
+ if (!this.mp4track.pps) {
239
+ this.parsePPS(unit.getPayload());
240
+ }
241
+ push = true;
242
+ break;
243
+ case NALU264.SPS:
244
+ if (!this.mp4track.sps) {
245
+ this.parseSPS(unit.getPayload());
246
+ }
247
+ push = true;
248
+ break;
249
+ case NALU264.AUD:
250
+ debug.log('AUD - ignoing');
251
+ break;
252
+ case NALU264.SEI:
253
+ debug.log('SEI - ignoing');
254
+ break;
255
+ default:
256
+ }
257
+
258
+ if (!this.readyToDecode && this.mp4track.pps && this.mp4track.sps) {
259
+ this.readyToDecode = true;
260
+ }
261
+
262
+ return push;
263
+ }
108
264
  }
@@ -0,0 +1,288 @@
1
+ import * as debug from '../util/debug';
2
+ import { H265Parser, NALU265 } from '../parsers/h265.js';
3
+ import { BaseRemuxer } from './base.js';
4
+ import { appendByteArray } from '../util/utils.js';
5
+
6
+ export class H265Remuxer extends BaseRemuxer {
7
+
8
+ constructor(timescale, duration, frameDuration) {
9
+ super('H264Remuxer');
10
+ this.frameDuration = frameDuration;
11
+ this.readyToDecode = false;
12
+ this.nextDts = 0;
13
+ this.dts = 0;
14
+ this.mp4track = {
15
+ id: BaseRemuxer.getTrackID(),
16
+ type: 'video',
17
+ len: 0,
18
+ fragmented: true,
19
+ vps: '',
20
+ sps: '',
21
+ pps: '',
22
+ hvcC: {},
23
+ fps: 30,
24
+ width: 0,
25
+ height: 0,
26
+ timescale: timescale,
27
+ duration: duration,
28
+ samples: [],
29
+ };
30
+ this.samples = [];
31
+ this.remainingData = new Uint8Array();
32
+ this.kfCounter = 0;
33
+ this.pendingUnits = {};
34
+ }
35
+
36
+ resetTrack() {
37
+ this.readyToDecode = false;
38
+ this.mp4track.vps = '';
39
+ this.mp4track.sps = '';
40
+ this.mp4track.pps = '';
41
+ this.mp4track.hvcC = {};
42
+ this.nextDts = 0;
43
+ this.dts = 0;
44
+ this.remainingData = new Uint8Array();
45
+ this.kfCounter = 0;
46
+ this.pendingUnits = {};
47
+ }
48
+
49
+ feed(data, duration, compositionTimeOffset) {
50
+ let slices = [];
51
+ let left;
52
+ data = appendByteArray(this.remainingData, data);
53
+ [slices, left] = H265Parser.extractNALu(data);
54
+ this.remainingData = left || new Uint8Array();
55
+
56
+ if (slices.length > 0) {
57
+ this.remux(this.getVideoFrames(slices, duration, compositionTimeOffset));
58
+ return true;
59
+ } else {
60
+ debug.error('Failed to extract any NAL units from video data:', left);
61
+ this.dispatch('outOfData');
62
+ return false;
63
+ }
64
+ }
65
+
66
+ getVideoFrames(nalus, duration, compositionTimeOffset) {
67
+ let units = [],
68
+ frames = [],
69
+ fd = 0,
70
+ tt = 0,
71
+ keyFrame = false,
72
+ vcl = false;
73
+
74
+ if (this.pendingUnits.units) {
75
+ units = this.pendingUnits.units;
76
+ vcl = this.pendingUnits.vcl;
77
+ keyFrame = this.pendingUnits.keyFrame;
78
+ this.pendingUnits = {};
79
+ }
80
+
81
+ for (let nalu of nalus) {
82
+ let unit = new NALU265(nalu);
83
+
84
+ // frame boundary detection
85
+ if (units.length && vcl && (unit.isFirstSlice || !unit.isVCL)) {
86
+ frames.push({
87
+ units,
88
+ keyFrame
89
+ });
90
+ units = [];
91
+ keyFrame = false;
92
+ vcl = false;
93
+ }
94
+
95
+ units.push(unit);
96
+ keyFrame = keyFrame || unit.isKeyframe;
97
+ vcl = vcl || unit.isVCL;
98
+ }
99
+
100
+ if (units.length) {
101
+ if (!duration) {
102
+ this.pendingUnits = {
103
+ units,
104
+ keyFrame,
105
+ vcl
106
+ };
107
+ } else if (vcl) {
108
+ frames.push({
109
+ units,
110
+ keyFrame
111
+ });
112
+ } else {
113
+ let last = frames.length - 1;
114
+ if (last >= 0) {
115
+ frames[last].units = frames[last].units.concat(units);
116
+ }
117
+ }
118
+ }
119
+
120
+ fd = duration ? (duration / frames.length) | 0 : this.frameDuration;
121
+ tt = duration ? duration - fd * frames.length : 0;
122
+
123
+ frames.map((frame) => {
124
+ frame.duration = fd;
125
+ frame.compositionTimeOffset = compositionTimeOffset;
126
+ if (tt > 0) {
127
+ frame.duration++;
128
+ tt--;
129
+ }
130
+ this.kfCounter++;
131
+ if (frame.keyFrame) {
132
+ this.dispatch('keyframePosition', (this.kfCounter * fd) / 1000);
133
+ }
134
+ });
135
+
136
+ debug.log(`jmuxer: No. of H265 frames of the last chunk: ${frames.length}`);
137
+ return frames;
138
+ }
139
+
140
+ remux(frames) {
141
+ for (let frame of frames) {
142
+ let units = [];
143
+ let size = 0;
144
+ for (let unit of frame.units) {
145
+ if (this.parseNAL(unit)) {
146
+ units.push(unit);
147
+ size += unit.getSize();
148
+ }
149
+ }
150
+ if (units.length > 0 && this.readyToDecode) {
151
+ this.mp4track.len += size;
152
+ this.samples.push({
153
+ units: units,
154
+ size: size,
155
+ keyFrame: frame.keyFrame,
156
+ duration: frame.duration,
157
+ compositionTimeOffset: frame.compositionTimeOffset
158
+ });
159
+ }
160
+ }
161
+ }
162
+
163
+ getPayload() {
164
+ if (!this.isReady()) {
165
+ return null;
166
+ }
167
+ let payload = new Uint8Array(this.mp4track.len);
168
+ let offset = 0;
169
+ let samples = this.mp4track.samples;
170
+ let mp4Sample,
171
+ duration;
172
+ this.dts = this.nextDts;
173
+ while (this.samples.length) {
174
+ let sample = this.samples.shift(),
175
+ units = sample.units;
176
+
177
+ duration = sample.duration;
178
+ if (duration <= 0) {
179
+ debug.log(`remuxer: invalid sample duration at DTS: ${this.nextDts} :${duration}`);
180
+ this.mp4track.len -= sample.size;
181
+ continue;
182
+ }
183
+ this.nextDts += duration;
184
+ mp4Sample = {
185
+ size: sample.size,
186
+ duration: duration,
187
+ cts: sample.compositionTimeOffset || 0,
188
+ flags: {
189
+ isLeading: 0,
190
+ isDependedOn: 0,
191
+ hasRedundancy: 0,
192
+ degradPrio: 0,
193
+ isNonSync: sample.keyFrame ? 0 : 1,
194
+ dependsOn: sample.keyFrame ? 2 : 1,
195
+ },
196
+ };
197
+
198
+ for (const unit of units) {
199
+ payload.set(unit.getData(), offset);
200
+ offset += unit.getSize();
201
+ }
202
+ samples.push(mp4Sample);
203
+ }
204
+
205
+ if (!samples.length) return null;
206
+
207
+ return new Uint8Array(payload.buffer, 0, this.mp4track.len);
208
+ }
209
+
210
+ parseSPS(sps) {
211
+ this.mp4track.sps = [new Uint8Array(sps)];
212
+
213
+ sps = H265Parser.removeEmulationPreventionBytes(sps);
214
+ const config = H265Parser.readSPS(new Uint8Array(sps));
215
+
216
+ this.mp4track.fps = config.fps || this.mp4track.fps;
217
+ this.mp4track.width = config.width;
218
+ this.mp4track.height = config.height;
219
+
220
+ this.mp4track.codec = `hvc1.${config.profile_idc}.${config.profile_compatibility_flags.toString(16)}`
221
+ + `.L${config.level_idc}${config.tier_flag ? 'H' : 'L'}`
222
+ + `.${config.constraint_indicator_flags.map(b => b.toString(16)).join('.').toUpperCase()}`;
223
+
224
+ this.mp4track.hvcC = {
225
+ profile_space: config.profile_space,
226
+ tier_flag: config.tier_flag,
227
+ profile_idc: config.profile_idc,
228
+ profile_compatibility_flags: config.profile_compatibility_flags,
229
+ constraint_indicator_flags: config.constraint_indicator_flags,
230
+ level_idc: config.level_idc,
231
+ chroma_format_idc: config.chroma_format_idc
232
+ };
233
+ }
234
+
235
+ parsePPS(pps) {
236
+ this.mp4track.pps = [pps];
237
+ }
238
+
239
+ parseVPS(vps) {
240
+ this.mp4track.vps = [vps];
241
+ }
242
+
243
+ parseNAL(unit) {
244
+ if (!unit) return false;
245
+
246
+ if (unit.isVCL) {
247
+ return true;
248
+ }
249
+
250
+ let push = false;
251
+ switch (unit.type()) {
252
+ case NALU265.VPS:
253
+ if (!this.mp4track.vps) {
254
+ this.parseVPS(unit.getPayload());
255
+ }
256
+ push = true;
257
+ break;
258
+
259
+ case NALU265.SPS:
260
+ if (!this.mp4track.sps) {
261
+ this.parseSPS(unit.getPayload());
262
+ }
263
+ push = true;
264
+ break;
265
+
266
+ case NALU265.PPS:
267
+ if (!this.mp4track.pps) {
268
+ this.parsePPS(unit.getPayload());
269
+ }
270
+ push = true;
271
+ break;
272
+ case NALU265.AUD:
273
+ debug.log('AUD - ignoing');
274
+ break;
275
+ case NALU265.SEI:
276
+ case NALU265.SEI2:
277
+ debug.log('SEI - ignoing');
278
+ break;
279
+ default:
280
+ }
281
+
282
+ if (!this.readyToDecode && this.mp4track.vps && this.mp4track.sps && this.mp4track.pps) {
283
+ this.readyToDecode = true;
284
+ }
285
+
286
+ return push;
287
+ }
288
+ }
package/src/util/debug.js CHANGED
@@ -1,10 +1,10 @@
1
1
  let logger;
2
2
  let errorLogger;
3
3
 
4
- export function setLogger() {
4
+ export function setLogger(log, err) {
5
5
  /*eslint-disable */
6
- logger = console.log;
7
- errorLogger = console.error;
6
+ logger = log;
7
+ errorLogger = err;
8
8
  /*eslint-enable */
9
9
  }
10
10
 
package/src/util/event.js CHANGED
@@ -27,10 +27,10 @@ export default class Event {
27
27
  this.listener = {};
28
28
  }
29
29
 
30
- dispatch(event, data) {
30
+ dispatch(event, ...data) {
31
31
  if (this.listener[event]) {
32
32
  this.listener[event].map((each) => {
33
- each.apply(null, [data]);
33
+ each.apply(null, data);
34
34
  });
35
35
  return true;
36
36
  }
@@ -14,6 +14,8 @@ export class MP4 {
14
14
  esds: [],
15
15
  ftyp: [],
16
16
  hdlr: [],
17
+ hev1: [],
18
+ hvcC: [],
17
19
  mdat: [],
18
20
  mdhd: [],
19
21
  mdia: [],
@@ -179,9 +181,9 @@ export class MP4 {
179
181
  (timescale >> 16) & 0xFF,
180
182
  (timescale >> 8) & 0xFF,
181
183
  timescale & 0xFF, // timescale
182
- (duration >> 24),
183
- (duration >> 16) & 0xFF,
184
- (duration >> 8) & 0xFF,
184
+ (duration >>> 24) & 0xFF,
185
+ (duration >>> 16) & 0xFF,
186
+ (duration >>> 8) & 0xFF,
185
187
  duration & 0xFF, // duration
186
188
  0x55, 0xc4, // 'und' language (undetermined)
187
189
  0x00, 0x00,
@@ -251,9 +253,9 @@ export class MP4 {
251
253
  (timescale >> 16) & 0xFF,
252
254
  (timescale >> 8) & 0xFF,
253
255
  timescale & 0xFF, // timescale
254
- (duration >> 24) & 0xFF,
255
- (duration >> 16) & 0xFF,
256
- (duration >> 8) & 0xFF,
256
+ (duration >>> 24) & 0xFF,
257
+ (duration >>> 16) & 0xFF,
258
+ (duration >>> 8) & 0xFF,
257
259
  duration & 0xFF, // duration
258
260
  0x00, 0x01, 0x00, 0x00, // 1.0 rate
259
261
  0x01, 0x00, // 1.0 volume
@@ -375,6 +377,116 @@ export class MP4 {
375
377
  0x00, 0x2d, 0xc6, 0xc0])) // avgBitrate
376
378
  );
377
379
  }
380
+
381
+ static hev1(track) {
382
+ let vps = [],
383
+ sps = [],
384
+ pps = [],
385
+ data,
386
+ len;
387
+
388
+ // assemble the VPSs
389
+ for (let i = 0; i < (track.vps?.length || 0); i++) {
390
+ data = track.vps[i];
391
+ len = data.byteLength;
392
+ vps.push((len >>> 8) & 0xFF, len & 0xFF);
393
+ vps = vps.concat(Array.prototype.slice.call(data));
394
+ }
395
+
396
+ // assemble the SPSs
397
+ for (let i = 0; i < (track.sps?.length || 0); i++) {
398
+ data = track.sps[i];
399
+ len = data.byteLength;
400
+ sps.push((len >>> 8) & 0xFF, len & 0xFF);
401
+ sps = sps.concat(Array.prototype.slice.call(data));
402
+ }
403
+
404
+ // assemble the PPSs
405
+ for (let i = 0; i < (track.pps?.length || 0); i++) {
406
+ data = track.pps[i];
407
+ len = data.byteLength;
408
+ pps.push((len >>> 8) & 0xFF, len & 0xFF);
409
+ pps = pps.concat(Array.prototype.slice.call(data));
410
+ }
411
+
412
+ let {
413
+ profile_space,
414
+ tier_flag,
415
+ profile_idc,
416
+ profile_compatibility_flags,
417
+ constraint_indicator_flags,
418
+ level_idc,
419
+ chroma_format_idc
420
+ } = track.hvcC;
421
+
422
+ const hvcc = MP4.box(MP4.types.hvcC, new Uint8Array([
423
+ 0x01, // configurationVersion
424
+ (profile_space << 6) | (tier_flag << 5) | profile_idc,
425
+ (profile_compatibility_flags >> 24) & 0xFF,
426
+ (profile_compatibility_flags >> 16) & 0xFF,
427
+ (profile_compatibility_flags >> 8) & 0xFF,
428
+ profile_compatibility_flags & 0xFF,
429
+ ...constraint_indicator_flags,
430
+ level_idc,
431
+ 0xF0, 0x00, // min_spatial_segmentation_idc = 0
432
+ 0xFC | 0, // parallelismType = 0
433
+ 0xFC | chroma_format_idc, // chromaFormat (from SPS)
434
+ 0xF8 | 0, // bitDepthLumaMinus8 = 0 (8-bit)
435
+ 0xF8 | 0, // bitDepthChromaMinus8 = 0
436
+ 0x00, 0x00, // avgFrameRate = 0
437
+ 0x03, // constantFrameRate = 0, numTemporalLayers = 0, lengthSizeMinusOne = 3 (AKA 4)
438
+ 0x03, // numOfArrays
439
+
440
+ 0x20, // array_completeness + NAL_unit_type (32 = VPS)
441
+ 0x00, 0x01, // numNalus
442
+ ...vps,
443
+
444
+ 0x21, // NAL_unit_type (33 = SPS)
445
+ 0x00, 0x01,
446
+ ...sps,
447
+
448
+ 0x22, // NAL_unit_type (34 = PPS)
449
+ 0x00, 0x01,
450
+ ...pps
451
+ ]));
452
+
453
+ const width = track.width;
454
+ const height = track.height;
455
+
456
+ return MP4.box(MP4.types.hev1, new Uint8Array([
457
+ 0x00, 0x00, 0x00, // reserved
458
+ 0x00, 0x00, 0x00, // reserved
459
+ 0x00, 0x01, // data_reference_index
460
+ 0x00, 0x00, // pre_defined
461
+ 0x00, 0x00, // reserved
462
+ 0x00, 0x00, 0x00, 0x00,
463
+ 0x00, 0x00, 0x00, 0x00,
464
+ 0x00, 0x00, 0x00, 0x00, // pre_defined
465
+ (width >> 8) & 0xFF, width & 0xff,
466
+ (height >> 8) & 0xFF, height & 0xff,
467
+ 0x00, 0x48, 0x00, 0x00, // horizresolution
468
+ 0x00, 0x48, 0x00, 0x00, // vertresolution
469
+ 0x00, 0x00, 0x00, 0x00, // reserved
470
+ 0x00, 0x01, // frame_count
471
+ 0x12,
472
+ 0x62, 0x69, 0x6E, 0x65, // 'binelpro.ru'
473
+ 0x6C, 0x70, 0x72, 0x6F,
474
+ 0x2E, 0x72, 0x75, 0x00,
475
+ 0x00, 0x00, 0x00, 0x00,
476
+ 0x00, 0x00, 0x00, 0x00,
477
+ 0x00, 0x00, 0x00, 0x00,
478
+ 0x00, 0x00, 0x00, 0x00,
479
+ 0x00, 0x00, 0x00, // compressorname padding
480
+ 0x00, 0x18, // depth = 24
481
+ 0x11, 0x11 // pre_defined = -1
482
+ ]),
483
+ hvcc,
484
+ MP4.box(MP4.types.btrt, new Uint8Array([
485
+ 0x00, 0x1c, 0x9c, 0x80, // bufferSizeDB
486
+ 0x00, 0x2d, 0xc6, 0xc0, // maxBitrate
487
+ 0x00, 0x2d, 0xc6, 0xc0 // avgBitrate
488
+ ])));
489
+ }
378
490
 
379
491
  static esds(track) {
380
492
  var configlen = track.config.byteLength;
@@ -445,6 +557,9 @@ export class MP4 {
445
557
  if (track.type === 'audio') {
446
558
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
447
559
  } else {
560
+ if (track.codec.startsWith('hvc1')) {
561
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hev1(track));
562
+ }
448
563
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
449
564
  }
450
565
  }
@@ -465,9 +580,9 @@ export class MP4 {
465
580
  (id >> 8) & 0xFF,
466
581
  id & 0xFF, // track_ID
467
582
  0x00, 0x00, 0x00, 0x00, // reserved
468
- (duration >> 24),
469
- (duration >> 16) & 0xFF,
470
- (duration >> 8) & 0xFF,
583
+ (duration >>> 24) & 0xFF,
584
+ (duration >>> 16) & 0xFF,
585
+ (duration >>> 8) & 0xFF,
471
586
  duration & 0xFF, // duration
472
587
  0x00, 0x00, 0x00, 0x00,
473
588
  0x00, 0x00, 0x00, 0x00, // reserved
package/loader-thumb.jpg DELETED
Binary file
package/packet-format.png DELETED
Binary file