hls.js 1.6.0-beta.1.0.canary.10767 → 1.6.0-beta.1.0.canary.10770

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.
@@ -601,6 +601,15 @@ export default class BaseStreamController
601
601
  }
602
602
  }
603
603
 
604
+ protected waitForLive(levelInfo: Level) {
605
+ const details = levelInfo.details;
606
+ return (
607
+ details?.live &&
608
+ details.type !== 'EVENT' &&
609
+ (this.levelLastLoaded !== levelInfo || details.expired)
610
+ );
611
+ }
612
+
604
613
  protected flushMainBuffer(
605
614
  startOffset: number,
606
615
  endOffset: number,
@@ -1077,7 +1086,7 @@ export default class BaseStreamController
1077
1086
  const { level: levelIndex, sn, part: partIndex } = chunkMeta;
1078
1087
  if (!levels?.[levelIndex]) {
1079
1088
  this.warn(
1080
- `Levels object was unset while buffering fragment ${sn} of level ${levelIndex}. The current chunk will not be buffered.`,
1089
+ `Levels object was unset while buffering fragment ${sn} of ${this.playlistLabel()} ${levelIndex}. The current chunk will not be buffered.`,
1081
1090
  );
1082
1091
  return null;
1083
1092
  }
@@ -1643,6 +1652,7 @@ export default class BaseStreamController
1643
1652
  // Leave this.startPosition at -1, so that we can use `getInitialLiveFragment` logic when startPosition has
1644
1653
  // not been specified via the config or an as an argument to startLoad (#3736).
1645
1654
  startPosition = this.hls.liveSyncPosition || sliding;
1655
+ this.startPosition = -1;
1646
1656
  } else {
1647
1657
  this.log(`setting startPosition to 0 by default`);
1648
1658
  this.startPosition = startPosition = 0;
@@ -1666,9 +1676,14 @@ export default class BaseStreamController
1666
1676
  }
1667
1677
 
