# All files / src/controller base-stream-controller.js

32.2% Statements 19/59
25.58% Branches 11/43
55.56% Functions 5/9
32.73% Lines 18/55

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

    1x

    86x
    86x

    86x

    86x
    86x
    86x
    86x
    86x

    7x

    7x
    2x
    2x

    5x

    48x
    48x

    48x
    48x

     import TaskLoop from '../task-loop';
    import { FragmentState } from './fragment-tracker';
    import { BufferHelper } from '../utils/buffer-helper';
    import { logger } from '../utils/logger';

    export const State = {
      STOPPED: 'STOPPED',
      STARTING: 'STARTING',
      IDLE: 'IDLE',
      PAUSED: 'PAUSED',
      KEY_LOADING: 'KEY_LOADING',
      FRAG_LOADING: 'FRAG_LOADING',
      FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY',
      WAITING_TRACK: 'WAITING_TRACK',
      PARSING: 'PARSING',
      PARSED: 'PARSED',
      BUFFER_FLUSHING: 'BUFFER_FLUSHING',
      ENDED: 'ENDED',
      ERROR: 'ERROR',
      WAITING_INIT_PTS: 'WAITING_INIT_PTS',
      WAITING_LEVEL: 'WAITING_LEVEL'
    };

    export default class BaseStreamController extends TaskLoop {
      doTick () {}

      startLoad () {}

      stopLoad () {
        let frag = this.fragCurrent;
        Iif (frag) {
          if (frag.loader) {
            frag.loader.abort();
          }
          this.fragmentTracker.removeFragment(frag);
        }
        Iif (this.demuxer) {
          this.demuxer.destroy();
          this.demuxer = null;
        }
        this.fragCurrent = null;
        this.fragPrevious = null;
        this.clearInterval();
        this.clearNextTick();
        this.state = State.STOPPED;
      }

      _streamEnded (bufferInfo, levelDetails) {
        const { fragCurrent, fragmentTracker } = this;
        // we just got done loading the final fragment and there is no other buffered range after ...
        // rationale is that in case there are any buffered ranges after, it means that there are unbuffered portion in between
        // so we should not switch to ENDED in that case, to be able to buffer them
        // dont switch to ENDED if we need to backtrack last fragment
        if (!levelDetails.live && fragCurrent && !fragCurrent.backtracked && fragCurrent.sn === levelDetails.endSN && !bufferInfo.nextStart) {
          const fragState = fragmentTracker.getState(fragCurrent);
          return fragState === FragmentState.PARTIAL || fragState === FragmentState.OK;
        }
        return false;
      }

      onMediaSeeking () {
        const { config, media, mediaBuffer, state } = this;
        const currentTime = media ? media.currentTime : null;
        const bufferInfo = BufferHelper.bufferInfo(mediaBuffer || media, currentTime, this.config.maxBufferHole);

        if (Number.isFinite(currentTime)) {
          logger.log(`media seeking to ${currentTime.toFixed(3)}`);
        }

        if (state === State.FRAG_LOADING) {
          let fragCurrent = this.fragCurrent;
          // check if we are seeking to a unbuffered area AND if frag loading is in progress
          if (bufferInfo.len === 0 && fragCurrent) {
            const tolerance = config.maxFragLookUpTolerance;
            const fragStartOffset = fragCurrent.start - tolerance;
            const fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance;
            // check if we seek position will be out of currently loaded frag range : if out cancel frag load, if in, don't do anything
            if (currentTime < fragStartOffset || currentTime > fragEndOffset) {
              if (fragCurrent.loader) {
                logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
                fragCurrent.loader.abort();
              }
              this.fragCurrent = null;
              this.fragPrevious = null;
              // switch to IDLE state to load new fragment
              this.state = State.IDLE;
            } else {
              logger.log('seeking outside of buffer but within currently loaded fragment range');
            }
          }
        } else if (state === State.ENDED) {
          // if seeking to unbuffered area, clean up fragPrevious
          if (bufferInfo.len === 0) {
            this.fragPrevious = null;
            this.fragCurrent = null;
          }

          // switch to IDLE state to check for potential new fragment
          this.state = State.IDLE;
        }
        if (media) {
          this.lastCurrentTime = currentTime;
        }

        // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target
        if (!this.loadedmetadata) {
          this.nextLoadPosition = this.startPosition = currentTime;
        }

        // tick to speed up processing
        this.tick();
      }

      onMediaEnded () {
        // reset startPosition and lastCurrentTime to restart playback @ stream beginning
        this.startPosition = this.lastCurrentTime = 0;
      }

      onHandlerDestroying () {
        this.stopLoad();
        super.onHandlerDestroying();
      }

      onHandlerDestroyed () {
        this.state = State.STOPPED;
        this.fragmentTracker = null;
      }

      computeLivePosition (sliding, levelDetails) {
        let targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration;
        return sliding + Math.max(0, levelDetails.totalduration - targetLatency);
      }
    }