hls.js 1.6.0-beta.1.0.canary.10750 → 1.6.0-beta.1.0.canary.10752

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/package.json CHANGED
@@ -130,5 +130,5 @@
130
130
  "url-toolkit": "2.2.5",
131
131
  "wrangler": "3.85.0"
132
132
  },
133
- "version": "1.6.0-beta.1.0.canary.10750"
133
+ "version": "1.6.0-beta.1.0.canary.10752"
134
134
  }
@@ -285,9 +285,10 @@ class AbrController extends Logger implements AbrComponentAPI {
285
285
  const bwEstimate: number = this.getBwEstimate();
286
286
  const levels = hls.levels;
287
287
  const level = levels[frag.level];
288
- const expectedLen =
289
- stats.total ||
290
- Math.max(stats.loaded, Math.round((duration * level.averageBitrate) / 8));
288
+ const expectedLen = Math.max(
289
+ stats.loaded,
290
+ Math.round((duration * (frag.bitrate || level.averageBitrate)) / 8),
291
+ );
291
292
  let timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading;
292
293
  if (timeStreaming < 1 && loadedFirstByte) {
293
294
  timeStreaming = Math.min(timeLoading, (stats.loaded * 8) / bwEstimate);
@@ -880,8 +881,8 @@ class AbrController extends Logger implements AbrComponentAPI {
880
881
  currentFragDuration &&
881
882
  bufferStarvationDelay >= currentFragDuration * 2 &&
882
883
  maxStarvationDelay === 0
883
- ? levels[i].averageBitrate
884
- : levels[i].maxBitrate;
884
+ ? levelInfo.averageBitrate
885
+ : levelInfo.maxBitrate;
885
886
  const fetchDuration: number = this.getTimeToLoadFrag(
886
887
  ttfbEstimateSec,
887
888
  adjustedbw,
@@ -477,18 +477,19 @@ export default class BaseStreamController
477
477
  }
478
478
 
479
479
  private _loadFragForPlayback(
480
- frag: Fragment,
480
+ fragment: Fragment,
481
481
  level: Level,
482
482
  targetBufferTime: number,
483
483
  ) {
484
484
  const progressCallback: FragmentLoadProgressCallback = (
485
485
  data: FragLoadedData,
486
486
  ) => {
487
+ const frag = data.frag;
487
488
  if (this.fragContextChanged(frag)) {
488
489
  this.warn(
489
- `Fragment ${frag.sn}${
490
- data.part ? ' p: ' + data.part.index : ''
491
- } of level ${frag.level} was dropped during download.`,
490
+ `${frag.type} sn: ${frag.sn}${
491
+ data.part ? ' part: ' + data.part.index : ''
492
+ } of ${this.fragInfo(frag, false, data.part)}) was dropped during download.`,
492
493
  );
493
494
  this.fragmentTracker.removeFragment(frag);
494
495
  return;
@@ -497,13 +498,14 @@ export default class BaseStreamController
497
498
  this._handleFragmentLoadProgress(data);
498
499
  };
499
500
 
500
- this._doFragLoad(frag, level, targetBufferTime, progressCallback)
501
+ this._doFragLoad(fragment, level, targetBufferTime, progressCallback)
501
502
  .then((data) => {
502
503
  if (!data) {
503
504
  // if we're here we probably needed to backtrack or are waiting for more parts
504
505
  return;
505
506
  }
506
507
  const state = this.state;
508
+ const frag = data.frag;
507
509
  if (this.fragContextChanged(frag)) {
508
510
  if (
509
511
  state === State.FRAG_LOADING ||
@@ -530,7 +532,7 @@ export default class BaseStreamController
530
532
  return;
531
533
  }
532
534
  this.warn(`Frag error: ${reason?.message || reason}`);
533
- this.resetFragmentLoading(frag);
535
+ this.resetFragmentLoading(fragment);
534
536
  });
535
537
  }
536
538
 
@@ -608,10 +610,11 @@ export default class BaseStreamController
608
610
  this.hls.trigger(Events.BUFFER_FLUSHING, flushScope);
609
611
  }
610
612
 
611
- protected _loadInitSegment(frag: Fragment, level: Level) {
612
- this._doFragLoad(frag, level)
613
+ protected _loadInitSegment(fragment: Fragment, level: Level) {
614
+ this._doFragLoad(fragment, level)
613
615
  .then((data) => {
614
- if (!data || this.fragContextChanged(frag) || !this.levels) {
616
+ const frag = data?.frag;
617
+ if (!frag || this.fragContextChanged(frag) || !this.levels) {
615
618
  throw new Error('init load aborted');
616
619
  }
617
620
 
@@ -619,7 +622,7 @@ export default class BaseStreamController
619
622
  })
620
623
  .then((data: FragLoadedData) => {
621
624
  const { hls } = this;
622
- const { payload } = data;
625
+ const { frag, payload } = data;
623
626
  const decryptData = frag.decryptdata;
624
627
 
625
628
  // check to see if the payload needs to be decrypted
@@ -671,7 +674,7 @@ export default class BaseStreamController
671
674
  return;
672
675
  }
673
676
  this.warn(reason);
674
- this.resetFragmentLoading(frag);
677
+ this.resetFragmentLoading(fragment);
675
678
  });
676
679
  }
677
680
 
@@ -705,7 +708,7 @@ export default class BaseStreamController
705
708
  this.log(
706
709
  `Buffered ${frag.type} sn: ${frag.sn}${
707
710
  part ? ' part: ' + part.index : ''
708
- } of ${this.fragInfo(frag)} > buffer:${
711
+ } of ${this.fragInfo(frag, false, part)} > buffer:${
709
712
  media
710
713
  ? TimeRanges.toString(BufferHelper.getBuffered(media))
711
714
  : '(detached)'
@@ -823,12 +826,11 @@ export default class BaseStreamController
823
826
  const partIndex = this.getNextPart(partList, frag, targetBufferTime);
824
827
  if (partIndex > -1) {
825
828
  const part = partList[partIndex];
829
+ frag = this.fragCurrent = part.fragment;
826
830
  this.log(
827
- `Loading part sn: ${frag.sn} p: ${part.index} cc: ${
831
+ `Loading ${frag.type} sn: ${frag.sn} part: ${part.index} (${partIndex}/${partList.length - 1}) of ${this.fragInfo(frag, false, part)}) cc: ${
828
832
  frag.cc
829
- } of playlist [${details.startSN}-${
830
- details.endSN
831
- }] parts [0-${partIndex}-${partList.length - 1}] ${this.playlistLabel()}: ${frag.level}, target: ${parseFloat(
833
+ } [${details.startSN}-${details.endSN}], target: ${parseFloat(
832
834
  targetBufferTime.toFixed(3),
833
835
  )}`,
834
836
  );
@@ -1947,7 +1949,7 @@ export default class BaseStreamController
1947
1949
  this.log(
1948
1950
  `Parsed ${frag.type} sn: ${frag.sn}${
1949
1951
  part ? ' part: ' + part.index : ''
1950
- } of ${this.fragInfo(frag)})`,
1952
+ } of ${this.fragInfo(frag, false, part)})`,
1951
1953
  );
1952
1954
  this.hls.trigger(Events.FRAG_PARSED, { frag, part });
1953
1955
  }
@@ -1956,10 +1958,16 @@ export default class BaseStreamController
1956
1958
  return this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track';
1957
1959
  }
1958
1960
 
1959
- private fragInfo(frag: Fragment, pts: boolean = true): string {
1960
- return `${this.playlistLabel()} ${frag.level} (frag:[${((pts ? frag.startPTS : frag.start) ?? NaN).toFixed(3)}-${(
1961
- (pts ? frag.endPTS : frag.end) ?? NaN
1962
- ).toFixed(3)}]`;
1961
+ private fragInfo(
1962
+ frag: Fragment,
1963
+ pts: boolean = true,
1964
+ part?: Part | null,
1965
+ ): string {
1966
+ return `${this.playlistLabel()} ${frag.level} (${part ? 'part' : 'frag'}:[${((pts && !part ? frag.startPTS : (part || frag).start) ?? NaN).toFixed(3)}-${(
1967
+ (pts && !part ? frag.endPTS : (part || frag).end) ?? NaN
1968
+ ).toFixed(
1969
+ 3,
1970
+ )}]${part && frag.type === 'main' ? 'INDEPENDENT=' + (part.independent ? 'YES' : 'NO') : ''}`;
1963
1971
  }
1964
1972
 
1965
1973
  protected resetTransmuxer() {
@@ -1151,11 +1151,12 @@ export default class StreamController
1151
1151
  return audioCodec;
1152
1152
  }
1153
1153
 
1154
- private _loadBitrateTestFrag(frag: Fragment, level: Level) {
1155
- frag.bitrateTest = true;
1156
- this._doFragLoad(frag, level).then((data) => {
1154
+ private _loadBitrateTestFrag(fragment: Fragment, level: Level) {
1155
+ fragment.bitrateTest = true;
1156
+ this._doFragLoad(fragment, level).then((data) => {
1157
1157
  const { hls } = this;
1158
- if (!data || this.fragContextChanged(frag)) {
1158
+ const frag = data?.frag;
1159
+ if (!frag || this.fragContextChanged(frag)) {
1159
1160
  return;
1160
1161
  }
1161
1162
  level.fragmentError = 0;
@@ -12,13 +12,13 @@ import Transmuxer, {
12
12
  } from '../demux/transmuxer';
13
13
  import { ErrorDetails, ErrorTypes } from '../errors';
14
14
  import { Events } from '../events';
15
+ import { PlaylistLevelType } from '../types/loader';
15
16
  import { getM2TSSupportedAudioTypes } from '../utils/codecs';
16
17
  import type { WorkerContext } from './inject-worker';
17
18
  import type { HlsEventEmitter, HlsListeners } from '../events';
18
19
  import type Hls from '../hls';
19
20
  import type { MediaFragment, Part } from '../loader/fragment';
20
21
  import type { ErrorData, FragDecryptedData } from '../types/events';
21
- import type { PlaylistLevelType } from '../types/loader';
22
22
  import type { ChunkMetadata, TransmuxerResult } from '../types/transmuxer';
23
23
  import type { RationalTimestamp } from '../utils/timescale-conversion';
24
24
 
@@ -234,7 +234,9 @@ export default class TransmuxerInterface {
234
234
  );
235
235
  if (!contiguous || discontinuity || initSegmentChange) {
236
236
  this.hls.logger
237
- .log(`[transmuxer-interface, ${frag.type}]: Starting new transmux session for sn: ${chunkMeta.sn} p: ${chunkMeta.part} level: ${chunkMeta.level} id: ${chunkMeta.id}
237
+ .log(`[transmuxer-interface]: Starting new transmux session for ${frag.type} sn: ${chunkMeta.sn}${
238
+ chunkMeta.part > -1 ? ' part: ' + chunkMeta.part : ''
239
+ } ${this.id === PlaylistLevelType.MAIN ? 'level' : 'track'}: ${chunkMeta.level} id: ${chunkMeta.id}
238
240
  discontinuity: ${discontinuity}
239
241
  trackSwitch: ${trackSwitch}
240
242
  contiguous: ${contiguous}
@@ -290,7 +290,7 @@ export default class Transmuxer {
290
290
  const { accurateTimeOffset, timeOffset } = this.currentTransmuxState;
291
291
  this.logger.log(
292
292
  `[transmuxer.ts]: Flushed ${this.id} sn: ${chunkMeta.sn}${
293
- chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : ''
293
+ chunkMeta.part > -1 ? ' part: ' + chunkMeta.part : ''
294
294
  } of ${this.id === PlaylistLevelType.MAIN ? 'level' : 'track'} ${chunkMeta.level}`,
295
295
  );
296
296
  const remuxResult = this.remuxer!.remux(
@@ -1,7 +1,7 @@
1
1
  import { ErrorDetails, ErrorTypes } from '../errors';
2
2
  import { getLoaderConfigWithoutReties } from '../utils/error-helper';
3
- import type { HlsConfig } from '../config';
4
3
  import type { BaseSegment, Fragment, Part } from './fragment';
4
+ import type { HlsConfig } from '../config';
5
5
  import type {
6
6
  ErrorData,
7
7
  FragLoadedData,
@@ -159,6 +159,8 @@ export class Fragment extends BaseSegment {
159
159
  private _decryptdata: LevelKey | null = null;
160
160
  private _programDateTime: number | null = null;
161
161
  private _ref: MediaFragmentRef | null = null;
162
+ // Approximate bit rate of the fragment expressed in bits per second (bps) as indicated by the last EXT-X-BITRATE (kbps) tag
163
+ private _bitrate?: number;
162
164
 
163
165
  public rawProgramDateTime: string | null = null;
164
166
  public tagList: Array<string[]> = [];
@@ -219,6 +221,37 @@ export class Fragment extends BaseSegment {
219
221
  this.type = type;
220
222
  }
221
223
 
224
+ get byteLength(): number | null {
225
+ if (this.hasStats) {
226
+ const total = this.stats.total;
227
+ if (total) {
228
+ return total;
229
+ }
230
+ }
231
+ if (this.byteRange) {
232
+ const start = this.byteRange[0];
233
+ const end = this.byteRange[1];
234
+ if (Number.isFinite(start) && Number.isFinite(end)) {
235
+ return (end as number) - (start as number);
236
+ }
237
+ }
238
+ return null;
239
+ }
240
+
241
+ get bitrate(): number | null {
242
+ if (this.byteLength) {
243
+ return (this.byteLength * 8) / this.duration;
244
+ }
245
+ if (this._bitrate) {
246
+ return this._bitrate;
247
+ }
248
+ return null;
249
+ }
250
+
251
+ set bitrate(value: number) {
252
+ this._bitrate = value;
253
+ }
254
+
222
255
  get decryptdata(): LevelKey | null {
223
256
  const { levelkeys } = this;
224
257
  if (!levelkeys && !this._decryptdata) {
@@ -314,6 +314,7 @@ export default class M3U8Parser {
314
314
  let currentPart = 0;
315
315
  let totalduration = 0;
316
316
  let discontinuityCounter = 0;
317
+ let currentBitrate = 0;
317
318
  let prevFrag: Fragment | null = null;
318
319
  let frag: Fragment = new Fragment(type, base);
319
320
  let result: RegExpExecArray | RegExpMatchArray | null;
@@ -338,6 +339,9 @@ export default class M3U8Parser {
338
339
  frag.start = totalduration;
339
340
  frag.sn = currentSN;
340
341
  frag.cc = discontinuityCounter;
342
+ if (currentBitrate) {
343
+ frag.bitrate = currentBitrate;
344
+ }
341
345
  frag.level = id;
342
346
  if (currentInitSegment) {
343
347
  frag.initSegment = currentInitSegment;
@@ -481,6 +485,12 @@ export default class M3U8Parser {
481
485
  break;
482
486
  case 'BITRATE':
483
487
  frag.tagList.push([tag, value1]);
488
+ currentBitrate = parseInt(value1) * 1000;
489
+ if (Number.isFinite(currentBitrate)) {
490
+ frag.bitrate = currentBitrate;
491
+ } else {
492
+ currentBitrate = 0;
493
+ }
484
494
  break;
485
495
  case 'DATERANGE': {
486
496
  const dateRangeAttr = new AttrList(value1, level);