Home Reference Source

src/loader/level-details.ts

  1. import { Part } from './fragment';
  2. import type { Fragment } from './fragment';
  3. import type { AttrList } from '../utils/attr-list';
  4. import type { DateRange } from './date-range';
  5.  
  6. const DEFAULT_TARGET_DURATION = 10;
  7.  
  8. export class LevelDetails {
  9. public PTSKnown: boolean = false;
  10. public alignedSliding: boolean = false;
  11. public averagetargetduration?: number;
  12. public endCC: number = 0;
  13. public endSN: number = 0;
  14. public fragments: Fragment[];
  15. public fragmentHint?: Fragment;
  16. public partList: Part[] | null = null;
  17. public dateRanges: Record<string, DateRange>;
  18. public live: boolean = true;
  19. public ageHeader: number = 0;
  20. public advancedDateTime?: number;
  21. public updated: boolean = true;
  22. public advanced: boolean = true;
  23. public availabilityDelay?: number; // Manifest reload synchronization
  24. public misses: number = 0;
  25. public startCC: number = 0;
  26. public startSN: number = 0;
  27. public startTimeOffset: number | null = null;
  28. public targetduration: number = 0;
  29. public totalduration: number = 0;
  30. public type: string | null = null;
  31. public url: string;
  32. public m3u8: string = '';
  33. public version: number | null = null;
  34. public canBlockReload: boolean = false;
  35. public canSkipUntil: number = 0;
  36. public canSkipDateRanges: boolean = false;
  37. public skippedSegments: number = 0;
  38. public recentlyRemovedDateranges?: string[];
  39. public partHoldBack: number = 0;
  40. public holdBack: number = 0;
  41. public partTarget: number = 0;
  42. public preloadHint?: AttrList;
  43. public renditionReports?: AttrList[];
  44. public tuneInGoal: number = 0;
  45. public deltaUpdateFailed?: boolean;
  46. public driftStartTime: number = 0;
  47. public driftEndTime: number = 0;
  48. public driftStart: number = 0;
  49. public driftEnd: number = 0;
  50. public encryptedFragments: Fragment[];
  51.  
  52. constructor(baseUrl) {
  53. this.fragments = [];
  54. this.encryptedFragments = [];
  55. this.dateRanges = {};
  56. this.url = baseUrl;
  57. }
  58.  
  59. reloaded(previous: LevelDetails | undefined) {
  60. if (!previous) {
  61. this.advanced = true;
  62. this.updated = true;
  63. return;
  64. }
  65. const partSnDiff = this.lastPartSn - previous.lastPartSn;
  66. const partIndexDiff = this.lastPartIndex - previous.lastPartIndex;
  67. this.updated =
  68. this.endSN !== previous.endSN || !!partIndexDiff || !!partSnDiff;
  69. this.advanced =
  70. this.endSN > previous.endSN ||
  71. partSnDiff > 0 ||
  72. (partSnDiff === 0 && partIndexDiff > 0);
  73. if (this.updated || this.advanced) {
  74. this.misses = Math.floor(previous.misses * 0.6);
  75. } else {
  76. this.misses = previous.misses + 1;
  77. }
  78. this.availabilityDelay = previous.availabilityDelay;
  79. }
  80.  
  81. get hasProgramDateTime(): boolean {
  82. if (this.fragments.length) {
  83. return Number.isFinite(
  84. this.fragments[this.fragments.length - 1].programDateTime as number
  85. );
  86. }
  87. return false;
  88. }
  89.  
  90. get levelTargetDuration(): number {
  91. return (
  92. this.averagetargetduration ||
  93. this.targetduration ||
  94. DEFAULT_TARGET_DURATION
  95. );
  96. }
  97.  
  98. get drift(): number {
  99. const runTime = this.driftEndTime - this.driftStartTime;
  100. if (runTime > 0) {
  101. const runDuration = this.driftEnd - this.driftStart;
  102. return (runDuration * 1000) / runTime;
  103. }
  104. return 1;
  105. }
  106.  
  107. get edge(): number {
  108. return this.partEnd || this.fragmentEnd;
  109. }
  110.  
  111. get partEnd(): number {
  112. if (this.partList?.length) {
  113. return this.partList[this.partList.length - 1].end;
  114. }
  115. return this.fragmentEnd;
  116. }
  117.  
  118. get fragmentEnd(): number {
  119. if (this.fragments?.length) {
  120. return this.fragments[this.fragments.length - 1].end;
  121. }
  122. return 0;
  123. }
  124.  
  125. get age(): number {
  126. if (this.advancedDateTime) {
  127. return Math.max(Date.now() - this.advancedDateTime, 0) / 1000;
  128. }
  129. return 0;
  130. }
  131.  
  132. get lastPartIndex(): number {
  133. if (this.partList?.length) {
  134. return this.partList[this.partList.length - 1].index;
  135. }
  136. return -1;
  137. }
  138.  
  139. get lastPartSn(): number {
  140. if (this.partList?.length) {
  141. return this.partList[this.partList.length - 1].fragment.sn as number;
  142. }
  143. return this.endSN;
  144. }
  145. }