parseProducerReferenceTime_(prftNode, streamInfo, presentationTimeline) {
    const TXml = shaka.util.TXml;

    if (this.parsedPrftNodes_.has(prftNode)) {
      return;
    }

    this.parsedPrftNodes_.add(prftNode);
    const presentationTime = TXml.parseAttr(
        prftNode, 'presentationTime', TXml.parseNonNegativeInt) || 0;
    const utcTiming = TXml.findChild(prftNode, 'UTCTiming');
    let wallClockTime;
    const parseAsNtp = !utcTiming || !utcTiming.attributes['schemeIdUri'] ||
        shaka.dash.DashParser.isNtpScheme_(utcTiming.attributes['schemeIdUri']);
    if (parseAsNtp) {
      const ntpTimestamp = TXml.parseAttr(
          prftNode, 'wallClockTime', TXml.parseNonNegativeInt) || 0;
      wallClockTime = shaka.util.TimeUtils.convertNtp(ntpTimestamp);
    } else {
      wallClockTime = (TXml.parseAttr(
          prftNode, 'wallClockTime', TXml.parseDate) || 0) * 1000;
    }
    const programStartDate = new Date(wallClockTime -
      (presentationTime / streamInfo.timescale) * 1000);
    const programStartTime = programStartDate.getTime() / 1000;
    if (!isNaN(programStartTime)) {
      if (!presentationTimeline.isStartTimeLocked()) {
        presentationTimeline.setInitialProgramDateTime(programStartTime);
      }
      /** @type {shaka.extern.ProducerReferenceTime} */
      const prftInfo = {
        wallClockTime,
        programStartDate,
      };
      const eventName = shaka.util.FakeEvent.EventName.Prft;
      const data = (new Map()).set('detail', prftInfo);
      const event = new shaka.util.FakeEvent(eventName, data);
      this.playerInterface_.onEvent(event);
    }
  }
@param {!shaka.extern.xml.Node} prftNode
@param {!shaka.dash.DashParser.StreamInfo} streamInfo
@param {!shaka.media.PresentationTimeline} presentationTimeline
@private