Пример #1
0
        /// <summary>
        /// Validates RtpCapabilities. It may modify given data by adding missing
        /// fields with default values.
        /// It throws if invalid.
        /// </summary>
        public static void ValidateRtpCapabilities(RtpCapabilities caps)
        {
            if (caps == null)
            {
                throw new ArgumentNullException(nameof(caps));
            }
            if (caps.Codecs == null)
            {
                caps.Codecs = new List <RtpCodecCapability>();
            }

            foreach (var codec in caps.Codecs)
            {
                ValidateRtpCodecCapability(codec);
            }

            // headerExtensions is optional. If unset, fill with an empty array.
            if (caps.HeaderExtensions == null)
            {
                caps.HeaderExtensions = Array.Empty <RtpHeaderExtension>();
            }

            foreach (var ext in caps.HeaderExtensions)
            {
                ValidateRtpHeaderExtension(ext);
            }
        }
Пример #2
0
        /// <summary>
        /// Check whether the given RTP capabilities can consume the given Producer.
        /// </summary>
        public static bool CanConsume(RtpParameters consumableParams, RtpCapabilities caps)
        {
            // This may throw.
            ValidateRtpCapabilities(caps);

            var matchingCodecs = new List <RtpCodecParameters>();

            foreach (var codec in consumableParams.Codecs)
            {
                var matchedCapCodec = caps.Codecs
                                      .Where(capCodec => MatchCodecs(capCodec, codec, true))
                                      .FirstOrDefault();

                if (matchedCapCodec == null)
                {
                    continue;
                }

                matchingCodecs.Add(codec);
            }

            // Ensure there is at least one media codec.
            if (matchingCodecs.Count == 0 || IsRtxMimeType(matchingCodecs[0].MimeType))
            {
                return(false);
            }

            return(true);
        }
Пример #3
0
 /// <summary>
 /// <para>Events:</para>
 /// <para>@emits workerclose</para>
 /// <para>@emits @close</para>
 /// <para>Observer events:</para>
 /// <para>@emits close</para>
 /// <para>@emits newtransport - (transport: Transport)</para>
 /// <para>@emits newrtpobserver - (rtpObserver: RtpObserver)</para>
 /// </summary>
 /// <param name="logger"></param>
 /// <param name="routerId"></param>
 /// <param name="rtpCapabilities"></param>
 /// <param name="channel"></param>
 /// <param name="appData"></param>
 public Router(ILoggerFactory loggerFactory,
               string routerId,
               RtpCapabilities rtpCapabilities,
               Channel channel,
               Dictionary <string, object>?appData)
 {
     _loggerFactory = loggerFactory;
     _logger        = loggerFactory.CreateLogger <Router>();
     RouterId       = routerId;
     _internal      = new
     {
         RouterId,
     };
     RtpCapabilities = rtpCapabilities;
     _channel        = channel;
     AppData         = appData;
 }
Пример #4
0
        /// <summary>
        /// Check whether the given RTP capabilities can consume the given Producer.
        /// </summary>
        public bool CanConsume(string producerId, RtpCapabilities rtpCapabilities)
        {
            if (!_producers.TryGetValue(producerId, out Producer producer))
            {
                _logger.LogError($"CanConsume() | Producer with id {producerId} not found");

                return(false);
            }

            try
            {
                return(ORTC.CanConsume(producer.ConsumableRtpParameters, rtpCapabilities));
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "CanConsume() | unexpected error");

                return(false);
            }
        }
