hls.js 1.5.14-0.canary.10429 → 1.5.14-0.canary.10432

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
@@ -420,7 +420,7 @@ function enableLogs(debugConfig, context, id) {
420
420
  // Some browsers don't allow to use bind on console object anyway
421
421
  // fallback to default if needed
422
422
  try {
423
- newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.14-0.canary.10429"}`);
423
+ newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.5.14-0.canary.10432"}`);
424
424
  } catch (e) {
425
425
  /* log fn threw an exception. All logger methods are no-ops. */
426
426
  return createLogger();
@@ -8674,30 +8674,14 @@ function findFirstFragWithCC(fragments, cc) {
8674
8674
  }
8675
8675
  return null;
8676
8676
  }
8677
- function shouldAlignOnDiscontinuities(lastFrag, switchDetails, details) {
8678
- if (switchDetails) {
8679
- if (details.endCC > details.startCC || lastFrag && lastFrag.cc < details.startCC) {
8677
+ function shouldAlignOnDiscontinuities(refDetails, details) {
8678
+ if (refDetails) {
8679
+ if (details.startCC < refDetails.endCC && details.endCC > refDetails.startCC) {
8680
8680
  return true;
8681
8681
  }
8682
8682
  }
8683
8683
  return false;
8684
8684
  }
8685
-
8686
- // Find the first frag in the previous level which matches the CC of the first frag of the new level
8687
- function findDiscontinuousReferenceFrag(prevDetails, curDetails) {
8688
- const prevFrags = prevDetails.fragments;
8689
- const curFrags = curDetails.fragments;
8690
- if (!curFrags.length || !prevFrags.length) {
8691
- logger.log('No fragments to align');
8692
- return;
8693
- }
8694
- const prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc);
8695
- if (!prevStartFrag || prevStartFrag && !prevStartFrag.startPTS) {
8696
- logger.log('No frag in previous level to align on');
8697
- return;
8698
- }
8699
- return prevStartFrag;
8700
- }
8701
8685
  function adjustFragmentStart(frag, sliding) {
8702
8686
  if (frag) {
8703
8687
  const start = frag.start + sliding;
@@ -8732,7 +8716,7 @@ function alignStream(lastFrag, switchDetails, details) {
8732
8716
  if (!switchDetails) {
8733
8717
  return;
8734
8718
  }
8735
- alignDiscontinuities(lastFrag, details, switchDetails);
8719
+ alignDiscontinuities(details, switchDetails);
8736
8720
  if (!details.alignedSliding && switchDetails) {
8737
8721
  // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level.
8738
8722
  // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same
@@ -8748,20 +8732,24 @@ function alignStream(lastFrag, switchDetails, details) {
8748
8732
  }
8749
8733
 
8750
8734
  /**
8751
- * Computes the PTS if a new level's fragments using the PTS of a fragment in the last level which shares the same
8752
- * discontinuity sequence.
8753
- * @param lastFrag - The last Fragment which shares the same discontinuity sequence
8735
+ * Ajust the start of fragments in `details` by the difference in time between fragments of the latest
8736
+ * shared discontinuity sequence change.
8754
8737
  * @param lastLevel - The details of the last loaded level
8755
8738
  * @param details - The details of the new level
8756
8739
  */
8757
- function alignDiscontinuities(lastFrag, details, switchDetails) {
8758
- if (shouldAlignOnDiscontinuities(lastFrag, switchDetails, details)) {
8759
- const referenceFrag = findDiscontinuousReferenceFrag(switchDetails, details);
8760
- if (referenceFrag && isFiniteNumber(referenceFrag.start)) {
8761
- logger.log(`Adjusting PTS using last level due to CC increase within current level ${details.url}`);
8762
- adjustSlidingStart(referenceFrag.start, details);
8763
- }
8740
+ function alignDiscontinuities(details, refDetails) {
8741
+ if (!shouldAlignOnDiscontinuities(refDetails, details)) {
8742
+ return;
8743
+ }
8744
+ const targetCC = Math.min(refDetails.endCC, details.endCC);
8745
+ const refFrag = findFirstFragWithCC(refDetails.fragments, targetCC);
8746
+ const frag = findFirstFragWithCC(details.fragments, targetCC);
8747
+ if (!refFrag || !frag) {
8748
+ return;
8764
8749
  }
8750
+ logger.log(`Aligning playlist at start of dicontinuity sequence ${targetCC}`);
8751
+ const delta = refFrag.start - frag.start;
8752
+ adjustSlidingStart(delta, details);
8765
8753
  }
8766
8754
 
8767
8755
  /**
@@ -9850,6 +9838,10 @@ class BaseStreamController extends TaskLoop {
9850
9838
  }
9851
9839
  onHandlerDestroying() {
9852
9840
  this.stopLoad();
9841
+ if (this.transmuxer) {
9842
+ this.transmuxer.destroy();
9843
+ this.transmuxer = null;
9844
+ }
9853
9845
  super.onHandlerDestroying();
9854
9846
  // @ts-ignore
9855
9847
  this.hls = this.onMediaSeeking = this.onMediaEnded = null;
@@ -9896,7 +9888,7 @@ class BaseStreamController extends TaskLoop {
9896
9888
  return;
9897
9889
  }
9898
9890
  if ('payload' in data) {
9899
- this.log(`Loaded fragment ${frag.sn} of level ${frag.level}`);
9891
+ this.log(`Loaded ${frag.type} sn: ${frag.sn} of ${this.playlistLabel()} ${frag.level}`);
9900
9892
  this.hls.trigger(Events.FRAG_LOADED, data);
9901
9893
  }
9902
9894
 
@@ -10046,9 +10038,9 @@ class BaseStreamController extends TaskLoop {
10046
10038
  return !frag || !fragCurrent || frag.sn !== fragCurrent.sn || frag.level !== fragCurrent.level;
10047
10039
  }
10048
10040
  fragBufferedComplete(frag, part) {
10049
- var _frag$startPTS, _frag$endPTS, _this$fragCurrent, _this$fragPrevious;
10041
+ var _this$fragCurrent, _this$fragPrevious;
10050
10042
  const media = this.mediaBuffer ? this.mediaBuffer : this.media;
10051
- this.log(`Buffered ${frag.type} sn: ${frag.sn}${part ? ' part: ' + part.index : ''} of ${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'} ${frag.level} (frag:[${((_frag$startPTS = frag.startPTS) != null ? _frag$startPTS : NaN).toFixed(3)}-${((_frag$endPTS = frag.endPTS) != null ? _frag$endPTS : NaN).toFixed(3)}] > buffer:${media ? TimeRanges.toString(BufferHelper.getBuffered(media)) : '(detached)'})`);
10043
+ this.log(`Buffered ${frag.type} sn: ${frag.sn}${part ? ' part: ' + part.index : ''} of ${this.fragInfo(frag)} > buffer:${media ? TimeRanges.toString(BufferHelper.getBuffered(media)) : '(detached)'})`);
10052
10044
  if (frag.sn !== 'initSegment') {
10053
10045
  var _this$levels;
10054
10046
  if (frag.type !== PlaylistLevelType.SUBTITLE) {
@@ -10105,7 +10097,7 @@ class BaseStreamController extends TaskLoop {
10105
10097
  }
10106
10098
  let keyLoadingPromise = null;
10107
10099
  if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) {
10108
- this.log(`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'} ${frag.level}`);
10100
+ this.log(`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${this.playlistLabel()} ${frag.level}`);
10109
10101
  this.state = State.KEY_LOADING;
10110
10102
  this.fragCurrent = frag;
10111
10103
  keyLoadingPromise = this.keyLoader.load(frag).then(keyLoadedData => {
@@ -10144,7 +10136,7 @@ class BaseStreamController extends TaskLoop {
10144
10136
  const partIndex = this.getNextPart(partList, frag, targetBufferTime);
10145
10137
  if (partIndex > -1) {
10146
10138
  const part = partList[partIndex];
10147
- this.log(`Loading part sn: ${frag.sn} p: ${part.index} cc: ${frag.cc} of playlist [${details.startSN}-${details.endSN}] parts [0-${partIndex}-${partList.length - 1}] ${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
10139
+ this.log(`Loading part sn: ${frag.sn} p: ${part.index} cc: ${frag.cc} of playlist [${details.startSN}-${details.endSN}] parts [0-${partIndex}-${partList.length - 1}] ${this.playlistLabel()}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
10148
10140
  this.nextLoadPosition = part.start + part.duration;
10149
10141
  this.state = State.FRAG_LOADING;
10150
10142
  let _result;
@@ -10180,7 +10172,7 @@ class BaseStreamController extends TaskLoop {
10180
10172
  // Selected fragment hint for part but not loading parts
10181
10173
  return Promise.resolve(null);
10182
10174
  }
10183
- this.log(`Loading fragment ${frag.sn} cc: ${frag.cc} ${details ? 'of [' + details.startSN + '-' + details.endSN + '] ' : ''}${this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${frag.level}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
10175
+ this.log(`Loading ${frag.type} sn: ${frag.sn} of ${this.fragInfo(frag, false)}) cc: ${frag.cc} ${details ? '[' + details.startSN + '-' + details.endSN + ']' : ''}, target: ${parseFloat(targetBufferTime.toFixed(3))}`);
10184
10176
  // Don't update nextLoadPosition for fragments which are not buffered
10185
10177
  if (isFiniteNumber(frag.sn) && !this.bitrateTest) {
10186
10178
  this.nextLoadPosition = frag.start + frag.duration;
@@ -10959,16 +10951,22 @@ class BaseStreamController extends TaskLoop {
10959
10951
  // For this error fallthrough. Marking parsed will allow advancing to next fragment.
10960
10952
  }
10961
10953
  this.state = State.PARSED;
10954
+ this.log(`Parsed ${frag.type} sn: ${frag.sn}${part ? ' part: ' + part.index : ''} of ${this.fragInfo(frag)})`);
10962
10955
  this.hls.trigger(Events.FRAG_PARSED, {
10963
10956
  frag,
10964
10957
  part
10965
10958
  });
10966
10959
  }
10960
+ playlistLabel() {
10961
+ return this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track';
10962
+ }
10963
+ fragInfo(frag, pts = true) {
10964
+ var _ref, _ref2;
10965
+ return `${this.playlistLabel()} ${frag.level} (frag:[${((_ref = pts ? frag.startPTS : frag.start) != null ? _ref : NaN).toFixed(3)}-${((_ref2 = pts ? frag.endPTS : frag.end) != null ? _ref2 : NaN).toFixed(3)}]`;
10966
+ }
10967
10967
  resetTransmuxer() {
10968
- if (this.transmuxer) {
10969
- this.transmuxer.destroy();
10970
- this.transmuxer = null;
10971
- }
10968
+ var _this$transmuxer2;
10969
+ (_this$transmuxer2 = this.transmuxer) == null ? void 0 : _this$transmuxer2.reset();
10972
10970
  }
10973
10971
  recoverWorkerError(data) {
10974
10972
  if (data.event === 'demuxerWorker') {
@@ -11031,29 +11029,66 @@ function concatUint8Arrays(chunks, dataLength) {
11031
11029
  return result;
11032
11030
  }
11033
11031
 
11032
+ const version = "1.5.14-0.canary.10432";
11033
+
11034
11034
  // ensure the worker ends up in the bundle
11035
11035
  // If the worker should not be included this gets aliased to empty.js
11036
+ const workerStore = {};
11036
11037
  function hasUMDWorker() {
11037
11038
  return typeof __HLS_WORKER_BUNDLE__ === 'function';
11038
11039
  }
11039
11040
  function injectWorker() {
11041
+ const workerContext = workerStore[version];
11042
+ if (workerContext) {
11043
+ workerContext.clientCount++;
11044
+ return workerContext;
11045
+ }
11040
11046
  const blob = new self.Blob([`var exports={};var module={exports:exports};function define(f){f()};define.amd=true;(${__HLS_WORKER_BUNDLE__.toString()})(true);`], {
11041
11047
  type: 'text/javascript'
11042
11048
  });
11043
11049
  const objectURL = self.URL.createObjectURL(blob);
11044
11050
  const worker = new self.Worker(objectURL);
11045
- return {
11051
+ const result = {
11046
11052
  worker,
11047
- objectURL
11053
+ objectURL,
11054
+ clientCount: 1
11048
11055
  };
11056
+ workerStore[version] = result;
11057
+ return result;
11049
11058
  }
11050
11059
  function loadWorker(path) {
11060
+ const workerContext = workerStore[path];
11061
+ if (workerContext) {
11062
+ workerContext.clientCount++;
11063
+ return workerContext;
11064
+ }
11051
11065
  const scriptURL = new self.URL(path, self.location.href).href;
11052
11066
  const worker = new self.Worker(scriptURL);
11053
- return {
11067
+ const result = {
11054
11068
  worker,
11055
- scriptURL
11069
+ scriptURL,
11070
+ clientCount: 1
11056
11071
  };
11072
+ workerStore[path] = result;
11073
+ return result;
11074
+ }
11075
+ function removeWorkerFromStore(path) {
11076
+ const workerContext = workerStore[path || version];
11077
+ if (workerContext) {
11078
+ const clientCount = workerContext.clientCount--;
11079
+ if (clientCount === 1) {
11080
+ const {
11081
+ worker,
11082
+ objectURL
11083
+ } = workerContext;
11084
+ delete workerStore[path || version];
11085
+ if (objectURL) {
11086
+ // revoke the Object URL that was used to create transmuxer worker, so as not to leak it
11087
+ self.URL.revokeObjectURL(objectURL);
11088
+ }
11089
+ worker.terminate();
11090
+ }
11091
+ }
11057
11092
  }
11058
11093
 
11059
11094
  function dummyTrack(type = '', inputTimeScale = 90000) {
@@ -11730,7 +11765,7 @@ class AACDemuxer extends BaseAudioDemuxer {
11730
11765
  }
11731
11766
 
11732
11767
  // Source for probe info - https://wiki.multimedia.cx/index.php?title=ADTS
11733
- static probe(data) {
11768
+ static probe(data, logger) {
11734
11769
  if (!data) {
11735
11770
  return false;
11736
11771
  }
@@ -13356,7 +13391,8 @@ class SampleAesDecrypter {
13356
13391
 
13357
13392
  const PACKET_LENGTH = 188;
13358
13393
  class TSDemuxer {
13359
- constructor(observer, config, typeSupported) {
13394
+ constructor(observer, config, typeSupported, logger) {
13395
+ this.logger = void 0;
13360
13396
  this.observer = void 0;
13361
13397
  this.config = void 0;
13362
13398
  this.typeSupported = void 0;
@@ -13376,9 +13412,10 @@ class TSDemuxer {
13376
13412
  this.observer = observer;
13377
13413
  this.config = config;
13378
13414
  this.typeSupported = typeSupported;
13415
+ this.logger = logger;
13379
13416
  this.videoParser = null;
13380
13417
  }
13381
- static probe(data) {
13418
+ static probe(data, logger) {
13382
13419
  const syncOffset = TSDemuxer.syncOffset(data);
13383
13420
  if (syncOffset > 0) {
13384
13421
  logger.warn(`MPEG2-TS detected but first sync word found @ offset ${syncOffset}`);
@@ -13540,7 +13577,7 @@ class TSDemuxer {
13540
13577
  switch (pid) {
13541
13578
  case videoPid:
13542
13579
  if (stt) {
13543
- if (videoData && (pes = parsePES(videoData))) {
13580
+ if (videoData && (pes = parsePES(videoData, this.logger))) {
13544
13581
  if (this.videoParser === null) {
13545
13582
  switch (videoTrack.segmentCodec) {
13546
13583
  case 'avc':
@@ -13569,7 +13606,7 @@ class TSDemuxer {
13569
13606
  break;
13570
13607
  case audioPid:
13571
13608
  if (stt) {
13572
- if (audioData && (pes = parsePES(audioData))) {
13609
+ if (audioData && (pes = parsePES(audioData, this.logger))) {
13573
13610
  switch (audioTrack.segmentCodec) {
13574
13611
  case 'aac':
13575
13612
  this.parseAACPES(audioTrack, pes);
@@ -13596,7 +13633,7 @@ class TSDemuxer {
13596
13633
  break;
13597
13634
  case id3Pid:
13598
13635
  if (stt) {
13599
- if (id3Data && (pes = parsePES(id3Data))) {
13636
+ if (id3Data && (pes = parsePES(id3Data, this.logger))) {
13600
13637
  this.parseID3PES(id3Track, pes);
13601
13638
  }
13602
13639
  id3Data = {
@@ -13614,14 +13651,14 @@ class TSDemuxer {
13614
13651
  offset += data[offset] + 1;
13615
13652
  }
13616
13653
  pmtId = this._pmtId = parsePAT(data, offset);
13617
- // logger.log('PMT PID:' + this._pmtId);
13654
+ // this.logger.log('PMT PID:' + this._pmtId);
13618
13655
  break;
13619
13656
  case pmtId:
13620
13657
  {
13621
13658
  if (stt) {
13622
13659
  offset += data[offset] + 1;
13623
13660
  }
13624
- const parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes, this.observer);
13661
+ const parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes, this.observer, this.logger);
13625
13662
 
13626
13663
  // only update track id if track PID found while parsing PMT
13627
13664
  // this is to avoid resetting the PID to -1 in case
@@ -13644,7 +13681,7 @@ class TSDemuxer {
13644
13681
  id3Track.pid = id3Pid;
13645
13682
  }
13646
13683
  if (unknownPID !== null && !pmtParsed) {
13647
- logger.warn(`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`);
13684
+ this.logger.warn(`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`);
13648
13685
  unknownPID = null;
13649
13686
  // we set it to -188, the += 188 in the for loop will reset start to 0
13650
13687
  start = syncOffset - 188;
@@ -13664,7 +13701,7 @@ class TSDemuxer {
13664
13701
  }
13665
13702
  }
13666
13703
  if (tsPacketErrors > 0) {
13667
- emitParsingError(this.observer, new Error(`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`));
13704
+ emitParsingError(this.observer, new Error(`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`), undefined, this.logger);
13668
13705
  }
13669
13706
  videoTrack.pesData = videoData;
13670
13707
  audioTrack.pesData = audioData;
@@ -13714,7 +13751,7 @@ class TSDemuxer {
13714
13751
  const id3Data = id3Track.pesData;
13715
13752
  // try to parse last PES packets
13716
13753
  let pes;
13717
- if (videoData && (pes = parsePES(videoData))) {
13754
+ if (videoData && (pes = parsePES(videoData, this.logger))) {
13718
13755
  if (this.videoParser === null) {
13719
13756
  switch (videoTrack.segmentCodec) {
13720
13757
  case 'avc':
@@ -13735,7 +13772,7 @@ class TSDemuxer {
13735
13772
  // either avcData null or PES truncated, keep it for next frag parsing
13736
13773
  videoTrack.pesData = videoData;
13737
13774
  }
13738
- if (audioData && (pes = parsePES(audioData))) {
13775
+ if (audioData && (pes = parsePES(audioData, this.logger))) {
13739
13776
  switch (audioTrack.segmentCodec) {
13740
13777
  case 'aac':
13741
13778
  this.parseAACPES(audioTrack, pes);
@@ -13752,13 +13789,13 @@ class TSDemuxer {
13752
13789
  audioTrack.pesData = null;
13753
13790
  } else {
13754
13791
  if (audioData != null && audioData.size) {
13755
- logger.log('last AAC PES packet truncated,might overlap between fragments');
13792
+ this.logger.log('last AAC PES packet truncated,might overlap between fragments');
13756
13793
  }
13757
13794
 
13758
13795
  // either audioData null or PES truncated, keep it for next frag parsing
13759
13796
  audioTrack.pesData = audioData;
13760
13797
  }
13761
- if (id3Data && (pes = parsePES(id3Data))) {
13798
+ if (id3Data && (pes = parsePES(id3Data, this.logger))) {
13762
13799
  this.parseID3PES(id3Track, pes);
13763
13800
  id3Track.pesData = null;
13764
13801
  } else {
@@ -13832,7 +13869,7 @@ class TSDemuxer {
13832
13869
  } else {
13833
13870
  reason = 'No ADTS header found in AAC PES';
13834
13871
  }
13835
- emitParsingError(this.observer, new Error(reason), recoverable);
13872
+ emitParsingError(this.observer, new Error(reason), recoverable, this.logger);
13836
13873
  if (!recoverable) {
13837
13874
  return;
13838
13875
  }
@@ -13847,7 +13884,7 @@ class TSDemuxer {
13847
13884
  const frameDuration = getFrameDuration(track.samplerate);
13848
13885
  pts = aacOverFlow.sample.pts + frameDuration;
13849
13886
  } else {
13850
- logger.warn('[tsdemuxer]: AAC PES unknown PTS');
13887
+ this.logger.warn('[tsdemuxer]: AAC PES unknown PTS');
13851
13888
  return;
13852
13889
  }
13853
13890
 
@@ -13877,7 +13914,7 @@ class TSDemuxer {
13877
13914
  let offset = 0;
13878
13915
  const pts = pes.pts;
13879
13916
  if (pts === undefined) {
13880
- logger.warn('[tsdemuxer]: MPEG PES unknown PTS');
13917
+ this.logger.warn('[tsdemuxer]: MPEG PES unknown PTS');
13881
13918
  return;
13882
13919
  }
13883
13920
  while (offset < length) {
@@ -13901,7 +13938,7 @@ class TSDemuxer {
13901
13938
  const data = pes.data;
13902
13939
  const pts = pes.pts;
13903
13940
  if (pts === undefined) {
13904
- logger.warn('[tsdemuxer]: AC3 PES unknown PTS');
13941
+ this.logger.warn('[tsdemuxer]: AC3 PES unknown PTS');
13905
13942
  return;
13906
13943
  }
13907
13944
  const length = data.length;
@@ -13915,7 +13952,7 @@ class TSDemuxer {
13915
13952
  }
13916
13953
  parseID3PES(id3Track, pes) {
13917
13954
  if (pes.pts === undefined) {
13918
- logger.warn('[tsdemuxer]: ID3 PES unknown PTS');
13955
+ this.logger.warn('[tsdemuxer]: ID3 PES unknown PTS');
13919
13956
  return;
13920
13957
  }
13921
13958
  const id3Sample = _extends({}, pes, {
@@ -13933,7 +13970,7 @@ function parsePAT(data, offset) {
13933
13970
  // skip the PSI header and parse the first PMT entry
13934
13971
  return (data[offset + 10] & 0x1f) << 8 | data[offset + 11];
13935
13972
  }
13936
- function parsePMT(data, offset, typeSupported, isSampleAes, observer) {
13973
+ function parsePMT(data, offset, typeSupported, isSampleAes, observer, logger) {
13937
13974
  const result = {
13938
13975
  audioPid: -1,
13939
13976
  videoPid: -1,
@@ -13955,7 +13992,7 @@ function parsePMT(data, offset, typeSupported, isSampleAes, observer) {
13955
13992
  case 0xcf:
13956
13993
  // SAMPLE-AES AAC
13957
13994
  if (!isSampleAes) {
13958
- logEncryptedSamplesFoundInUnencryptedStream('ADTS AAC');
13995
+ logEncryptedSamplesFoundInUnencryptedStream('ADTS AAC', this.logger);
13959
13996
  break;
13960
13997
  }
13961
13998
  /* falls through */
@@ -13977,7 +14014,7 @@ function parsePMT(data, offset, typeSupported, isSampleAes, observer) {
13977
14014
  case 0xdb:
13978
14015
  // SAMPLE-AES AVC
13979
14016
  if (!isSampleAes) {
13980
- logEncryptedSamplesFoundInUnencryptedStream('H.264');
14017
+ logEncryptedSamplesFoundInUnencryptedStream('H.264', this.logger);
13981
14018
  break;
13982
14019
  }
13983
14020
  /* falls through */
@@ -14005,7 +14042,7 @@ function parsePMT(data, offset, typeSupported, isSampleAes, observer) {
14005
14042
  case 0xc1:
14006
14043
  // SAMPLE-AES AC3
14007
14044
  if (!isSampleAes) {
14008
- logEncryptedSamplesFoundInUnencryptedStream('AC-3');
14045
+ logEncryptedSamplesFoundInUnencryptedStream('AC-3', this.logger);
14009
14046
  break;
14010
14047
  }
14011
14048
  /* falls through */
@@ -14051,7 +14088,7 @@ function parsePMT(data, offset, typeSupported, isSampleAes, observer) {
14051
14088
  case 0xc2: // SAMPLE-AES EC3
14052
14089
  /* falls through */
14053
14090
  case 0x87:
14054
- emitParsingError(observer, new Error('Unsupported EC-3 in M2TS found'));
14091
+ emitParsingError(observer, new Error('Unsupported EC-3 in M2TS found'), undefined, this.logger);
14055
14092
  return result;
14056
14093
  case 0x24:
14057
14094
  // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC)
@@ -14070,7 +14107,7 @@ function parsePMT(data, offset, typeSupported, isSampleAes, observer) {
14070
14107
  }
14071
14108
  return result;
14072
14109
  }
14073
- function emitParsingError(observer, error, levelRetry) {
14110
+ function emitParsingError(observer, error, levelRetry, logger) {
14074
14111
  logger.warn(`parsing error: ${error.message}`);
14075
14112
  observer.emit(Events.ERROR, Events.ERROR, {
14076
14113
  type: ErrorTypes.MEDIA_ERROR,
@@ -14081,10 +14118,10 @@ function emitParsingError(observer, error, levelRetry) {
14081
14118
  reason: error.message
14082
14119
  });
14083
14120
  }
14084
- function logEncryptedSamplesFoundInUnencryptedStream(type) {
14121
+ function logEncryptedSamplesFoundInUnencryptedStream(type, logger) {
14085
14122
  logger.log(`${type} with AES-128-CBC encryption found in unencrypted stream`);
14086
14123
  }
14087
- function parsePES(stream) {
14124
+ function parsePES(stream, logger) {
14088
14125
  let i = 0;
14089
14126
  let frag;
14090
14127
  let pesLen;
@@ -14975,7 +15012,8 @@ const AC3_SAMPLES_PER_FRAME = 1536;
14975
15012
  let chromeVersion = null;
14976
15013
  let safariWebkitVersion = null;
14977
15014
  class MP4Remuxer {
14978
- constructor(observer, config, typeSupported, vendor = '') {
15015
+ constructor(observer, config, typeSupported, logger) {
15016
+ this.logger = void 0;
14979
15017
  this.observer = void 0;
14980
15018
  this.config = void 0;
14981
15019
  this.typeSupported = void 0;
@@ -14991,6 +15029,7 @@ class MP4Remuxer {
14991
15029
  this.observer = observer;
14992
15030
  this.config = config;
14993
15031
  this.typeSupported = typeSupported;
15032
+ this.logger = logger;
14994
15033
  this.ISGenerated = false;
14995
15034
  if (chromeVersion === null) {
14996
15035
  const userAgent = navigator.userAgent || '';
@@ -15007,16 +15046,16 @@ class MP4Remuxer {
15007
15046
  this.config = this.videoTrackConfig = this._initPTS = this._initDTS = null;
15008
15047
  }
15009
15048
  resetTimeStamp(defaultTimeStamp) {
15010
- logger.log('[mp4-remuxer]: initPTS & initDTS reset');
15049
+ this.logger.log('[mp4-remuxer]: initPTS & initDTS reset');
15011
15050
  this._initPTS = this._initDTS = defaultTimeStamp;
15012
15051
  }
15013
15052
  resetNextTimestamp() {
15014
- logger.log('[mp4-remuxer]: reset next timestamp');
15053
+ this.logger.log('[mp4-remuxer]: reset next timestamp');
15015
15054
  this.isVideoContiguous = false;
15016
15055
  this.isAudioContiguous = false;
15017
15056
  }
15018
15057
  resetInitSegment() {
15019
- logger.log('[mp4-remuxer]: ISGenerated flag reset');
15058
+ this.logger.log('[mp4-remuxer]: ISGenerated flag reset');
15020
15059
  this.ISGenerated = false;
15021
15060
  this.videoTrackConfig = undefined;
15022
15061
  }
@@ -15035,7 +15074,7 @@ class MP4Remuxer {
15035
15074
  }
15036
15075
  }, videoSamples[0].pts);
15037
15076
  if (rolloverDetected) {
15038
- logger.debug('PTS rollover detected');
15077
+ this.logger.debug('PTS rollover detected');
15039
15078
  }
15040
15079
  return startPTS;
15041
15080
  }
@@ -15079,14 +15118,14 @@ class MP4Remuxer {
15079
15118
  if (!isVideoContiguous && this.config.forceKeyFrameOnDiscontinuity) {
15080
15119
  independent = true;
15081
15120
  if (firstKeyFrameIndex > 0) {
15082
- logger.warn(`[mp4-remuxer]: Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`);
15121
+ this.logger.warn(`[mp4-remuxer]: Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`);
15083
15122
  const startPTS = this.getVideoStartPts(videoTrack.samples);
15084
15123
  videoTrack.samples = videoTrack.samples.slice(firstKeyFrameIndex);
15085
15124
  videoTrack.dropped += firstKeyFrameIndex;
15086
15125
  videoTimeOffset += (videoTrack.samples[0].pts - startPTS) / videoTrack.inputTimeScale;
15087
15126
  firstKeyFramePTS = videoTimeOffset;
15088
15127
  } else if (firstKeyFrameIndex === -1) {
15089
- logger.warn(`[mp4-remuxer]: No keyframe found out of ${length} video samples`);
15128
+ this.logger.warn(`[mp4-remuxer]: No keyframe found out of ${length} video samples`);
15090
15129
  independent = false;
15091
15130
  }
15092
15131
  }
@@ -15108,7 +15147,7 @@ class MP4Remuxer {
15108
15147
  if (enoughAudioSamples) {
15109
15148
  // if initSegment was generated without audio samples, regenerate it again
15110
15149
  if (!audioTrack.samplerate) {
15111
- logger.warn('[mp4-remuxer]: regenerate InitSegment as audio detected');
15150
+ this.logger.warn('[mp4-remuxer]: regenerate InitSegment as audio detected');
15112
15151
  initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset);
15113
15152
  }
15114
15153
  audio = this.remuxAudio(audioTrack, audioTimeOffset, this.isAudioContiguous, accurateTimeOffset, hasVideo || enoughVideoSamples || playlistType === PlaylistLevelType.AUDIO ? videoTimeOffset : undefined);
@@ -15116,7 +15155,7 @@ class MP4Remuxer {
15116
15155
  const audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0;
15117
15156
  // if initSegment was generated without video samples, regenerate it again
15118
15157
  if (!videoTrack.inputTimeScale) {
15119
- logger.warn('[mp4-remuxer]: regenerate InitSegment as video detected');
15158
+ this.logger.warn('[mp4-remuxer]: regenerate InitSegment as video detected');
15120
15159
  initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset);
15121
15160
  }
15122
15161
  video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, audioTrackLength);
@@ -15322,9 +15361,9 @@ class MP4Remuxer {
15322
15361
  const foundOverlap = delta < -1;
15323
15362
  if (foundHole || foundOverlap) {
15324
15363
  if (foundHole) {
15325
- logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`);
15364
+ this.logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`);
15326
15365
  } else {
15327
- logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`);
15366
+ this.logger.warn(`${(track.segmentCodec || '').toUpperCase()}: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`);
15328
15367
  }
15329
15368
  if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) {
15330
15369
  firstDTS = nextAvcDts;
@@ -15353,7 +15392,7 @@ class MP4Remuxer {
15353
15392
  }
15354
15393
  }
15355
15394
  }
15356
- logger.log(`Video: Initial PTS/DTS adjusted: ${toMsFromMpegTsClock(firstPTS, true)}/${toMsFromMpegTsClock(firstDTS, true)}, delta: ${toMsFromMpegTsClock(delta, true)} ms`);
15395
+ this.logger.log(`Video: Initial PTS/DTS adjusted: ${toMsFromMpegTsClock(firstPTS, true)}/${toMsFromMpegTsClock(firstDTS, true)}, delta: ${toMsFromMpegTsClock(delta, true)} ms`);
15357
15396
  }
15358
15397
  }
15359
15398
  }
@@ -15453,7 +15492,7 @@ class MP4Remuxer {
15453
15492
  } else {
15454
15493
  stretchedLastFrame = true;
15455
15494
  }
15456
- logger.log(`[mp4-remuxer]: It is approximately ${deltaToFrameEnd / 90} ms to the next segment; using duration ${mp4SampleDuration / 90} ms for the last video frame.`);
15495
+ this.logger.log(`[mp4-remuxer]: It is approximately ${deltaToFrameEnd / 90} ms to the next segment; using duration ${mp4SampleDuration / 90} ms for the last video frame.`);
15457
15496
  } else {
15458
15497
  mp4SampleDuration = lastFrameDuration;
15459
15498
  }
@@ -15481,7 +15520,7 @@ class MP4Remuxer {
15481
15520
  // Fix for "CNN special report, with CC" in test-streams (Safari browser only)
15482
15521
  // Ignore DTS when frame durations are irregular. Safari MSE does not handle this leading to gaps.
15483
15522
  if (maxPtsDelta - minPtsDelta < maxDtsDelta - minDtsDelta && averageSampleDuration / maxDtsDelta < 0.025 && outputSamples[0].cts === 0) {
15484
- logger.warn('Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.');
15523
+ this.logger.warn('Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.');
15485
15524
  let dts = firstDTS;
15486
15525
  for (let i = 0, len = outputSamples.length; i < len; i++) {
15487
15526
  const nextDts = dts + outputSamples[i].duration;
@@ -15606,7 +15645,7 @@ class MP4Remuxer {
15606
15645
  // When remuxing with video, if we're overlapping by more than a duration, drop this sample to stay in sync
15607
15646
  if (delta <= -maxAudioFramesDrift * inputSampleDuration && alignedWithVideo) {
15608
15647
  if (i === 0) {
15609
- logger.warn(`Audio frame @ ${(pts / inputTimeScale).toFixed(3)}s overlaps nextAudioPts by ${Math.round(1000 * delta / inputTimeScale)} ms.`);
15648
+ this.logger.warn(`Audio frame @ ${(pts / inputTimeScale).toFixed(3)}s overlaps nextAudioPts by ${Math.round(1000 * delta / inputTimeScale)} ms.`);
15610
15649
  this.nextAudioPts = nextAudioPts = nextPts = pts;
15611
15650
  }
15612
15651
  } // eslint-disable-line brace-style
@@ -15628,12 +15667,12 @@ class MP4Remuxer {
15628
15667
  if (i === 0) {
15629
15668
  this.nextAudioPts = nextAudioPts = nextPts;
15630
15669
  }
15631
- logger.warn(`[mp4-remuxer]: Injecting ${missing} audio frame @ ${(nextPts / inputTimeScale).toFixed(3)}s due to ${Math.round(1000 * delta / inputTimeScale)} ms gap.`);
15670
+ this.logger.warn(`[mp4-remuxer]: Injecting ${missing} audio frame @ ${(nextPts / inputTimeScale).toFixed(3)}s due to ${Math.round(1000 * delta / inputTimeScale)} ms gap.`);
15632
15671
  for (let j = 0; j < missing; j++) {
15633
15672
  const newStamp = Math.max(nextPts, 0);
15634
15673
  let fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount);
15635
15674
  if (!fillFrame) {
15636
- logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
15675
+ this.logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.');
15637
15676
  fillFrame = sample.unit.subarray();
15638
15677
  }
15639
15678
  inputSamples.splice(i, 0, {
@@ -15832,7 +15871,8 @@ class Mp4Sample {
15832
15871
  }
15833
15872
 
15834
15873
  class PassThroughRemuxer {
15835
- constructor() {
15874
+ constructor(observer, config, typeSupported, logger) {
15875
+ this.logger = void 0;
15836
15876
  this.emitInitSegment = false;
15837
15877
  this.audioCodec = void 0;
15838
15878
  this.videoCodec = void 0;
@@ -15840,6 +15880,7 @@ class PassThroughRemuxer {
15840
15880
  this.initPTS = null;
15841
15881
  this.initTracks = void 0;
15842
15882
  this.lastEndTime = null;
15883
+ this.logger = logger;
15843
15884
  }
15844
15885
  destroy() {}
15845
15886
  resetTimeStamp(defaultInitPTS) {
@@ -15897,7 +15938,7 @@ class PassThroughRemuxer {
15897
15938
  id: 'main'
15898
15939
  };
15899
15940
  } else {
15900
- logger.warn('[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.');
15941
+ this.logger.warn('[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.');
15901
15942
  }
15902
15943
  this.initTracks = tracks;
15903
15944
  }
@@ -15939,7 +15980,7 @@ class PassThroughRemuxer {
15939
15980
  }
15940
15981
  if (!((_initData2 = initData) != null && _initData2.length)) {
15941
15982
  // We can't remux if the initSegment could not be generated
15942
- logger.warn('[passthrough-remuxer.ts]: Failed to generate initSegment.');
15983
+ this.logger.warn('[passthrough-remuxer.ts]: Failed to generate initSegment.');
15943
15984
  return result;
15944
15985
  }
15945
15986
  if (this.emitInitSegment) {
@@ -15952,7 +15993,7 @@ class PassThroughRemuxer {
15952
15993
  if (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || initSegment.timescale !== initPTS.timescale && accurateTimeOffset) {
15953
15994
  initSegment.initPTS = decodeTime - timeOffset;
15954
15995
  if (initPTS && initPTS.timescale === 1) {
15955
- logger.warn(`Adjusting initPTS @${timeOffset} from ${initPTS.baseTime / initPTS.timescale} to ${initSegment.initPTS}`);
15996
+ this.logger.warn(`Adjusting initPTS @${timeOffset} from ${initPTS.baseTime / initPTS.timescale} to ${initSegment.initPTS}`);
15956
15997
  }
15957
15998
  this.initPTS = initPTS = {
15958
15999
  baseTime: initSegment.initPTS,
@@ -15965,7 +16006,7 @@ class PassThroughRemuxer {
15965
16006
  if (duration > 0) {
15966
16007
  this.lastEndTime = endTime;
15967
16008
  } else {
15968
- logger.warn('Duration parsed from mp4 should be greater than zero');
16009
+ this.logger.warn('Duration parsed from mp4 should be greater than zero');
15969
16010
  this.resetNextTimestamp();
15970
16011
  }
15971
16012
  const hasAudio = !!initData.audio;
@@ -16023,12 +16064,12 @@ function getParsedTrackCodec(track, type) {
16023
16064
  return getCodecCompatibleName(parsedCodec, preferManagedMediaSource);
16024
16065
  }
16025
16066
  const result = 'mp4a.40.5';
16026
- logger.info(`Parsed audio codec "${parsedCodec}" or audio object type not handled. Using "${result}"`);
16067
+ this.logger.info(`Parsed audio codec "${parsedCodec}" or audio object type not handled. Using "${result}"`);
16027
16068
  return result;
16028
16069
  }
16029
16070
  // Provide defaults based on codec type
16030
16071
  // This allows for some playback of some fmp4 playlists without CODECS defined in manifest
16031
- logger.warn(`Unhandled video codec "${parsedCodec}"`);
16072
+ this.logger.warn(`Unhandled video codec "${parsedCodec}"`);
16032
16073
  if (parsedCodec === 'hvc1' || parsedCodec === 'hev1') {
16033
16074
  return 'hvc1.1.6.L120.90';
16034
16075
  }
@@ -16043,8 +16084,7 @@ let now;
16043
16084
  try {
16044
16085
  now = self.performance.now.bind(self.performance);
16045
16086
  } catch (err) {
16046
- logger.debug('Unable to use Performance API on this environment');
16047
- now = optionalSelf == null ? void 0 : optionalSelf.Date.now;
16087
+ now = Date.now;
16048
16088
  }
16049
16089
  const muxConfig = [{
16050
16090
  demux: MP4Demuxer,
@@ -16066,8 +16106,9 @@ const muxConfig = [{
16066
16106
  });
16067
16107
  }
16068
16108
  class Transmuxer {
16069
- constructor(observer, typeSupported, config, vendor, id) {
16070
- this.async = false;
16109
+ constructor(observer, typeSupported, config, vendor, id, logger) {
16110
+ this.asyncResult = false;
16111
+ this.logger = void 0;
16071
16112
  this.observer = void 0;
16072
16113
  this.typeSupported = void 0;
16073
16114
  this.config = void 0;
@@ -16085,6 +16126,7 @@ class Transmuxer {
16085
16126
  this.config = config;
16086
16127
  this.vendor = vendor;
16087
16128
  this.id = id;
16129
+ this.logger = logger;
16088
16130
  }
16089
16131
  configure(transmuxConfig) {
16090
16132
  this.transmuxConfig = transmuxConfig;
@@ -16139,6 +16181,7 @@ class Transmuxer {
16139
16181
  }
16140
16182
  uintData = new Uint8Array(decryptedData);
16141
16183
  } else {
16184
+ this.asyncResult = true;
16142
16185
  this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(decryptedData => {
16143
16186
  // Calling push here is important; if flush() is called while this is still resolving, this ensures that
16144
16187
  // the decrypted data has been transmuxed
@@ -16153,7 +16196,7 @@ class Transmuxer {
16153
16196
  if (resetMuxers) {
16154
16197
  const error = this.configureTransmuxer(uintData);
16155
16198
  if (error) {
16156
- logger.warn(`[transmuxer] ${error.message}`);
16199
+ this.logger.warn(`[transmuxer] ${error.message}`);
16157
16200
  this.observer.emit(Events.ERROR, Events.ERROR, {
16158
16201
  type: ErrorTypes.MEDIA_ERROR,
16159
16202
  details: ErrorDetails.FRAG_PARSING_ERROR,
@@ -16175,6 +16218,7 @@ class Transmuxer {
16175
16218
  this.resetContiguity();
16176
16219
  }
16177
16220
  const result = this.transmux(uintData, keyData, timeOffset, accurateTimeOffset, chunkMeta);
16221
+ this.asyncResult = isPromise(result);
16178
16222
  const currentState = this.currentTransmuxState;
16179
16223
  currentState.contiguous = true;
16180
16224
  currentState.discontinuity = false;
@@ -16193,6 +16237,7 @@ class Transmuxer {
16193
16237
  decryptionPromise
16194
16238
  } = this;
16195
16239
  if (decryptionPromise) {
16240
+ this.asyncResult = true;
16196
16241
  // Upon resolution, the decryption promise calls push() and returns its TransmuxerResult up the stack. Therefore
16197
16242
  // only flushing is required for async decryption
16198
16243
  return decryptionPromise.then(() => {
@@ -16220,10 +16265,15 @@ class Transmuxer {
16220
16265
  if (!demuxer || !remuxer) {
16221
16266
  // If probing failed, then Hls.js has been given content its not able to handle
16222
16267
  stats.executeEnd = now();
16223
- return [emptyResult(chunkMeta)];
16268
+ const emptyResults = [emptyResult(chunkMeta)];
16269
+ if (this.asyncResult) {
16270
+ return Promise.resolve(emptyResults);
16271
+ }
16272
+ return emptyResults;
16224
16273
  }
16225
16274
  const demuxResultOrPromise = demuxer.flush(timeOffset);
16226
16275
  if (isPromise(demuxResultOrPromise)) {
16276
+ this.asyncResult = true;
16227
16277
  // Decrypt final SAMPLE-AES samples
16228
16278
  return demuxResultOrPromise.then(demuxResult => {
16229
16279
  this.flushRemux(transmuxResults, demuxResult, chunkMeta);
@@ -16231,6 +16281,9 @@ class Transmuxer {
16231
16281
  });
16232
16282
  }
16233
16283
  this.flushRemux(transmuxResults, demuxResultOrPromise, chunkMeta);
16284
+ if (this.asyncResult) {
16285
+ return Promise.resolve(transmuxResults);
16286
+ }
16234
16287
  return transmuxResults;
16235
16288
  }
16236
16289
  flushRemux(transmuxResults, demuxResult, chunkMeta) {
@@ -16244,7 +16297,7 @@ class Transmuxer {
16244
16297
  accurateTimeOffset,
16245
16298
  timeOffset
16246
16299
  } = this.currentTransmuxState;
16247
- logger.log(`[transmuxer.ts]: Flushed fragment ${chunkMeta.sn}${chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : ''} of level ${chunkMeta.level}`);
16300
+ this.logger.log(`[transmuxer.ts]: Flushed ${this.id} sn: ${chunkMeta.sn}${chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : ''} of ${this.id === PlaylistLevelType.MAIN ? 'level' : 'track'} ${chunkMeta.level}`);
16248
16301
  const remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, true, this.id);
16249
16302
  transmuxResults.push({
16250
16303
  remuxResult,
@@ -16337,7 +16390,7 @@ class Transmuxer {
16337
16390
  let mux;
16338
16391
  for (let i = 0, len = muxConfig.length; i < len; i++) {
16339
16392
  var _muxConfig$i$demux;
16340
- if ((_muxConfig$i$demux = muxConfig[i].demux) != null && _muxConfig$i$demux.probe(data)) {
16393
+ if ((_muxConfig$i$demux = muxConfig[i].demux) != null && _muxConfig$i$demux.probe(data, this.logger)) {
16341
16394
  mux = muxConfig[i];
16342
16395
  break;
16343
16396
  }
@@ -16351,10 +16404,10 @@ class Transmuxer {
16351
16404
  const Remuxer = mux.remux;
16352
16405
  const Demuxer = mux.demux;
16353
16406
  if (!remuxer || !(remuxer instanceof Remuxer)) {
16354
- this.remuxer = new Remuxer(observer, config, typeSupported, vendor);
16407
+ this.remuxer = new Remuxer(observer, config, typeSupported, this.logger);
16355
16408
  }
16356
16409
  if (!demuxer || !(demuxer instanceof Demuxer)) {
16357
- this.demuxer = new Demuxer(observer, config, typeSupported);
16410
+ this.demuxer = new Demuxer(observer, config, typeSupported, this.logger);
16358
16411
  this.probe = Demuxer.probe;
16359
16412
  }
16360
16413
  }
@@ -16759,31 +16812,96 @@ var eventemitter3 = {exports: {}};
16759
16812
  var eventemitter3Exports = eventemitter3.exports;
16760
16813
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
16761
16814
 
16815
+ let transmuxerInstanceCount = 0;
16762
16816
  class TransmuxerInterface {
16763
- constructor(hls, id, onTransmuxComplete, onFlush) {
16817
+ constructor(_hls, id, onTransmuxComplete, onFlush) {
16764
16818
  this.error = null;
16765
16819
  this.hls = void 0;
16766
16820
  this.id = void 0;
16821
+ this.instanceNo = transmuxerInstanceCount++;
16767
16822
  this.observer = void 0;
16768
16823
  this.frag = null;
16769
16824
  this.part = null;
16770
16825
  this.useWorker = void 0;
16771
16826
  this.workerContext = null;
16772
- this.onwmsg = void 0;
16773
16827
  this.transmuxer = null;
16774
16828
  this.onTransmuxComplete = void 0;
16775
16829
  this.onFlush = void 0;
16776
- const config = hls.config;
16777
- this.hls = hls;
16830
+ this.onWorkerMessage = event => {
16831
+ const data = event.data;
16832
+ const hls = this.hls;
16833
+ if (!hls || !(data != null && data.event) || data.instanceNo !== this.instanceNo) {
16834
+ return;
16835
+ }
16836
+ switch (data.event) {
16837
+ case 'init':
16838
+ {
16839
+ var _this$workerContext;
16840
+ const objectURL = (_this$workerContext = this.workerContext) == null ? void 0 : _this$workerContext.objectURL;
16841
+ if (objectURL) {
16842
+ // revoke the Object URL that was used to create transmuxer worker, so as not to leak it
16843
+ self.URL.revokeObjectURL(objectURL);
16844
+ }
16845
+ break;
16846
+ }
16847
+ case 'transmuxComplete':
16848
+ {
16849
+ this.handleTransmuxComplete(data.data);
16850
+ break;
16851
+ }
16852
+ case 'flush':
16853
+ {
16854
+ this.onFlush(data.data);
16855
+ break;
16856
+ }
16857
+
16858
+ // pass logs from the worker thread to the main logger
16859
+ case 'workerLog':
16860
+ {
16861
+ if (hls.logger[data.data.logType]) {
16862
+ hls.logger[data.data.logType](data.data.message);
16863
+ }
16864
+ break;
16865
+ }
16866
+ default:
16867
+ {
16868
+ data.data = data.data || {};
16869
+ data.data.frag = this.frag;
16870
+ data.data.part = this.part;
16871
+ data.data.id = this.id;
16872
+ hls.trigger(data.event, data.data);
16873
+ break;
16874
+ }
16875
+ }
16876
+ };
16877
+ this.onWorkerError = event => {
16878
+ if (!this.hls) {
16879
+ return;
16880
+ }
16881
+ const error = new Error(`${event.message} (${event.filename}:${event.lineno})`);
16882
+ this.hls.config.enableWorker = false;
16883
+ this.hls.logger.warn(`Error in "${this.id}" Web Worker, fallback to inline`);
16884
+ this.hls.trigger(Events.ERROR, {
16885
+ type: ErrorTypes.OTHER_ERROR,
16886
+ details: ErrorDetails.INTERNAL_EXCEPTION,
16887
+ fatal: false,
16888
+ event: 'demuxerWorker',
16889
+ error
16890
+ });
16891
+ };
16892
+ const config = _hls.config;
16893
+ this.hls = _hls;
16778
16894
  this.id = id;
16779
16895
  this.useWorker = !!config.enableWorker;
16780
16896
  this.onTransmuxComplete = onTransmuxComplete;
16781
16897
  this.onFlush = onFlush;
16782
16898
  const forwardMessage = (ev, data) => {
16783
16899
  data = data || {};
16784
- data.frag = this.frag;
16785
- data.id = this.id;
16900
+ data.frag = this.frag || undefined;
16786
16901
  if (ev === Events.ERROR) {
16902
+ data = data;
16903
+ data.parent = this.id;
16904
+ data.part = this.part;
16787
16905
  this.error = data.error;
16788
16906
  }
16789
16907
  this.hls.trigger(ev, data);
@@ -16795,6 +16913,7 @@ class TransmuxerInterface {
16795
16913
  this.observer.on(Events.ERROR, forwardMessage);
16796
16914
  const m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
16797
16915
  if (this.useWorker && typeof Worker !== 'undefined') {
16916
+ const logger = this.hls.logger;
16798
16917
  const canCreateWorker = config.workerPath || hasUMDWorker();
16799
16918
  if (canCreateWorker) {
16800
16919
  try {
@@ -16805,61 +16924,63 @@ class TransmuxerInterface {
16805
16924
  logger.log(`injecting Web Worker for "${id}"`);
16806
16925
  this.workerContext = injectWorker();
16807
16926
  }
16808
- this.onwmsg = event => this.onWorkerMessage(event);
16809
16927
  const {
16810
16928
  worker
16811
16929
  } = this.workerContext;
16812
- worker.addEventListener('message', this.onwmsg);
16813
- worker.onerror = event => {
16814
- const error = new Error(`${event.message} (${event.filename}:${event.lineno})`);
16815
- config.enableWorker = false;
16816
- logger.warn(`Error in "${id}" Web Worker, fallback to inline`);
16817
- this.hls.trigger(Events.ERROR, {
16818
- type: ErrorTypes.OTHER_ERROR,
16819
- details: ErrorDetails.INTERNAL_EXCEPTION,
16820
- fatal: false,
16821
- event: 'demuxerWorker',
16822
- error
16823
- });
16824
- };
16930
+ worker.addEventListener('message', this.onWorkerMessage);
16931
+ worker.addEventListener('error', this.onWorkerError);
16825
16932
  worker.postMessage({
16933
+ instanceNo: this.instanceNo,
16826
16934
  cmd: 'init',
16827
16935
  typeSupported: m2tsTypeSupported,
16828
- vendor: '',
16829
- id: id,
16936
+ id,
16830
16937
  config: JSON.stringify(config)
16831
16938
  });
16832
16939
  } catch (err) {
16833
16940
  logger.warn(`Error setting up "${id}" Web Worker, fallback to inline`, err);
16834
- this.resetWorker();
16941
+ this.terminateWorker();
16835
16942
  this.error = null;
16836
- this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id);
16943
+ this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id, _hls.logger);
16837
16944
  }
16838
16945
  return;
16839
16946
  }
16840
16947
  }
16841
- this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id);
16948
+ this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id, _hls.logger);
16949
+ }
16950
+ reset() {
16951
+ this.frag = null;
16952
+ this.part = null;
16953
+ if (this.workerContext) {
16954
+ const instanceNo = this.instanceNo;
16955
+ this.instanceNo = transmuxerInstanceCount++;
16956
+ const config = this.hls.config;
16957
+ const m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource);
16958
+ this.workerContext.worker.postMessage({
16959
+ instanceNo: this.instanceNo,
16960
+ cmd: 'reset',
16961
+ resetNo: instanceNo,
16962
+ typeSupported: m2tsTypeSupported,
16963
+ id: this.id,
16964
+ config: JSON.stringify(config)
16965
+ });
16966
+ }
16842
16967
  }
16843
- resetWorker() {
16968
+ terminateWorker() {
16844
16969
  if (this.workerContext) {
16845
16970
  const {
16846
- worker,
16847
- objectURL
16971
+ worker
16848
16972
  } = this.workerContext;
16849
- if (objectURL) {
16850
- // revoke the Object URL that was used to create transmuxer worker, so as not to leak it
16851
- self.URL.revokeObjectURL(objectURL);
16852
- }
16853
- worker.removeEventListener('message', this.onwmsg);
16854
- worker.onerror = null;
16855
- worker.terminate();
16856
16973
  this.workerContext = null;
16974
+ worker.removeEventListener('message', this.onWorkerMessage);
16975
+ worker.removeEventListener('error', this.onWorkerError);
16976
+ removeWorkerFromStore(this.hls.config.workerPath);
16857
16977
  }
16858
16978
  }
16859
16979
  destroy() {
16860
16980
  if (this.workerContext) {
16861
- this.resetWorker();
16862
- this.onwmsg = undefined;
16981
+ this.terminateWorker();
16982
+ // @ts-ignore
16983
+ this.onWorkerMessage = this.onWorkerError = null;
16863
16984
  } else {
16864
16985
  const transmuxer = this.transmuxer;
16865
16986
  if (transmuxer) {
@@ -16872,6 +16993,7 @@ class TransmuxerInterface {
16872
16993
  observer.removeAllListeners();
16873
16994
  }
16874
16995
  this.frag = null;
16996
+ this.part = null;
16875
16997
  // @ts-ignore
16876
16998
  this.observer = null;
16877
16999
  // @ts-ignore
@@ -16881,6 +17003,7 @@ class TransmuxerInterface {
16881
17003
  var _frag$initSegment, _lastFrag$initSegment;
16882
17004
  chunkMeta.transmuxing.start = self.performance.now();
16883
17005
  const {
17006
+ instanceNo,
16884
17007
  transmuxer
16885
17008
  } = this;
16886
17009
  const timeOffset = part ? part.start : frag.start;
@@ -16903,7 +17026,7 @@ class TransmuxerInterface {
16903
17026
  const initSegmentChange = !(lastFrag && ((_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.url) === ((_lastFrag$initSegment = lastFrag.initSegment) == null ? void 0 : _lastFrag$initSegment.url));
16904
17027
  const state = new TransmuxState(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange);
16905
17028
  if (!contiguous || discontinuity || initSegmentChange) {
16906
- logger.log(`[transmuxer-interface, ${frag.type}]: Starting new transmux session for sn: ${chunkMeta.sn} p: ${chunkMeta.part} level: ${chunkMeta.level} id: ${chunkMeta.id}
17029
+ this.hls.logger.log(`[transmuxer-interface, ${frag.type}]: Starting new transmux session for sn: ${chunkMeta.sn} p: ${chunkMeta.part} level: ${chunkMeta.level} id: ${chunkMeta.id}
16907
17030
  discontinuity: ${discontinuity}
16908
17031
  trackSwitch: ${trackSwitch}
16909
17032
  contiguous: ${contiguous}
@@ -16920,6 +17043,7 @@ class TransmuxerInterface {
16920
17043
  if (this.workerContext) {
16921
17044
  // post fragment payload as transferable objects for ArrayBuffer (no copy)
16922
17045
  this.workerContext.worker.postMessage({
17046
+ instanceNo,
16923
17047
  cmd: 'demux',
16924
17048
  data,
16925
17049
  decryptdata,
@@ -16929,14 +17053,12 @@ class TransmuxerInterface {
16929
17053
  } else if (transmuxer) {
16930
17054
  const transmuxResult = transmuxer.push(data, decryptdata, chunkMeta, state);
16931
17055
  if (isPromise(transmuxResult)) {
16932
- transmuxer.async = true;
16933
17056
  transmuxResult.then(data => {
16934
17057
  this.handleTransmuxComplete(data);
16935
17058
  }).catch(error => {
16936
17059
  this.transmuxerError(error, chunkMeta, 'transmuxer-interface push error');
16937
17060
  });
16938
17061
  } else {
16939
- transmuxer.async = false;
16940
17062
  this.handleTransmuxComplete(transmuxResult);
16941
17063
  }
16942
17064
  }
@@ -16944,20 +17066,18 @@ class TransmuxerInterface {
16944
17066
  flush(chunkMeta) {
16945
17067
  chunkMeta.transmuxing.start = self.performance.now();
16946
17068
  const {
17069
+ instanceNo,
16947
17070
  transmuxer
16948
17071
  } = this;
16949
17072
  if (this.workerContext) {
16950
17073
  this.workerContext.worker.postMessage({
17074
+ instanceNo,
16951
17075
  cmd: 'flush',
16952
17076
  chunkMeta
16953
17077
  });
16954
17078
  } else if (transmuxer) {
16955
- let transmuxResult = transmuxer.flush(chunkMeta);
16956
- const asyncFlush = isPromise(transmuxResult);
16957
- if (asyncFlush || transmuxer.async) {
16958
- if (!isPromise(transmuxResult)) {
16959
- transmuxResult = Promise.resolve(transmuxResult);
16960
- }
17079
+ const transmuxResult = transmuxer.flush(chunkMeta);
17080
+ if (isPromise(transmuxResult)) {
16961
17081
  transmuxResult.then(data => {
16962
17082
  this.handleFlushResult(data, chunkMeta);
16963
17083
  }).catch(error => {
@@ -16978,6 +17098,7 @@ class TransmuxerInterface {
16978
17098
  details: ErrorDetails.FRAG_PARSING_ERROR,
16979
17099
  chunkMeta,
16980
17100
  frag: this.frag || undefined,
17101
+ part: this.part || undefined,
16981
17102
  fatal: false,
16982
17103
  error,
16983
17104
  err: error,
@@ -16990,60 +17111,14 @@ class TransmuxerInterface {
16990
17111
  });
16991
17112
  this.onFlush(chunkMeta);
16992
17113
  }
16993
- onWorkerMessage(event) {
16994
- const data = event.data;
16995
- if (!(data != null && data.event)) {
16996
- logger.warn(`worker message received with no ${data ? 'event name' : 'data'}`);
16997
- return;
16998
- }
16999
- const hls = this.hls;
17000
- if (!this.hls) {
17001
- return;
17002
- }
17003
- switch (data.event) {
17004
- case 'init':
17005
- {
17006
- var _this$workerContext;
17007
- const objectURL = (_this$workerContext = this.workerContext) == null ? void 0 : _this$workerContext.objectURL;
17008
- if (objectURL) {
17009
- // revoke the Object URL that was used to create transmuxer worker, so as not to leak it
17010
- self.URL.revokeObjectURL(objectURL);
17011
- }
17012
- break;
17013
- }
17014
- case 'transmuxComplete':
17015
- {
17016
- this.handleTransmuxComplete(data.data);
17017
- break;
17018
- }
17019
- case 'flush':
17020
- {
17021
- this.onFlush(data.data);
17022
- break;
17023
- }
17024
-
17025
- // pass logs from the worker thread to the main logger
17026
- case 'workerLog':
17027
- if (logger[data.data.logType]) {
17028
- logger[data.data.logType](data.data.message);
17029
- }
17030
- break;
17031
- default:
17032
- {
17033
- data.data = data.data || {};
17034
- data.data.frag = this.frag;
17035
- data.data.id = this.id;
17036
- hls.trigger(data.event, data.data);
17037
- break;
17038
- }
17039
- }
17040
- }
17041
17114
  configureTransmuxer(config) {
17042
17115
  const {
17116
+ instanceNo,
17043
17117
  transmuxer
17044
17118
  } = this;
17045
17119
  if (this.workerContext) {
17046
17120
  this.workerContext.worker.postMessage({
17121
+ instanceNo,
17047
17122
  cmd: 'configure',
17048
17123
  config
17049
17124
  });
@@ -29268,7 +29343,7 @@ class Hls {
29268
29343
  * Get the video-dev/hls.js package version.
29269
29344
  */
29270
29345
  static get version() {
29271
- return "1.5.14-0.canary.10429";
29346
+ return version;
29272
29347
  }
29273
29348
 
29274
29349
  /**