Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix current quality reporting when seeking back from edge in Low-Latency streams #5102

Merged
merged 1 commit into from
Jan 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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