From e1c2904da78e76c882b529f9ae9444b4fa0bb4e8 Mon Sep 17 00:00:00 2001 From: Rob Walch Date: Fri, 31 May 2024 17:31:26 -0700 Subject: [PATCH] Don't append over first fragment when next fragment aligns with playlist within 1/200s tolerance (#6471) Fixes edge-case starting in v1.5 that causes #6441 --- src/controller/base-stream-controller.ts | 5 ++-- src/controller/fragment-finders.ts | 37 ++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/controller/base-stream-controller.ts b/src/controller/base-stream-controller.ts index fec1a33ffa2..6a3674d9874 100644 --- a/src/controller/base-stream-controller.ts +++ b/src/controller/base-stream-controller.ts @@ -1278,7 +1278,7 @@ export default class BaseStreamController let { fragPrevious } = this; let { fragments, endSN } = levelDetails; const { fragmentHint } = levelDetails; - const tolerance = config.maxFragLookUpTolerance; + const { maxFragLookUpTolerance } = config; const partList = levelDetails.partList; const loadingParts = !!( @@ -1294,7 +1294,8 @@ export default class BaseStreamController let frag; if (bufferEnd < end) { - const lookupTolerance = bufferEnd > end - tolerance ? 0 : tolerance; + const lookupTolerance = + bufferEnd > end - maxFragLookUpTolerance ? 0 : maxFragLookUpTolerance; // Remove the tolerance if it would put the bufferEnd past the actual end of stream // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE) frag = findFragmentByPTS( diff --git a/src/controller/fragment-finders.ts b/src/controller/fragment-finders.ts index 47f19ae2c30..c289e9dab60 100644 --- a/src/controller/fragment-finders.ts +++ b/src/controller/fragment-finders.ts @@ -58,6 +58,7 @@ export function findFragmentByPTS( fragments: Array, bufferEnd: number = 0, maxFragLookUpTolerance: number = 0, + nextFragLookupTolerance: number = 0.005, ): Fragment | null { let fragNext: Fragment | null = null; if (fragPrevious) { @@ -76,9 +77,17 @@ export function findFragmentByPTS( // Prefer the next fragment if it's within tolerance if ( fragNext && - (!fragPrevious || fragPrevious.level === fragNext.level) && - fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === - 0 + (((!fragPrevious || fragPrevious.level === fragNext.level) && + fragmentWithinToleranceTest( + bufferEnd, + maxFragLookUpTolerance, + fragNext, + ) === 0) || + fragmentWithinFastStartSwitch( + fragNext, + fragPrevious, + Math.min(nextFragLookupTolerance, maxFragLookUpTolerance), + )) ) { return fragNext; } @@ -94,6 +103,28 @@ export function findFragmentByPTS( return fragNext; } +function fragmentWithinFastStartSwitch( + fragNext: Fragment, + fragPrevious: Fragment | null, + nextFragLookupTolerance: number, +): boolean { + if ( + fragPrevious && + fragPrevious.start === 0 && + fragPrevious.level < fragNext.level && + (fragPrevious.endPTS || 0) > 0 + ) { + const firstDuration = fragPrevious.tagList.reduce((duration, tag) => { + if (tag[0] === 'INF') { + duration += parseFloat(tag[1]); + } + return duration; + }, nextFragLookupTolerance); + return fragNext.start <= firstDuration; + } + return false; +} + /** * The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions. * @param candidate - The fragment to test