/// <summary> /// Validates RtpHeaderExtensionParameteters. It may modify given data by adding missing /// fields with default values. /// It throws if invalid. /// </summary> public static void ValidateRtpHeaderExtensionParameters(RtpHeaderExtensionParameters ext) { if (ext == null) { throw new ArgumentNullException(nameof(ext)); } // uri is mandatory. if (ext.Uri.IsNullOrEmpty()) { throw new ArgumentException(nameof(ext.Uri)); } // id is mandatory. // 在 Node.js 实现在,判断了 id 的数据类型。在强类型语言中不需要。 // encrypt is optional. If unset set it to false. if (!ext.Encrypt.HasValue) { ext.Encrypt = false; } // parameters is optional. If unset, set it to an empty object. // TODO: (alby)强类型无法转 Empty 对象。 if (ext.Parameters == null) { ext.Parameters = new Dictionary <string, object>(); } foreach (var item in ext.Parameters) { var key = item.Key; var value = item.Value; if (value == null) { ext.Parameters[item.Key] = ""; value = ""; } if (!value.IsStringType() && !value.IsNumericType()) { throw new ArgumentOutOfRangeException($"invalid codec parameter[key:${ key }, value:${ value }]"); } } }
/// <summary> /// Generate RTP parameters to be internally used by Consumers given the RTP /// parameters in a Producer and the RTP capabilities in the Router. /// </summary> public static RtpParameters GetConsumableRtpParameters(MediaKind kind, RtpParameters parameters, RtpCapabilities caps, RtpMapping rtpMapping) { var consumableParams = new RtpParameters { Codecs = new List <RtpCodecParameters>(), HeaderExtensions = new List <RtpHeaderExtensionParameters>(), Encodings = new List <RtpEncodingParameters>(), Rtcp = new RtcpParameters(), }; foreach (var codec in parameters.Codecs) { if (IsRtxMimeType(codec.MimeType)) { continue; } var consumableCodecPt = rtpMapping.Codecs .Where(entry => entry.PayloadType == codec.PayloadType) .Select(m => m.MappedPayloadType) .FirstOrDefault(); var matchedCapCodec = caps.Codecs .Where(capCodec => capCodec.PreferredPayloadType == consumableCodecPt) .FirstOrDefault(); var consumableCodec = new RtpCodecParameters { MimeType = matchedCapCodec.MimeType, PayloadType = matchedCapCodec.PreferredPayloadType.Value, // TODO: (alby)注意 null 引用 ClockRate = matchedCapCodec.ClockRate, Channels = matchedCapCodec.Channels, Parameters = codec.Parameters, // Keep the Producer codec parameters. RtcpFeedback = matchedCapCodec.RtcpFeedback }; consumableParams.Codecs.Add(consumableCodec); var consumableCapRtxCodec = caps.Codecs .Where(capRtxCodec => IsRtxMimeType(capRtxCodec.MimeType) && MatchCodecsWithPayloadTypeAndApt(consumableCodec.PayloadType, capRtxCodec.Parameters)) .FirstOrDefault(); if (consumableCapRtxCodec != null) { var consumableRtxCodec = new RtpCodecParameters { MimeType = consumableCapRtxCodec.MimeType, PayloadType = consumableCapRtxCodec.PreferredPayloadType.Value, // TODO: (alby)注意 null 引用 ClockRate = consumableCapRtxCodec.ClockRate, Channels = consumableCapRtxCodec.Channels, Parameters = consumableCapRtxCodec.Parameters, // Keep the Producer codec parameters. RtcpFeedback = consumableCapRtxCodec.RtcpFeedback }; consumableParams.Codecs.Add(consumableRtxCodec); } } foreach (var capExt in caps.HeaderExtensions) // TODO: (alby)注意 null 引用 { // Just take RTP header extension that can be used in Consumers. if (capExt.Kind != kind || (capExt.Direction != RtpHeaderExtensionDirection.SendReceive && capExt.Direction != RtpHeaderExtensionDirection.SendOnly)) { continue; } var consumableExt = new RtpHeaderExtensionParameters { Uri = capExt.Uri, Id = capExt.PreferredId, Encrypt = capExt.PreferredEncrypt, Parameters = new Dictionary <string, object>(), }; consumableParams.HeaderExtensions.Add(consumableExt); } // Clone Producer encodings since we'll mangle them. var consumableEncodings = parameters.Encodings.DeepClone <List <RtpEncodingParameters> >(); for (var i = 0; i < consumableEncodings.Count; ++i) { var consumableEncoding = consumableEncodings[i]; var mappedSsrc = rtpMapping.Encodings[i].MappedSsrc; // Remove useless fields. // 在 Node.js 实现中,rid, rtx, codecPayloadType 被 delete 了。 consumableEncoding.Rid = null; consumableEncoding.Rtx = null; consumableEncoding.CodecPayloadType = null; // Set the mapped ssrc. consumableEncoding.Ssrc = mappedSsrc; consumableParams.Encodings.Add(consumableEncoding); } consumableParams.Rtcp = new RtcpParameters { CNAME = parameters.Rtcp.CNAME, // TODO: (alby)注意 null 引用 ReducedSize = true, Mux = true, }; return(consumableParams); }