/// <summary> /// Get a mapping in codec payloads and encodings in the given Producer RTP /// parameters as values expected by the Router. /// /// It may throw if invalid or non supported RTP parameters are given. /// </summary> public static RtpMapping GetProducerRtpParametersMapping(RtpParameters parameters, RtpCapabilities caps) { var rtpMapping = new RtpMapping { Codecs = new List <RtpMappingCodec>(), Encodings = new List <RtpMappingEncoding>() }; // Match parameters media codecs to capabilities media codecs. var codecToCapCodec = new Dictionary <RtpCodecParameters, RtpCodecCapability>(); foreach (var codec in parameters.Codecs) { if (IsRtxMimeType(codec.MimeType)) { continue; } // Search for the same media codec in capabilities. var matchedCapCodec = caps.Codecs .Where(capCodec => MatchCodecs(codec, capCodec, true, true)) .FirstOrDefault(); codecToCapCodec[codec] = matchedCapCodec ?? throw new Exception($"unsupported codec[mimeType:{ codec.MimeType }, payloadType:{ codec.PayloadType }, Channels:{ codec.Channels }]"); } // Match parameters RTX codecs to capabilities RTX codecs. foreach (var codec in parameters.Codecs) { if (!IsRtxMimeType(codec.MimeType)) { continue; } // Search for the associated media codec. var associatedMediaCodec = parameters.Codecs .Where(mediaCodec => MatchCodecsWithPayloadTypeAndApt(mediaCodec.PayloadType, codec.Parameters)) .FirstOrDefault(); if (associatedMediaCodec == null) { throw new Exception($"missing media codec found for RTX PT { codec.PayloadType}"); } var capMediaCodec = codecToCapCodec[associatedMediaCodec]; // Ensure that the capabilities media codec has a RTX codec. var associatedCapRtxCodec = caps.Codecs .Where(capCodec => IsRtxMimeType(capCodec.MimeType) && MatchCodecsWithPayloadTypeAndApt(capMediaCodec.PreferredPayloadType, capCodec.Parameters)) .FirstOrDefault(); codecToCapCodec[codec] = associatedCapRtxCodec ?? throw new Exception($"no RTX codec for capability codec PT { capMediaCodec.PreferredPayloadType}"); } // Generate codecs mapping. foreach (var item in codecToCapCodec) { rtpMapping.Codecs.Add(new RtpMappingCodec { PayloadType = item.Key.PayloadType, MappedPayloadType = item.Value.PreferredPayloadType !.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); }