constructor(playerInterface) {
    /** @private {?shaka.drm.DrmEngine.PlayerInterface} */
    this.playerInterface_ = playerInterface;

    /** @private {MediaKeys} */
    this.mediaKeys_ = null;

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

    /** @private {boolean} */
    this.initialized_ = false;

    /** @private {boolean} */
    this.initializedForStorage_ = false;

    /** @private {number} */
    this.licenseTimeSeconds_ = 0;

    /** @private {?shaka.extern.DrmInfo} */
    this.currentDrmInfo_ = null;

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

    /**
     * @private {!Map<MediaKeySession,
     *           shaka.drm.DrmEngine.SessionMetaData>}
     */
    this.activeSessions_ = new Map();

    /** @private {!Array<!shaka.net.NetworkingEngine.PendingRequest>} */
    this.activeRequests_ = [];

    /**
     * @private {!Map<string,
     *           {initData: ?Uint8Array, initDataType: ?string}>}
     */
    this.storedPersistentSessions_ = new Map();

    /** @private {boolean} */
    this.hasInitData_ = false;

    /** @private {!shaka.util.PublicPromise} */
    this.allSessionsLoaded_ = new shaka.util.PublicPromise();

    /** @private {?shaka.extern.DrmConfiguration} */
    this.config_ = null;

    /** @private {function(!shaka.util.Error)} */
    this.onError_ = (err) => {
      if (err.severity == shaka.util.Error.Severity.CRITICAL) {
        this.allSessionsLoaded_.reject(err);
      }

      playerInterface.onError(err);
    };

    /**
     * The most recent key status information we have.
     * We may not have announced this information to the outside world yet,
     * which we delay to batch up changes and avoid spurious "missing key"
     * errors.
     * @private {!Map<string, string>}
     */
    this.keyStatusByKeyId_ = new Map();

    /**
     * The key statuses most recently announced to other classes.
     * We may have more up-to-date information being collected in
     * this.keyStatusByKeyId_, which has not been batched up and released yet.
     * @private {!Map<string, string>}
     */
    this.announcedKeyStatusByKeyId_ = new Map();

    /** @private {shaka.util.Timer} */
    this.keyStatusTimer_ =
        new shaka.util.Timer(() => this.processKeyStatusChanges_());

    /** @private {boolean} */
    this.usePersistentLicenses_ = false;

    /** @private {!Array<!MediaKeyMessageEvent>} */
    this.mediaKeyMessageEvents_ = [];

    /** @private {boolean} */
    this.initialRequestsSent_ = false;

    /** @private {?shaka.util.Timer} */
    this.expirationTimer_ = new shaka.util.Timer(() => {
      this.pollExpiration_();
    });

    // Add a catch to the Promise to avoid console logs about uncaught errors.
    const noop = () => {};
    this.allSessionsLoaded_.catch(noop);

    /** @const {!shaka.util.Destroyer} */
    this.destroyer_ = new shaka.util.Destroyer(() => this.destroyNow_());

    /** @private {boolean} */
    this.srcEquals_ = false;

    /** @private {Promise} */
    this.mediaKeysAttached_ = null;

    /** @private {?shaka.extern.InitDataOverride} */
    this.manifestInitData_ = null;

    /** @private {function():boolean} */
    this.isPreload_ = () => false;
  }
@param {shaka.drm.DrmEngine.PlayerInterface} playerInterface