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

95.83% Statements 23/24
87.88% Branches 29/33
100% Functions 4/4
95.83% Lines 23/24

    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

    12x
    4x

    8x
    1x

    7x
    1x

    6x
    6x
    16x
    16x
    6x

    5x

    5x
    3x

    2x

    13x
    13x
    3x
    10x

    3x

    7x

    20x
    20x

     import BinarySearch from '../utils/binary-search';

    /**
     * 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, PDTValue, maxFragLookUpTolerance) {
      if (!Array.isArray(fragments) || !fragments.length || !Number.isFinite(PDTValue)) {
        return null;
      }

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

      if (PDTValue >= fragments[fragments.length - 1].endProgramDateTime) {
        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, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0) {
      const fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 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) {
      // 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, maxFragLookUpTolerance, candidate) {
      let candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1000;
      return candidate.endProgramDateTime - candidateLookupTolerance > pdtBufferEnd;
    }