hls.js 1.5.2-0.canary.9970 → 1.5.2-0.canary.9971

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.
@@ -431,7 +431,7 @@ function enableLogs(debugConfig, context, id) {
431
431
  // Some browsers don't allow to use bind on console object anyway
432
432
  // fallback to default if needed
433
433
  try {
434
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.2-0.canary.9970"}`);
434
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.2-0.canary.9971"}`);
435
435
  } catch (e) {
436
436
  /* log fn threw an exception. All logger methods are no-ops. */
437
437
  return createLogger();
@@ -14074,6 +14074,110 @@ class BaseVideoParser {
14074
14074
  logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
14075
14075
  }
14076
14076
  }
14077
+ parseNALu(track, array) {
14078
+ const len = array.byteLength;
14079
+ let state = track.naluState || 0;
14080
+ const lastState = state;
14081
+ const units = [];
14082
+ let i = 0;
14083
+ let value;
14084
+ let overflow;
14085
+ let unitType;
14086
+ let lastUnitStart = -1;
14087
+ let lastUnitType = 0;
14088
+ // logger.log('PES:' + Hex.hexDump(array));
14089
+
14090
+ if (state === -1) {
14091
+ // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14092
+ lastUnitStart = 0;
14093
+ // NALu type is value read from offset 0
14094
+ lastUnitType = this.getNALuType(array, 0);
14095
+ state = 0;
14096
+ i = 1;
14097
+ }
14098
+ while (i < len) {
14099
+ value = array[i++];
14100
+ // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14101
+ if (!state) {
14102
+ state = value ? 0 : 1;
14103
+ continue;
14104
+ }
14105
+ if (state === 1) {
14106
+ state = value ? 0 : 2;
14107
+ continue;
14108
+ }
14109
+ // here we have state either equal to 2 or 3
14110
+ if (!value) {
14111
+ state = 3;
14112
+ } else if (value === 1) {
14113
+ overflow = i - state - 1;
14114
+ if (lastUnitStart >= 0) {
14115
+ const unit = {
14116
+ data: array.subarray(lastUnitStart, overflow),
14117
+ type: lastUnitType
14118
+ };
14119
+ // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14120
+ units.push(unit);
14121
+ } else {
14122
+ // lastUnitStart is undefined => this is the first start code found in this PES packet
14123
+ // first check if start code delimiter is overlapping between 2 PES packets,
14124
+ // ie it started in last packet (lastState not zero)
14125
+ // and ended at the beginning of this PES packet (i <= 4 - lastState)
14126
+ const lastUnit = this.getLastNalUnit(track.samples);
14127
+ if (lastUnit) {
14128
+ if (lastState && i <= 4 - lastState) {
14129
+ // start delimiter overlapping between PES packets
14130
+ // strip start delimiter bytes from the end of last NAL unit
14131
+ // check if lastUnit had a state different from zero
14132
+ if (lastUnit.state) {
14133
+ // strip last bytes
14134
+ lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14135
+ }
14136
+ }
14137
+ // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14138
+
14139
+ if (overflow > 0) {
14140
+ // logger.log('first NALU found with overflow:' + overflow);
14141
+ lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14142
+ lastUnit.state = 0;
14143
+ }
14144
+ }
14145
+ }
14146
+ // check if we can read unit type
14147
+ if (i < len) {
14148
+ unitType = this.getNALuType(array, i);
14149
+ // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14150
+ lastUnitStart = i;
14151
+ lastUnitType = unitType;
14152
+ state = 0;
14153
+ } else {
14154
+ // not enough byte to read unit type. let's read it on next PES parsing
14155
+ state = -1;
14156
+ }
14157
+ } else {
14158
+ state = 0;
14159
+ }
14160
+ }
14161
+ if (lastUnitStart >= 0 && state >= 0) {
14162
+ const unit = {
14163
+ data: array.subarray(lastUnitStart, len),
14164
+ type: lastUnitType,
14165
+ state: state
14166
+ };
14167
+ units.push(unit);
14168
+ // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14169
+ }
14170
+ // no NALu found
14171
+ if (units.length === 0) {
14172
+ // append pes.data to previous NAL unit
14173
+ const lastUnit = this.getLastNalUnit(track.samples);
14174
+ if (lastUnit) {
14175
+ lastUnit.data = appendUint8Array(lastUnit.data, array);
14176
+ }
14177
+ }
14178
+ track.naluState = state;
14179
+ return units;
14180
+ }
14077
14181
  }
14078
14182
 
