Fix Low-Latency HLS subtitle part loading (#7893)

Fixes #7885
This commit is contained in:
Rob Walch
2026-06-04 12:44:57 -07:00
committed by GitHub
parent 13f71b93e1
commit ed68f686d2
5 changed files with 37 additions and 5 deletions
+21 -1
View File
@@ -1998,6 +1998,26 @@ export class Fragment extends BaseSegment {
urlId: number;
}
// Warning: (ae-missing-release-tag) "FragmentEntity" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export interface FragmentEntity {
// (undocumented)
appendedPTS: number | null;
// (undocumented)
body: MediaFragment;
// (undocumented)
buffered: boolean;
// (undocumented)
loaded: FragLoadedData | null;
// Warning: (ae-forgotten-export) The symbol "FragmentBufferedRange" needs to be exported by the entry point hls.d.ts
//
// (undocumented)
range: {
[key in SourceBufferName | 'subs']: FragmentBufferedRange;
};
}
// Warning: (ae-missing-release-tag) "FragmentLoader" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -2076,7 +2096,7 @@ export class FragmentTracker implements ComponentAPI {
detectEvictedFragments(elementaryStream: SourceBufferName, timeRange: TimeRanges, playlistType: PlaylistLevelType, appendedFrag?: MediaFragment | null, appendedPart?: Part | null, removeAppending?: boolean): void;
detectPartialFragments(data: FragBufferedData): void;
// (undocumented)
fragBuffered(frag: MediaFragment, force?: true): void;
fragBuffered(frag: MediaFragment, force?: true): FragmentEntity | undefined;
getAppendedFrag(position: number, levelType: PlaylistLevelType): MediaFragment | Part | null;
getBackBufferEvictionEnd(beforePosition: number, levelType: PlaylistLevelType, bytesNeeded: number): number;
getBufferedFrag(position: number, levelType: PlaylistLevelType): MediaFragment | null;
+7 -2
View File
@@ -318,7 +318,10 @@ export class FragmentTracker implements ComponentAPI {
);
}
public fragBuffered(frag: MediaFragment, force?: true) {
public fragBuffered(
frag: MediaFragment,
force?: true,
): FragmentEntity | undefined {
const fragKey = getFragmentKey(frag);
let fragmentEntity = this.fragments[fragKey];
if (!fragmentEntity && force) {
@@ -337,6 +340,7 @@ export class FragmentTracker implements ComponentAPI {
fragmentEntity.loaded = null;
this.bufferedEnd(fragmentEntity, frag);
}
return fragmentEntity;
}
private getBufferedTimes(
@@ -646,7 +650,8 @@ function isPartial(fragmentEntity: FragmentEntity): boolean {
fragmentEntity.body.gap ||
fragmentEntity.range.video?.partial ||
fragmentEntity.range.audio?.partial ||
fragmentEntity.range.audiovideo?.partial
fragmentEntity.range.audiovideo?.partial ||
fragmentEntity.range.subs?.partial
)
);
}
+7 -1
View File
@@ -156,7 +156,13 @@ export class SubtitleStreamController
buffered.push(timeRange);
}
if (!part || end >= frag.end) {
this.fragmentTracker.fragBuffered(frag as MediaFragment);
const entity = this.fragmentTracker.fragBuffered(frag as MediaFragment);
if (part && entity) {
entity.range.subs = {
time: [{ startPTS: frag.start, endPTS: end }],
partial: true,
};
}
if (isMediaFragment(frag) && !this.fragContextChanged(frag)) {
this.fragPrevious = frag;
}
+1
View File
@@ -1453,6 +1453,7 @@ export type {
FragmentState,
FragmentTracker,
} from './controller/fragment-tracker';
export type { FragmentEntity } from './types/fragment-tracker';
export type {
PathwayClone,
SteeringManifest,
+1 -1
View File
@@ -9,7 +9,7 @@ export interface FragmentEntity {
appendedPTS: number | null;
loaded: FragLoadedData | null;
buffered: boolean;
range: { [key in SourceBufferName]: FragmentBufferedRange };
range: { [key in SourceBufferName | 'subs']: FragmentBufferedRange };
}
export interface FragmentTimeRange {