mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
fix(spf): bridge media-playlist turnover via PDT, not the target-duration estimate
When a live reload skips the whole window (no media-sequence overlap), placeOnPreviousTimeline bridged the gap with a target-duration estimate, which drifts whenever actual segment durations differ from the declared ceiling. PROGRAM-DATE-TIME is the spec-consistent cross-reload reference and places the turnover window exactly; use it when present, falling back to the estimate only when PDT is absent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
bc617eb1e7
commit
2a1fe85c28
@@ -110,10 +110,20 @@ function placeOnPreviousTimeline(
|
||||
anchor = localBase;
|
||||
} else {
|
||||
const last = prevSegments[prevSegments.length - 1]!;
|
||||
anchor = last.startTime + last.duration + (offset - prevSegments.length) * targetDuration;
|
||||
console.warn(
|
||||
`[parseMediaPlaylist] full window turnover (offset ${offset} >= ${prevSegments.length}); estimating from previous end.`
|
||||
);
|
||||
const newFirst = segments[0]!;
|
||||
if (last.startDate !== undefined && newFirst.startDate !== undefined) {
|
||||
// PDT bridges the gap exactly — the spec-consistent cross-reload reference,
|
||||
// immune to the target-duration over-estimate when actual segment durations
|
||||
// differ from the declared ceiling. (`startDate` is intrinsic PDT, unaffected
|
||||
// by the new window's local positioning.)
|
||||
anchor = last.startTime + (newFirst.startDate - last.startDate);
|
||||
} else {
|
||||
anchor = last.startTime + last.duration + (offset - prevSegments.length) * targetDuration;
|
||||
console.warn(
|
||||
`[parseMediaPlaylist] full window turnover (offset ${offset} >= ${prevSegments.length}); ` +
|
||||
'no PDT to bridge — estimating from previous end.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const shift = anchor - localBase;
|
||||
|
||||
@@ -546,6 +546,38 @@ segment11.m4s`;
|
||||
// anchor = previous end (18) + (offset 10 − 3) × 6 = 60
|
||||
expect(next.segments.map((s) => s.startTime)).toEqual([60, 66]);
|
||||
});
|
||||
|
||||
it('bridges a full window turnover exactly via PDT, not the target-duration estimate', () => {
|
||||
// Actual segment duration (5s) is below the declared TARGETDURATION (6s), so
|
||||
// the target-duration estimate over-shoots — PDT (the spec-consistent
|
||||
// cross-reload reference) places the turnover window exactly.
|
||||
const first = `#EXTM3U
|
||||
#EXT-X-TARGETDURATION:6
|
||||
#EXT-X-MEDIA-SEQUENCE:0
|
||||
#EXT-X-PROGRAM-DATE-TIME:2024-01-01T00:00:00.000Z
|
||||
#EXTINF:5.0,
|
||||
segment0.m4s
|
||||
#EXTINF:5.0,
|
||||
segment1.m4s
|
||||
#EXTINF:5.0,
|
||||
segment2.m4s`;
|
||||
const previous = parseMediaPlaylist(first, unresolvedVideo); // [0, 5, 10], seg2 PDT = origin+10
|
||||
|
||||
// Turnover (offset 10 ≥ 3), 50s of real elapsed (10 × 5s) — PDT says so.
|
||||
const reload = `#EXTM3U
|
||||
#EXT-X-TARGETDURATION:6
|
||||
#EXT-X-MEDIA-SEQUENCE:10
|
||||
#EXT-X-PROGRAM-DATE-TIME:2024-01-01T00:00:50.000Z
|
||||
#EXTINF:5.0,
|
||||
segment10.m4s
|
||||
#EXTINF:5.0,
|
||||
segment11.m4s`;
|
||||
const next = parseMediaPlaylist(reload, previous);
|
||||
|
||||
// PDT-exact: seg2 sits at 10 with PDT origin+10; seg10 is origin+50 → 10 + 40 = 50.
|
||||
// (The target-duration estimate would over-shoot to 15 + (10−3)×6 = 57.)
|
||||
expect(next.segments.map((s) => s.startTime)).toEqual([50, 55]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('EXT-X-PROGRAM-DATE-TIME', () => {
|
||||
|
||||
Reference in New Issue
Block a user