# All files / src/utils discontinuities.js

75.41% Statements 46/61
65.96% Branches 31/47
60% Functions 6/10
75.41% Lines 46/61

    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
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    13016x
    16x

    16x
    2x

    2x
    2x
    2x
    1x
    1x

    2x

    16x

    16x
    6x
    6x
    3x
    2x

    6x

    16x
    4x
    4x

    4x
    2x
    2x

    2x

    2x
    1x
    1x

    1x

    16x
    2x
    6x
    6x
    6x
    6x

    2x

    16x

    16x

    16x
    1x
    1x

    1x
    1x

    1x
    1x
    1x
    1x

     import BinarySearch from './binary-search';
    import { logger } from '../utils/logger';

    export function findFirstFragWithCC (fragments, cc) {
      let firstFrag = null;

      for (let i = 0; i < fragments.length; i += 1) {
        const currentFrag = fragments[i];
        if (currentFrag && currentFrag.cc === cc) {
          firstFrag = currentFrag;
          break;
        }
      }

      return firstFrag;
    }

    export function findFragWithCC (fragments, CC) {
      return BinarySearch.search(fragments, (candidate) => {
        if (candidate.cc < CC) {
          return 1;
        } else if (candidate.cc > CC) {
          return -1;
        } else {
          return 0;
        }
      });
    }

    export function shouldAlignOnDiscontinuities (lastFrag, lastLevel, details) {
      let shouldAlign = false;
      if (lastLevel && lastLevel.details && details) {
        if (details.endCC > details.startCC || (lastFrag && lastFrag.cc < details.startCC)) {
          shouldAlign = true;
        }
      }
      return shouldAlign;
    }

    // Find the first frag in the previous level which matches the CC of the first frag of the new level
    export function findDiscontinuousReferenceFrag (prevDetails, curDetails) {
      const prevFrags = prevDetails.fragments;
      const curFrags = curDetails.fragments;

      if (!curFrags.length || !prevFrags.length) {
        logger.log('No fragments to align');
        return;
      }

      const prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc);

      if (!prevStartFrag || (prevStartFrag && !prevStartFrag.startPTS)) {
        logger.log('No frag in previous level to align on');
        return;
      }

      return prevStartFrag;
    }

    export function adjustPts (sliding, details) {
      details.fragments.forEach((frag) => {
        Eif (frag) {
          let start = frag.start + sliding;
          frag.start = frag.startPTS = start;
          frag.endPTS = start + frag.duration;
        }
      });
      details.PTSKnown = true;
    }

    /**
     * Using the parameters of the last level, this function computes PTS' of the new fragments so that they form a
     * contiguous stream with the last fragments.
     * The PTS of a fragment lets Hls.js know where it fits into a stream - by knowing every PTS, we know which fragment to
     * download at any given time. PTS is normally computed when the fragment is demuxed, so taking this step saves us time
     * and an extra download.
     * @param lastFrag
     * @param lastLevel
     * @param details
     */
    export function alignStream (lastFrag, lastLevel, details) {
      alignDiscontinuities(lastFrag, details, lastLevel);
      if (!details.PTSKnown && lastLevel) {
        // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level.
        // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same
        // discontinuity sequence.
        alignPDT(details, lastLevel.details);
      }
    }

    /**
     * Computes the PTS if a new level's fragments using the PTS of a fragment in the last level which shares the same
     * discontinuity sequence.
     * @param lastLevel - The details of the last loaded level
     * @param details - The details of the new level
     */
    export function alignDiscontinuities (lastFrag, details, lastLevel) {
      if (shouldAlignOnDiscontinuities(lastFrag, lastLevel, details)) {
        const referenceFrag = findDiscontinuousReferenceFrag(lastLevel.details, details);
        if (referenceFrag) {
          logger.log('Adjusting PTS using last level due to CC increase within current level');
          adjustPts(referenceFrag.start, details);
        }
      }
    }

    /**
     * Computes the PTS of a new level's fragments using the difference in Program Date Time from the last level.
     * @param details - The details of the new level
     * @param lastDetails - The details of the last loaded level
     */
    export function alignPDT (details, lastDetails) {
      Eif (lastDetails && lastDetails.fragments.length) {
        Iif (!details.hasProgramDateTime || !lastDetails.hasProgramDateTime) {
          return;
        }
        // if last level sliding is 1000 and its first frag PROGRAM-DATE-TIME is 2017-08-20 1:10:00 AM
        // and if new details first frag PROGRAM DATE-TIME is 2017-08-20 1:10:08 AM
        // then we can deduce that playlist B sliding is 1000+8 = 1008s
        let lastPDT = lastDetails.fragments[0].programDateTime;
        let newPDT = details.fragments[0].programDateTime;
        // date diff is in ms. frag.start is in seconds
        let sliding = (newPDT - lastPDT) / 1000 + lastDetails.fragments[0].start;
        Eif (Number.isFinite(sliding)) {
          logger.log(`adjusting PTS using programDateTime delta, sliding:${sliding.toFixed(3)}`);
          adjustPts(sliding, details);
        }
      }
    }