Skip to content

Commit

Permalink
Fix currentLevel and FRAG_CHANGED reporting when seeking back from ed…
Browse files Browse the repository at this point in the history
…ge in Low-Latency streams

Related to #5085 and #5077
  • Loading branch information
robwalch committed Jan 9, 2023
1 parent d2ea336 commit 4a9d594
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 40 deletions.
2 changes: 0 additions & 2 deletions api-extractor/report/hls.js.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -745,8 +745,6 @@ export class Fragment extends BaseSegment {
// (undocumented)
abortRequests(): void;
// (undocumented)
appendedPTS?: number;
// (undocumented)
bitrateTest: boolean;
// (undocumented)
cc: number;
Expand Down
88 changes: 53 additions & 35 deletions src/controller/fragment-tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export enum FragmentState {
}

export class FragmentTracker implements ComponentAPI {
private activeFragment: Fragment | null = null;
private mainFragEntity: FragmentEntity | null = null;
private activeParts: Part[] | null = null;
private endListFragments: { [key in PlaylistLevelType]?: FragmentEntity } =
Object.create(null);
Expand Down Expand Up @@ -65,7 +65,7 @@ export class FragmentTracker implements ComponentAPI {
// @ts-ignore
this.endListFragments =
this.timeRanges =
this.activeFragment =
this.mainFragEntity =
this.activeParts =
null;
}
Expand All @@ -79,34 +79,33 @@ export class FragmentTracker implements ComponentAPI {
levelType: PlaylistLevelType
): Fragment | Part | null {
if (levelType === PlaylistLevelType.MAIN) {
const { activeFragment, activeParts } = this;
if (!activeFragment) {
return null;
}
if (activeParts) {
for (let i = activeParts.length; i--; ) {
const activePart = activeParts[i];
const appendedPTS = activePart
? activePart.end
: activeFragment.appendedPTS;
if (
activePart.start <= position &&
appendedPTS !== undefined &&
position <= appendedPTS
) {
// 9 is a magic number. remove parts from lookup after a match but keep some short seeks back.
if (i > 9) {
this.activeParts = activeParts.slice(i - 9);
const { mainFragEntity, activeParts } = this;
if (mainFragEntity) {
if (mainFragEntity && activeParts) {
for (let i = activeParts.length; i--; ) {
const activePart = activeParts[i];
const appendedPTS = activePart
? activePart.end
: mainFragEntity.appendedPTS;
if (
activePart.start <= position &&
appendedPTS !== null &&
position <= appendedPTS
) {
// 9 is a magic number. remove parts from lookup after a match but keep some short seeks back.
if (i > 9) {
this.activeParts = activeParts.slice(i - 9);
}
return activePart;
}
return activePart;
}
} else if (
mainFragEntity.body.start <= position &&
mainFragEntity.appendedPTS !== null &&
position <= mainFragEntity.appendedPTS
) {
return mainFragEntity.body;
}
} else if (
activeFragment.start <= position &&
activeFragment.appendedPTS !== undefined &&
position <= activeFragment.appendedPTS
) {
return activeFragment;
}
}
return this.getBufferedFrag(position, levelType);
Expand Down Expand Up @@ -362,6 +361,7 @@ export class FragmentTracker implements ComponentAPI {
const fragKey = getFragmentKey(frag);
this.fragments[fragKey] = {
body: frag,
appendedPTS: null,
loaded: data,
buffered: false,
range: Object.create(null),
Expand All @@ -373,10 +373,23 @@ export class FragmentTracker implements ComponentAPI {
data: BufferAppendedData
) {
const { frag, part, timeRanges } = data;
let mainFragEntity = this.mainFragEntity;
if (frag.type === PlaylistLevelType.MAIN) {
if (this.activeFragment !== frag) {
this.activeFragment = frag;
frag.appendedPTS = undefined;
const lastMainFrag = mainFragEntity ? mainFragEntity.body : null;
if (lastMainFrag !== frag) {
if (mainFragEntity && lastMainFrag && lastMainFrag.sn !== frag.sn) {
// archive frag for getBufferedFrag()
mainFragEntity.buffered = true;
this.fragments[getFragmentKey(lastMainFrag)] = mainFragEntity;
}
const fragKey = getFragmentKey(frag);
mainFragEntity = this.mainFragEntity = this.fragments[fragKey] || {
body: frag,
appendedPTS: null,
loaded: null,
buffered: false,
range: Object.create(null),
};
}
if (part) {
let activeParts = this.activeParts;
Expand All @@ -393,17 +406,20 @@ export class FragmentTracker implements ComponentAPI {
Object.keys(timeRanges).forEach((elementaryStream: SourceBufferName) => {
const timeRange = timeRanges[elementaryStream] as TimeRanges;
this.detectEvictedFragments(elementaryStream, timeRange);
if (!part && frag.type === PlaylistLevelType.MAIN) {
if (!part && mainFragEntity) {
const streamInfo = frag.elementaryStreams[elementaryStream];
if (!streamInfo) {
return;
}
for (let i = 0; i < timeRange.length; i++) {
const rangeEnd = timeRange.end(i);
if (rangeEnd <= streamInfo.endPTS && rangeEnd > streamInfo.startPTS) {
frag.appendedPTS = Math.max(rangeEnd, frag.appendedPTS || 0);
mainFragEntity.appendedPTS = Math.max(
rangeEnd,
mainFragEntity.appendedPTS || 0
);
} else {
frag.appendedPTS = streamInfo.endPTS;
mainFragEntity.appendedPTS = streamInfo.endPTS;
}
}
}
Expand Down Expand Up @@ -446,7 +462,9 @@ export class FragmentTracker implements ComponentAPI {
const fragKey = getFragmentKey(fragment);
fragment.stats.loaded = 0;
fragment.clearElementaryStreamInfo();
fragment.appendedPTS = undefined;
if (this.mainFragEntity === this.fragments[fragKey]) {
this.mainFragEntity = null;
}
delete this.fragments[fragKey];
if (fragment.endList) {
delete this.endListFragments[fragment.type];
Expand All @@ -456,7 +474,7 @@ export class FragmentTracker implements ComponentAPI {
public removeAllFragments() {
this.fragments = Object.create(null);
this.endListFragments = Object.create(null);
this.activeFragment = null;
this.mainFragEntity = null;
this.activeParts = null;
}
}
Expand Down
1 change: 0 additions & 1 deletion src/controller/level-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ export function mergeDetails(
) {
newFrag.start = newFrag.startPTS = oldFrag.startPTS as number;
newFrag.startDTS = oldFrag.startDTS;
newFrag.appendedPTS = oldFrag.appendedPTS;
newFrag.maxStartPTS = oldFrag.maxStartPTS;

newFrag.endPTS = oldFrag.endPTS;
Expand Down
2 changes: 0 additions & 2 deletions src/loader/fragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,6 @@ export class Fragment extends BaseSegment {
public startPTS?: number;
// The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.
public endPTS?: number;
// The latest Presentation Time Stamp (PTS) appended to the buffer.
public appendedPTS?: number;
// The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
public startDTS!: number;
// The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete.
Expand Down
3 changes: 3 additions & 0 deletions src/types/fragment-tracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import type { FragLoadedData } from './events';

export interface FragmentEntity {
body: Fragment;
// appendedPTS is the latest buffered presentation time within the fragment's time range.
// It is used to determine: which fragment is appended at any given position, and hls.currentLevel.
appendedPTS: number | null;
loaded: FragLoadedData | null;
buffered: boolean;
range: { [key in SourceBufferName]: FragmentBufferedRange };
Expand Down

0 comments on commit 4a9d594

Please sign in to comment.