/// <summary> /// Validates RtpCodecParameters. It may modify given data by adding missing /// fields with default values. /// It throws if invalid. /// </summary> public static void ValidateRtpCodecParameters(RtpCodecParameters codec) { if (codec == null) { throw new ArgumentNullException(nameof(codec)); } // mimeType is mandatory. if (codec.MimeType.IsNullOrWhiteSpace()) { throw new ArgumentException(nameof(codec.MimeType)); } var mimeType = codec.MimeType.ToLower(); if (!MimeTypeRegex.IsMatch(mimeType)) { throw new ArgumentException(nameof(codec.MimeType)); } // payloadType is mandatory. // 在 Node.js 实现中,判断了 payloadType 的数据类型。在强类型语言中不需要。 // clockRate is mandatory. // 在 Node.js 实现中,判断了 clockRate 的数据类型。在强类型语言中不需要。 // channels is optional. If unset, set it to 1 (just if audio). // 在 Node.js 实现中,如果是音频会 delete 掉 Channels 。 if (mimeType.StartsWith("audio") && (!codec.Channels.HasValue || codec.Channels < 1)) { codec.Channels = 1; } // parameters is optional. If unset, set it to an empty object. if (codec.Parameters == null) { codec.Parameters = new Dictionary <string, object>(); } foreach (var item in codec.Parameters) { var key = item.Key; var value = item.Value; if (value == null) { codec.Parameters[item.Key] = ""; value = ""; } if (!value.IsStringType() && !value.IsNumericType()) { throw new ArgumentOutOfRangeException($"invalid codec parameter[key:${ key }, value:${ value }]"); } if (key == "apt" && !value.IsNumericType()) { throw new ArgumentOutOfRangeException("invalid codec apt parameter"); } } // rtcpFeedback is optional. If unset, set it to an empty array. if (codec.RtcpFeedback == null) { codec.RtcpFeedback = Array.Empty <RtcpFeedback>(); } foreach (var fb in codec.RtcpFeedback) { ValidateRtcpFeedback(fb); } }
/// <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); }