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.
package/dist/hls.mjs CHANGED
@@ -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();
@@ -11427,6 +11427,110 @@ class BaseVideoParser {
11427
11427
  logger.log(VideoSample.pts + '/' + VideoSample.dts + ':' + VideoSample.debug);
11428
11428
  }
11429
11429
  }
11430
+ parseNALu(track, array) {
11431
+ const len = array.byteLength;
11432
+ let state = track.naluState || 0;
11433
+ const lastState = state;
11434
+ const units = [];
11435
+ let i = 0;
11436
+ let value;
11437
+ let overflow;
11438
+ let unitType;
11439
+ let lastUnitStart = -1;
11440
+ let lastUnitType = 0;
11441
+ // logger.log('PES:' + Hex.hexDump(array));
11442
+
11443
+ if (state === -1) {
11444
+ // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
11445
+ lastUnitStart = 0;
11446
+ // NALu type is value read from offset 0
11447
+ lastUnitType = this.getNALuType(array, 0);
11448
+ state = 0;
11449
+ i = 1;
11450
+ }
11451
+ while (i < len) {
11452
+ value = array[i++];
11453
+ // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
11454
+ if (!state) {
11455
+ state = value ? 0 : 1;
11456
+ continue;
11457
+ }
11458
+ if (state === 1) {
11459
+ state = value ? 0 : 2;
11460
+ continue;
11461
+ }
11462
+ // here we have state either equal to 2 or 3
11463
+ if (!value) {
11464
+ state = 3;
11465
+ } else if (value === 1) {
11466
+ overflow = i - state - 1;
11467
+ if (lastUnitStart >= 0) {
11468
+ const unit = {
11469
+ data: array.subarray(lastUnitStart, overflow),
11470
+ type: lastUnitType
11471
+ };
11472
+ // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
11473
+ units.push(unit);
11474
+ } else {
11475
+ // lastUnitStart is undefined => this is the first start code found in this PES packet
11476
+ // first check if start code delimiter is overlapping between 2 PES packets,
11477
+ // ie it started in last packet (lastState not zero)
11478
+ // and ended at the beginning of this PES packet (i <= 4 - lastState)
11479
+ const lastUnit = this.getLastNalUnit(track.samples);
11480
+ if (lastUnit) {
11481
+ if (lastState && i <= 4 - lastState) {
11482
+ // start delimiter overlapping between PES packets
11483
+ // strip start delimiter bytes from the end of last NAL unit
11484
+ // check if lastUnit had a state different from zero
11485
+ if (lastUnit.state) {
11486
+ // strip last bytes
11487
+ lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
11488
+ }
11489
+ }
11490
+ // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
11491
+
11492
+ if (overflow > 0) {
11493
+ // logger.log('first NALU found with overflow:' + overflow);
11494
+ lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
11495
+ lastUnit.state = 0;
11496
+ }
11497
+ }
11498
+ }
11499
+ // check if we can read unit type
11500
+ if (i < len) {
11501
+ unitType = this.getNALuType(array, i);
11502
+ // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
11503
+ lastUnitStart = i;
11504
+ lastUnitType = unitType;
11505
+ state = 0;
11506
+ } else {
11507
+ // not enough byte to read unit type. let's read it on next PES parsing
11508
+ state = -1;
11509
+ }
11510
+ } else {
11511
+ state = 0;
11512
+ }
11513
+ }
11514
+ if (lastUnitStart >= 0 && state >= 0) {
11515
+ const unit = {
11516
+ data: array.subarray(lastUnitStart, len),
11517
+ type: lastUnitType,
11518
+ state: state
11519
+ };
11520
+ units.push(unit);
11521
+ // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
11522
+ }
11523
+ // no NALu found
11524
+ if (units.length === 0) {
11525
+ // append pes.data to previous NAL unit
11526
+ const lastUnit = this.getLastNalUnit(track.samples);
11527
+ if (lastUnit) {
11528
+ lastUnit.data = appendUint8Array(lastUnit.data, array);
11529
+ }
11530
+ }
11531
+ track.naluState = state;
11532
+ return units;
11533
+ }
11430
11534
  }
11431
11535
 
