hls.js 1.5.17 → 1.5.19

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.
@@ -411,7 +411,7 @@ function enableLogs(debugConfig, id) {
411
411
  // Some browsers don't allow to use bind on console object anyway
412
412
  // fallback to default if needed
413
413
  try {
414
- exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.5.17"}`);
414
+ exportedLogger.log(`Debug logs enabled for "${id}" in hls.js version ${"1.5.19"}`);
415
415
  } catch (e) {
416
416
  exportedLogger = fakeLogger;
417
417
  }
@@ -4730,15 +4730,16 @@ function mergeDetails(oldDetails, newDetails) {
4730
4730
  delete oldDetails.fragmentHint.endPTS;
4731
4731
  }
4732
4732
  // check if old/new playlists have fragments in common
4733
- // loop through overlapping SN and update startPTS , cc, and duration if any found
4734
- let ccOffset = 0;
4733
+ // loop through overlapping SN and update startPTS, cc, and duration if any found
4735
4734
  let PTSFrag;
4736
- mapFragmentIntersection(oldDetails, newDetails, (oldFrag, newFrag) => {
4737
- if (oldFrag.relurl) {
4738
- // Do not compare CC if the old fragment has no url. This is a level.fragmentHint used by LL-HLS parts.
4739
- // It maybe be off by 1 if it was created before any parts or discontinuity tags were appended to the end
4740
- // of the playlist.
4741
- ccOffset = oldFrag.cc - newFrag.cc;
4735
+ mapFragmentIntersection(oldDetails, newDetails, (oldFrag, newFrag, newFragIndex, newFragments) => {
4736
+ if (newDetails.skippedSegments) {
4737
+ if (newFrag.cc !== oldFrag.cc) {
4738
+ const ccOffset = oldFrag.cc - newFrag.cc;
4739
+ for (let i = newFragIndex; i < newFragments.length; i++) {
4740
+ newFragments[i].cc += ccOffset;
4741
+ }
4742
+ }
4742
4743
  }
4743
4744
  if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) {
4744
4745
  newFrag.start = newFrag.startPTS = oldFrag.startPTS;
@@ -4763,8 +4764,9 @@ function mergeDetails(oldDetails, newDetails) {
4763
4764
  currentInitSegment = oldFrag.initSegment;
4764
4765
  }
4765
4766
  });
4767
+ const newFragments = newDetails.fragments;
4766
4768
  if (currentInitSegment) {
4767
- const fragmentsToCheck = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments;
4769
+ const fragmentsToCheck = newDetails.fragmentHint ? newFragments.concat(newDetails.fragmentHint) : newFragments;
4768
4770
  fragmentsToCheck.forEach(frag => {
4769
4771
  var _currentInitSegment;
4770
4772
  if (frag && (!frag.initSegment || frag.initSegment.relurl === ((_currentInitSegment = currentInitSegment) == null ? void 0 : _currentInitSegment.relurl))) {
@@ -4773,27 +4775,20 @@ function mergeDetails(oldDetails, newDetails) {
4773
4775
  });
4774
4776
  }
4775
4777
  if (newDetails.skippedSegments) {
4776
- newDetails.deltaUpdateFailed = newDetails.fragments.some(frag => !frag);
4778
+ newDetails.deltaUpdateFailed = newFragments.some(frag => !frag);
4777
4779
  if (newDetails.deltaUpdateFailed) {
4778
4780
  logger.warn('[level-helper] Previous playlist missing segments skipped in delta playlist');
4779
4781
  for (let i = newDetails.skippedSegments; i--;) {
4780
- newDetails.fragments.shift();
4782
+ newFragments.shift();
4783
+ }
4784
+ newDetails.startSN = newFragments[0].sn;
4785
+ } else {
4786
+ if (newDetails.canSkipDateRanges) {
4787
+ newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails.dateRanges, newDetails.recentlyRemovedDateranges);
4781
4788
  }
4782
- newDetails.startSN = newDetails.fragments[0].sn;
4783
- newDetails.startCC = newDetails.fragments[0].cc;
4784
- } else if (newDetails.canSkipDateRanges) {
4785
- newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails.dateRanges, newDetails.recentlyRemovedDateranges);
4786
- }
4787
- }
4788
- const newFragments = newDetails.fragments;
4789
- if (ccOffset) {
4790
- logger.warn('discontinuity sliding from playlist, take drift into account');
4791
- for (let i = 0; i < newFragments.length; i++) {
4792
- newFragments[i].cc += ccOffset;
4793
4789
  }
4794
- }
4795
- if (newDetails.skippedSegments) {
4796
4790
  newDetails.startCC = newDetails.fragments[0].cc;
4791
+ newDetails.endCC = newFragments[newFragments.length - 1].cc;
4797
4792
  }
4798
4793
 
4799
4794
  // Merge parts
@@ -4877,7 +4872,7 @@ function mapFragmentIntersection(oldDetails, newDetails, intersectionFn) {
4877
4872
  newFrag = newDetails.fragments[i] = oldFrag;
4878
4873
  }
4879
4874
  if (oldFrag && newFrag) {
4880
- intersectionFn(oldFrag, newFrag);
4875
+ intersectionFn(oldFrag, newFrag, i, newFrags);
4881
4876
  }
4882
4877
  }
4883
4878
  }
@@ -7220,6 +7215,7 @@ class BufferController {
7220
7215
  this.resetBuffer(type);
7221
7216
  });
7222
7217
  this._initSourceBuffer();
7218
+ this.hls.resumeBuffering();
7223
7219
  }
