hls.js 1.6.0-beta.2.0.canary.10882 → 1.6.0-beta.2.0.canary.10883

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.d.mts CHANGED
@@ -571,6 +571,19 @@ export declare type BufferInfo = {
571
571
  start: number;
572
572
  end: number;
573
573
  nextStart?: number;
574
+ buffered?: BufferTimeRange[];
575
+ };
576
+
577
+ /**
578
+ * Provides methods dealing with buffer length retrieval for example.
579
+ *
580
+ * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property.
581
+ *
582
+ * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered
583
+ */
584
+ export declare type BufferTimeRange = {
585
+ start: number;
586
+ end: number;
574
587
  };
575
588
 
576
589
  export declare class CapLevelController implements ComponentAPI {
@@ -1017,6 +1030,7 @@ export declare interface ErrorData {
1017
1030
  fatal: boolean;
1018
1031
  errorAction?: IErrorAction;
1019
1032
  buffer?: number;
1033
+ bufferInfo?: BufferInfo;
1020
1034
  bytes?: number;
1021
1035
  chunkMeta?: ChunkMetadata;
1022
1036
  context?: PlaylistLoaderContext;
@@ -1027,6 +1041,9 @@ export declare interface ErrorData {
1027
1041
  levelRetry?: boolean;
1028
1042
  loader?: Loader<LoaderContext>;
1029
1043
  networkDetails?: any;
1044
+ stalled?: {
1045
+ start: number;
1046
+ };
1030
1047
  stats?: LoaderStats;
1031
1048
  mimeType?: string;
1032
1049
  reason?: string;
@@ -1110,6 +1127,7 @@ export declare enum Events {
1110
1127
  MEDIA_DETACHING = "hlsMediaDetaching",
1111
1128
  MEDIA_DETACHED = "hlsMediaDetached",
1112
1129
  MEDIA_ENDED = "hlsMediaEnded",
1130
+ STALL_RESOLVED = "hlsStallResolved",
1113
1131
  BUFFER_RESET = "hlsBufferReset",
1114
1132
  BUFFER_CODECS = "hlsBufferCodecs",
1115
1133
  BUFFER_CREATED = "hlsBufferCreated",
@@ -1948,6 +1966,7 @@ export declare type HlsConfig = {
1948
1966
  progressive: boolean;
1949
1967
  lowLatencyMode: boolean;
1950
1968
  primarySessionId?: string;
1969
+ detectStallWithCurrentTimeMs: number;
1951
1970
  } & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig;
1952
1971
 
1953
1972
  export declare interface HlsEventEmitter {
@@ -1969,6 +1988,7 @@ export declare interface HlsListeners {
1969
1988
  [Events.MEDIA_DETACHING]: (event: Events.MEDIA_DETACHING, data: MediaDetachingData) => void;
1970
1989
  [Events.MEDIA_DETACHED]: (event: Events.MEDIA_DETACHED, data: MediaDetachedData) => void;
1971
1990
  [Events.MEDIA_ENDED]: (event: Events.MEDIA_ENDED, data: MediaEndedData) => void;
1991
+ [Events.STALL_RESOLVED]: (event: Events.STALL_RESOLVED, data: {}) => void;
1972
1992
  [Events.BUFFER_RESET]: (event: Events.BUFFER_RESET) => void;
1973
1993
  [Events.BUFFER_CODECS]: (event: Events.BUFFER_CODECS, data: BufferCodecsData) => void;
1974
1994
  [Events.BUFFER_CREATED]: (event: Events.BUFFER_CREATED, data: BufferCreatedData) => void;
@@ -3340,6 +3360,7 @@ export declare class StreamController extends BaseStreamController implements Ne
3340
3360
  protected flushMainBuffer(startOffset: number, endOffset: number): void;
3341
3361
  protected onMediaAttached(event: Events.MEDIA_ATTACHED, data: MediaAttachedData): void;
3342
3362
  protected onMediaDetaching(event: Events.MEDIA_DETACHING, data: MediaDetachingData): void;
3363
+ private onMediaWaiting;
3343
3364
  private onMediaPlaying;
3344
3365
  private onMediaSeeked;
3345
3366
  protected triggerEnded(): void;
package/dist/hls.d.ts CHANGED
@@ -571,6 +571,19 @@ export declare type BufferInfo = {
571
571
  start: number;
572
572
  end: number;
573
573
  nextStart?: number;
574
+ buffered?: BufferTimeRange[];
575
+ };
576
+
577
+ /**
578
+ * Provides methods dealing with buffer length retrieval for example.
579
+ *
580
+ * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property.
581
+ *
582
+ * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered
583
+ */
584
+ export declare type BufferTimeRange = {
585
+ start: number;
586
+ end: number;
574
587
  };
575
588
 
576
589
  export declare class CapLevelController implements ComponentAPI {
@@ -1017,6 +1030,7 @@ export declare interface ErrorData {
1017
1030
  fatal: boolean;
1018
1031
  errorAction?: IErrorAction;
1019
1032
  buffer?: number;
1033
+ bufferInfo?: BufferInfo;
1020
1034
  bytes?: number;
1021
1035
  chunkMeta?: ChunkMetadata;
1022
1036
  context?: PlaylistLoaderContext;
@@ -1027,6 +1041,9 @@ export declare interface ErrorData {
1027
1041
  levelRetry?: boolean;
1028
1042
  loader?: Loader<LoaderContext>;
1029
1043
  networkDetails?: any;
1044
+ stalled?: {
1045
+ start: number;
1046
+ };
1030
1047
  stats?: LoaderStats;
1031
1048
  mimeType?: string;
1032
1049
  reason?: string;
@@ -1110,6 +1127,7 @@ export declare enum Events {
1110
1127
  MEDIA_DETACHING = "hlsMediaDetaching",
1111
1128
  MEDIA_DETACHED = "hlsMediaDetached",
1112
1129
  MEDIA_ENDED = "hlsMediaEnded",
1130
+ STALL_RESOLVED = "hlsStallResolved",
1113
1131
  BUFFER_RESET = "hlsBufferReset",
1114
1132
  BUFFER_CODECS = "hlsBufferCodecs",
1115
1133
  BUFFER_CREATED = "hlsBufferCreated",
@@ -1948,6 +1966,7 @@ export declare type HlsConfig = {
1948
1966
  progressive: boolean;
1949
1967
  lowLatencyMode: boolean;
1950
1968
  primarySessionId?: string;
1969
+ detectStallWithCurrentTimeMs: number;
1951
1970
  } & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig;
1952
1971
 
1953
1972
  export declare interface HlsEventEmitter {
@@ -1969,6 +1988,7 @@ export declare interface HlsListeners {
1969
1988
  [Events.MEDIA_DETACHING]: (event: Events.MEDIA_DETACHING, data: MediaDetachingData) => void;
1970
1989
  [Events.MEDIA_DETACHED]: (event: Events.MEDIA_DETACHED, data: MediaDetachedData) => void;
1971
1990
  [Events.MEDIA_ENDED]: (event: Events.MEDIA_ENDED, data: MediaEndedData) => void;
1991
+ [Events.STALL_RESOLVED]: (event: Events.STALL_RESOLVED, data: {}) => void;
1972
1992
  [Events.BUFFER_RESET]: (event: Events.BUFFER_RESET) => void;
1973
1993
  [Events.BUFFER_CODECS]: (event: Events.BUFFER_CODECS, data: BufferCodecsData) => void;
1974
1994
  [Events.BUFFER_CREATED]: (event: Events.BUFFER_CREATED, data: BufferCreatedData) => void;
@@ -3340,6 +3360,7 @@ export declare class StreamController extends BaseStreamController implements Ne
3340
3360
  protected flushMainBuffer(startOffset: number, endOffset: number): void;
3341
3361
  protected onMediaAttached(event: Events.MEDIA_ATTACHED, data: MediaAttachedData): void;
3342
3362
  protected onMediaDetaching(event: Events.MEDIA_DETACHING, data: MediaDetachingData): void;
3363
+ private onMediaWaiting;
3343
3364
  private onMediaPlaying;
3344
3365
  private onMediaSeeked;
3345
3366
  protected triggerEnded(): void;
package/dist/hls.js CHANGED
@@ -785,6 +785,7 @@
785
785
  Events["MEDIA_DETACHING"] = "hlsMediaDetaching";
786
786
  Events["MEDIA_DETACHED"] = "hlsMediaDetached";
787
787
  Events["MEDIA_ENDED"] = "hlsMediaEnded";
788
+ Events["STALL_RESOLVED"] = "hlsStallResolved";
788
789
  Events["BUFFER_RESET"] = "hlsBufferReset";
789
790
  Events["BUFFER_CODECS"] = "hlsBufferCodecs";
790
791
  Events["BUFFER_CREATED"] = "hlsBufferCreated";
@@ -1058,7 +1059,7 @@
1058
1059
  // Some browsers don't allow to use bind on console object anyway
1059
1060
  // fallback to default if needed
1060
1061
  try {
1061
- newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.10882");
1062
+ newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + "1.6.0-beta.2.0.canary.10883");
1062
1063
  } catch (e) {
1063
1064
  /* log fn threw an exception. All logger methods are no-ops. */
1064
1065
  return createLogger();
@@ -6521,8 +6522,7 @@
6521
6522
  return {
6522
6523
  len: 0,
6523
6524
  start: pos,
6524
- end: pos,
6525
- nextStart: undefined
6525
+ end: pos
6526
6526
  };
6527
6527
  };
6528
6528
  BufferHelper.bufferedInfo = function bufferedInfo(buffered, pos, maxHoleDuration) {
@@ -6587,7 +6587,8 @@
6587
6587
  len: bufferLen,
6588
6588
  start: bufferStart || 0,
6589
6589
  end: bufferEnd || 0,
6590
- nextStart: bufferStartNext
6590
+ nextStart: bufferStartNext,
6591
+ buffered: buffered
6591
6592
  };
6592
6593
  }
6593
6594
 
@@ -16320,7 +16321,7 @@
16320
16321
  return !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment;
16321
16322
  }
16322
16323
 
16323
- var version = "1.6.0-beta.2.0.canary.10882";
16324
+ var version = "1.6.0-beta.2.0.canary.10883";
16324
16325
 
16325
16326
  // ensure the worker ends up in the bundle
16326
16327
  // If the worker should not be included this gets aliased to empty.js
@@ -30362,6 +30363,7 @@
30362
30363
  progressive: false,
30363
30364
  lowLatencyMode: true,
30364
30365
  cmcd: undefined,
30366
+ detectStallWithCurrentTimeMs: 1250,
30365
30367
  enableDateRangeMetadataCues: true,
30366
30368
  enableEmsgMetadataCues: true,
30367
30369
  enableEmsgKLVMetadata: false,
@@ -31787,25 +31789,23 @@
31787
31789
  });
31788
31790
  }
31789
31791
 
31790
- var STALL_MINIMUM_DURATION_MS = 250;
31791
31792
  var MAX_START_GAP_JUMP = 2.0;
31792
31793
  var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
31793
31794
  var SKIP_BUFFER_RANGE_START = 0.05;
31794
31795
  var GapController = /*#__PURE__*/function (_Logger) {
31795
- function GapController(config, media, fragmentTracker, hls) {
31796
+ function GapController(media, fragmentTracker, hls) {
31796
31797
  var _this;
31797
31798
  _this = _Logger.call(this, 'gap-controller', hls.logger) || this;
31798
- _this.config = undefined;
31799
31799
  _this.media = null;
31800
- _this.fragmentTracker = undefined;
31801
- _this.hls = undefined;
31800
+ _this.fragmentTracker = null;
31801
+ _this.hls = null;
31802
31802
  _this.nudgeRetry = 0;
31803
31803
  _this.stallReported = false;
31804
31804
  _this.stalled = null;
31805
31805
  _this.moved = false;
31806
31806
  _this.seeking = false;
31807
31807
  _this.ended = 0;
31808
- _this.config = config;
31808
+ _this.waiting = 0;
31809
31809
  _this.media = media;
31810
31810
  _this.fragmentTracker = fragmentTracker;
31811
31811
  _this.hls = hls;
@@ -31814,9 +31814,7 @@
31814
31814
  _inheritsLoose(GapController, _Logger);
31815
31815
  var _proto = GapController.prototype;
31816
31816
  _proto.destroy = function destroy() {
31817
- this.media = null;
31818
- // @ts-ignore
31819
- this.hls = this.fragmentTracker = null;
31817
+ this.media = this.hls = this.fragmentTracker = null;
31820
31818
  }
31821
31819
 
31822
31820
  /**
@@ -31826,10 +31824,10 @@
31826
31824
  * @param lastCurrentTime - Previously read playhead position
31827
31825
  */;
31828
31826
  _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) {
31829
- var config = this.config,
31830
- media = this.media,
31827
+ var _this$hls;
31828
+ var media = this.media,
31831
31829
  stalled = this.stalled;
31832
- if (media === null) {
31830
+ if (!media) {
31833
31831
  return;
31834
31832
  }
31835
31833
  var currentTime = media.currentTime,
@@ -31847,43 +31845,45 @@
31847
31845
  if (!seeking) {
31848
31846
  this.nudgeRetry = 0;
31849
31847
  }
31850
- if (stalled !== null) {
31851
- // The playhead is now moving, but was previously stalled
31852
- if (this.stallReported) {
31853
- var _stalledDuration = self.performance.now() - stalled;
31854
- this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms");
31855
- this.stallReported = false;
31856
- }
31857
- this.stalled = null;
31848
+ if (this.waiting === 0) {
31849
+ this.stallResolved(currentTime);
31858
31850
  }
31859
31851
  return;
31860
31852
  }
31861
31853
 
31862
31854
  // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek
31863
31855
  if (beginSeek || seeked) {
31864
- this.stalled = null;
31856
+ if (seeked) {
31857
+ this.stallResolved(currentTime);
31858
+ }
31865
31859
  return;
31866
31860
  }
31867
31861
 
31868
31862
  // The playhead should not be moving
31869
- if (media.paused && !seeking || media.ended || media.playbackRate === 0 || !BufferHelper.getBuffered(media).length) {
31863
+ if (media.paused && !seeking || media.ended || media.playbackRate === 0) {
31864
+ this.nudgeRetry = 0;
31865
+ this.stallResolved(currentTime);
31870
31866
  // Fire MEDIA_ENDED to workaround event not being dispatched by browser
31871
- if (!this.ended && media.ended) {
31867
+ if (!this.ended && media.ended && this.hls) {
31872
31868
  this.ended = currentTime || 1;
31873
31869
  this.hls.trigger(Events.MEDIA_ENDED, {
31874
31870
  stalled: false
31875
31871
  });
31876
31872
  }
31873
+ return;
31874
+ }
31875
+ if (!BufferHelper.getBuffered(media).length) {
31877
31876
  this.nudgeRetry = 0;
31878
31877
  return;
31879
31878
  }
31880
31879
  var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
31881
31880
  var nextStart = bufferInfo.nextStart || 0;
31882
- if (seeking) {
31881
+ var fragmentTracker = this.fragmentTracker;
31882
+ if (seeking && fragmentTracker) {
31883
31883
  // Waiting for seeking in a buffered range to complete
31884
31884
  var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
31885
31885
  // Next buffered range is too far ahead to jump to while still seeking
31886
- var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !this.fragmentTracker.getPartialFragment(currentTime);
31886
+ var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
31887
31887
  if (hasEnoughBuffer || noBufferGap) {
31888
31888
  return;
31889
31889
  }
@@ -31893,7 +31893,7 @@
31893
31893
 
31894
31894
  // Skip start gaps if we haven't played, but the last poll detected the start of a stall
31895
31895
  // The addition poll gives the browser a chance to jump the gap for us
31896
- if (!this.moved && this.stalled !== null) {
31896
+ if (!this.moved && this.stalled !== null && fragmentTracker) {
31897
31897
  // There is no playable buffer (seeked, waiting for buffer)
31898
31898
  var isBuffered = bufferInfo.len > 0;
31899
31899
  if (!isBuffered && !nextStart) {
@@ -31907,7 +31907,7 @@
31907
31907
  // that begins over 1 target duration after the video start position.
31908
31908
  var isLive = !!(levelDetails != null && levelDetails.live);
31909
31909
  var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
31910
- var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime);
31910
+ var partialOrGap = fragmentTracker.getPartialFragment(currentTime);
31911
31911
  if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
31912
31912
  if (!media.paused) {
31913
31913
  this._trySkipBufferHole(partialOrGap);
@@ -31917,16 +31917,27 @@
31917
31917
  }
31918
31918
 
31919
31919
  // Start tracking stall time
31920
+ var config = (_this$hls = this.hls) == null ? undefined : _this$hls.config;
31921
+ if (!config) {
31922
+ return;
31923
+ }
31924
+ var detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
31920
31925
  var tnow = self.performance.now();
31926
+ var tWaiting = this.waiting;
31921
31927
  if (stalled === null) {
31922
- this.stalled = tnow;
31928
+ // Use time of recent "waiting" event
31929
+ if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
31930
+ this.stalled = tWaiting;
31931
+ } else {
31932
+ this.stalled = tnow;
31933
+ }
31923
31934
  return;
31924
31935
  }
31925
31936
  var stalledDuration = tnow - stalled;
31926
- if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) {
31937
+ if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
31927
31938
  // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream
31928
31939
  if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? undefined : levelDetails.edge) || 0)) < 1) {
31929
- if (stalledDuration < 1000 || this.ended) {
31940
+ if (this.ended) {
31930
31941
  return;
31931
31942
  }
31932
31943
  this.ended = currentTime || 1;
@@ -31937,12 +31948,26 @@
31937
31948
  }
31938
31949
  // Report stalling after trying to fix
31939
31950
  this._reportStall(bufferInfo);
31940
- if (!this.media) {
31951
+ if (!this.media || !this.hls) {
31941
31952
  return;
31942
31953
  }
31943
31954
  }
31944
31955
  var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
31945
31956
  this._tryFixBufferStall(bufferedWithHoles, stalledDuration);
31957
+ };
31958
+ _proto.stallResolved = function stallResolved(currentTime) {
31959
+ var stalled = this.stalled;
31960
+ if (stalled && this.hls) {
31961
+ this.stalled = null;
31962
+ // The playhead is now moving, but was previously stalled
31963
+ if (this.stallReported) {
31964
+ var stalledDuration = self.performance.now() - stalled;
31965
+ this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(stalledDuration) + "ms");
31966
+ this.stallReported = false;
31967
+ this.waiting = 0;
31968
+ this.hls.trigger(Events.STALL_RESOLVED, {});
31969
+ }
31970
+ }
31946
31971
  }
31947
31972
 
31948
31973
  /**
@@ -31952,10 +31977,11 @@
31952
31977
  * @private
31953
31978
  */;
31954
31979
  _proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) {
31955
- var config = this.config,
31956
- fragmentTracker = this.fragmentTracker,
31980
+ var _this$hls2;
31981
+ var fragmentTracker = this.fragmentTracker,
31957
31982
  media = this.media;
31958
- if (media === null) {
31983
+ var config = (_this$hls2 = this.hls) == null ? undefined : _this$hls2.config;
31984
+ if (!media || !fragmentTracker || !config) {
31959
31985
  return;
31960
31986
  }
31961
31987
  var currentTime = media.currentTime;
@@ -31975,13 +32001,12 @@
31975
32001
  // we may just have to "nudge" the playlist as the browser decoding/rendering engine
31976
32002
  // needs to cross some sort of threshold covering all source-buffers content
31977
32003
  // to start playing properly.
31978
- if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
32004
+ var bufferedRanges = bufferInfo.buffered;
32005
+ if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) {
31979
32006
  this.warn('Trying to nudge playhead over buffer-hole');
31980
32007
  // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
31981
32008
  // We only try to jump the hole if it's under the configured size
31982
- // Reset stalled so to rearm watchdog timer
31983
- this.stalled = null;
31984
- this._tryNudgeBuffer();
32009
+ this._tryNudgeBuffer(bufferInfo);
31985
32010
  }
31986
32011
  }
31987
32012
 
@@ -31993,8 +32018,9 @@
31993
32018
  _proto._reportStall = function _reportStall(bufferInfo) {
31994
32019
  var hls = this.hls,
31995
32020
  media = this.media,
31996
- stallReported = this.stallReported;
31997
- if (!stallReported && media) {
32021
+ stallReported = this.stallReported,
32022
+ stalled = this.stalled;
32023
+ if (!stallReported && stalled !== null && media && hls) {
31998
32024
  // Report stalled error once
31999
32025
  this.stallReported = true;
32000
32026
  var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")");
@@ -32004,7 +32030,11 @@
32004
32030
  details: ErrorDetails.BUFFER_STALLED_ERROR,
32005
32031
  fatal: false,
32006
32032
  error: error,
32007
- buffer: bufferInfo.len
32033
+ buffer: bufferInfo.len,
32034
+ bufferInfo: bufferInfo,
32035
+ stalled: {
32036
+ start: stalled
32037
+ }
32008
32038
  });
32009
32039
  }
32010
32040
  }
@@ -32015,10 +32045,11 @@
32015
32045
  * @private
32016
32046
  */;
32017
32047
  _proto._trySkipBufferHole = function _trySkipBufferHole(partial) {
32018
- var config = this.config,
32019
- hls = this.hls,
32048
+ var _this$hls3;
32049
+ var fragmentTracker = this.fragmentTracker,
32020
32050
  media = this.media;
32021
- if (media === null) {
32051
+ var config = (_this$hls3 = this.hls) == null ? undefined : _this$hls3.config;
32052
+ if (!media || !fragmentTracker || !config) {
32022
32053
  return 0;
32023
32054
  }
32024
32055
 
@@ -32033,7 +32064,6 @@
32033
32064
  if (gapLength > 0 && (bufferStarved || waiting)) {
32034
32065
  // Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial
32035
32066
  if (gapLength > config.maxBufferHole) {
32036
- var fragmentTracker = this.fragmentTracker;
32037
32067
  var startGap = false;
32038
32068
  if (currentTime === 0) {
32039
32069
  var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
@@ -32064,17 +32094,18 @@
32064
32094
  var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
32065
32095
  this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime);
32066
32096
  this.moved = true;
32067
- this.stalled = null;
32068
32097
  media.currentTime = targetTime;
32069
- if (partial && !partial.gap) {
32098
+ if (partial && !partial.gap && this.hls) {
32070
32099
  var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime);
32071
- hls.trigger(Events.ERROR, {
32100
+ this.hls.trigger(Events.ERROR, {
32072
32101
  type: ErrorTypes.MEDIA_ERROR,
32073
32102
  details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
32074
32103
  fatal: false,
32075
32104
  error: error,
32076
32105
  reason: error.message,
32077
- frag: partial
32106
+ frag: partial,
32107
+ buffer: bufferInfo.len,
32108
+ bufferInfo: bufferInfo
32078
32109
  });
32079
32110
  }
32080
32111
  return targetTime;
@@ -32087,13 +32118,13 @@
32087
32118
  * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount.
32088
32119
  * @private
32089
32120
  */;
32090
- _proto._tryNudgeBuffer = function _tryNudgeBuffer() {
32091
- var config = this.config,
32092
- hls = this.hls,
32121
+ _proto._tryNudgeBuffer = function _tryNudgeBuffer(bufferInfo) {
32122
+ var hls = this.hls,
32093
32123
  media = this.media,
32094
32124
  nudgeRetry = this.nudgeRetry;
32095
- if (media === null) {
32096
- return;
32125
+ var config = hls == null ? undefined : hls.config;
32126
+ if (!media || !config) {
32127
+ return 0;
32097
32128
  }
32098
32129
  var currentTime = media.currentTime;
32099
32130
  this.nudgeRetry++;
@@ -32107,7 +32138,9 @@
32107
32138
  type: ErrorTypes.MEDIA_ERROR,
32108
32139
  details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
32109
32140
  error: error,
32110
- fatal: false
32141
+ fatal: false,
32142
+ buffer: bufferInfo.len,
32143
+ bufferInfo: bufferInfo
32111
32144
  });
32112
32145
  } else {
32113
32146
  var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges");
@@ -32116,7 +32149,9 @@
32116
32149
  type: ErrorTypes.MEDIA_ERROR,
32117
32150
  details: ErrorDetails.BUFFER_STALLED_ERROR,
32118
32151
  error: _error,
32119
- fatal: true
32152
+ fatal: true,
32153
+ buffer: bufferInfo.len,
32154
+ bufferInfo: bufferInfo
32120
32155
  });
32121
32156
  }
32122
32157
  };
@@ -32172,11 +32207,18 @@
32172
32207
  _this.backtrackFragment = null;
32173
32208
  _this.audioCodecSwitch = false;
32174
32209
  _this.videoBuffer = null;
32210
+ _this.onMediaWaiting = function () {
32211
+ var gapController = _this.gapController;
32212
+ if (gapController) {
32213
+ gapController.waiting = self.performance.now();
32214
+ }
32215
+ };
32175
32216
  _this.onMediaPlaying = function () {
32176
32217
  // tick to speed up FRAG_CHANGED triggering
32177
32218
  var gapController = _this.gapController;
32178
32219
  if (gapController) {
32179
32220
  gapController.ended = 0;
32221
+ gapController.waiting = 0;
32180
32222
  }
32181
32223
  _this.tick();
32182
32224
  };
@@ -32231,7 +32273,7 @@
32231
32273
  };
32232
32274
  _proto.onHandlerDestroying = function onHandlerDestroying() {
32233
32275
  // @ts-ignore
32234
- this.onMediaPlaying = this.onMediaSeeked = null;
32276
+ this.onMediaPlaying = this.onMediaSeeked = this.onMediaWaiting = null;
32235
32277
  this.unregisterListeners();
32236
32278
  _BaseStreamController.prototype.onHandlerDestroying.call(this);
32237
32279
  };
@@ -32552,15 +32594,18 @@
32552
32594
  var media = data.media;
32553
32595
  media.removeEventListener('playing', this.onMediaPlaying);
32554
32596
  media.removeEventListener('seeked', this.onMediaSeeked);
32597
+ media.removeEventListener('waiting', this.onMediaWaiting);
32555
32598
  media.addEventListener('playing', this.onMediaPlaying);
32556
32599
  media.addEventListener('seeked', this.onMediaSeeked);
32557
- this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls);
32600
+ media.addEventListener('waiting', this.onMediaWaiting);
32601
+ this.gapController = new GapController(media, this.fragmentTracker, this.hls);
32558
32602
  };
32559
32603
  _proto.onMediaDetaching = function onMediaDetaching(event, data) {
32560
32604
  var media = this.media;
32561
32605
  if (media) {
32562
32606
  media.removeEventListener('playing', this.onMediaPlaying);
32563
32607
  media.removeEventListener('seeked', this.onMediaSeeked);
32608
+ media.removeEventListener('waiting', this.onMediaWaiting);
32564
32609
  }
32565
32610
  this.videoBuffer = null;
32566
32611
  this.fragPlaying = null;
@@ -32970,7 +33015,7 @@
32970
33015
  var startPosition = this.startPosition;
32971
33016
  // only adjust currentTime if different from startPosition or if startPosition not buffered
32972
33017
  // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered
32973
- if (startPosition >= 0) {
33018
+ if (startPosition >= 0 && currentTime < startPosition) {
32974
33019
  if (media.seeking) {
32975
33020
  this.log("could not seek to " + startPosition + ", already seeking at " + currentTime);
32976
33021
  return;
package/dist/hls.js.d.ts CHANGED
@@ -571,6 +571,19 @@ export declare type BufferInfo = {
571
571
  start: number;
572
572
  end: number;
573
573
  nextStart?: number;
574
+ buffered?: BufferTimeRange[];
575
+ };
576
+
577
+ /**
578
+ * Provides methods dealing with buffer length retrieval for example.
579
+ *
580
+ * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property.
581
+ *
582
+ * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered
583
+ */
584
+ export declare type BufferTimeRange = {
585
+ start: number;
586
+ end: number;
574
587
  };
575
588
 
576
589
  export declare class CapLevelController implements ComponentAPI {
@@ -1017,6 +1030,7 @@ export declare interface ErrorData {
1017
1030
  fatal: boolean;
1018
1031
  errorAction?: IErrorAction;
1019
1032
  buffer?: number;
1033
+ bufferInfo?: BufferInfo;
1020
1034
  bytes?: number;
1021
1035
  chunkMeta?: ChunkMetadata;
1022
1036
  context?: PlaylistLoaderContext;
@@ -1027,6 +1041,9 @@ export declare interface ErrorData {
1027
1041
  levelRetry?: boolean;
1028
1042
  loader?: Loader<LoaderContext>;
1029
1043
  networkDetails?: any;
1044
+ stalled?: {
1045
+ start: number;
1046
+ };
1030
1047
  stats?: LoaderStats;
1031
1048
  mimeType?: string;
1032
1049
  reason?: string;
@@ -1110,6 +1127,7 @@ export declare enum Events {
1110
1127
  MEDIA_DETACHING = "hlsMediaDetaching",
1111
1128
  MEDIA_DETACHED = "hlsMediaDetached",
1112
1129
  MEDIA_ENDED = "hlsMediaEnded",
1130
+ STALL_RESOLVED = "hlsStallResolved",
1113
1131
  BUFFER_RESET = "hlsBufferReset",
1114
1132
  BUFFER_CODECS = "hlsBufferCodecs",
1115
1133
  BUFFER_CREATED = "hlsBufferCreated",
@@ -1948,6 +1966,7 @@ export declare type HlsConfig = {
1948
1966
  progressive: boolean;
1949
1967
  lowLatencyMode: boolean;
1950
1968
  primarySessionId?: string;
1969
+ detectStallWithCurrentTimeMs: number;
1951
1970
  } & ABRControllerConfig & BufferControllerConfig & CapLevelControllerConfig & EMEControllerConfig & FPSControllerConfig & LevelControllerConfig & MP4RemuxerConfig & StreamControllerConfig & SelectionPreferences & LatencyControllerConfig & MetadataControllerConfig & TimelineControllerConfig & TSDemuxerConfig & HlsLoadPolicies & FragmentLoaderConfig & PlaylistLoaderConfig;
1952
1971
 
1953
1972
  export declare interface HlsEventEmitter {
@@ -1969,6 +1988,7 @@ export declare interface HlsListeners {
1969
1988
  [Events.MEDIA_DETACHING]: (event: Events.MEDIA_DETACHING, data: MediaDetachingData) => void;
1970
1989
  [Events.MEDIA_DETACHED]: (event: Events.MEDIA_DETACHED, data: MediaDetachedData) => void;
1971
1990
  [Events.MEDIA_ENDED]: (event: Events.MEDIA_ENDED, data: MediaEndedData) => void;
1991
+ [Events.STALL_RESOLVED]: (event: Events.STALL_RESOLVED, data: {}) => void;
1972
1992
  [Events.BUFFER_RESET]: (event: Events.BUFFER_RESET) => void;
1973
1993
  [Events.BUFFER_CODECS]: (event: Events.BUFFER_CODECS, data: BufferCodecsData) => void;
1974
1994
  [Events.BUFFER_CREATED]: (event: Events.BUFFER_CREATED, data: BufferCreatedData) => void;
@@ -3340,6 +3360,7 @@ export declare class StreamController extends BaseStreamController implements Ne
3340
3360
  protected flushMainBuffer(startOffset: number, endOffset: number): void;
3341
3361
  protected onMediaAttached(event: Events.MEDIA_ATTACHED, data: MediaAttachedData): void;
3342
3362
  protected onMediaDetaching(event: Events.MEDIA_DETACHING, data: MediaDetachingData): void;
3363
+ private onMediaWaiting;
3343
3364
  private onMediaPlaying;
3344
3365
  private onMediaSeeked;
3345
3366
  protected triggerEnded(): void;