11432
11536
  /**
@@ -11569,21 +11673,171 @@ class ExpGolomb {
11569
11673
  readUInt() {
11570
11674
  return this.readBits(32);
11571
11675
  }
11676
+ }
11677
+
11678
+ class AvcVideoParser extends BaseVideoParser {
11679
+ parsePES(track, textTrack, pes, last, duration) {
11680
+ const units = this.parseNALu(track, pes.data);
11681
+ let VideoSample = this.VideoSample;
11682
+ let push;
11683
+ let spsfound = false;
11684
+ // free pes.data to save up some memory
11685
+ pes.data = null;
11686
+
11687
+ // if new NAL units found and last sample still there, let's push ...
11688
+ // this helps parsing streams with missing AUD (only do this if AUD never found)
11689
+ if (VideoSample && units.length && !track.audFound) {
11690
+ this.pushAccessUnit(VideoSample, track);
11691
+ VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
11692
+ }
11693
+ units.forEach(unit => {
11694
+ var _VideoSample2;
11695
+ switch (unit.type) {
11696
+ // NDR
11697
+ case 1:
11698
+ {
11699
+ let iskey = false;
11700
+ push = true;
11701
+ const data = unit.data;
11702
+ // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
11703
+ if (spsfound && data.length > 4) {
11704
+ // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
11705
+ const sliceType = this.readSliceType(data);
11706
+ // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
11707
+ // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
11708
+ // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
11709
+ // I slice: A slice that is not an SI slice that is decoded using intra prediction only.
11710
+ // if (sliceType === 2 || sliceType === 7) {
11711
+ if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
11712
+ iskey = true;
11713
+ }
11714
+ }
11715
+ if (iskey) {
11716
+ var _VideoSample;
11717
+ // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push
11718
+ if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
11719
+ this.pushAccessUnit(VideoSample, track);
11720
+ VideoSample = this.VideoSample = null;
11721
+ }
11722
+ }
11723
+ if (!VideoSample) {
11724
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
11725
+ }
11726
+ VideoSample.frame = true;
11727
+ VideoSample.key = iskey;
11728
+ break;
11729
+ // IDR
11730
+ }
11731
+ case 5:
11732
+ push = true;
11733
+ // handle PES not starting with AUD
11734
+ // if we have frame data already, that cannot belong to the same frame, so force a push
11735
+ if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) {
11736
+ this.pushAccessUnit(VideoSample, track);
11737
+ VideoSample = this.VideoSample = null;
11738
+ }
11739
+ if (!VideoSample) {
11740
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
11741
+ }
11742
+ VideoSample.key = true;
11743
+ VideoSample.frame = true;
11744
+ break;
11745
+ // SEI
11746
+ case 6:
11747
+ {
11748
+ push = true;
11749
+ parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples);
11750
+ break;
11751
+ // SPS
11752
+ }
11753
+ case 7:
11754
+ {
11755
+ var _track$pixelRatio, _track$pixelRatio2;
11756
+ push = true;
11757
+ spsfound = true;
11758
+ const sps = unit.data;
11759
+ const config = this.readSPS(sps);
11760
+ 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]) {
11761
+ track.width = config.width;
11762
+ track.height = config.height;
11763
+ track.pixelRatio = config.pixelRatio;
11764
+ track.sps = [sps];
11765
+ track.duration = duration;
11766
+ const codecarray = sps.subarray(1, 4);
11767
+ let codecstring = 'avc1.';
11768
+ for (let i = 0; i < 3; i++) {
11769
+ let h = codecarray[i].toString(16);
11770
+ if (h.length < 2) {
11771
+ h = '0' + h;
11772
+ }
11773
+ codecstring += h;
11774
+ }
11775
+ track.codec = codecstring;
11776
+ }
11777
+ break;
11778
+ }
11779
+ // PPS
11780
+ case 8:
11781
+ push = true;
11782
+ track.pps = [unit.data];
11783
+ break;
11784
+ // AUD
11785
+ case 9:
11786
+ push = true;
11787
+ track.audFound = true;
11788
+ if (VideoSample) {
11789
+ this.pushAccessUnit(VideoSample, track);
11790
+ }
11791
+ VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
11792
+ break;
11793
+ // Filler Data
11794
+ case 12:
11795
+ push = true;
11796
+ break;
11797
+ default:
11798
+ push = false;
11799
+ if (VideoSample) {
11800
+ VideoSample.debug += 'unknown NAL ' + unit.type + ' ';
11801
+ }
11802
+ break;
11803
+ }
11804
+ if (VideoSample && push) {
11805
+ const units = VideoSample.units;
11806
+ units.push(unit);
11807
+ }
11808
+ });
11809
+ // if last PES packet, push samples
11810
+ if (last && VideoSample) {
11811
+ this.pushAccessUnit(VideoSample, track);
11812
+ this.VideoSample = null;
11813
+ }
11814
+ }
11815
+ getNALuType(data, offset) {
11816
+ return data[offset] & 0x1f;
11817
+ }
11818
+ readSliceType(data) {
11819
+ const eg = new ExpGolomb(data);
11820
+ // skip NALu type
11821
+ eg.readUByte();
11822
+ // discard first_mb_in_slice
11823
+ eg.readUEG();
11824
+ // return slice_type
11825
+ return eg.readUEG();
11826
+ }
11572
11827
 
11573
11828
  /**
11574
- * Advance the ExpGolomb decoder past a scaling list. The scaling
11575
- * list is optionally transmitted as part of a sequence parameter
11829
+ * The scaling list is optionally transmitted as part of a sequence parameter
11576
11830
  * set and is not relevant to transmuxing.
11577
11831
  * @param count the number of entries in this scaling list
11578
11832
  * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1
11579
11833
  */