Пример #5
0
 static RtpCapabilities()
 {
     SupportedRtpCapabilities = new RtpCapabilities
     {
         Codecs = new List <RtpCodecCapability>
         {
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/opus",
                 ClockRate    = 48000,
                 Channels     = 2,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind                 = MediaKind.Audio,
                 MimeType             = "audio/PCMU",
                 PreferredPayloadType = 0,
                 ClockRate            = 8000,
                 RtcpFeedback         = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind                 = MediaKind.Audio,
                 MimeType             = "audio/PCMA",
                 PreferredPayloadType = 8,
                 ClockRate            = 8000,
                 RtcpFeedback         = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/ISAC",
                 ClockRate    = 32000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/ISAC",
                 ClockRate    = 16000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind                 = MediaKind.Audio,
                 MimeType             = "audio/G722",
                 PreferredPayloadType = 9,
                 ClockRate            = 8000,
                 RtcpFeedback         = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/iLBC",
                 ClockRate    = 8000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/SILK",
                 ClockRate    = 24000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/SILK",
                 ClockRate    = 16000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/SILK",
                 ClockRate    = 12000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Audio,
                 MimeType     = "audio/SILK",
                 ClockRate    = 8000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind                 = MediaKind.Audio,
                 MimeType             = "audio/CN",
                 PreferredPayloadType = 13,
                 ClockRate            = 32000
             },
             new RtpCodecCapability {
                 Kind                 = MediaKind.Audio,
                 MimeType             = "audio/CN",
                 PreferredPayloadType = 13,
                 ClockRate            = 16000
             },
             new RtpCodecCapability {
                 Kind                 = MediaKind.Audio,
                 MimeType             = "audio/CN",
                 PreferredPayloadType = 13,
                 ClockRate            = 8000
             },
             new RtpCodecCapability {
                 Kind      = MediaKind.Audio,
                 MimeType  = "audio/telephone-event",
                 ClockRate = 48000
             },
             new RtpCodecCapability {
                 Kind      = MediaKind.Audio,
                 MimeType  = "audio/telephone-event",
                 ClockRate = 32000
             },
             new RtpCodecCapability {
                 Kind      = MediaKind.Audio,
                 MimeType  = "audio/telephone-event",
                 ClockRate = 16000
             },
             new RtpCodecCapability {
                 Kind      = MediaKind.Audio,
                 MimeType  = "audio/telephone-event",
                 ClockRate = 8000
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Video,
                 MimeType     = "video/VP8",
                 ClockRate    = 90000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "nack",
                     },
                     new RtcpFeedback {
                         Type = "nack", Parameter = "pli",
                     },
                     new RtcpFeedback {
                         Type = "ccm", Parameter = "fir",
                     },
                     new RtcpFeedback {
                         Type = "goog-remb",
                     },
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind         = MediaKind.Video,
                 MimeType     = "video/VP9",
                 ClockRate    = 90000,
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "nack",
                     },
                     new RtcpFeedback {
                         Type = "nack", Parameter = "pli",
                     },
                     new RtcpFeedback {
                         Type = "ccm", Parameter = "fir",
                     },
                     new RtcpFeedback {
                         Type = "goog-remb",
                     },
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind       = MediaKind.Video,
                 MimeType   = "video/H264",
                 ClockRate  = 90000,
                 Parameters = new Dictionary <string, object> {
                     { "packetization-mode", 1 },
                     { "level-asymmetry-allowed", 1 },
                 },
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "nack",
                     },
                     new RtcpFeedback {
                         Type = "nack", Parameter = "pli",
                     },
                     new RtcpFeedback {
                         Type = "ccm", Parameter = "fir",
                     },
                     new RtcpFeedback {
                         Type = "goog-remb",
                     },
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind       = MediaKind.Video,
                 MimeType   = "video/H264",
                 ClockRate  = 90000,
                 Parameters = new Dictionary <string, object> {
                     { "packetization-mode", 0 },
                     { "level-asymmetry-allowed", 1 },
                 },
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "nack",
                     },
                     new RtcpFeedback {
                         Type = "nack", Parameter = "pli",
                     },
                     new RtcpFeedback {
                         Type = "ccm", Parameter = "fir",
                     },
                     new RtcpFeedback {
                         Type = "goog-remb",
                     },
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind       = MediaKind.Video,
                 MimeType   = "video/H265",
                 ClockRate  = 90000,
                 Parameters = new Dictionary <string, object> {
                     { "packetization-mode", 1 },
                     { "level-asymmetry-allowed", 1 },
                 },
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "nack",
                     },
                     new RtcpFeedback {
                         Type = "nack", Parameter = "pli",
                     },
                     new RtcpFeedback {
                         Type = "ccm", Parameter = "fir",
                     },
                     new RtcpFeedback {
                         Type = "goog-remb",
                     },
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             },
             new RtpCodecCapability {
                 Kind       = MediaKind.Video,
                 MimeType   = "video/H265",
                 ClockRate  = 90000,
                 Parameters = new Dictionary <string, object> {
                     { "packetization-mode", 0 },
                     { "level-asymmetry-allowed", 1 },
                 },
                 RtcpFeedback = new RtcpFeedback[]
                 {
                     new RtcpFeedback {
                         Type = "nack",
                     },
                     new RtcpFeedback {
                         Type = "nack", Parameter = "pli",
                     },
                     new RtcpFeedback {
                         Type = "ccm", Parameter = "fir",
                     },
                     new RtcpFeedback {
                         Type = "goog-remb",
                     },
                     new RtcpFeedback {
                         Type = "transport-cc",
                     },
                 }
             }
         },
         HeaderExtensions = new RtpHeaderExtension[]
         {
             new RtpHeaderExtension {
                 Kind             = MediaKind.Audio,
                 Uri              = "urn:ietf:params:rtp-hdrext:sdes:mid",
                 PreferredId      = 1,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "urn:ietf:params:rtp-hdrext:sdes:mid",
                 PreferredId      = 1,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id",
                 PreferredId      = 2,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.ReceiveOnly
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id",
                 PreferredId      = 3,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.ReceiveOnly
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Audio,
                 Uri              = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
                 PreferredId      = 4,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
                 PreferredId      = 4,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             // NOTE: For audio we just enable transport-wide-cc-01 when receiving media.
             new RtpHeaderExtension {
                 Kind             = MediaKind.Audio,
                 Uri              = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
                 PreferredId      = 5,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.ReceiveOnly,
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
                 PreferredId      = 5,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             // NOTE: Remove this once framemarking draft becomes RFC.
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "http://tools.ietf.org/html/draft-ietf-avtext-framemarking-07",
                 PreferredId      = 6,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "urn:ietf:params:rtp-hdrext:framemarking",
                 PreferredId      = 7,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Audio,
                 Uri              = "urn:ietf:params:rtp-hdrext:ssrc-audio-level",
                 PreferredId      = 10,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "urn:3gpp:video-orientation",
                 PreferredId      = 11,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             },
             new RtpHeaderExtension {
                 Kind             = MediaKind.Video,
                 Uri              = "urn:ietf:params:rtp-hdrext:toffset",
                 PreferredId      = 12,
                 PreferredEncrypt = false,
                 Direction        = RtpHeaderExtensionDirection.SendReceive
             }
         }
     };
 }
Пример #6
0
        /// <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,
                });
Пример #7
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.GetEnumStringValue()}/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);
        }
Пример #8
0
        /// <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);
        }
Пример #9
0
        /// <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);
        }