Exemplo n.º 1
0
        /// <summary>
        /// Validates RtpCodecCapability. It may modify given data by adding missing
        /// fields with default values.
        /// It throws if invalid.
        /// </summary>
        public static void ValidateRtpCodecCapability(RtpCodecCapability 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));
            }

            // Just override kind with media component in mimeType.
            codec.Kind = mimeType.StartsWith("video") ? MediaKind.Video : MediaKind.Audio;

            // preferredPayloadType is optional.
            // 在 Node.js 实现中,判断了 preferredPayloadType 在有值的情况下的数据类型。在强类型语言中不需要。

            // clockRate is mandatory.
            // 在 Node.js 实现中,判断了 mandatory 的数据类型。在强类型语言中不需要。

            // channels is optional. If unset, set it to 1 (just if audio).
            if (codec.Kind == MediaKind.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}]");
                }
                // Specific parameters validation.
                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);
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Generate RTP capabilities for the Router based on the given media codecs and
        /// mediasoup supported RTP capabilities.
        /// </summary>
        public static RtpCapabilities GenerateRouterRtpCapabilities(RtpCodecCapability[] mediaCodecs)
        {
            if (mediaCodecs == null)
            {
                throw new ArgumentNullException(nameof(mediaCodecs));
            }

            // Normalize supported RTP capabilities.
            ValidateRtpCapabilities(RtpCapabilities.SupportedRtpCapabilities);

            var clonedSupportedRtpCapabilities = RtpCapabilities.SupportedRtpCapabilities.DeepClone <RtpCapabilities>();
            var dynamicPayloadTypes            = DynamicPayloadTypes.DeepClone <int[]>().ToList();
            var caps = new RtpCapabilities
            {
                Codecs           = new List <RtpCodecCapability>(),
                HeaderExtensions = clonedSupportedRtpCapabilities.HeaderExtensions
            };

            foreach (var mediaCodec in mediaCodecs)
            {
                // This may throw.
                ValidateRtpCodecCapability(mediaCodec);

                var matchedSupportedCodec = clonedSupportedRtpCapabilities
                                            .Codecs !
                                            .Where(supportedCodec => MatchCodecs(mediaCodec, supportedCodec, false)).FirstOrDefault();

                if (matchedSupportedCodec == null)
                {
                    throw new Exception($"media codec not supported[mimeType:{mediaCodec.MimeType}]");
                }

                // Clone the supported codec.
                var codec = matchedSupportedCodec.DeepClone <RtpCodecCapability>();

                // If the given media codec has preferredPayloadType, keep it.
                if (mediaCodec.PreferredPayloadType.HasValue)
                {
                    codec.PreferredPayloadType = mediaCodec.PreferredPayloadType;

                    // Also remove the pt from the list in available dynamic values.
                    dynamicPayloadTypes.Remove(codec.PreferredPayloadType.Value);
                }
                // Otherwise if the supported codec has preferredPayloadType, use it.
                else if (codec.PreferredPayloadType.HasValue)
                {
                    // No need to remove it from the list since it's not a dynamic value.
                }
                // Otherwise choose a dynamic one.
                else
                {
                    // Take the first available pt and remove it from the list.
                    var pt = dynamicPayloadTypes.FirstOrDefault();

                    if (pt == 0)
                    {
                        throw new Exception("cannot allocate more dynamic codec payload types");
                    }

                    dynamicPayloadTypes.RemoveAt(0);

                    codec.PreferredPayloadType = pt;
                }

                // Ensure there is not duplicated preferredPayloadType values.
                if (caps.Codecs.Any(c => c.PreferredPayloadType == codec.PreferredPayloadType))
                {
                    throw new Exception("duplicated codec.preferredPayloadType");
                }

                // Merge the media codec parameters.
                codec.Parameters = codec.Parameters.Merge(mediaCodec.Parameters);

                // Append to the codec list.
                caps.Codecs.Add(codec);

                // Add a RTX video codec if video.
                if (codec.Kind == MediaKind.Video)
                {
                    // Take the first available pt and remove it from the list.
                    var pt = dynamicPayloadTypes.FirstOrDefault();

                    if (pt == 0)
                    {
                        throw new Exception("cannot allocate more dynamic codec payload types");
                    }

                    dynamicPayloadTypes.RemoveAt(0);

                    var rtxCodec = new RtpCodecCapability
                    {
                        Kind                 = codec.Kind,
                        MimeType             = $"{codec.Kind.GetEnumMemberValue()}/rtx",
                        PreferredPayloadType = pt,
                        ClockRate            = codec.ClockRate,
                        Parameters           = new Dictionary <string, object>
                        {
                            { "apt", codec.PreferredPayloadType }
                        },
                        RtcpFeedback = Array.Empty <RtcpFeedback>(),
                    };

                    // Append to the codec list.
                    caps.Codecs.Add(rtxCodec);
                }
            }

            return(caps);
        }