11580
- skipScalingList(count) {
11834
+ skipScalingList(count, reader) {
11581
11835
  let lastScale = 8;
11582
11836
  let nextScale = 8;
11583
11837
  let deltaScale;
11584
11838
  for (let j = 0; j < count; j++) {
11585
11839
  if (nextScale !== 0) {
11586
- deltaScale = this.readEG();
11840
+ deltaScale = reader.readEG();
11587
11841
  nextScale = (lastScale + deltaScale + 256) % 256;
11588
11842
  }
11589
11843
  lastScale = nextScale === 0 ? lastScale : nextScale;
@@ -11598,7 +11852,8 @@ class ExpGolomb {
11598
11852
  * sequence parameter set, including the dimensions of the
11599
11853
  * associated video frames.
11600
11854
  */
11601
- readSPS() {
11855
+ readSPS(sps) {
11856
+ const eg = new ExpGolomb(sps);
11602
11857
  let frameCropLeftOffset = 0;
11603
11858
  let frameCropRightOffset = 0;
11604
11859
  let frameCropTopOffset = 0;
@@ -11606,13 +11861,13 @@ class ExpGolomb {
11606
11861
  let numRefFramesInPicOrderCntCycle;
11607
11862
  let scalingListCount;
11608
11863
  let i;
11609
- const readUByte = this.readUByte.bind(this);
11610
- const readBits = this.readBits.bind(this);
11611
- const readUEG = this.readUEG.bind(this);
11612
- const readBoolean = this.readBoolean.bind(this);
11613
- const skipBits = this.skipBits.bind(this);
11614
- const skipEG = this.skipEG.bind(this);
11615
- const skipUEG = this.skipUEG.bind(this);
11864
+ const readUByte = eg.readUByte.bind(eg);
11865
+ const readBits = eg.readBits.bind(eg);
11866
+ const readUEG = eg.readUEG.bind(eg);
11867
+ const readBoolean = eg.readBoolean.bind(eg);
11868
+ const skipBits = eg.skipBits.bind(eg);
11869
+ const skipEG = eg.skipEG.bind(eg);
11870
+ const skipUEG = eg.skipUEG.bind(eg);
11616
11871
  const skipScalingList = this.skipScalingList.bind(this);
11617
11872
  readUByte();
11618
11873
  const profileIdc = readUByte(); // profile_idc
@@ -11637,9 +11892,9 @@ class ExpGolomb {
11637
11892
  if (readBoolean()) {
11638
11893
  // seq_scaling_list_present_flag[ i ]
11639
11894
  if (i < 6) {
11640
- skipScalingList(16);
11895
+ skipScalingList(16, eg);
11641
11896
  } else {
11642
- skipScalingList(64);
11897
+ skipScalingList(64, eg);
11643
11898
  }
11644
11899
  }
11645
11900
  }
@@ -11744,19 +11999,15 @@ class ExpGolomb {
11744
11999
  pixelRatio: pixelRatio
11745
12000
  };
11746
12001
  }
11747
- readSliceType() {
11748
- // skip NALu type
11749
- this.readUByte();
11750
- // discard first_mb_in_slice
11751
- this.readUEG();
11752
- // return slice_type
11753
- return this.readUEG();
11754
- }
11755
12002
  }
11756
12003
 
11757
- class AvcVideoParser extends BaseVideoParser {
11758
- parseAVCPES(track, textTrack, pes, last, duration) {
11759
- const units = this.parseAVCNALu(track, pes.data);
12004
+ class HevcVideoParser extends BaseVideoParser {
12005
+ constructor(...args) {
12006
+ super(...args);
12007
+ this.initVPS = null;
12008
+ }
12009
+ parsePES(track, textTrack, pes, last, duration) {
12010
+ const units = this.parseNALu(track, pes.data);
11760
12011
  let VideoSample = this.VideoSample;
11761
12012
  let push;
11762
12013
  let spsfound = false;
@@ -11772,42 +12023,49 @@ class AvcVideoParser extends BaseVideoParser {
11772
12023
  units.forEach(unit => {
11773
12024
  var _VideoSample2;
11774
12025
  switch (unit.type) {
11775
- // NDR
12026
+ // NON-IDR, NON RANDOM ACCESS SLICE
12027
+ case 0:
11776
12028
  case 1:
11777
- {
11778
- let iskey = false;
11779
- push = true;
11780
- const data = unit.data;
11781
- // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...)
11782
- if (spsfound && data.length > 4) {
11783
- // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR
11784
- const sliceType = new ExpGolomb(data).readSliceType();
11785
- // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
11786
- // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples.
11787
- // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice.
11788
- // I slice: A slice that is not an SI slice that is decoded using intra prediction only.
11789
- // if (sliceType === 2 || sliceType === 7) {
11790
- if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) {
11791
- iskey = true;
11792
- }
11793
- }
11794
- if (iskey) {
11795
- var _VideoSample;
11796
- // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push
11797
- if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
11798
- this.pushAccessUnit(VideoSample, track);
11799
- VideoSample = this.VideoSample = null;
11800
- }
11801
- }
11802
- if (!VideoSample) {
11803
- VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
12029
+ case 2:
12030
+ case 3:
12031
+ case 4:
12032
+ case 5:
12033
+ case 6:
12034
+ case 7:
12035
+ case 8:
12036
+ case 9:
12037
+ if (!VideoSample) {
12038
+ VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
12039
+ }
12040
+ VideoSample.frame = true;
12041
+ push = true;
12042
+ break;
12043
+
12044
+ // CRA, BLA (random access picture)
12045
+ case 16:
12046
+ case 17:
12047
+ case 18:
12048
+ case 21:
12049
+ push = true;
12050
+ if (spsfound) {
12051
+ var _VideoSample;
12052
+ // handle PES not starting with AUD
12053
+ // if we have frame data already, that cannot belong to the same frame, so force a push
12054
+ if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) {
12055
+ this.pushAccessUnit(VideoSample, track);
12056
+ VideoSample = this.VideoSample = null;
11804
12057
  }
11805
- VideoSample.frame = true;
11806
- VideoSample.key = iskey;
11807
- break;
11808
- // IDR
11809
12058
  }
11810
- case 5:
12059
+ if (!VideoSample) {
12060
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
12061
+ }
12062
+ VideoSample.key = true;
12063
+ VideoSample.frame = true;
12064
+ break;
12065
+
12066
+ // IDR
12067
+ case 19:
12068
+ case 20:
11811
12069
  push = true;
11812
12070
  // handle PES not starting with AUD
11813
12071
  // if we have frame data already, that cannot belong to the same frame, so force a push
@@ -11821,48 +12079,76 @@ class AvcVideoParser extends BaseVideoParser {
11821
12079
  VideoSample.key = true;
11822
12080
  VideoSample.frame = true;
11823
12081
  break;
12082
+
11824
12083
  // SEI
11825
- case 6:
11826
- {
11827
- push = true;
11828
- parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples);
11829
- break;
11830
- // SPS
12084
+ case 39:
12085
+ push = true;
12086
+ parseSEIMessageFromNALu(unit.data, 2,
12087
+ // NALu header size
12088
+ pes.pts, textTrack.samples);
12089
+ break;
12090
+
12091
+ // VPS
12092
+ case 32:
12093
+ push = true;
12094
+ if (!track.vps) {
12095
+ const config = this.readVPS(unit.data);
12096
+ track.params = _objectSpread2({}, config);
12097
+ this.initVPS = unit.data;
11831
12098
  }
11832
- case 7:
11833
- {
11834
- var _track$pixelRatio, _track$pixelRatio2;
11835
- push = true;
11836
- spsfound = true;
11837
- const sps = unit.data;
11838
- const expGolombDecoder = new ExpGolomb(sps);
11839
- const config = expGolombDecoder.readSPS();
11840
- 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]) {
12099
+ track.vps = [unit.data];
12100
+ break;
12101
+
12102
+ // SPS
12103
+ case 33:
12104
+ push = true;
12105
+ spsfound = true;
12106
+ if (typeof track.params === 'object') {
12107
+ if (track.vps !== undefined && track.vps[0] !== this.initVPS && track.sps !== undefined && !this.matchSPS(track.sps[0], unit.data)) {
12108
+ this.initVPS = track.vps[0];
12109
+ track.sps = track.pps = undefined;
12110
+ }
12111
+ if (!track.sps) {
12112
+ const config = this.readSPS(unit.data);
11841
12113
  track.width = config.width;
11842
12114
  track.height = config.height;
11843
12115
  track.pixelRatio = config.pixelRatio;
11844
- track.sps = [sps];
11845
12116
  track.duration = duration;
11846
- const codecarray = sps.subarray(1, 4);
11847
- let codecstring = 'avc1.';
11848
- for (let i = 0; i < 3; i++) {
11849
- let h = codecarray[i].toString(16);
11850
- if (h.length < 2) {
11851
- h = '0' + h;
11852
- }
11853
- codecstring += h;
12117
+ track.codec = config.codecString;
12118
+ track.sps = [];
12119
+ for (const prop in config.params) {
12120
+ track.params[prop] = config.params[prop];
11854
12121
  }
11855
- track.codec = codecstring;
11856
12122
  }
11857
- break;
12123
+ if (track.vps !== undefined && track.vps[0] === this.initVPS) {
12124
+ track.sps.push(unit.data);
12125
+ }
12126
+ }
12127
+ if (!VideoSample) {
12128
+ VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts, '');
11858
12129
  }
12130
+ VideoSample.key = true;
12131
+ break;
12132
+
11859
12133
  // PPS
11860
- case 8:
12134
+ case 34:
11861
12135
  push = true;
11862
- track.pps = [unit.data];
12136
+ if (typeof track.params === 'object') {
12137
+ if (!track.pps) {
12138
+ track.pps = [];
12139
+ const config = this.readPPS(unit.data);
12140
+ for (const prop in config) {
12141
+ track.params[prop] = config[prop];
12142
+ }
12143
+ }
12144
+ if (this.initVPS !== null || track.pps.length === 0) {
12145
+ track.pps.push(unit.data);
12146
+ }
12147
+ }
11863
12148
  break;
11864
- // AUD
11865
- case 9:
12149
+
12150
+ // ACCESS UNIT DELIMITER
12151
+ case 35:
11866
12152
  push = true;
11867
12153
  track.audFound = true;
11868
12154
  if (VideoSample) {
@@ -11870,14 +12156,10 @@ class AvcVideoParser extends BaseVideoParser {
11870
12156
  }
11871
12157
  VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts, '');
11872
12158
  break;
11873
- // Filler Data
11874
- case 12:
11875
- push = true;
11876
- break;
11877
12159
  default:
11878
12160
  push = false;
11879
12161
  if (VideoSample) {
11880
- VideoSample.debug += 'unknown NAL ' + unit.type + ' ';
12162
+ VideoSample.debug += 'unknown or irrelevant NAL ' + unit.type + ' ';
11881
12163
  }
11882
12164
  break;
11883
12165
  }
@@ -11892,109 +12174,423 @@ class AvcVideoParser extends BaseVideoParser {
11892
12174
  this.VideoSample = null;
11893
12175
  }
11894
12176
  }
11895
- parseAVCNALu(track, array) {
11896
- const len = array.byteLength;
11897
- let state = track.naluState || 0;
11898
- const lastState = state;
11899
- const units = [];
11900
- let i = 0;
11901
- let value;
11902
- let overflow;
11903
- let unitType;
11904
- let lastUnitStart = -1;
11905
- let lastUnitType = 0;
11906
- // logger.log('PES:' + Hex.hexDump(array));
11907
-
11908
- if (state === -1) {
11909
- // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet
11910
- lastUnitStart = 0;
11911
- // NALu type is value read from offset 0
11912
- lastUnitType = array[0] & 0x1f;
11913
- state = 0;
11914
- i = 1;
11915
- }
11916
- while (i < len) {
11917
- value = array[i++];
11918
- // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case
11919
- if (!state) {
11920
- state = value ? 0 : 1;
11921
- continue;
11922
- }
11923
- if (state === 1) {
11924
- state = value ? 0 : 2;
11925
- continue;
12177
+ getNALuType(data, offset) {
12178
+ return (data[offset] & 0x7e) >>> 1;
12179
+ }
12180
+ ebsp2rbsp(arr) {
12181
+ const dst = new Uint8Array(arr.byteLength);
12182
+ let dstIdx = 0;
12183
+ for (let i = 0; i < arr.byteLength; i++) {
12184
+ if (i >= 2) {
12185
+ // Unescape: Skip 0x03 after 00 00
12186
+ if (arr[i] === 0x03 && arr[i - 1] === 0x00 && arr[i - 2] === 0x00) {
12187
+ continue;
12188
+ }
11926
12189
  }
11927
- // here we have state either equal to 2 or 3
11928
- if (!value) {
11929
- state = 3;
11930
- } else if (value === 1) {
11931
- overflow = i - state - 1;
11932
- if (lastUnitStart >= 0) {
11933
- const unit = {
11934
- data: array.subarray(lastUnitStart, overflow),
11935
- type: lastUnitType
11936
- };
11937
- // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength);
11938
- units.push(unit);
11939
- } else {
11940
- // lastUnitStart is undefined => this is the first start code found in this PES packet
11941
- // first check if start code delimiter is overlapping between 2 PES packets,
11942
- // ie it started in last packet (lastState not zero)
11943
- // and ended at the beginning of this PES packet (i <= 4 - lastState)
11944
- const lastUnit = this.getLastNalUnit(track.samples);
11945
- if (lastUnit) {
11946
- if (lastState && i <= 4 - lastState) {
11947
- // start delimiter overlapping between PES packets
11948
- // strip start delimiter bytes from the end of last NAL unit
11949
- // check if lastUnit had a state different from zero
11950
- if (lastUnit.state) {
11951
- // strip last bytes
11952
- lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState);
11953
- }
11954
- }
11955
- // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit.
12190
+ dst[dstIdx] = arr[i];
12191
+ dstIdx++;
12192
+ }
12193
+ return new Uint8Array(dst.buffer, 0, dstIdx);
12194
+ }
12195
+ readVPS(vps) {
12196
+ const eg = new ExpGolomb(vps);
12197
+ // remove header
12198
+ eg.readUByte();
12199
+ eg.readUByte();
12200
+ eg.readBits(4); // video_parameter_set_id
12201
+ eg.skipBits(2);
12202
+ eg.readBits(6); // max_layers_minus1
12203
+ const max_sub_layers_minus1 = eg.readBits(3);
12204
+ const temporal_id_nesting_flag = eg.readBoolean();
12205
+ // ...vui fps can be here, but empty fps value is not critical for metadata
11956
12206
 
11957
- if (overflow > 0) {
11958
- // logger.log('first NALU found with overflow:' + overflow);
11959
- lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow));
11960
- lastUnit.state = 0;
12207
+ return {
12208
+ numTemporalLayers: max_sub_layers_minus1 + 1,
12209
+ temporalIdNested: temporal_id_nesting_flag
12210
+ };
12211
+ }
12212
+ readSPS(sps) {
12213
+ const eg = new ExpGolomb(this.ebsp2rbsp(sps));
12214
+ eg.readUByte();
12215
+ eg.readUByte();
12216
+ eg.readBits(4); //video_parameter_set_id
12217
+ const max_sub_layers_minus1 = eg.readBits(3);
12218
+ eg.readBoolean(); // temporal_id_nesting_flag
12219
+
12220
+ // profile_tier_level
12221
+ const general_profile_space = eg.readBits(2);
12222
+ const general_tier_flag = eg.readBoolean();
12223
+ const general_profile_idc = eg.readBits(5);
12224
+ const general_profile_compatibility_flags_1 = eg.readUByte();
12225
+ const general_profile_compatibility_flags_2 = eg.readUByte();
12226
+ const general_profile_compatibility_flags_3 = eg.readUByte();
12227
+ const general_profile_compatibility_flags_4 = eg.readUByte();
12228
+ const general_constraint_indicator_flags_1 = eg.readUByte();
12229
+ const general_constraint_indicator_flags_2 = eg.readUByte();
12230
+ const general_constraint_indicator_flags_3 = eg.readUByte();
12231
+ const general_constraint_indicator_flags_4 = eg.readUByte();
12232
+ const general_constraint_indicator_flags_5 = eg.readUByte();
12233
+ const general_constraint_indicator_flags_6 = eg.readUByte();
12234
+ const general_level_idc = eg.readUByte();
12235
+ const sub_layer_profile_present_flags = [];
12236
+ const sub_layer_level_present_flags = [];
12237
+ for (let i = 0; i < max_sub_layers_minus1; i++) {
12238
+ sub_layer_profile_present_flags.push(eg.readBoolean());
12239
+ sub_layer_level_present_flags.push(eg.readBoolean());
12240
+ }
12241
+ if (max_sub_layers_minus1 > 0) {
12242
+ for (let i = max_sub_layers_minus1; i < 8; i++) {
12243
+ eg.readBits(2);
12244
+ }
12245
+ }
12246
+ for (let i = 0; i < max_sub_layers_minus1; i++) {
12247
+ if (sub_layer_profile_present_flags[i]) {
12248
+ eg.readUByte(); // sub_layer_profile_space, sub_layer_tier_flag, sub_layer_profile_idc
12249
+ eg.readUByte();
12250
+ eg.readUByte();
12251
+ eg.readUByte();
12252
+ eg.readUByte(); // sub_layer_profile_compatibility_flag
12253
+ eg.readUByte();
12254
+ eg.readUByte();
12255
+ eg.readUByte();
12256
+ eg.readUByte();
12257
+ eg.readUByte();
12258
+ eg.readUByte();
12259
+ }
12260
+ if (sub_layer_level_present_flags[i]) {
12261
+ eg.readUByte();
12262
+ }
12263
+ }
12264
+ eg.readUEG(); // seq_parameter_set_id
12265
+ const chroma_format_idc = eg.readUEG();
12266
+ if (chroma_format_idc == 3) {
12267
+ eg.skipBits(1); //separate_colour_plane_flag
12268
+ }
12269
+ const pic_width_in_luma_samples = eg.readUEG();
12270
+ const pic_height_in_luma_samples = eg.readUEG();
12271
+ const conformance_window_flag = eg.readBoolean();
12272
+ let pic_left_offset = 0,
12273
+ pic_right_offset = 0,
12274
+ pic_top_offset = 0,
12275
+ pic_bottom_offset = 0;
12276
+ if (conformance_window_flag) {
12277
+ pic_left_offset += eg.readUEG();
12278
+ pic_right_offset += eg.readUEG();
12279
+ pic_top_offset += eg.readUEG();
12280
+ pic_bottom_offset += eg.readUEG();
12281
+ }
12282
+ const bit_depth_luma_minus8 = eg.readUEG();
12283
+ const bit_depth_chroma_minus8 = eg.readUEG();
12284
+ const log2_max_pic_order_cnt_lsb_minus4 = eg.readUEG();
12285
+ const sub_layer_ordering_info_present_flag = eg.readBoolean();
12286
+ for (let i = sub_layer_ordering_info_present_flag ? 0 : max_sub_layers_minus1; i <= max_sub_layers_minus1; i++) {
12287
+ eg.skipUEG(); // max_dec_pic_buffering_minus1[i]
12288
+ eg.skipUEG(); // max_num_reorder_pics[i]
12289
+ eg.skipUEG(); // max_latency_increase_plus1[i]
12290
+ }
12291
+ eg.skipUEG(); // log2_min_luma_coding_block_size_minus3
12292
+ eg.skipUEG(); // log2_diff_max_min_luma_coding_block_size
12293
+ eg.skipUEG(); // log2_min_transform_block_size_minus2
12294
+ eg.skipUEG(); // log2_diff_max_min_transform_block_size
12295
+ eg.skipUEG(); // max_transform_hierarchy_depth_inter
12296
+ eg.skipUEG(); // max_transform_hierarchy_depth_intra
12297
+ const scaling_list_enabled_flag = eg.readBoolean();
12298
+ if (scaling_list_enabled_flag) {
12299
+ const sps_scaling_list_data_present_flag = eg.readBoolean();
12300
+ if (sps_scaling_list_data_present_flag) {
12301
+ for (let sizeId = 0; sizeId < 4; sizeId++) {
12302
+ for (let matrixId = 0; matrixId < (sizeId === 3 ? 2 : 6); matrixId++) {
12303
+ const scaling_list_pred_mode_flag = eg.readBoolean();
12304
+ if (!scaling_list_pred_mode_flag) {
12305
+ eg.readUEG(); // scaling_list_pred_matrix_id_delta
12306
+ } else {
12307
+ const coefNum = Math.min(64, 1 << 4 + (sizeId << 1));
12308
+ if (sizeId > 1) {
12309
+ eg.readEG();
12310
+ }
12311
+ for (let i = 0; i < coefNum; i++) {
12312
+ eg.readEG();
12313
+ }
11961
12314
  }
11962
12315
  }
11963
12316
  }
11964
- // check if we can read unit type
11965
- if (i < len) {
11966
- unitType = array[i] & 0x1f;
11967
- // logger.log('find NALU @ offset:' + i + ',type:' + unitType);
11968
- lastUnitStart = i;
11969
- lastUnitType = unitType;
11970
- state = 0;
11971
- } else {
11972
- // not enough byte to read unit type. let's read it on next PES parsing
11973
- state = -1;
11974
- }
11975
- } else {
11976
- state = 0;
11977
12317
  }
11978
12318
  }
11979
- if (lastUnitStart >= 0 && state >= 0) {
11980
- const unit = {
11981
- data: array.subarray(lastUnitStart, len),
11982
- type: lastUnitType,
11983
- state: state
11984
- };
11985
- units.push(unit);
11986
- // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state);
11987
- }
11988
- // no NALu found
11989
- if (units.length === 0) {
11990
- // append pes.data to previous NAL unit
11991
- const lastUnit = this.getLastNalUnit(track.samples);
11992
- if (lastUnit) {
11993
- lastUnit.data = appendUint8Array(lastUnit.data, array);
12319
+ eg.readBoolean(); // amp_enabled_flag
12320
+ eg.readBoolean(); // sample_adaptive_offset_enabled_flag
12321
+ const pcm_enabled_flag = eg.readBoolean();
12322
+ if (pcm_enabled_flag) {
12323
+ eg.readUByte();
12324
+ eg.skipUEG();
12325
+ eg.skipUEG();
12326
+ eg.readBoolean();
12327
+ }
12328
+ const num_short_term_ref_pic_sets = eg.readUEG();
12329
+ let num_delta_pocs = 0;
12330
+ for (let i = 0; i < num_short_term_ref_pic_sets; i++) {
12331
+ let inter_ref_pic_set_prediction_flag = false;
12332
+ if (i !== 0) {
12333
+ inter_ref_pic_set_prediction_flag = eg.readBoolean();
12334
+ }
12335
+ if (inter_ref_pic_set_prediction_flag) {
12336
+ if (i === num_short_term_ref_pic_sets) {
12337
+ eg.readUEG();
12338
+ }
12339
+ eg.readBoolean();
12340
+ eg.readUEG();
12341
+ let next_num_delta_pocs = 0;
12342
+ for (let j = 0; j <= num_delta_pocs; j++) {
12343
+ const used_by_curr_pic_flag = eg.readBoolean();
12344
+ let use_delta_flag = false;
12345
+ if (!used_by_curr_pic_flag) {
12346
+ use_delta_flag = eg.readBoolean();
12347
+ }
12348
+ if (used_by_curr_pic_flag || use_delta_flag) {
12349
+ next_num_delta_pocs++;
12350
+ }
12351
+ }
12352
+ num_delta_pocs = next_num_delta_pocs;
12353
+ } else {
12354
+ const num_negative_pics = eg.readUEG();
12355
+ const num_positive_pics = eg.readUEG();
12356
+ num_delta_pocs = num_negative_pics + num_positive_pics;
12357
+ for (let j = 0; j < num_negative_pics; j++) {
12358
+ eg.readUEG();
12359
+ eg.readBoolean();
12360
+ }
12361
+ for (let j = 0; j < num_positive_pics; j++) {
12362
+ eg.readUEG();
12363
+ eg.readBoolean();
12364
+ }
12365
+ }
12366
+ }
12367
+ const long_term_ref_pics_present_flag = eg.readBoolean();
12368
+ if (long_term_ref_pics_present_flag) {
12369
+ const num_long_term_ref_pics_sps = eg.readUEG();
12370
+ for (let i = 0; i < num_long_term_ref_pics_sps; i++) {
12371
+ for (let j = 0; j < log2_max_pic_order_cnt_lsb_minus4 + 4; j++) {
12372
+ eg.readBits(1);
12373
+ }
12374
+ eg.readBits(1);
12375
+ }
12376
+ }
12377
+ let min_spatial_segmentation_idc = 0;
12378
+ let sar_width = 1,
12379
+ sar_height = 1;
12380
+ let fps_fixed = true,
12381
+ fps_den = 1,
12382
+ fps_num = 0;
12383
+ eg.readBoolean(); // sps_temporal_mvp_enabled_flag
12384
+ eg.readBoolean(); // strong_intra_smoothing_enabled_flag
12385
+ let default_display_window_flag = false;
12386
+ const vui_parameters_present_flag = eg.readBoolean();
12387
+ if (vui_parameters_present_flag) {
12388
+ const aspect_ratio_info_present_flag = eg.readBoolean();
12389
+ if (aspect_ratio_info_present_flag) {
12390
+ const aspect_ratio_idc = eg.readUByte();
12391
+ const sar_width_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];
12392
+ const sar_height_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];
12393
+ if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {
12394
+ sar_width = sar_width_table[aspect_ratio_idc - 1];
12395
+ sar_height = sar_height_table[aspect_ratio_idc - 1];
12396
+ } else if (aspect_ratio_idc === 255) {
12397
+ sar_width = eg.readBits(16);
12398
+ sar_height = eg.readBits(16);
12399
+ }
12400
+ }
12401
+ const overscan_info_present_flag = eg.readBoolean();
12402
+ if (overscan_info_present_flag) {
12403
+ eg.readBoolean();
12404
+ }
12405
+ const video_signal_type_present_flag = eg.readBoolean();
12406
+ if (video_signal_type_present_flag) {
12407
+ eg.readBits(3);
12408
+ eg.readBoolean();
12409
+ const colour_description_present_flag = eg.readBoolean();
12410
+ if (colour_description_present_flag) {
12411
+ eg.readUByte();
12412
+ eg.readUByte();
12413
+ eg.readUByte();
12414
+ }
12415
+ }
12416
+ const chroma_loc_info_present_flag = eg.readBoolean();
12417
+ if (chroma_loc_info_present_flag) {
12418
+ eg.readUEG();
12419
+ eg.readUEG();
12420
+ }
12421
+ eg.readBoolean(); // neutral_chroma_indication_flag
12422
+ eg.readBoolean(); // field_seq_flag
12423
+ eg.readBoolean(); // frame_field_info_present_flag
12424
+ default_display_window_flag = eg.readBoolean();
12425
+ if (default_display_window_flag) {
12426
+ pic_left_offset += eg.readUEG();
12427
+ pic_right_offset += eg.readUEG();
12428
+ pic_top_offset += eg.readUEG();
12429
+ pic_bottom_offset += eg.readUEG();
12430
+ }
12431
+ const vui_timing_info_present_flag = eg.readBoolean();
12432
+ if (vui_timing_info_present_flag) {
12433
+ fps_den = eg.readBits(32);
12434
+ fps_num = eg.readBits(32);
12435
+ const vui_poc_proportional_to_timing_flag = eg.readBoolean();
12436
+ if (vui_poc_proportional_to_timing_flag) {
12437
+ eg.readUEG();
12438
+ }
12439
+ const vui_hrd_parameters_present_flag = eg.readBoolean();
12440
+ if (vui_hrd_parameters_present_flag) {
12441
+ //const commonInfPresentFlag = true;
12442
+ //if (commonInfPresentFlag) {
12443
+ const nal_hrd_parameters_present_flag = eg.readBoolean();
12444
+ const vcl_hrd_parameters_present_flag = eg.readBoolean();
12445
+ let sub_pic_hrd_params_present_flag = false;
12446
+ if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
12447
+ sub_pic_hrd_params_present_flag = eg.readBoolean();
12448
+ if (sub_pic_hrd_params_present_flag) {
12449
+ eg.readUByte();
12450
+ eg.readBits(5);
12451
+ eg.readBoolean();
12452
+ eg.readBits(5);
12453
+ }
12454
+ eg.readBits(4); // bit_rate_scale
12455
+ eg.readBits(4); // cpb_size_scale
12456
+ if (sub_pic_hrd_params_present_flag) {
12457
+ eg.readBits(4);
12458
+ }
12459
+ eg.readBits(5);
12460
+ eg.readBits(5);
12461
+ eg.readBits(5);
12462
+ }
12463
+ //}
12464
+ for (let i = 0; i <= max_sub_layers_minus1; i++) {
12465
+ fps_fixed = eg.readBoolean(); // fixed_pic_rate_general_flag
12466
+ const fixed_pic_rate_within_cvs_flag = fps_fixed || eg.readBoolean();
12467
+ let low_delay_hrd_flag = false;
12468
+ if (fixed_pic_rate_within_cvs_flag) {
12469
+ eg.readEG();
12470
+ } else {
12471
+ low_delay_hrd_flag = eg.readBoolean();
12472
+ }
12473
+ const cpb_cnt = low_delay_hrd_flag ? 1 : eg.readUEG() + 1;
12474
+ if (nal_hrd_parameters_present_flag) {
12475
+ for (let j = 0; j < cpb_cnt; j++) {
12476
+ eg.readUEG();
12477
+ eg.readUEG();
12478
+ if (sub_pic_hrd_params_present_flag) {
12479
+ eg.readUEG();
12480
+ eg.readUEG();
12481
+ }
12482
+ eg.skipBits(1);
12483
+ }
12484
+ }
12485
+ if (vcl_hrd_parameters_present_flag) {
12486
+ for (let j = 0; j < cpb_cnt; j++) {
12487
+ eg.readUEG();
12488
+ eg.readUEG();
12489
+ if (sub_pic_hrd_params_present_flag) {
12490
+ eg.readUEG();
12491
+ eg.readUEG();
12492
+ }
12493
+ eg.skipBits(1);
12494
+ }
12495
+ }
12496
+ }
12497
+ }
11994
12498
  }
12499
+ const bitstream_restriction_flag = eg.readBoolean();
12500
+ if (bitstream_restriction_flag) {
12501
+ eg.readBoolean(); // tiles_fixed_structure_flag
12502
+ eg.readBoolean(); // motion_vectors_over_pic_boundaries_flag
12503
+ eg.readBoolean(); // restricted_ref_pic_lists_flag
12504
+ min_spatial_segmentation_idc = eg.readUEG();
12505
+ }
12506
+ }
12507
+ let width = pic_width_in_luma_samples,
12508
+ height = pic_height_in_luma_samples;
12509
+ if (conformance_window_flag || default_display_window_flag) {
12510
+ let chroma_scale_w = 1,
12511
+ chroma_scale_h = 1;
12512
+ if (chroma_format_idc === 1) {
12513
+ // YUV 420
12514
+ chroma_scale_w = chroma_scale_h = 2;
12515
+ } else if (chroma_format_idc == 2) {
12516
+ // YUV 422
12517
+ chroma_scale_w = 2;
12518
+ }
12519
+ width = pic_width_in_luma_samples - chroma_scale_w * pic_right_offset - chroma_scale_w * pic_left_offset;
12520
+ height = pic_height_in_luma_samples - chroma_scale_h * pic_bottom_offset - chroma_scale_h * pic_top_offset;
12521
+ }
12522
+ const profile_space_string = general_profile_space ? ['A', 'B', 'C'][general_profile_space] : '';
12523
+ 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;
12524
+ let profile_compatibility_rev = 0;
12525
+ for (let i = 0; i < 32; i++) {
12526
+ profile_compatibility_rev = (profile_compatibility_rev | (profile_compatibility_buf >> i & 1) << 31 - i) >>> 0; // reverse bit position (and cast as UInt32)
12527
+ }
12528
+ let profile_compatibility_flags_string = profile_compatibility_rev.toString(16);
12529
+ if (general_profile_idc === 1 && profile_compatibility_flags_string === '2') {
12530
+ profile_compatibility_flags_string = '6';
12531
+ }
12532
+ const tier_flag_string = general_tier_flag ? 'H' : 'L';
12533
+ return {
12534
+ codecString: `hvc1.${profile_space_string}${general_profile_idc}.${profile_compatibility_flags_string}.${tier_flag_string}${general_level_idc}.B0`,
12535
+ params: {
12536
+ general_tier_flag,
12537
+ general_profile_idc,
12538
+ general_profile_space,
12539
+ general_profile_compatibility_flags: [general_profile_compatibility_flags_1, general_profile_compatibility_flags_2, general_profile_compatibility_flags_3, general_profile_compatibility_flags_4],
12540
+ 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],
12541
+ general_level_idc,
12542
+ bit_depth: bit_depth_luma_minus8 + 8,
12543
+ bit_depth_luma_minus8,
12544
+ bit_depth_chroma_minus8,
12545
+ min_spatial_segmentation_idc,
12546
+ chroma_format_idc: chroma_format_idc,
12547
+ frame_rate: {
12548
+ fixed: fps_fixed,
12549
+ fps: fps_num / fps_den
12550
+ }
12551
+ },
12552
+ width,
12553
+ height,
12554
+ pixelRatio: [sar_width, sar_height]
12555
+ };
12556
+ }
12557
+ readPPS(pps) {
12558
+ const eg = new ExpGolomb(this.ebsp2rbsp(pps));
12559
+ eg.readUByte();
12560
+ eg.readUByte();
12561
+ eg.skipUEG(); // pic_parameter_set_id
12562
+ eg.skipUEG(); // seq_parameter_set_id
12563
+ eg.skipBits(2); // dependent_slice_segments_enabled_flag, output_flag_present_flag
12564
+ eg.skipBits(3); // num_extra_slice_header_bits
12565
+ eg.skipBits(2); // sign_data_hiding_enabled_flag, cabac_init_present_flag
12566
+ eg.skipUEG();
12567
+ eg.skipUEG();
12568
+ eg.skipEG(); // init_qp_minus26
12569
+ eg.skipBits(2); // constrained_intra_pred_flag, transform_skip_enabled_flag
12570
+ const cu_qp_delta_enabled_flag = eg.readBoolean();
12571
+ if (cu_qp_delta_enabled_flag) {
12572
+ eg.skipUEG();
12573
+ }
12574
+ eg.skipEG(); // cb_qp_offset
12575
+ eg.skipEG(); // cr_qp_offset
12576
+ eg.skipBits(4); // pps_slice_chroma_qp_offsets_present_flag, weighted_pred_flag, weighted_bipred_flag, transquant_bypass_enabled_flag
12577
+ const tiles_enabled_flag = eg.readBoolean();
12578
+ const entropy_coding_sync_enabled_flag = eg.readBoolean();
12579
+ let parallelismType = 1; // slice-based parallel decoding
12580
+ if (entropy_coding_sync_enabled_flag && tiles_enabled_flag) {
12581
+ parallelismType = 0; // mixed-type parallel decoding
12582
+ } else if (entropy_coding_sync_enabled_flag) {
12583
+ parallelismType = 3; // wavefront-based parallel decoding
12584
+ } else if (tiles_enabled_flag) {
12585
+ parallelismType = 2; // tile-based parallel decoding
11995
12586
  }
11996
- track.naluState = state;
11997
- return units;
12587
+ return {
12588
+ parallelismType
12589
+ };
12590
+ }
12591
+ matchSPS(sps1, sps2) {
12592
+ // compare without headers and VPS related params
12593
+ return String.fromCharCode.apply(null, sps1).substr(3) === String.fromCharCode.apply(null, sps2).substr(3);
11998
12594
  }
11999
12595
  }
12000
12596
 
@@ -12126,7 +12722,7 @@ class TSDemuxer {
12126
12722
  this.observer = observer;
12127
12723
  this.config = config;
12128
12724
  this.typeSupported = typeSupported;
12129
- this.videoParser = new AvcVideoParser();
12725
+ this.videoParser = null;
12130
12726
  }
12131
12727
  static probe(data) {
12132
12728
  const syncOffset = TSDemuxer.syncOffset(data);
@@ -12291,7 +12887,19 @@ class TSDemuxer {
12291
12887
  case videoPid:
12292
12888
  if (stt) {
12293
12889
  if (videoData && (pes = parsePES(videoData))) {
12294
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, false, this._duration);
12890
+ if (this.videoParser === null) {
12891
+ switch (videoTrack.segmentCodec) {
12892
+ case 'avc':
12893
+ this.videoParser = new AvcVideoParser();
12894
+ break;
12895
+ case 'hevc':
12896
+ this.videoParser = new HevcVideoParser();
12897
+ break;
12898
+ }
12899
+ }
12900
+ if (this.videoParser !== null) {
12901
+ this.videoParser.parsePES(videoTrack, textTrack, pes, false, this._duration);
12902
+ }
12295
12903
  }
12296
12904
  videoData = {
12297
12905
  data: [],
@@ -12458,8 +13066,20 @@ class TSDemuxer {
12458
13066
  // try to parse last PES packets
12459
13067
  let pes;
12460
13068
  if (videoData && (pes = parsePES(videoData))) {
12461
- this.videoParser.parseAVCPES(videoTrack, textTrack, pes, true, this._duration);
12462
- videoTrack.pesData = null;
13069
+ if (this.videoParser === null) {
13070
+ switch (videoTrack.segmentCodec) {
13071
+ case 'avc':
13072
+ this.videoParser = new AvcVideoParser();
13073
+ break;
13074
+ case 'hevc':
13075
+ this.videoParser = new HevcVideoParser();
13076
+ break;
13077
+ }
13078
+ }
13079
+ if (this.videoParser !== null) {
13080
+ this.videoParser.parsePES(videoTrack, textTrack, pes, true, this._duration);
13081
+ videoTrack.pesData = null;
13082
+ }
12463
13083
  } else {
12464
13084
  // either avcData null or PES truncated, keep it for next frag parsing
12465
13085
  videoTrack.pesData = videoData;
@@ -12792,7 +13412,12 @@ function parsePMT(data, offset, typeSupported, isSampleAes) {
12792
13412
  logger.warn('Unsupported EC-3 in M2TS found');
12793
13413
  break;
12794
13414
  case 0x24:
12795
- logger.warn('Unsupported HEVC in M2TS found');
13415
+ // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
13416
+ if (result.videoPid === -1) {
13417
+ result.videoPid = pid;
13418
+ result.segmentVideoCodec = 'hevc';
13419
+ logger.log('HEVC in M2TS found');
13420
+ }
12796
13421
  break;
12797
13422
  }
12798
13423
  // move to the next table entry
@@ -13015,6 +13640,8 @@ class MP4 {
13015
13640
  avc1: [],
13016
13641
  // codingname
13017
13642
  avcC: [],
13643
+ hvc1: [],
13644
+ hvcC: [],
13018
13645
  btrt: [],
13019
13646
  dinf: [],
13020
13647
  dref: [],
@@ -13439,8 +14066,10 @@ class MP4 {
13439
14066
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track));
13440
14067
  }
13441
14068
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track));
13442
- } else {
14069
+ } else if (track.segmentCodec === 'avc') {
13443
14070
  return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track));
14071
+ } else {
14072
+ return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track));
13444
14073
  }
13445
14074
  }
13446
14075
  static tkhd(track) {
@@ -13578,6 +14207,84 @@ class MP4 {
13578
14207
  const result = appendUint8Array(MP4.FTYP, movie);
13579
14208
  return result;
13580
14209
  }
14210
+ static hvc1(track) {
14211
+ const ps = track.params;
14212
+ const units = [track.vps, track.sps, track.pps];
14213
+ const NALuLengthSize = 4;
14214
+ 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]);
14215
+
14216
+ // compute hvcC size in bytes
14217
+ let length = config.length;
14218
+ for (let i = 0; i < units.length; i += 1) {
14219
+ length += 3;
14220
+ for (let j = 0; j < units[i].length; j += 1) {
14221
+ length += 2 + units[i][j].length;
14222
+ }
14223
+ }
14224
+ const hvcC = new Uint8Array(length);
14225
+ hvcC.set(config, 0);
14226
+ length = config.length;
14227
+ // append parameter set units: one vps, one or more sps and pps
14228
+ const iMax = units.length - 1;
14229
+ for (let i = 0; i < units.length; i += 1) {
14230
+ hvcC.set(new Uint8Array([32 + i | (i === iMax ? 128 : 0), 0x00, units[i].length]), length);
14231
+ length += 3;
14232
+ for (let j = 0; j < units[i].length; j += 1) {
14233
+ hvcC.set(new Uint8Array([units[i][j].length >> 8, units[i][j].length & 255]), length);
14234
+ length += 2;
14235
+ hvcC.set(units[i][j], length);
14236
+ length += units[i][j].length;
14237
+ }
14238
+ }
14239
+ const hvcc = MP4.box(MP4.types.hvcC, hvcC);
14240
+ const width = track.width;
14241
+ const height = track.height;
14242
+ const hSpacing = track.pixelRatio[0];
14243
+ const vSpacing = track.pixelRatio[1];
14244
+ return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00,
14245
+ // reserved
14246
+ 0x00, 0x00, 0x00,
14247
+ // reserved
14248
+ 0x00, 0x01,
14249
+ // data_reference_index
14250
+ 0x00, 0x00,
14251
+ // pre_defined
14252
+ 0x00, 0x00,
14253
+ // reserved
14254
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
14255
+ // pre_defined
14256
+ width >> 8 & 0xff, width & 0xff,
14257
+ // width
14258
+ height >> 8 & 0xff, height & 0xff,
14259
+ // height
14260
+ 0x00, 0x48, 0x00, 0x00,
14261
+ // horizresolution
14262
+ 0x00, 0x48, 0x00, 0x00,
14263
+ // vertresolution
14264
+ 0x00, 0x00, 0x00, 0x00,
14265
+ // reserved
14266
+ 0x00, 0x01,
14267
+ // frame_count
14268
+ 0x12, 0x64, 0x61, 0x69, 0x6c,
14269
+ // dailymotion/hls.js
14270
+ 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,
14271
+ // compressorname
14272
+ 0x00, 0x18,
14273
+ // depth = 24
14274
+ 0x11, 0x11]),
14275
+ // pre_defined = -1
14276
+ hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80,
14277
+ // bufferSizeDB
14278
+ 0x00, 0x2d, 0xc6, 0xc0,
14279
+ // maxBitrate
14280
+ 0x00, 0x2d, 0xc6, 0xc0])),
14281
+ // avgBitrate
14282
+ MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24,
14283
+ // hSpacing
14284
+ hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24,
14285
+ // vSpacing
14286
+ vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff])));
14287
+ }
13581
14288
  }