7224
7220
  resetBuffer(type) {
7225
7221
  const sb = this.sourceBuffer[type];
@@ -8857,47 +8853,51 @@ class XhrLoader {
8857
8853
  xhr.onprogress = null;
8858
8854
  const status = xhr.status;
8859
8855
  // http status between 200 to 299 are all successful
8860
- const useResponse = xhr.responseType !== 'text';
8861
- if (status >= 200 && status < 300 && (useResponse && xhr.response || xhr.responseText !== null)) {
8862
- stats.loading.end = Math.max(self.performance.now(), stats.loading.first);
8863
- const data = useResponse ? xhr.response : xhr.responseText;
8864
- const len = xhr.responseType === 'arraybuffer' ? data.byteLength : data.length;
8865
- stats.loaded = stats.total = len;
8866
- stats.bwEstimate = stats.total * 8000 / (stats.loading.end - stats.loading.first);
8867
- if (!this.callbacks) {
8868
- return;
8869
- }
8870
- const onProgress = this.callbacks.onProgress;
8871
- if (onProgress) {
8872
- onProgress(stats, context, data, xhr);
8873
- }
8874
- if (!this.callbacks) {
8856
+ const useResponseText = xhr.responseType === 'text' ? xhr.responseText : null;
8857
+ if (status >= 200 && status < 300) {
8858
+ const data = useResponseText != null ? useResponseText : xhr.response;
8859
+ if (data != null) {
8860
+ stats.loading.end = Math.max(self.performance.now(), stats.loading.first);
8861
+ const len = xhr.responseType === 'arraybuffer' ? data.byteLength : data.length;
8862
+ stats.loaded = stats.total = len;
8863
+ stats.bwEstimate = stats.total * 8000 / (stats.loading.end - stats.loading.first);
8864
+ if (!this.callbacks) {
8865
+ return;
8866
+ }
8867
+ const onProgress = this.callbacks.onProgress;
8868
+ if (onProgress) {
8869
+ onProgress(stats, context, data, xhr);
8870
+ }
8871
+ if (!this.callbacks) {
8872
+ return;
8873
+ }
8874
+ const _response = {
8875
+ url: xhr.responseURL,
8876
+ data: data,
8877
+ code: status
8878
+ };
8879
+ this.callbacks.onSuccess(_response, stats, context, xhr);
8875
8880
  return;
8876
8881
  }
8877
- const response = {
8878
- url: xhr.responseURL,
8879
- data: data,
8880
- code: status
8881
- };
8882
- this.callbacks.onSuccess(response, stats, context, xhr);
8882
+ }
8883
+
8884
+ // Handle bad status or nullish response
8885
+ const retryConfig = config.loadPolicy.errorRetry;
8886
+ const retryCount = stats.retry;
8887
+ // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
8888
+ const response = {
8889
+ url: context.url,
8890
+ data: undefined,
8891
+ code: status
8892
+ };
8893
+ if (shouldRetry(retryConfig, retryCount, false, response)) {
8894
+ this.retry(retryConfig);
8883
8895
  } else {
8884
- const retryConfig = config.loadPolicy.errorRetry;
8885
- const retryCount = stats.retry;
8886
- // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
8887
- const response = {
8888
- url: context.url,
8889
- data: undefined,
8890
- code: status
8891
- };
8892
- if (shouldRetry(retryConfig, retryCount, false, response)) {
8893
- this.retry(retryConfig);
8894
- } else {
8895
- logger.error(`${status} while loading ${context.url}`);
8896
- this.callbacks.onError({
8897
- code: status,
8898
- text: xhr.statusText
8899
- }, context, xhr, stats);
8900
- }
8896
+ logger.error(`${status} while loading ${context.url}`);
8897
+ this.callbacks.onError({
8898
+ code: status,
8899
+ text: xhr.statusText
8900
+ }, context, xhr, stats);
8901
8901
  }
8902
8902
  }
8903
8903
  }
@@ -10939,7 +10939,7 @@ class KeyLoader {
10939
10939
  }
10940
10940
  }
10941
10941
  load(frag) {
10942
- if (!frag.decryptdata && frag.encrypted && this.emeController) {
10942
+ if (!frag.decryptdata && frag.encrypted && this.emeController && this.config.emeEnabled) {
10943
10943
  // Multiple keys, but none selected, resolve in eme-controller
10944
10944
  return this.emeController.selectKeySystemFormat(frag).then(keySystemFormat => {
10945
10945
  return this.loadInternal(frag, keySystemFormat);
@@ -11899,6 +11899,7 @@ class BaseStreamController extends TaskLoop {
11899
11899
  this.startFragRequested = false;
11900
11900
  this.decrypter = void 0;
11901
11901
  this.initPTS = [];
11902
+ this.buffering = true;
11902
11903
  this.onvseeking = null;
11903
11904
  this.onvended = null;
11904
11905
  this.logPrefix = '';
@@ -11938,6 +11939,12 @@ class BaseStreamController extends TaskLoop {
11938
11939
  this.clearNextTick();
11939
11940
  this.state = State.STOPPED;
11940
11941
  }
11942
+ pauseBuffering() {
11943
+ this.buffering = false;
11944
+ }
11945
+ resumeBuffering() {
11946
+ this.buffering = true;
11947
+ }
11941
11948
  _streamEnded(bufferInfo, levelDetails) {
11942
11949
  // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached,
11943
11950
  // of nothing loading/loaded return false
@@ -16187,19 +16194,23 @@ class MP4Remuxer {
16187
16194
  this.videoTrackConfig = undefined;
16188
16195
  }
16189
16196
  getVideoStartPts(videoSamples) {
16197
+ // Get the minimum PTS value relative to the first sample's PTS, normalized for 33-bit wrapping
16190
16198
  let rolloverDetected = false;
16199
+ const firstPts = videoSamples[0].pts;
16191
16200
  const startPTS = videoSamples.reduce((minPTS, sample) => {
16192
- const delta = sample.pts - minPTS;
16201
+ let pts = sample.pts;
16202
+ let delta = pts - minPTS;
16193
16203
  if (delta < -4294967296) {
16194
16204
  // 2^32, see PTSNormalize for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation
16195
16205
  rolloverDetected = true;
16196
- return normalizePts(minPTS, sample.pts);
16197
- } else if (delta > 0) {
16206
+ pts = normalizePts(pts, firstPts);
16207
+ delta = pts - minPTS;
16208
+ }
16209
+ if (delta > 0) {
16198
16210
  return minPTS;
16199
- } else {
16200
- return sample.pts;
16201
16211
  }
16202
- }, videoSamples[0].pts);
16212
+ return pts;
16213
+ }, firstPts);
16203
16214
  if (rolloverDetected) {
16204
16215
  logger.debug('PTS rollover detected');
16205
16216
  }
@@ -18745,7 +18756,7 @@ class StreamController extends BaseStreamController {
18745
18756
  if (this.altAudio && this.audioOnly) {
18746
18757
  return;
18747
18758
  }
18748
- const level = hls.nextLoadLevel;
18759
+ const level = this.buffering ? hls.nextLoadLevel : hls.loadLevel;
18749
18760
  if (!(levels != null && levels[level])) {
18750
18761
  return;
18751
18762
  }
@@ -18767,6 +18778,9 @@ class StreamController extends BaseStreamController {
18767
18778
  this.state = State.ENDED;
18768
18779
  return;
18769
18780
  }
18781
+ if (!this.buffering) {
18782
+ return;
18783
+ }
18770
18784
 
18771
18785
  // set next load level : this will trigger a playlist load if needed
18772
18786
  if (hls.loadLevel !== level && hls.manualLevel === -1) {
@@ -19729,7 +19743,7 @@ class Hls {
19729
19743
  * Get the video-dev/hls.js package version.
19730
19744
  */
19731
19745
  static get version() {
19732
- return "1.5.17";
19746
+ return "1.5.19";
19733
19747
  }
19734
19748
 
19735
19749
  /**
@@ -20010,9 +20024,13 @@ class Hls {
20010
20024
  startLoad(startPosition = -1) {
20011
20025
  logger.log(`startLoad(${startPosition})`);
20012
20026
  this.started = true;
20013
- this.networkControllers.forEach(controller => {
20014
- controller.startLoad(startPosition);
20015
- });
20027
+ this.resumeBuffering();
20028
+ for (let i = 0; i < this.networkControllers.length; i++) {
20029
+ this.networkControllers[i].startLoad(startPosition);
20030
+ if (!this.started || !this.networkControllers) {
20031
+ break;
20032
+ }
20033
+ }
20016
20034
  }
20017
20035
 
20018
20036
  /**
@@ -20021,32 +20039,35 @@ class Hls {
20021
20039
  stopLoad() {
20022
20040
  logger.log('stopLoad');
20023
20041
  this.started = false;
20024
- this.networkControllers.forEach(controller => {
20025
- controller.stopLoad();
20026
- });
20042
+ for (let i = 0; i < this.networkControllers.length; i++) {
20043
+ this.networkControllers[i].stopLoad();
20044
+ if (this.started || !this.networkControllers) {
20045
+ break;
20046
+ }
20047
+ }
20027
20048
  }
20028
20049
 
20029
20050
  /**
20030
- * Resumes stream controller segment loading if previously started.
20051
+ * Resumes stream controller segment loading after `pauseBuffering` has been called.
20031
20052
  */
20032
20053
  resumeBuffering() {
20033
- if (this.started) {
20034
- this.networkControllers.forEach(controller => {
20035
- if ('fragmentLoader' in controller) {
20036
- controller.startLoad(-1);
20037
- }
20038
- });
20039
- }
20054
+ logger.log(`resume buffering`);
20055
+ this.networkControllers.forEach(controller => {
20056
+ if (controller.resumeBuffering) {
20057
+ controller.resumeBuffering();
20058
+ }
20059
+ });
20040
20060
  }
20041
20061
 
20042
20062
  /**
20043
- * Stops stream controller segment loading without changing 'started' state like stopLoad().
20063
+ * Prevents stream controller from loading new segments until `resumeBuffering` is called.
20044
20064
  * This allows for media buffering to be paused without interupting playlist loading.
20045
20065
  */
20046
20066
  pauseBuffering() {
20067
+ logger.log(`pause buffering`);
20047
20068
  this.networkControllers.forEach(controller => {
20048
- if ('fragmentLoader' in controller) {
20049
- controller.stopLoad();
20069
+ if (controller.pauseBuffering) {
20070
+ controller.pauseBuffering();
20050
20071
  }
20051
20072
  });
20052
20073
  }