constructor()

in lib/player.js [420:706]


  constructor(mediaElement, dependencyInjector) {
    super();

    /** @private {shaka.Player.LoadMode} */
    this.loadMode_ = shaka.Player.LoadMode.NOT_LOADED;

    /** @private {HTMLMediaElement} */
    this.video_ = null;

    /** @private {HTMLElement} */
    this.videoContainer_ = null;

    /**
     * Since we may not always have a text displayer created (e.g. before |load|
     * is called), we need to track what text visibility SHOULD be so that we
     * can ensure that when we create the text displayer. When we create our
     * text displayer, we will use this to show (or not show) text as per the
     * user's requests.
     *
     * @private {boolean}
     */
    this.isTextVisible_ = false;

    /** @private {shaka.util.EventManager} */
    this.eventManager_ = new shaka.util.EventManager();

    /** @private {shaka.net.NetworkingEngine} */
    this.networkingEngine_ = null;

    /** @private {shaka.media.DrmEngine} */
    this.drmEngine_ = null;

    /** @private {shaka.media.MediaSourceEngine} */
    this.mediaSourceEngine_ = null;

    /** @private {shaka.media.Playhead} */
    this.playhead_ = null;

    /**
     * The playhead observers are used to monitor the position of the playhead
     * and some other source of data (e.g. buffered content), and raise events.
     *
     * @private {shaka.media.PlayheadObserverManager}
     */
    this.playheadObservers_ = null;

    /**
     * This is our control over the playback rate of the media element. This
     * provides the missing functionality that we need to provide trick play,
     * for example a negative playback rate.
     *
     * @private {shaka.media.PlayRateController}
     */
    this.playRateController_ = null;

    // We use the buffering observer and timer to track when we move from having
    // enough buffered content to not enough. They only exist when content has
    // been loaded and are not re-used between loads.
    /** @private {shaka.util.Timer} */
    this.bufferPoller_ = null;

    /** @private {shaka.media.BufferingObserver} */
    this.bufferObserver_ = null;

    /** @private {shaka.media.RegionTimeline} */
    this.regionTimeline_ = null;

    /** @private {shaka.util.CmcdManager} */
    this.cmcdManager_ = null;

    /** @private {shaka.media.QualityObserver} */
    this.qualityObserver_ = null;

    /** @private {shaka.media.StreamingEngine} */
    this.streamingEngine_ = null;

    /** @private {shaka.extern.ManifestParser} */
    this.parser_ = null;

    /** @private {?shaka.extern.ManifestParser.Factory} */
    this.parserFactory_ = null;

    /** @private {?shaka.extern.Manifest} */
    this.manifest_ = null;

    /** @private {?string} */
    this.assetUri_ = null;

    /** @private {shaka.extern.AbrManager} */
    this.abrManager_ = null;

    /**
     * The factory that was used to create the abrManager_ instance.
     * @private {?shaka.extern.AbrManager.Factory}
     */
    this.abrManagerFactory_ = null;

    /**
     * Contains an ID for use with creating streams.  The manifest parser should
     * start with small IDs, so this starts with a large one.
     * @private {number}
     */
    this.nextExternalStreamId_ = 1e9;

    /** @private {?shaka.extern.PlayerConfiguration} */
    this.config_ = this.defaultConfig_();

    /**
     * The TextDisplayerFactory that was last used to make a text displayer.
     * Stored so that we can tell if a new type of text displayer is desired.
     * @private {?shaka.extern.TextDisplayer.Factory}
     */
    this.lastTextFactory_;

    /** @private {{width: number, height: number}} */
    this.maxHwRes_ = {width: Infinity, height: Infinity};

    /** @private {shaka.util.Stats} */
    this.stats_ = null;

    /** @private {!shaka.media.AdaptationSetCriteria} */
    this.currentAdaptationSetCriteria_ =
        new shaka.media.PreferenceBasedCriteria(
            this.config_.preferredAudioLanguage,
            this.config_.preferredVariantRole,
            this.config_.preferredAudioChannelCount);

    /** @private {string} */
    this.currentTextLanguage_ = this.config_.preferredTextLanguage;

    /** @private {string} */
    this.currentTextRole_ = this.config_.preferredTextRole;

    /** @private {boolean} */
    this.currentTextForced_ = this.config_.preferForcedSubs;

    /** @private {!Array.<function():(!Promise|undefined)>} */
    this.cleanupOnUnload_ = [];

    /**
     * This playback start position will be used when
     * <code>updateStartTime()</code> has been called to provide an updated
     * start position during the media loading process.
     *
     * @private {?number}
     */
    this.updatedStartTime_ = null;

    if (dependencyInjector) {
      dependencyInjector(this);
    }

    this.networkingEngine_ = this.createNetworkingEngine();
    this.networkingEngine_.setForceHTTPS(this.config_.streaming.forceHTTPS);

    /** @private {shaka.extern.IAdManager} */
    this.adManager_ = null;

    if (shaka.Player.adManagerFactory_) {
      this.adManager_ =
          shaka.util.Functional.callFactory(shaka.Player.adManagerFactory_);
    }

    // If the browser comes back online after being offline, then try to play
    // again.
    this.eventManager_.listen(window, 'online', () => {
      this.retryStreaming();
    });

    /** @private {shaka.routing.Node} */
    this.detachNode_ = {name: 'detach'};
    /** @private {shaka.routing.Node} */
    this.attachNode_ = {name: 'attach'};
    /** @private {shaka.routing.Node} */
    this.unloadNode_ = {name: 'unload'};
    /** @private {shaka.routing.Node} */
    this.parserNode_ = {name: 'manifest-parser'};
    /** @private {shaka.routing.Node} */
    this.manifestNode_ = {name: 'manifest'};
    /** @private {shaka.routing.Node} */
    this.mediaSourceNode_ = {name: 'media-source'};
    /** @private {shaka.routing.Node} */
    this.drmNode_ = {name: 'drm-engine'};
    /** @private {shaka.routing.Node} */
    this.loadNode_ = {name: 'load'};
    /** @private {shaka.routing.Node} */
    this.srcEqualsDrmNode_ = {name: 'src-equals-drm-engine'};
    /** @private {shaka.routing.Node} */
    this.srcEqualsNode_ = {name: 'src-equals'};

    const AbortableOperation = shaka.util.AbortableOperation;

    const actions = new Map();
    actions.set(this.attachNode_, (has, wants) => {
      return AbortableOperation.notAbortable(this.onAttach_(has, wants));
    });
    actions.set(this.detachNode_, (has, wants) => {
      return AbortableOperation.notAbortable(this.onDetach_(has, wants));
    });
    actions.set(this.unloadNode_, (has, wants) => {
      return AbortableOperation.notAbortable(this.onUnload_(has, wants));
    });
    actions.set(this.mediaSourceNode_, (has, wants) => {
      const p = this.onInitializeMediaSourceEngine_(has, wants);
      return AbortableOperation.notAbortable(p);
    });
    actions.set(this.parserNode_, (has, wants) => {
      const p = this.onInitializeParser_(has, wants);
      return AbortableOperation.notAbortable(p);
    });
    actions.set(this.manifestNode_, (has, wants) => {
      // This action is actually abortable, so unlike the other callbacks, this
      // one will return an abortable operation.
      return this.onParseManifest_(has, wants);
    });
    actions.set(this.drmNode_, (has, wants) => {
      const p = this.onInitializeDrm_(has, wants);
      return AbortableOperation.notAbortable(p);
    });
    actions.set(this.loadNode_, (has, wants) => {
      return AbortableOperation.notAbortable(this.onLoad_(has, wants));
    });

    actions.set(this.srcEqualsDrmNode_, (has, wants) => {
      const p = this.onInitializeSrcEqualsDrm_(has, wants);
      return AbortableOperation.notAbortable(p);
    });
    actions.set(this.srcEqualsNode_, (has, wants) => {
      return this.onSrcEquals_(has, wants);
    });

    /** @private {shaka.routing.Walker.Implementation} */
    const walkerImplementation = {
      getNext: (at, has, goingTo, wants) => {
        return this.getNextStep_(at, has, goingTo, wants);
      },
      enterNode: (node, has, wants) => {
        this.dispatchEvent(this.makeEvent_(
            /* name= */ shaka.Player.EventName.OnStateChange,
            /* data= */ (new Map()).set('state', node.name)));

        const action = actions.get(node);
        return action(has, wants);
      },
      handleError: async (has, error) => {
        shaka.log.warning('The walker saw an error:');
        if (error instanceof shaka.util.Error) {
          shaka.log.warning('Error Code:', error.code);
        } else {
          shaka.log.warning('Error Message:', error.message);
          shaka.log.warning('Error Stack:', error.stack);
        }

        // Regardless of what state we were in, if there is an error, we unload.
        // This ensures that any initialized system will be torn-down and we
        // will go back to a safe foundation. We assume that the media element
        // is always safe to use after an error.
        await this.onUnload_(has, shaka.Player.createEmptyPayload_());

        // There are only two nodes that come before we start loading content,
        // attach and detach. If we have a media element, it means we were
        // attached to the element, and we can safely return to the attach state
        // (we assume that the video element is always re-usable). We favor
        // returning to the attach node since it means that the app won't need
        // to re-attach if it saw an error.
        return has.mediaElement ? this.attachNode_ : this.detachNode_;
      },
      onIdle: (node) => {
        this.dispatchEvent(this.makeEvent_(
            /* name= */ shaka.Player.EventName.OnStateIdle,
            /* data= */ (new Map()).set('state', node.name)));
      },
    };

    /** @private {shaka.routing.Walker} */
    this.walker_ = new shaka.routing.Walker(
        this.detachNode_,
        shaka.Player.createEmptyPayload_(),
        walkerImplementation);

    // Even though |attach| will start in later interpreter cycles, it should be
    // the LAST thing we do in the constructor because conceptually it relies on
    // player having been initialized.
    if (mediaElement) {
      this.attach(mediaElement, /* initializeMediaSource= */ true);
    }
  }