# All files / src/controller fragment-finders.ts

84.85% Statements 28/33
90% Branches 36/40
100% Functions 4/4
96.55% Lines 28/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105

    12x
    4x

    8x
    8x
    1x

    7x
    7x
    1x

    6x
    6x
    16x
    16x
    6x

    10x
    5x

    5x
    3x

    2x

    26x

    13x
    13x
    3x
    10x

    3x

    7x

    20x

    20x
    20x

     import BinarySearch from '../utils/binary-search';
    import Fragment from '../loader/fragment';

    /**
     * Returns first fragment whose endPdt value exceeds the given PDT.
     * @param {Array<Fragment>} fragments - The array of candidate fragments
     * @param {number|null} [PDTValue = null] - The PDT value which must be exceeded
     * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start/end can be within in order to be considered contiguous
     * @returns {*|null} fragment - The best matching fragment
     */
    export function findFragmentByPDT (fragments: Array<Fragment>, PDTValue: number | null, maxFragLookUpTolerance: number): Fragment | null {
      if (PDTValue === null || !Array.isArray(fragments) || !fragments.length || !Number.isFinite(PDTValue)) {
        return null;
      }

      // if less than start
      const startPDT = fragments[0].programDateTime;
      if (PDTValue < (startPDT || 0)) {
        return null;
      }

      const endPDT = fragments[fragments.length - 1].endProgramDateTime;
      if (PDTValue >= (endPDT || 0)) {
        return null;
      }

      maxFragLookUpTolerance = maxFragLookUpTolerance || 0;
      for (let seg = 0; seg < fragments.length; ++seg) {
        let frag = fragments[seg];
        if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) {
          return frag;
        }
      }

      return null;
    }

    /**
     * Finds a fragment based on the SN of the previous fragment; or based on the needs of the current buffer.
     * This method compensates for small buffer gaps by applying a tolerance to the start of any candidate fragment, thus
     * breaking any traps which would cause the same fragment to be continuously selected within a small range.
     * @param {*} fragPrevious - The last frag successfully appended
     * @param {Array<Fragment>} fragments - The array of candidate fragments
     * @param {number} [bufferEnd = 0] - The end of the contiguous buffered range the playhead is currently within
     * @param {number} maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous
     * @returns {*} foundFrag - The best matching fragment
     */
    export function findFragmentByPTS (fragPrevious: Fragment, fragments: Array<Fragment>, bufferEnd: number = 0, maxFragLookUpTolerance: number = 0): Fragment | null I{
      const fragNext = fragPrevious ? fragments[fragPrevious.sn as number - (fragments[0].sn as number) + 1] : null;
      // Prefer the next fragment if it's within tolerance
      if (fragNext && !fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext)) {
        return fragNext;
      }
      return BinarySearch.search(fragments, fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance));
    }

    /**
     * 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
     * @param {number} [bufferEnd = 0] - The end of the current buffered range the playhead is currently within
     * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start can be within in order to be considered contiguous
     * @returns {number} - 0 if it matches, 1 if too low, -1 if too high
     */
    export function fragmentWithinToleranceTest (bufferEnd = 0, maxFragLookUpTolerance = 0, candidate: Fragment) I{
      // offset should be within fragment boundary - config.maxFragLookUpTolerance
      // this is to cope with situations like
      // bufferEnd = 9.991
      // frag[Ø] : [0,10]
      // frag[1] : [10,20]
      // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here
      //              frag start               frag start+duration
      //                  |-----------------------------|
      //              <--->                         <--->
      //  ...--------><-----------------------------><---------....
      // previous frag         matching fragment         next frag
      //  return -1             return 0                 return 1
      // logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`);
      // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments
      let candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0));
      if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) {
        return 1;
      } else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) {
        // if maxFragLookUpTolerance will have negative value then don't return -1 for first element
        return -1;
      }

      return 0;
    }

    /**
     * The test function used by the findFragmentByPdt's BinarySearch to look for the best match to the current buffer conditions.
     * This function tests the candidate's program date time values, as represented in Unix time
     * @param {*} candidate - The fragment to test
     * @param {number} [pdtBufferEnd = 0] - The Unix time representing the end of the current buffered range
     * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start can be within in order to be considered contiguous
     * @returns {boolean} True if contiguous, false otherwise
     */
    export function pdtWithinToleranceTest (pdtBufferEnd: number, maxFragLookUpTolerance: number, candidate: Fragment): boolean {
      let candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1000;

      // endProgramDateTime can be null, default to zero
      const endProgramDateTime = candidate.endProgramDateTime || 0;
      return endProgramDateTime - candidateLookupTolerance > pdtBufferEnd;
    }