13582
14289
  MP4.types = void 0;
13583
14290
  MP4.HDLR_TYPES = void 0;
@@ -13959,9 +14666,9 @@ class MP4Remuxer {
13959
14666
  const foundOverlap = delta < -1;
13960
14667
  if (foundHole || foundOverlap) {
13961
14668
  if (foundHole) {
13962
- logger.warn(`AVC: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`);
14669
+ logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`);
13963
14670
  } else {
13964
- logger.warn(`AVC: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`);
14671
+ logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`);
13965
14672
  }
13966
14673
  if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
13967
14674
  firstDTS = nextAvcDts;
@@ -14135,7 +14842,7 @@ class MP4Remuxer {
14135
14842
  }
14136
14843
  }
14137
14844
  }
14138
- // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
14845
+ // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale)
14139
14846
  mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration;
14140
14847
  this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration;
14141
14848
  this.videoSampleDuration = mp4SampleDuration;
@@ -27813,7 +28520,7 @@ class Hls {
27813
28520
  * Get the video-dev/hls.js package version.
27814
28521
  */
27815
28522
  static get version() {
27816
- return "1.5.2-0.canary.9970";
28523
+ return "1.5.2-0.canary.9971";
27817
28524
  }
27818
28525
 
27819
28526
  /**