private calculateEncodingParameters()

in src/videouplinkbandwidthpolicy/DefaultSimulcastUplinkPolicy.ts [100:201]


  private calculateEncodingParameters(
    numSendersChanged: boolean
  ): Map<string, RTCRtpEncodingParameters> {
    // bitrates parameter min is not used for now
    const newBitrates: BitrateParameters[] = [
      new BitrateParameters(),
      new BitrateParameters(),
      new BitrateParameters(),
    ];

    let hysteresisIncrease = 0,
      hysteresisDecrease = 0;
    if (this.currentActiveStreams === ActiveStreams.kHi) {
      // Don't trigger redetermination based on rate if only one simulcast stream
      hysteresisIncrease = this.lastUplinkBandwidthKbps + 1;
      hysteresisDecrease = 0;
    } else if (this.currentActiveStreams === ActiveStreams.kHiAndLow) {
      hysteresisIncrease = 2400;
      hysteresisDecrease = DefaultSimulcastUplinkPolicy.kHiDisabledRate;
    } else if (this.currentActiveStreams === ActiveStreams.kMidAndLow) {
      hysteresisIncrease = 1000;
      hysteresisDecrease = DefaultSimulcastUplinkPolicy.kMidDisabledRate;
    } else {
      hysteresisIncrease = 300;
      hysteresisDecrease = 0;
    }

    if (
      numSendersChanged ||
      this.lastUplinkBandwidthKbps >= hysteresisIncrease ||
      this.lastUplinkBandwidthKbps <= hysteresisDecrease
    ) {
      if (this.shouldDisableSimulcast) {
        // See comment above `shouldDisableSimulcast` for usage.
        //
        // The value of `newActiveStreams` is somewhat irrelevant since in one to one calls
        // we forward REMBs, so this single stream will adapt anywhere from < 100 kbps to 1200 kbps
        // based on both sender and receiver network conditions. E.g. A receiver may calculate it's
        // receive BWE as 300 kbps, send that in a REMB which is forwarded, and on receipt the sender
        // will set its own BWE at 300 kbps, and start sending that as well (again, only for one-to-one
        // calls). Additionally the value `kHi` is only relevant to the send side (via
        // `encodingSimulcastLayersDidChange`) as it is not transmitted in anyform to the receiver.
        //
        // We use middle layer here to work around a bug in Chromium where
        // it seems when a transceiver is created when BWE is low (e.g. on a reconnection),
        // it will never reset the encoder even when `setParameters` is called.  WebRTC bug
        // #12788 seems to call a similar issue out as fixed for VP8, it's not clear if this
        // is the same issue for H.264. Additionally we are not able to force a keyframe
        // request from the backend since it will only be sending padding (which also
        // don't have MID due to #10822). Since we don't scale when simulcast is disabled
        // this doesn't have any end-user effect.
        //
        // Note that this still relies on a little bit (5-6 packets) of padding on reconnect
        // and that technically the browser will still eventually try to send all 3 streams.
        //
        // Also note that due to some uninvestigated logic in bitrate allocation, Chromium
        // will skip the bottom layer if we try setting it to 1200 kbps instead so it will
        // still take a while to recover (as it needs to send padding until it reaches around
        // 1000 kbps).
        this.newActiveStreams = ActiveStreams.kHi;
        newBitrates[0].maxBitrateKbps = 0;
        newBitrates[1].maxBitrateKbps = 1200;
        newBitrates[2].maxBitrateKbps = 0;
      } else if (
        this.numSenders <= 4 &&
        this.lastUplinkBandwidthKbps >= DefaultSimulcastUplinkPolicy.kHiDisabledRate
      ) {
        // 320x192+ (640x384)  + 1280x768
        this.newActiveStreams = ActiveStreams.kHiAndLow;
        newBitrates[0].maxBitrateKbps = 300;
        newBitrates[1].maxBitrateKbps = 0;
        newBitrates[2].maxBitrateKbps = 1200;
      } else if (this.lastUplinkBandwidthKbps >= DefaultSimulcastUplinkPolicy.kMidDisabledRate) {
        // 320x192 + 640x384 + (1280x768)
        this.newActiveStreams = ActiveStreams.kMidAndLow;
        newBitrates[0].maxBitrateKbps = this.lastUplinkBandwidthKbps >= 350 ? 200 : 150;
        newBitrates[1].maxBitrateKbps = this.numSenders <= 6 ? 600 : 350;
        newBitrates[2].maxBitrateKbps = 0;
      } else {
        // 320x192 + 640x384 + (1280x768)
        this.newActiveStreams = ActiveStreams.kLow;
        newBitrates[0].maxBitrateKbps = 300;
        newBitrates[1].maxBitrateKbps = 0;
        newBitrates[2].maxBitrateKbps = 0;
      }
      const bitrates: number[] = newBitrates.map((v, _i, _a) => {
        return v.maxBitrateKbps;
      });

      this.newQualityMap = this.fillEncodingParamWithBitrates(bitrates);
      if (!this.encodingParametersEqual()) {
        this.logger.info(
          `simulcast: policy:calculateEncodingParameters bw:${
            this.lastUplinkBandwidthKbps
          } numSources:${this.numSenders} shouldDisableSimulcast:${
            this.shouldDisableSimulcast
          } newQualityMap: ${this.getQualityMapString(this.newQualityMap)}`
        );
      }
    }
    return this.newQualityMap;
  }