14079
14183
  /**
@@ -14216,21 +14320,171 @@ class ExpGolomb {
14216
14320
  readUInt() {
14217
14321
  return this.readBits(32);
14218
14322
  }
14323
+ }
14324
+
14325
+ class AvcVideoParser extends BaseVideoParser {
14326
+ parsePES(track, textTrack, pes, last, duration) {
14327
+ const units = this.parseNALu(track, pes.data);
14328
+ let VideoSample = this.VideoSample;
14329
+ let push;
14330
+ let spsfound = false;
14331
+ // free pes.data to save up some memory
14332
+ pes.data = null;
14333
+
14334
+ // if new NAL units found and last sample still there, let's push ...
14335
+ // this helps parsing streams with missing AUD (only do this if AUD never found)
14336
+ if (VideoSample && units.length && !track.audFound) {
14337
+ this.pushAccessUnit(VideoSample, track);
14338
+ VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
14339
+ }
14340
+ units.forEach(unit => {
14341
+ var _VideoSample2;
14342
+ switch (unit.type) {
14343
+ // NDR
14344
+ case 1:
14345
+ {
14346
+ let iskey = false;
14347
+ push = true;
14348
+ const data = unit.data;
14349
+ // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
14350
+ if (spsfound && data.length > 4) {
14351
+ // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
14352
+ const sliceType = this.readSliceType(data);
14353
+ // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
14354
+ // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
14355
+ // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
14356
+ // I slice: A slice that is not an SI slice that is decoded using intra prediction only.
14357
+ // if (sliceType === 2 || sliceType === 7) {
14358
+ if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
14359
+ iskey = true;
14360
+ }
14361
+ }
14362
+ if (iskey) {
14363
+ var _VideoSample;
14364
+ // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push
14365
+ if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
14366
+ this.pushAccessUnit(VideoSample, track);
14367
+ VideoSample = this.VideoSample = null;
14368
+ }
14369
+ }
14370
+ if (!VideoSample) {
14371
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
14372
+ }
14373
+ VideoSample.frame = true;
14374
+ VideoSample.key = iskey;
14375
+ break;
14376
+ // IDR
14377
+ }
14378
+ case 5:
14379
+ push = true;
14380
+ // handle PES not starting with AUD
14381
+ // if we have frame data already, that cannot belong to the same frame, so force a push
14382
+ if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) {
14383
+ this.pushAccessUnit(VideoSample, track);
14384
+ VideoSample = this.VideoSample = null;
14385
+ }
14386
+ if (!VideoSample) {
14387
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
14388
+ }
14389
+ VideoSample.key = true;
14390
+ VideoSample.frame = true;
14391
+ break;
14392
+ // SEI
14393
+ case 6:
14394
+ {
14395
+ push = true;
14396
+ parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples);
14397
+ break;
14398
+ // SPS
14399
+ }
14400
+ case 7:
14401
+ {
14402
+ var _track$pixelRatio, _track$pixelRatio2;
14403
+ push = true;
14404
+ spsfound = true;
14405
+ const sps = unit.data;
14406
+ const config = this.readSPS(sps);
14407
+ if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) {
14408
+ track.width = config.width;
14409
+ track.height = config.height;
14410
+ track.pixelRatio = config.pixelRatio;
14411
+ track.sps = [sps];
14412
+ track.duration = duration;
14413
+ const codecarray = sps.subarray(1, 4);
14414
+ let codecstring = 'avc1.';
14415
+ for (let i = 0; i < 3; i++) {
14416
+ let h = codecarray[i].toString(16);
14417
+ if (h.length < 2) {
14418
+ h = '0' + h;
14419
+ }
14420
+ codecstring += h;
14421
+ }
14422
+ track.codec = codecstring;
14423
+ }
14424
+ break;
14425
+ }
14426
+ // PPS
14427
+ case 8:
14428
+ push = true;
14429
+ track.pps = [unit.data];
14430
+ break;
14431
+ // AUD
14432
+ case 9:
14433
+ push = true;
14434
+ track.audFound = true;
14435
+ if (VideoSample) {
14436
+ this.pushAccessUnit(VideoSample, track);
14437
+ }
14438
+ VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
14439
+ break;
14440
+ // Filler Data
14441
+ case 12:
14442
+ push = true;
14443
+ break;
14444
+ default:
14445
+ push = false;
14446
+ if (VideoSample) {
14447
+ VideoSample.debug += 'unknown NAL ' + unit.type + ' ';
14448
+ }
14449
+ break;
14450
+ }
14451
+ if (VideoSample && push) {
14452
+ const units = VideoSample.units;
14453
+ units.push(unit);
14454
+ }
14455
+ });
14456
+ // if last PES packet, push samples
14457
+ if (last && VideoSample) {
14458
+ this.pushAccessUnit(VideoSample, track);
14459
+ this.VideoSample = null;
14460
+ }
14461
+ }
14462
+ getNALuType(data, offset) {
14463
+ return data[offset] & 0x1f;
14464
+ }
14465
+ readSliceType(data) {
14466
+ const eg = new ExpGolomb(data);
14467
+ // skip NALu type
14468
+ eg.readUByte();
14469
+ // discard first_mb_in_slice
14470
+ eg.readUEG();
14471
+ // return slice_type
14472
+ return eg.readUEG();
14473
+ }
14219
14474
 
14220
14475
  /**
14221
- * Advance the ExpGolomb decoder past a scaling list. The scaling
14222
- * list is optionally transmitted as part of a sequence parameter
14476
+ * The scaling list is optionally transmitted as part of a sequence parameter
14223
14477
  * set and is not relevant to transmuxing.
14224
14478
  * @param count the number of entries in this scaling list
14225
14479
  * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
14226
14480
  */
