setPublishingLayersForSender method

Future<void> setPublishingLayersForSender(
  1. RTCRtpSender sender,
  2. List<RTCRtpEncoding> encodings,
  3. List<SubscribedQuality> layers, {
  4. bool isSVC = false,
})

Implementation

Future<void> setPublishingLayersForSender(
  rtc.RTCRtpSender sender,
  List<rtc.RTCRtpEncoding> encodings,
  List<lk_rtc.SubscribedQuality> layers, {
  bool isSVC = false,
}) async {
  logger.fine('Update publishing layers: $layers');

  final params = sender.parameters;

  var hasChanged = false;

  // NOTE: closable spatial layer is disabled due to video blur / frozen issues
  // with Chrome 113+ and LiveKit SFU PLI handling. See JS SDK LocalVideoTrack.ts:529-568.
  // For SVC codecs, all layers are kept enabled and the SFU handles layer selection.
  if (isSVC) {
    final hasEnabledEncoding = layers.any((q) => q.enabled);
    if (hasEnabledEncoding) {
      for (var q in layers) {
        q.enabled = true;
      }
    }
  }
  // simulcast dynacast encodings
  var idx = 0;
  for (var encoding in encodings) {
    var rid = encoding.rid ?? '';
    if (rid == '') {
      rid = 'q';
    }
    final quality = _videoQualityForRid(rid);
    final subscribedQuality = layers.firstWhereOrNull(
      (q) => q.quality == quality,
    );
    if (subscribedQuality == null) {
      continue;
    }
    if (encoding.active != subscribedQuality.enabled) {
      hasChanged = true;
      encoding.active = subscribedQuality.enabled;
      logger.fine(
        'setting layer ${subscribedQuality.quality} to ${encoding.active ? 'enabled' : 'disabled'}',
      );

      // FireFox does not support setting encoding.active to false, so we
      // have a workaround of lowering its bitrate and resolution to the min.
      if (kIsWeb && lkBrowser() == BrowserType.firefox) {
        if (subscribedQuality.enabled) {
          final encodingBackup = encodingBackups[(sender.senderId, idx)] ?? encoding;
          encoding.scaleResolutionDownBy = encodingBackup.scaleResolutionDownBy;
          encoding.maxBitrate = encodingBackup.maxBitrate;
          encoding.maxFramerate = encodingBackup.maxFramerate;
        } else {
          encodingBackups[(sender.senderId, idx)] = rtc.RTCRtpEncoding(
            scaleResolutionDownBy: encoding.scaleResolutionDownBy,
            maxBitrate: encoding.maxBitrate,
            maxFramerate: encoding.maxFramerate,
          );
          encoding.scaleResolutionDownBy = 4;
          encoding.maxBitrate = 10;
          encoding.maxFramerate = 2;
        }
      }
    }
    idx++;
  }

  if (hasChanged) {
    params.encodings = encodings;
    try {
      final result = await sender.setParameters(params);
      if (result == false) {
        logger.warning('Failed to update sender parameters');
      }
    } catch (e) {
      logger.warning('Failed to update sender parameters $e');
    }
  }
}