/// <summary> /// Parses the given scalabilityMode string according to the rules in webrtc-svc. /// </summary> /// <param name="scalabilityMode"></param> /// <returns></returns> public static ScalabilityMode Parse(string scalabilityMode) { var match = ScalabilityModeRegex.Match(scalabilityMode); var result = new ScalabilityMode(); if (match.Success) { result.SpatialLayers = int.Parse(match.Groups[1].Value); result.TemporalLayers = int.Parse(match.Groups[2].Value); result.Ksvc = match.Groups.Count >= 4 && match.Groups[3].Value == "_KEY"; } else { result.SpatialLayers = 1; result.TemporalLayers = 1; result.Ksvc = false; } return(result); }
/// <summary> /// Generate RTP parameters for a specific Consumer. /// /// It reduces encodings to just one and takes into account given RTP capabilities /// to reduce codecs, codecs' RTCP feedback and header extensions, and also enables /// or disabled RTX. /// </summary> public static RtpParameters GetConsumerRtpParameters(RtpParameters consumableParams, RtpCapabilities caps) { var consumerParams = new RtpParameters { Codecs = new List <RtpCodecParameters>(), HeaderExtensions = new List <RtpHeaderExtensionParameters>(), Encodings = new List <RtpEncodingParameters>(), Rtcp = consumableParams.Rtcp }; foreach (var capCodec in caps.Codecs) // TODO: (alby)注意 null 引用 { ValidateRtpCodecCapability(capCodec); } var consumableCodecs = consumableParams.Codecs.DeepClone <List <RtpCodecParameters> >(); var rtxSupported = false; foreach (var codec in consumableCodecs) { var matchedCapCodec = caps.Codecs .Where(capCodec => MatchCodecs(capCodec, codec, true)) .FirstOrDefault(); if (matchedCapCodec == null) { continue; } codec.RtcpFeedback = matchedCapCodec.RtcpFeedback; consumerParams.Codecs.Add(codec); if (!rtxSupported && IsRtxMimeType(codec.MimeType)) { rtxSupported = true; } } // Ensure there is at least one media codec. if (consumerParams.Codecs.Count == 0 || IsRtxMimeType(consumerParams.Codecs[0].MimeType)) { throw new Exception("no compatible media codecs"); } consumerParams.HeaderExtensions = consumableParams.HeaderExtensions .Where(ext => caps.HeaderExtensions .Any(capExt => capExt.PreferredId == ext.Id && capExt.Uri == ext.Uri) ).ToList(); // Reduce codecs' RTCP feedback. Use Transport-CC if available, REMB otherwise. if (consumerParams.HeaderExtensions.Any(ext => ext.Uri == "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01")) { foreach (var codec in consumerParams.Codecs) { codec.RtcpFeedback = codec.RtcpFeedback.Where(fb => fb.Type != "goog-remb").ToArray(); } } else if (consumerParams.HeaderExtensions.Any(ext => ext.Uri == "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time")) { foreach (var codec in consumerParams.Codecs) { codec.RtcpFeedback = codec.RtcpFeedback.Where(fb => fb.Type != "transport-cc").ToArray(); } } else { foreach (var codec in consumerParams.Codecs) { codec.RtcpFeedback = codec.RtcpFeedback.Where(fb => fb.Type != "transport-cc" && fb.Type != "goog-remb").ToArray(); } } var consumerEncoding = new RtpEncodingParameters { Ssrc = Utils.GenerateRandomNumber() }; if (rtxSupported) { consumerEncoding.Rtx = new Rtx { Ssrc = Utils.GenerateRandomNumber() }; } // If any in the consumableParams.Encodings has scalabilityMode, process it // (assume all encodings have the same value). var encodingWithScalabilityMode = consumableParams.Encodings.Where(encoding => !encoding.ScalabilityMode.IsNullOrWhiteSpace()).FirstOrDefault(); var scalabilityMode = encodingWithScalabilityMode?.ScalabilityMode; // If there is simulast, mangle spatial layers in scalabilityMode. if (consumableParams.Encodings.Count > 1) // TODO: (alby)注意 null 引用 { var scalabilityModeObject = ScalabilityMode.Parse(scalabilityMode); // TODO: (alby)注意 null 引用 scalabilityMode = $"S{ consumableParams.Encodings.Count }T{ scalabilityModeObject.TemporalLayers }"; } if (!scalabilityMode.IsNullOrWhiteSpace()) { consumerEncoding.ScalabilityMode = scalabilityMode; } // Use the maximum maxBitrate in any encoding and honor it in the Consumer's // encoding. var maxEncodingMaxBitrate = consumableParams.Encodings.Max(m => m.MaxBitrate); if (maxEncodingMaxBitrate.HasValue && maxEncodingMaxBitrate.Value > 0) { consumerEncoding.MaxBitrate = maxEncodingMaxBitrate; } // Set a single encoding for the Consumer. consumerParams.Encodings.Add(consumerEncoding); // Copy verbatim. consumerParams.Rtcp = consumableParams.Rtcp; return(consumerParams); }