14227
- skipScalingList(count) {
14481
+ skipScalingList(count, reader) {
14228
14482
  let lastScale = 8;
14229
14483
  let nextScale = 8;
14230
14484
  let deltaScale;
14231
14485
  for (let j = 0; j < count; j++) {
14232
14486
  if (nextScale !== 0) {
14233
- deltaScale = this.readEG();
14487
+ deltaScale = reader.readEG();
14234
14488
  nextScale = (lastScale + deltaScale + 256) % 256;
14235
14489
  }
14236
14490
  lastScale = nextScale === 0 ? lastScale : nextScale;
@@ -14245,7 +14499,8 @@ class ExpGolomb {
14245
14499
  * sequence parameter set, including the dimensions of the
14246
14500
  * associated video frames.
14247
14501
  */
14248
- readSPS() {
14502
+ readSPS(sps) {
14503
+ const eg = new ExpGolomb(sps);
14249
14504
  let frameCropLeftOffset = 0;
14250
14505
  let frameCropRightOffset = 0;
14251
14506
  let frameCropTopOffset = 0;
@@ -14253,13 +14508,13 @@ class ExpGolomb {
14253
14508
  let numRefFramesInPicOrderCntCycle;
14254
14509
  let scalingListCount;
14255
14510
  let i;
14256
- const readUByte = this.readUByte.bind(this);
14257
- const readBits = this.readBits.bind(this);
14258
- const readUEG = this.readUEG.bind(this);
14259
- const readBoolean = this.readBoolean.bind(this);
14260
- const skipBits = this.skipBits.bind(this);
14261
- const skipEG = this.skipEG.bind(this);
14262
- const skipUEG = this.skipUEG.bind(this);
14511
+ const readUByte = eg.readUByte.bind(eg);
14512
+ const readBits = eg.readBits.bind(eg);
14513
+ const readUEG = eg.readUEG.bind(eg);
14514
+ const readBoolean = eg.readBoolean.bind(eg);
14515
+ const skipBits = eg.skipBits.bind(eg);
14516
+ const skipEG = eg.skipEG.bind(eg);
14517
+ const skipUEG = eg.skipUEG.bind(eg);
14263
14518
  const skipScalingList = this.skipScalingList.bind(this);
14264
14519
  readUByte();
14265
14520
  const profileIdc = readUByte(); // profile_idc
@@ -14284,9 +14539,9 @@ class ExpGolomb {
14284
14539
  if (readBoolean()) {
14285
14540
  // seq_scaling_list_present_flag[ i ]
14286
14541
  if (i < 6) {
14287
- skipScalingList(16);
14542
+ skipScalingList(16, eg);
14288
14543
  } else {
14289
- skipScalingList(64);
14544
+ skipScalingList(64, eg);
14290
14545
  }
14291
14546
  }
14292
14547
  }
@@ -14391,19 +14646,15 @@ class ExpGolomb {
14391
14646
  pixelRatio: pixelRatio
14392
14647
  };
14393
14648
  }
14394
- readSliceType() {
14395
- // skip NALu type
14396
- this.readUByte();
14397
- // discard first_mb_in_slice
14398
- this.readUEG();
14399
- // return slice_type
14400
- return this.readUEG();
14401
- }
14402
14649
  }
14403
14650
 
14404
- class AvcVideoParser extends BaseVideoParser {
14405
- parseAVCPES(track, textTrack, pes, last, duration) {
14406
- const units = this.parseAVCNALu(track, pes.data);
14651
+ class HevcVideoParser extends BaseVideoParser {
14652
+ constructor(...args) {
14653
+ super(...args);
14654
+ this.initVPS = null;
14655
+ }
14656
+ parsePES(track, textTrack, pes, last, duration) {
14657
+ const units = this.parseNALu(track, pes.data);
14407
14658
  let VideoSample = this.VideoSample;
14408
14659
  let push;
14409
14660
  let spsfound = false;
@@ -14419,42 +14670,49 @@ class AvcVideoParser extends BaseVideoParser {
14419
14670
  units.forEach(unit => {
14420
14671
  var _VideoSample2;
14421
14672
  switch (unit.type) {
14422
- // NDR
14673
+ // NON-IDR, NON RANDOM ACCESS SLICE
14674
+ case 0:
14423
14675
  case 1:
14424
- {
14425
- let iskey = false;
14426
- push = true;
14427
- const data = unit.data;
14428
- // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
14429
- if (spsfound && data.length > 4) {
14430
- // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
14431
- const sliceType = new ExpGolomb(data).readSliceType();
14432
- // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
14433
- // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
14434
- // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
14435
- // I slice: A slice that is not an SI slice that is decoded using intra prediction only.
14436
- // if (sliceType === 2 || sliceType === 7) {
14437
- if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
14438
- iskey = true;
14439
- }
14440
- }
14441
- if (iskey) {
14442
- var _VideoSample;
14443
- // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push
14444
- if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
14445
- this.pushAccessUnit(VideoSample, track);
14446
- VideoSample = this.VideoSample = null;
14447
- }
14448
- }
14449
- if (!VideoSample) {
14450
- VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
14451
- }
14452
- VideoSample.frame = true;
14453
- VideoSample.key = iskey;
14454
- break;
14455
- // IDR
14676
+ case 2:
14677
+ case 3:
14678
+ case 4:
14679
+ case 5:
14680
+ case 6:
14681
+ case 7:
14682
+ case 8:
14683
+ case 9:
14684
+ if (!VideoSample) {
14685
+ VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
14686
+ }
14687
+ VideoSample.frame = true;
14688
+ push = true;
14689
+ break;
14690
+
14691
+ // CRA, BLA (random access picture)
14692
+ case 16:
14693
+ case 17:
14694
+ case 18:
14695
+ case 21:
14696
+ push = true;
14697
+ if (spsfound) {
14698
+ var _VideoSample;
14699
+ // handle PES not starting with AUD
14700
+ // if we have frame data already, that cannot belong to the same frame, so force a push
14701
+ if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
14702
+ this.pushAccessUnit(VideoSample, track);
14703
+ VideoSample = this.VideoSample = null;
14704
+ }
14456
14705
  }
14457
- case 5:
14706
+ if (!VideoSample) {
14707
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
14708
+ }
14709
+ VideoSample.key = true;
14710
+ VideoSample.frame = true;
14711
+ break;
14712
+
14713
+ // IDR
14714
+ case 19:
14715
+ case 20:
14458
14716
  push = true;
14459
14717
  // handle PES not starting with AUD
14460
14718
  // if we have frame data already, that cannot belong to the same frame, so force a push
@@ -14468,48 +14726,76 @@ class AvcVideoParser extends BaseVideoParser {
14468
14726
  VideoSample.key = true;
14469
14727
  VideoSample.frame = true;
14470
14728
  break;
14729
+
14471
14730
  // SEI
14472
- case 6:
14473
- {
14474
- push = true;
14475
- parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples);
14476
- break;
14477
- // SPS
14731
+ case 39:
14732
+ push = true;
14733
+ parseSEIMessageFromNALu(unit.data, 2,
14734
+ // NALu header size
14735
+ pes.pts, textTrack.samples);
14736
+ break;
14737
+
14738
+ // VPS
14739
+ case 32:
14740
+ push = true;
14741
+ if (!track.vps) {
14742
+ const config = this.readVPS(unit.data);
14743
+ track.params = _objectSpread2({}, config);
14744
+ this.initVPS = unit.data;
14478
14745
  }
14479
- case 7:
14480
- {
14481
- var _track$pixelRatio, _track$pixelRatio2;
14482
- push = true;
14483
- spsfound = true;
14484
- const sps = unit.data;
14485
- const expGolombDecoder = new ExpGolomb(sps);
14486
- const config = expGolombDecoder.readSPS();
14487
- if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) {
14746
+ track.vps = [unit.data];
14747
+ break;
14748
+
14749
+ // SPS
14750
+ case 33:
14751
+ push = true;
14752
+ spsfound = true;
14753
+ if (typeof track.params === 'object') {
14754
+ if (track.vps !== undefined && track.vps[0] !== this.initVPS && track.sps !== undefined && !this.matchSPS(track.sps[0], unit.data)) {
14755
+ this.initVPS = track.vps[0];
14756
+ track.sps = track.pps = undefined;
14757
+ }
14758
+ if (!track.sps) {
14759
+ const config = this.readSPS(unit.data);
14488
14760
  track.width = config.width;
14489
14761
  track.height = config.height;
14490
14762
  track.pixelRatio = config.pixelRatio;
14491
- track.sps = [sps];
14492
14763
  track.duration = duration;
14493
- const codecarray = sps.subarray(1, 4);
14494
- let codecstring = 'avc1.';
14495
- for (let i = 0; i < 3; i++) {
14496
- let h = codecarray[i].toString(16);
14497
- if (h.length < 2) {
14498
- h = '0' + h;
14499
- }
14500
- codecstring += h;
14764
+ track.codec = config.codecString;
14765
+ track.sps = [];
14766
+ for (const prop in config.params) {
14767
+ track.params[prop] = config.params[prop];
14501
14768
  }
14502
- track.codec = codecstring;
14503
14769
  }
14504
- break;
14770
+ if (track.vps !== undefined && track.vps[0] === this.initVPS) {
14771
+ track.sps.push(unit.data);
14772
+ }
14773
+ }
14774
+ if (!VideoSample) {
14775
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
14505
14776
  }
14777
+ VideoSample.key = true;
14778
+ break;
14779
+
14506
14780
  // PPS
14507
- case 8:
14781
+ case 34:
14508
14782
  push = true;
14509
- track.pps = [unit.data];
14783
+ if (typeof track.params === 'object') {
14784
+ if (!track.pps) {
14785
+ track.pps = [];
14786
+ const config = this.readPPS(unit.data);
14787
+ for (const prop in config) {
14788
+ track.params[prop] = config[prop];
14789
+ }
14790
+ }
14791
+ if (this.initVPS !== null || track.pps.length === 0) {
14792
+ track.pps.push(unit.data);
14793
+ }
14794
+ }
14510
14795
  break;
14511
- // AUD
14512
- case 9:
14796
+
14797
+ // ACCESS UNIT DELIMITER
14798
+ case 35:
14513
14799
  push = true;
14514
14800
  track.audFound = true;
14515
14801
  if (VideoSample) {
@@ -14517,14 +14803,10 @@ class AvcVideoParser extends BaseVideoParser {
14517
14803
  }
14518
14804
  VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
14519
14805
  break;
14520
- // Filler Data
14521
- case 12:
14522
- push = true;
14523
- break;
14524
14806
  default:
14525
14807
  push = false;
14526
14808
  if (VideoSample) {
14527
- VideoSample.debug += 'unknown NAL ' + unit.type + ' ';
14809
+ VideoSample.debug += 'unknown or irrelevant NAL ' + unit.type + ' ';
14528
14810
  }
14529
14811
  break;
14530
14812
  }
@@ -14539,109 +14821,423 @@ class AvcVideoParser extends BaseVideoParser {
14539
14821
  this.VideoSample = null;
14540
14822
  }
14541
14823
  }
14542
- parseAVCNALu(track, array) {
14543
- const len = array.byteLength;
14544
- let state = track.naluState || 0;
14545
- const lastState = state;
14546
- const units = [];
14547
- let i = 0;
14548
- let value;
14549
- let overflow;
14550
- let unitType;
14551
- let lastUnitStart = -1;
14552
- let lastUnitType = 0;
14553
- // logger.log('PES:' + Hex.hexDump(array));
14554
-
14555
- if (state === -1) {
14556
- // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
14557
- lastUnitStart = 0;
14558
- // NALu type is value read from offset 0
14559
- lastUnitType = array[0] & 0x1f;
14560
- state = 0;
14561
- i = 1;
14562
- }
14563
- while (i < len) {
14564
- value = array[i++];
14565
- // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
14566
- if (!state) {
14567
- state = value ? 0 : 1;
14568
- continue;
14569
- }
14570
- if (state === 1) {
14571
- state = value ? 0 : 2;
14572
- continue;
14824
+ getNALuType(data, offset) {
14825
+ return (data[offset] & 0x7e) >>> 1;
14826
+ }
14827
+ ebsp2rbsp(arr) {
14828
+ const dst = new Uint8Array(arr.byteLength);
14829
+ let dstIdx = 0;
14830
+ for (let i = 0; i < arr.byteLength; i++) {
14831
+ if (i >= 2) {
14832
+ // Unescape: Skip 0x03 after 00 00
14833
+ if (arr[i] === 0x03 && arr[i - 1] === 0x00 && arr[i - 2] === 0x00) {
14834
+ continue;
14835
+ }
14573
14836
  }
14574
- // here we have state either equal to 2 or 3
14575
- if (!value) {
14576
- state = 3;
14577
- } else if (value === 1) {
14578
- overflow = i - state - 1;
14579
- if (lastUnitStart >= 0) {
14580
- const unit = {
14581
- data: array.subarray(lastUnitStart, overflow),
14582
- type: lastUnitType
14583
- };
14584
- // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
14585
- units.push(unit);
14586
- } else {
14587
- // lastUnitStart is undefined => this is the first start code found in this PES packet
14588
- // first check if start code delimiter is overlapping between 2 PES packets,
14589
- // ie it started in last packet (lastState not zero)
14590
- // and ended at the beginning of this PES packet (i <= 4 - lastState)
14591
- const lastUnit = this.getLastNalUnit(track.samples);
14592
- if (lastUnit) {
14593
- if (lastState && i <= 4 - lastState) {
14594
- // start delimiter overlapping between PES packets
14595
- // strip start delimiter bytes from the end of last NAL unit
14596
- // check if lastUnit had a state different from zero
14597
- if (lastUnit.state) {
14598
- // strip last bytes
14599
- lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
14600
- }
14601
- }
14602
- // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
14837
+ dst[dstIdx] = arr[i];
14838
+ dstIdx++;
14839
+ }
14840
+ return new Uint8Array(dst.buffer, 0, dstIdx);
14841
+ }
14842
+ readVPS(vps) {
14843
+ const eg = new ExpGolomb(vps);
14844
+ // remove header
14845
+ eg.readUByte();
14846
+ eg.readUByte();
14847
+ eg.readBits(4); // video_parameter_set_id
14848
+ eg.skipBits(2);
14849
+ eg.readBits(6); // max_layers_minus1
14850
+ const max_sub_layers_minus1 = eg.readBits(3);
14851
+ const temporal_id_nesting_flag = eg.readBoolean();
14852
+ // ...vui fps can be here, but empty fps value is not critical for metadata
14603
14853
 
14604
- if (overflow > 0) {
14605
- // logger.log('first NALU found with overflow:' + overflow);
14606
- lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
14607
- lastUnit.state = 0;
14854
+ return {
14855
+ numTemporalLayers: max_sub_layers_minus1 + 1,
14856
+ temporalIdNested: temporal_id_nesting_flag
14857
+ };
14858
+ }
14859
+ readSPS(sps) {
14860
+ const eg = new ExpGolomb(this.ebsp2rbsp(sps));
14861
+ eg.readUByte();
14862
+ eg.readUByte();
14863
+ eg.readBits(4); //video_parameter_set_id
14864
+ const max_sub_layers_minus1 = eg.readBits(3);
14865
+ eg.readBoolean(); // temporal_id_nesting_flag
14866
+
14867
+ // profile_tier_level
14868
+ const general_profile_space = eg.readBits(2);
14869
+ const general_tier_flag = eg.readBoolean();
14870
+ const general_profile_idc = eg.readBits(5);
14871
+ const general_profile_compatibility_flags_1 = eg.readUByte();
14872
+ const general_profile_compatibility_flags_2 = eg.readUByte();
14873
+ const general_profile_compatibility_flags_3 = eg.readUByte();
14874
+ const general_profile_compatibility_flags_4 = eg.readUByte();
14875
+ const general_constraint_indicator_flags_1 = eg.readUByte();
14876
+ const general_constraint_indicator_flags_2 = eg.readUByte();
14877
+ const general_constraint_indicator_flags_3 = eg.readUByte();
14878
+ const general_constraint_indicator_flags_4 = eg.readUByte();
14879
+ const general_constraint_indicator_flags_5 = eg.readUByte();
14880
+ const general_constraint_indicator_flags_6 = eg.readUByte();
14881
+ const general_level_idc = eg.readUByte();
14882
+ const sub_layer_profile_present_flags = [];
14883
+ const sub_layer_level_present_flags = [];
14884
+ for (let i = 0; i < max_sub_layers_minus1; i++) {
14885
+ sub_layer_profile_present_flags.push(eg.readBoolean());
14886
+ sub_layer_level_present_flags.push(eg.readBoolean());
14887
+ }
14888
+ if (max_sub_layers_minus1 > 0) {
14889
+ for (let i = max_sub_layers_minus1; i < 8; i++) {
14890
+ eg.readBits(2);
14891
+ }
14892
+ }
14893
+ for (let i = 0; i < max_sub_layers_minus1; i++) {
14894
+ if (sub_layer_profile_present_flags[i]) {
14895
+ eg.readUByte(); // sub_layer_profile_space, sub_layer_tier_flag, sub_layer_profile_idc
14896
+ eg.readUByte();
14897
+ eg.readUByte();
14898
+ eg.readUByte();
14899
+ eg.readUByte(); // sub_layer_profile_compatibility_flag
14900
+ eg.readUByte();
14901
+ eg.readUByte();
14902
+ eg.readUByte();
14903
+ eg.readUByte();
14904
+ eg.readUByte();
14905
+ eg.readUByte();
14906
+ }
14907
+ if (sub_layer_level_present_flags[i]) {
14908
+ eg.readUByte();
14909
+ }
14910
+ }
14911
+ eg.readUEG(); // seq_parameter_set_id
14912
+ const chroma_format_idc = eg.readUEG();
14913
+ if (chroma_format_idc == 3) {
14914
+ eg.skipBits(1); //separate_colour_plane_flag
14915
+ }
14916
+ const pic_width_in_luma_samples = eg.readUEG();
14917
+ const pic_height_in_luma_samples = eg.readUEG();
14918
+ const conformance_window_flag = eg.readBoolean();
14919
+ let pic_left_offset = 0,
14920
+ pic_right_offset = 0,
14921
+ pic_top_offset = 0,
14922
+ pic_bottom_offset = 0;
14923
+ if (conformance_window_flag) {
14924
+ pic_left_offset += eg.readUEG();
14925
+ pic_right_offset += eg.readUEG();
14926
+ pic_top_offset += eg.readUEG();
14927
+ pic_bottom_offset += eg.readUEG();
14928
+ }
14929
+ const bit_depth_luma_minus8 = eg.readUEG();
14930
+ const bit_depth_chroma_minus8 = eg.readUEG();
14931
+ const log2_max_pic_order_cnt_lsb_minus4 = eg.readUEG();
14932
+ const sub_layer_ordering_info_present_flag = eg.readBoolean();
14933
+ for (let i = sub_layer_ordering_info_present_flag ? 0 : max_sub_layers_minus1; i <= max_sub_layers_minus1; i++) {
14934
+ eg.skipUEG(); // max_dec_pic_buffering_minus1[i]
14935
+ eg.skipUEG(); // max_num_reorder_pics[i]
14936
+ eg.skipUEG(); // max_latency_increase_plus1[i]
14937
+ }
14938
+ eg.skipUEG(); // log2_min_luma_coding_block_size_minus3
14939
+ eg.skipUEG(); // log2_diff_max_min_luma_coding_block_size
14940
+ eg.skipUEG(); // log2_min_transform_block_size_minus2
14941
+ eg.skipUEG(); // log2_diff_max_min_transform_block_size
14942
+ eg.skipUEG(); // max_transform_hierarchy_depth_inter
14943
+ eg.skipUEG(); // max_transform_hierarchy_depth_intra
14944
+ const scaling_list_enabled_flag = eg.readBoolean();
14945
+ if (scaling_list_enabled_flag) {
14946
+ const sps_scaling_list_data_present_flag = eg.readBoolean();
14947
+ if (sps_scaling_list_data_present_flag) {
14948
+ for (let sizeId = 0; sizeId < 4; sizeId++) {
14949
+ for (let matrixId = 0; matrixId < (sizeId === 3 ? 2 : 6); matrixId++) {
14950
+ const scaling_list_pred_mode_flag = eg.readBoolean();
14951
+ if (!scaling_list_pred_mode_flag) {
14952
+ eg.readUEG(); // scaling_list_pred_matrix_id_delta
14953
+ } else {
14954
+ const coefNum = Math.min(64, 1 << 4 + (sizeId << 1));
14955
+ if (sizeId > 1) {
14956
+ eg.readEG();
14957
+ }
14958
+ for (let i = 0; i < coefNum; i++) {
14959
+ eg.readEG();
14960
+ }
14608
14961
  }
14609
14962
  }
14610
14963
  }
14611
- // check if we can read unit type
14612
- if (i < len) {
14613
- unitType = array[i] & 0x1f;
14614
- // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
14615
- lastUnitStart = i;
14616
- lastUnitType = unitType;
14617
- state = 0;
14618
- } else {
14619
- // not enough byte to read unit type. let's read it on next PES parsing
14620
- state = -1;
14964
+ }
14965
+ }
14966
+ eg.readBoolean(); // amp_enabled_flag
14967
+ eg.readBoolean(); // sample_adaptive_offset_enabled_flag
14968
+ const pcm_enabled_flag = eg.readBoolean();
14969
+ if (pcm_enabled_flag) {
14970
+ eg.readUByte();
14971
+ eg.skipUEG();
14972
+ eg.skipUEG();
14973
+ eg.readBoolean();
14974
+ }
14975
+ const num_short_term_ref_pic_sets = eg.readUEG();
14976
+ let num_delta_pocs = 0;
14977
+ for (let i = 0; i < num_short_term_ref_pic_sets; i++) {
14978
+ let inter_ref_pic_set_prediction_flag = false;
14979
+ if (i !== 0) {
14980
+ inter_ref_pic_set_prediction_flag = eg.readBoolean();
14981
+ }
14982
+ if (inter_ref_pic_set_prediction_flag) {
14983
+ if (i === num_short_term_ref_pic_sets) {
14984
+ eg.readUEG();
14985
+ }
14986
+ eg.readBoolean();
14987
+ eg.readUEG();
14988
+ let next_num_delta_pocs = 0;
14989
+ for (let j = 0; j <= num_delta_pocs; j++) {
14990
+ const used_by_curr_pic_flag = eg.readBoolean();
14991
+ let use_delta_flag = false;
14992
+ if (!used_by_curr_pic_flag) {
14993
+ use_delta_flag = eg.readBoolean();
14994
+ }
14995
+ if (used_by_curr_pic_flag || use_delta_flag) {
14996
+ next_num_delta_pocs++;
14997
+ }
14621
14998
  }
14999
+ num_delta_pocs = next_num_delta_pocs;
14622
15000
  } else {
14623
- state = 0;
15001
+ const num_negative_pics = eg.readUEG();
15002
+ const num_positive_pics = eg.readUEG();
15003
+ num_delta_pocs = num_negative_pics + num_positive_pics;
15004
+ for (let j = 0; j < num_negative_pics; j++) {
15005
+ eg.readUEG();
15006
+ eg.readBoolean();
15007
+ }
15008
+ for (let j = 0; j < num_positive_pics; j++) {
15009
+ eg.readUEG();
15010
+ eg.readBoolean();
15011
+ }
14624
15012
  }
14625
15013
  }
14626
- if (lastUnitStart >= 0 && state >= 0) {
14627
- const unit = {
14628
- data: array.subarray(lastUnitStart, len),
14629
- type: lastUnitType,
14630
- state: state
14631
- };
14632
- units.push(unit);
14633
- // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
14634
- }
14635
- // no NALu found
14636
- if (units.length === 0) {
14637
- // append pes.data to previous NAL unit
14638
- const lastUnit = this.getLastNalUnit(track.samples);
14639
- if (lastUnit) {
14640
- lastUnit.data = appendUint8Array(lastUnit.data, array);
15014
+ const long_term_ref_pics_present_flag = eg.readBoolean();
15015
+ if (long_term_ref_pics_present_flag) {
15016
+ const num_long_term_ref_pics_sps = eg.readUEG();
15017
+ for (let i = 0; i < num_long_term_ref_pics_sps; i++) {
15018
+ for (let j = 0; j < log2_max_pic_order_cnt_lsb_minus4 + 4; j++) {
15019
+ eg.readBits(1);
15020
+ }
15021
+ eg.readBits(1);
15022
+ }
15023
+ }
15024
+ let min_spatial_segmentation_idc = 0;
15025
+ let sar_width = 1,
15026
+ sar_height = 1;
15027
+ let fps_fixed = true,
15028
+ fps_den = 1,
15029
+ fps_num = 0;
15030
+ eg.readBoolean(); // sps_temporal_mvp_enabled_flag
15031
+ eg.readBoolean(); // strong_intra_smoothing_enabled_flag
15032
+ let default_display_window_flag = false;
15033
+ const vui_parameters_present_flag = eg.readBoolean();
15034
+ if (vui_parameters_present_flag) {
15035
+ const aspect_ratio_info_present_flag = eg.readBoolean();
15036
+ if (aspect_ratio_info_present_flag) {
15037
+ const aspect_ratio_idc = eg.readUByte();
15038
+ const sar_width_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];
15039
+ const sar_height_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];
15040
+ if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {
15041
+ sar_width = sar_width_table[aspect_ratio_idc - 1];
15042
+ sar_height = sar_height_table[aspect_ratio_idc - 1];
15043
+ } else if (aspect_ratio_idc === 255) {
15044
+ sar_width = eg.readBits(16);
15045
+ sar_height = eg.readBits(16);
15046
+ }
15047
+ }
15048
+ const overscan_info_present_flag = eg.readBoolean();
15049
+ if (overscan_info_present_flag) {
15050
+ eg.readBoolean();
15051
+ }
15052
+ const video_signal_type_present_flag = eg.readBoolean();
15053
+ if (video_signal_type_present_flag) {
15054
+ eg.readBits(3);
15055
+ eg.readBoolean();
15056
+ const colour_description_present_flag = eg.readBoolean();
15057
+ if (colour_description_present_flag) {
15058
+ eg.readUByte();
15059
+ eg.readUByte();
15060
+ eg.readUByte();
15061
+ }
15062
+ }
15063
+ const chroma_loc_info_present_flag = eg.readBoolean();
15064
+ if (chroma_loc_info_present_flag) {
15065
+ eg.readUEG();
15066
+ eg.readUEG();
15067
+ }
15068
+ eg.readBoolean(); // neutral_chroma_indication_flag
15069
+ eg.readBoolean(); // field_seq_flag
15070
+ eg.readBoolean(); // frame_field_info_present_flag
15071
+ default_display_window_flag = eg.readBoolean();
15072
+ if (default_display_window_flag) {
15073
+ pic_left_offset += eg.readUEG();
15074
+ pic_right_offset += eg.readUEG();
15075
+ pic_top_offset += eg.readUEG();
15076
+ pic_bottom_offset += eg.readUEG();
15077
+ }
15078
+ const vui_timing_info_present_flag = eg.readBoolean();
15079
+ if (vui_timing_info_present_flag) {
15080
+ fps_den = eg.readBits(32);
15081
+ fps_num = eg.readBits(32);
15082
+ const vui_poc_proportional_to_timing_flag = eg.readBoolean();
15083
+ if (vui_poc_proportional_to_timing_flag) {
15084
+ eg.readUEG();
15085
+ }
15086
+ const vui_hrd_parameters_present_flag = eg.readBoolean();
15087
+ if (vui_hrd_parameters_present_flag) {
15088
+ //const commonInfPresentFlag = true;
15089
+ //if (commonInfPresentFlag) {
15090
+ const nal_hrd_parameters_present_flag = eg.readBoolean();
15091
+ const vcl_hrd_parameters_present_flag = eg.readBoolean();
15092
+ let sub_pic_hrd_params_present_flag = false;
15093
+ if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
15094
+ sub_pic_hrd_params_present_flag = eg.readBoolean();
15095
+ if (sub_pic_hrd_params_present_flag) {
15096
+ eg.readUByte();
15097
+ eg.readBits(5);
15098
+ eg.readBoolean();
15099
+ eg.readBits(5);
15100
+ }
15101
+ eg.readBits(4); // bit_rate_scale
15102
+ eg.readBits(4); // cpb_size_scale
15103
+ if (sub_pic_hrd_params_present_flag) {
15104
+ eg.readBits(4);
15105
+ }
15106
+ eg.readBits(5);
15107
+ eg.readBits(5);
15108
+ eg.readBits(5);
15109
+ }
15110
+ //}
15111
+ for (let i = 0; i <= max_sub_layers_minus1; i++) {
15112
+ fps_fixed = eg.readBoolean(); // fixed_pic_rate_general_flag
15113
+ const fixed_pic_rate_within_cvs_flag = fps_fixed || eg.readBoolean();
15114
+ let low_delay_hrd_flag = false;
15115
+ if (fixed_pic_rate_within_cvs_flag) {
15116
+ eg.readEG();
15117
+ } else {
15118
+ low_delay_hrd_flag = eg.readBoolean();
15119
+ }
15120
+ const cpb_cnt = low_delay_hrd_flag ? 1 : eg.readUEG() + 1;
15121
+ if (nal_hrd_parameters_present_flag) {
15122
+ for (let j = 0; j < cpb_cnt; j++) {
15123
+ eg.readUEG();
15124
+ eg.readUEG();
15125
+ if (sub_pic_hrd_params_present_flag) {
15126
+ eg.readUEG();
15127
+ eg.readUEG();
15128
+ }
15129
+ eg.skipBits(1);
15130
+ }
15131
+ }
15132
+ if (vcl_hrd_parameters_present_flag) {
15133
+ for (let j = 0; j < cpb_cnt; j++) {
15134
+ eg.readUEG();
15135
+ eg.readUEG();
15136
+ if (sub_pic_hrd_params_present_flag) {
15137
+ eg.readUEG();
15138
+ eg.readUEG();
15139
+ }
15140
+ eg.skipBits(1);
15141
+ }
15142
+ }
15143
+ }
15144
+ }
14641
15145
  }
15146
+ const bitstream_restriction_flag = eg.readBoolean();
15147
+ if (bitstream_restriction_flag) {
15148
+ eg.readBoolean(); // tiles_fixed_structure_flag
15149
+ eg.readBoolean(); // motion_vectors_over_pic_boundaries_flag
15150
+ eg.readBoolean(); // restricted_ref_pic_lists_flag
15151
+ min_spatial_segmentation_idc = eg.readUEG();
15152
+ }
15153
+ }
15154
+ let width = pic_width_in_luma_samples,
15155
+ height = pic_height_in_luma_samples;
15156
+ if (conformance_window_flag || default_display_window_flag) {
15157
+ let chroma_scale_w = 1,
15158
+ chroma_scale_h = 1;
15159
+ if (chroma_format_idc === 1) {
15160
+ // YUV 420
15161
+ chroma_scale_w = chroma_scale_h = 2;
15162
+ } else if (chroma_format_idc == 2) {
15163
+ // YUV 422
15164
+ chroma_scale_w = 2;
15165
+ }
15166
+ width = pic_width_in_luma_samples - chroma_scale_w * pic_right_offset - chroma_scale_w * pic_left_offset;
15167
+ height = pic_height_in_luma_samples - chroma_scale_h * pic_bottom_offset - chroma_scale_h * pic_top_offset;
15168
+ }
15169
+ const profile_space_string = general_profile_space ? ['A', 'B', 'C'][general_profile_space] : '';
15170
+ const profile_compatibility_buf = general_profile_compatibility_flags_1 << 24 | general_profile_compatibility_flags_2 << 16 | general_profile_compatibility_flags_3 << 8 | general_profile_compatibility_flags_4;
15171
+ let profile_compatibility_rev = 0;
15172
+ for (let i = 0; i < 32; i++) {
15173
+ profile_compatibility_rev = (profile_compatibility_rev | (profile_compatibility_buf >> i & 1) << 31 - i) >>> 0; // reverse bit position (and cast as UInt32)
15174
+ }
15175
+ let profile_compatibility_flags_string = profile_compatibility_rev.toString(16);
15176
+ if (general_profile_idc === 1 && profile_compatibility_flags_string === '2') {
15177
+ profile_compatibility_flags_string = '6';
15178
+ }
15179
+ const tier_flag_string = general_tier_flag ? 'H' : 'L';
15180
+ return {
15181
+ codecString: `hvc1.${profile_space_string}${general_profile_idc}.${profile_compatibility_flags_string}.${tier_flag_string}${general_level_idc}.B0`,
15182
+ params: {
15183
+ general_tier_flag,
15184
+ general_profile_idc,
15185
+ general_profile_space,
15186
+ general_profile_compatibility_flags: [general_profile_compatibility_flags_1, general_profile_compatibility_flags_2, general_profile_compatibility_flags_3, general_profile_compatibility_flags_4],
15187
+ general_constraint_indicator_flags: [general_constraint_indicator_flags_1, general_constraint_indicator_flags_2, general_constraint_indicator_flags_3, general_constraint_indicator_flags_4, general_constraint_indicator_flags_5, general_constraint_indicator_flags_6],
15188
+ general_level_idc,
15189
+ bit_depth: bit_depth_luma_minus8 + 8,
15190
+ bit_depth_luma_minus8,
15191
+ bit_depth_chroma_minus8,
15192
+ min_spatial_segmentation_idc,
15193
+ chroma_format_idc: chroma_format_idc,
15194
+ frame_rate: {
15195
+ fixed: fps_fixed,
15196
+ fps: fps_num / fps_den
15197
+ }
15198
+ },
15199
+ width,
15200
+ height,
15201
+ pixelRatio: [sar_width, sar_height]
15202
+ };
15203
+ }
15204
+ readPPS(pps) {
15205
+ const eg = new ExpGolomb(this.ebsp2rbsp(pps));
15206
+ eg.readUByte();
15207
+ eg.readUByte();
15208
+ eg.skipUEG(); // pic_parameter_set_id
15209
+ eg.skipUEG(); // seq_parameter_set_id
15210
+ eg.skipBits(2); // dependent_slice_segments_enabled_flag, output_flag_present_flag
15211
+ eg.skipBits(3); // num_extra_slice_header_bits
15212
+ eg.skipBits(2); // sign_data_hiding_enabled_flag, cabac_init_present_flag
15213
+ eg.skipUEG();
15214
+ eg.skipUEG();
15215
+ eg.skipEG(); // init_qp_minus26
15216
+ eg.skipBits(2); // constrained_intra_pred_flag, transform_skip_enabled_flag
15217
+ const cu_qp_delta_enabled_flag = eg.readBoolean();
15218
+ if (cu_qp_delta_enabled_flag) {
15219
+ eg.skipUEG();
15220
+ }
15221
+ eg.skipEG(); // cb_qp_offset
15222
+ eg.skipEG(); // cr_qp_offset
15223
+ eg.skipBits(4); // pps_slice_chroma_qp_offsets_present_flag, weighted_pred_flag, weighted_bipred_flag, transquant_bypass_enabled_flag
15224
+ const tiles_enabled_flag = eg.readBoolean();
15225
+ const entropy_coding_sync_enabled_flag = eg.readBoolean();
15226
+ let parallelismType = 1; // slice-based parallel decoding
15227
+ if (entropy_coding_sync_enabled_flag && tiles_enabled_flag) {
15228
+ parallelismType = 0; // mixed-type parallel decoding
15229
+ } else if (entropy_coding_sync_enabled_flag) {
15230
+ parallelismType = 3; // wavefront-based parallel decoding
15231
+ } else if (tiles_enabled_flag) {
15232
+ parallelismType = 2; // tile-based parallel decoding
14642
15233
  }
14643
- track.naluState = state;
14644
- return units;
15234
+ return {
15235
+ parallelismType
15236
+ };
15237
+ }
15238
+ matchSPS(sps1, sps2) {
15239
+ // compare without headers and VPS related params
15240
+ return String.fromCharCode.apply(null, sps1).substr(3) === String.fromCharCode.apply(null, sps2).substr(3);
14645
15241
  }
14646
15242
  }
14647
15243
 
@@ -14773,7 +15369,7 @@ class TSDemuxer {
14773
15369
  this.observer = observer;
14774
15370
  this.config = config;
14775
15371
  this.typeSupported = typeSupported;
14776
- this.videoParser = new AvcVideoParser();
15372
+ this.videoParser = null;
14777
15373
  }
14778
15374
  static probe(data) {
14779
15375
  const syncOffset = TSDemuxer.syncOffset(data);
@@ -14938,7 +15534,19 @@ class TSDemuxer {
14938
15534
  case videoPid:
14939
15535
  if (stt) {
14940
15536
  if (videoData && (pes = parsePES(videoData))) {
14941
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration);
15537
+ if (this.videoParser === null) {
15538
+ switch (videoTrack.segmentCodec) {
15539
+ case 'avc':
15540
+ this.videoParser = new AvcVideoParser();
15541
+ break;
15542
+ case 'hevc':
15543
+ this.videoParser = new HevcVideoParser();
15544
+ break;
15545
+ }
15546
+ }
15547
+ if (this.videoParser !== null) {
15548
+ this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
15549
+ }
14942
15550
  }
14943
15551
  videoData = {
14944
15552
  data: [],
@@ -15100,8 +15708,20 @@ class TSDemuxer {
15100
15708
  // try to parse last PES packets
15101
15709
  let pes;
15102
15710
  if (videoData && (pes = parsePES(videoData))) {
15103
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration);
15104
- videoTrack.pesData = null;
15711
+ if (this.videoParser === null) {
15712
+ switch (videoTrack.segmentCodec) {
15713
+ case 'avc':
15714
+ this.videoParser = new AvcVideoParser();
15715
+ break;
15716
+ case 'hevc':
15717
+ this.videoParser = new HevcVideoParser();
15718
+ break;
15719
+ }
15720
+ }
15721
+ if (this.videoParser !== null) {
15722
+ this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
15723
+ videoTrack.pesData = null;
15724
+ }
15105
15725
  } else {
15106
15726
  // either avcData null or PES truncated, keep it for next frag parsing
15107
15727
  videoTrack.pesData = videoData;
@@ -15404,7 +16024,12 @@ function parsePMT(data, offset, typeSupported, isSampleAes) {
15404
16024
  logger.warn('Unsupported EC-3 in M2TS found');
15405
16025
  break;
15406
16026
  case 0x24:
15407
- logger.warn('Unsupported HEVC in M2TS found');
16027
+ // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
16028
+ if (result.videoPid === -1) {
16029
+ result.videoPid = pid;
16030
+ result.segmentVideoCodec = 'hevc';
16031
+ logger.log('HEVC in M2TS found');
16032
+ }
15408
16033
  break;
15409
16034
  }
15410
16035
  // move to the next table entry
@@ -15627,6 +16252,8 @@ class MP4 {
15627
16252
  avc1: [],
15628
16253
  // codingname
15629
16254
  avcC: [],
16255
+ hvc1: [],
16256
+ hvcC: [],
15630
16257
  btrt: [],
15631
16258
  dinf: [],
15632
16259
  dref: [],
@@ -16051,8 +16678,10 @@ class MP4 {
16051
16678
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
16052
16679
  }
16053
16680
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
16054
- } else {
16681
+ } else if (track.segmentCodec === 'avc') {
16055
16682
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
16683
+ } else {
16684
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
16056
16685
  }
16057
16686
  }
16058
16687
  static tkhd(track) {
@@ -16190,6 +16819,84 @@ class MP4 {
16190
16819
  const result = appendUint8Array(MP4.FTYP, movie);
16191
16820
  return result;
16192
16821
  }
16822
+ static hvc1(track) {
16823
+ const ps = track.params;
16824
+ const units = [track.vps, track.sps, track.pps];
16825
+ const NALuLengthSize = 4;
16826
+ const config = new Uint8Array([0x01, ps.general_profile_space << 6 | (ps.general_tier_flag ? 32 : 0) | ps.general_profile_idc, ps.general_profile_compatibility_flags[0], ps.general_profile_compatibility_flags[1], ps.general_profile_compatibility_flags[2], ps.general_profile_compatibility_flags[3], ps.general_constraint_indicator_flags[0], ps.general_constraint_indicator_flags[1], ps.general_constraint_indicator_flags[2], ps.general_constraint_indicator_flags[3], ps.general_constraint_indicator_flags[4], ps.general_constraint_indicator_flags[5], ps.general_level_idc, 240 | ps.min_spatial_segmentation_idc >> 8, 255 & ps.min_spatial_segmentation_idc, 252 | ps.parallelismType, 252 | ps.chroma_format_idc, 248 | ps.bit_depth_luma_minus8, 248 | ps.bit_depth_chroma_minus8, 0x00, parseInt(ps.frame_rate.fps), NALuLengthSize - 1 | ps.temporal_id_nested << 2 | ps.num_temporal_layers << 3 | (ps.frame_rate.fixed ? 64 : 0), units.length]);
16827
+
16828
+ // compute hvcC size in bytes
16829
+ let length = config.length;
16830
+ for (let i = 0; i < units.length; i += 1) {
16831
+ length += 3;
16832
+ for (let j = 0; j < units[i].length; j += 1) {
16833
+ length += 2 + units[i][j].length;
16834
+ }
16835
+ }
16836
+ const hvcC = new Uint8Array(length);
16837
+ hvcC.set(config, 0);
16838
+ length = config.length;
16839
+ // append parameter set units: one vps, one or more sps and pps
16840
+ const iMax = units.length - 1;
16841
+ for (let i = 0; i < units.length; i += 1) {
16842
+ hvcC.set(new Uint8Array([32 + i | (i === iMax ? 128 : 0), 0x00, units[i].length]), length);
16843
+ length += 3;
16844
+ for (let j = 0; j < units[i].length; j += 1) {
16845
+ hvcC.set(new Uint8Array([units[i][j].length >> 8, units[i][j].length & 255]), length);
16846
+ length += 2;
16847
+ hvcC.set(units[i][j], length);
16848
+ length += units[i][j].length;
16849
+ }
16850
+ }
16851
+ const hvcc = MP4.box(MP4.types.hvcC, hvcC);
16852
+ const width = track.width;
16853
+ const height = track.height;
16854
+ const hSpacing = track.pixelRatio[0];
16855
+ const vSpacing = track.pixelRatio[1];
16856
+ return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
16857
+ // reserved
16858
+ 0x00, 0x00, 0x00,
16859
+ // reserved
16860
+ 0x00, 0x01,
16861
+ // data_reference_index
16862
+ 0x00, 0x00,
16863
+ // pre_defined
16864
+ 0x00, 0x00,
16865
+ // reserved
16866
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16867
+ // pre_defined
16868
+ width >> 8 & 0xff, width & 0xff,
16869
+ // width
16870
+ height >> 8 & 0xff, height & 0xff,
16871
+ // height
16872
+ 0x00, 0x48, 0x00, 0x00,
16873
+ // horizresolution
16874
+ 0x00, 0x48, 0x00, 0x00,
16875
+ // vertresolution
16876
+ 0x00, 0x00, 0x00, 0x00,
16877
+ // reserved
16878
+ 0x00, 0x01,
16879
+ // frame_count
16880
+ 0x12, 0x64, 0x61, 0x69, 0x6c,
16881
+ // dailymotion/hls.js
16882
+ 0x79, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x68, 0x6c, 0x73, 0x2e, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16883
+ // compressorname
16884
+ 0x00, 0x18,
16885
+ // depth = 24
16886
+ 0x11, 0x11]),
16887
+ // pre_defined = -1
16888
+ hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
16889
+ // bufferSizeDB
16890
+ 0x00, 0x2d, 0xc6, 0xc0,
16891
+ // maxBitrate
16892
+ 0x00, 0x2d, 0xc6, 0xc0])),
16893
+ // avgBitrate
16894
+ MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
16895
+ // hSpacing
16896
+ hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
16897
+ // vSpacing
16898
+ vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
16899
+ }
16193
16900
  }
16194
16901
  MP4.types = void 0;
16195
16902
  MP4.HDLR_TYPES = void 0;
@@ -16565,9 +17272,9 @@ class MP4Remuxer {
16565
17272
  const foundOverlap = delta < -1;
16566
17273
  if (foundHole || foundOverlap) {
16567
17274
  if (foundHole) {
16568
- logger.warn(`AVC: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`);
17275
+ logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`);
16569
17276
  } else {
16570
- logger.warn(`AVC: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`);
17277
+ logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`);
16571
17278
  }
16572
17279
  if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
16573
17280
  firstDTS = nextAvcDts;
@@ -16741,7 +17448,7 @@ class MP4Remuxer {
16741
17448
  }
16742
17449
  }
16743
17450
  }
16744
- // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
17451
+ // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
16745
17452
  mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
16746
17453
  this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
16747
17454
  this.videoSampleDuration = mp4SampleDuration;
@@ -19817,7 +20524,7 @@ class Hls {
19817
20524
  * Get the video-dev/hls.js package version.
19818
20525
  */
19819
20526
  static get version() {
19820
- return "1.5.2-0.canary.9970";
20527
+ return "1.5.2-0.canary.9971";
19821
20528
  }
19822
20529
 
19823
20530
  /**