1668
1678
  private handleFragLoadAborted(frag: Fragment, part: Part | undefined) {
1669
- if (this.transmuxer && isMediaFragment(frag) && frag.stats.aborted) {
1679
+ if (
1680
+ this.transmuxer &&
1681
+ frag.type === this.playlistType &&
1682
+ isMediaFragment(frag) &&
1683
+ frag.stats.aborted
1684
+ ) {
1670
1685
  this.warn(
1671
- `Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of level ${
1686
+ `Fragment ${frag.sn}${part ? ' part ' + part.index : ''} of ${this.playlistLabel()} ${
1672
1687
  frag.level
1673
1688
  } was aborted`,
1674
1689
  );
@@ -1861,7 +1876,7 @@ export default class BaseStreamController
1861
1876
 
1862
1877
  protected resetWhenMissingContext(chunkMeta: ChunkMetadata) {
1863
1878
  this.warn(
1864
- `The loading context changed while buffering fragment ${chunkMeta.sn} of level ${chunkMeta.level}. This chunk will not be buffered.`,
1879
+ `The loading context changed while buffering fragment ${chunkMeta.sn} of ${this.playlistLabel()} ${chunkMeta.level}. This chunk will not be buffered.`,
1865
1880
  );
1866
1881
  this.removeUnbufferedFrags();
1867
1882
  this.resetStartWhenNotLoaded(this.levelLastLoaded);
@@ -1930,7 +1945,7 @@ export default class BaseStreamController
1930
1945
  );
1931
1946
  if (!parsed && this.transmuxer?.error === null) {
1932
1947
  const error = new Error(
1933
- `Found no media in fragment ${frag.sn} of level ${frag.level} resetting transmuxer to fallback to playlist timing`,
1948
+ `Found no media in fragment ${frag.sn} of ${this.playlistLabel()} ${frag.level} resetting transmuxer to fallback to playlist timing`,
1934
1949
  );
1935
1950
  if (level.fragmentError === 0) {
1936
1951
  // Mark and track the odd empty segment as a gap to avoid reloading
@@ -1943,7 +1958,7 @@ export default class BaseStreamController
1943
1958
  fatal: false,
1944
1959
  error,
1945
1960
  frag,
1946
- reason: `Found no media in msn ${frag.sn} of level "${level.url}"`,
1961
+ reason: `Found no media in msn ${frag.sn} of ${this.playlistLabel()} "${level.url}"`,
1947
1962
  });
1948
1963
  if (!this.hls) {
1949
1964
  return;
@@ -648,6 +648,8 @@ export default class LevelController extends BasePlaylistController {
648
648
  }
649
649
 
650
650
  const pathwayId = currentLevel.attrs['PATHWAY-ID'];
651
+ const details = currentLevel.details;
652
+ const age = details?.age;
651
653
  this.log(
652
654
  `Loading level index ${currentLevelIndex}${
653
655
  hlsUrlParameters?.msn !== undefined
@@ -656,7 +658,7 @@ export default class LevelController extends BasePlaylistController {
656
658
  ' part ' +
657
659
  hlsUrlParameters.part
658
660
  : ''
659
- } with${pathwayId ? ' Pathway ' + pathwayId : ''} ${url}`,
661
+ }${pathwayId ? ' Pathway ' + pathwayId : ''}${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
660
662
  );
661
663
 
662
664
  // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId);
@@ -155,7 +155,11 @@ export default class StreamController
155
155
  this._hasEnoughToStart = false;
156
156
  }
157
157
  // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime
158
- if (lastCurrentTime > 0 && startPosition === -1) {
158
+ if (
159
+ lastCurrentTime > 0 &&
160
+ startPosition === -1 &&
161
+ !skipSeekToStartPosition
162
+ ) {
159
163
  this.log(
160
164
  `Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(
161
165
  3,
@@ -186,7 +190,9 @@ export default class StreamController
186
190
  const details = currentLevel?.details;
187
191
  if (
188
192
  details &&
189
- (!details.live || this.levelLastLoaded === currentLevel)
193
+ (!details.live ||
194
+ (this.levelLastLoaded === currentLevel &&
195
+ !this.waitForLive(currentLevel)))
190
196
  ) {
191
197
  if (this.waitForCdnTuneIn(details)) {
192
198
  break;
@@ -287,10 +293,11 @@ export default class StreamController
287
293
  if (
288
294
  !levelDetails ||
289
295
  this.state === State.WAITING_LEVEL ||
290
- (levelDetails.live && this.levelLastLoaded !== levelInfo)
296
+ this.waitForLive(levelInfo)
291
297
  ) {
292
298
  this.level = level;
293
299
  this.state = State.WAITING_LEVEL;
300
+ this.startFragRequested = false;
294
301
  return;
295
302
  }
296
303
 
@@ -649,7 +656,8 @@ export default class StreamController
649
656
  const level = data.levelInfo;
650
657
  if (
651
658
  !level.details ||
652
- (level.details.live && this.levelLastLoaded !== level) ||
659
+ (level.details.live &&
660
+ (this.levelLastLoaded !== level || level.details.expired)) ||
653
661
  this.waitForCdnTuneIn(level.details)
654
662
  ) {
655
663
  this.state = State.WAITING_LEVEL;
@@ -18,8 +18,8 @@ import {
18
18
  } from '../utils/encryption-methods-util';
19
19
  import { addSliding } from '../utils/level-helper';
20
20
  import { subtitleOptionsIdentical } from '../utils/media-option-attributes';
21
- import type Hls from '../hls';
22
21
  import type { FragmentTracker } from './fragment-tracker';
22
+ import type Hls from '../hls';
23
23
  import type KeyLoader from '../loader/key-loader';
24
24
  import type { LevelDetails } from '../loader/level-details';
25
25
  import type { NetworkComponentAPI } from '../types/component-api';
@@ -425,6 +425,9 @@ export class SubtitleStreamController
425
425
  if (!track || !levels.length || !track.details) {
426
426
  return;
427
427
  }
428
+ if (this.waitForLive(track)) {
429
+ return;
430
+ }
428
431
  const { config } = this;
429
432
  const currentTime = this.getLoadPosition();
430
433
  const bufferedInfo = BufferHelper.bufferedInfo(
@@ -447,7 +447,18 @@ class SubtitleTrackController extends BasePlaylistController {
447
447
  );
448
448
  }
449
449
  }
450
- this.log(`Loading subtitle playlist for id ${id}`);
450
+ const details = currentTrack.details;
451
+ const age = details?.age;
452
+ this.log(
453
+ `Loading subtitle ${id} "${currentTrack.name}" lang:${currentTrack.lang} group:${groupId}${
454
+ hlsUrlParameters?.msn !== undefined
455
+ ? ' at sn ' +
456
+ hlsUrlParameters.msn +
457
+ ' part ' +
458
+ hlsUrlParameters.part
459
+ : ''
460
+ }${age && details.live ? ' age ' + age.toFixed(1) + (details.type ? ' ' + details.type || '' : '') : ''} ${url}`,
461
+ );
451
462
  this.hls.trigger(Events.SUBTITLE_TRACK_LOADING, {
452
463
  url,
453
464
  id,
@@ -160,4 +160,16 @@ export class LevelDetails {
160
160
  }
161
161
  return this.endSN;
162
162
  }
163
+
164
+ get expired(): boolean {
165
+ if (this.live && this.age) {
166
+ const playlistWindowDuration = this.partEnd - this.fragmentStart;
167
+ return (
168
+ this.age >
169
+ Math.max(playlistWindowDuration, this.totalduration) +
170
+ this.levelTargetDuration
171
+ );
172
+ }
173
+ return false;
174
+ }
163
175
  }
@@ -240,17 +240,25 @@ class PlaylistLoader implements NetworkComponentAPI {
240
240
  // Check if a loader for this context already exists
241
241
  let loader = this.getInternalLoader(context);
242
242
  if (loader) {
243
+ const logger = this.hls.logger;
243
244
  const loaderContext = loader.context as PlaylistLoaderContext;
244
245
  if (
245
246
  loaderContext &&
246
- loaderContext.url === context.url &&
247
- loaderContext.levelOrTrack === context.levelOrTrack
247
+ loaderContext.levelOrTrack === context.levelOrTrack &&
248
+ (loaderContext.url === context.url ||
249
+ (loaderContext.deliveryDirectives && !context.deliveryDirectives))
248
250
  ) {
249
- // same URL can't overlap
250
- this.hls.logger.trace('[playlist-loader]: playlist request ongoing');
251
+ // same URL can't overlap, or wait for blocking request
252
+ if (loaderContext.url === context.url) {
253
+ logger.log(`[playlist-loader]: playlist request ongoing`);
254
+ } else {
255
+ logger.log(
256
+ `[playlist-loader]: ignore ${context.url} in favor of ${loaderContext.url}`,
257
+ );
258
+ }
251
259
  return;
252
260
  }
253
- this.hls.logger.log(
261
+ logger.log(
254
262
  `[playlist-loader]: aborting previous loader for type: ${context.type}`,
255
263
  );
256
264
  loader.abort();