Ejemplo n.º 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);
            }
        }
Ejemplo n.º 2
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="payloadChannel"></param>
 /// <param name="appData"></param>
 public Router(ILoggerFactory loggerFactory,
               string routerId,
               RtpCapabilities rtpCapabilities,
               Channel channel,
               PayloadChannel payloadChannel,
               Dictionary <string, object>?appData
               )
 {
     _loggerFactory = loggerFactory;
     _logger        = loggerFactory.CreateLogger <Router>();
     _internal      = new RouterInternalData
     {
         RouterId = routerId
     };
     RtpCapabilities = rtpCapabilities;
     _channel        = channel;
     _payloadChannel = payloadChannel;
     AppData         = appData;
 }
Ejemplo n.º 3
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 NotSupportedException($"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,
                });
Ejemplo n.º 4
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);
        }
Ejemplo n.º 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
             }
         }
     };
 }