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

16.67% Statements 6/36
25.71% Branches 9/35
25% Functions 1/4
16.67% Lines 6/36

    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

    14x

    7x

    7x
    2x
    2x

    5x

     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 () {}

      _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;
      }
    }