private async void CallBackConnectServer4(IntPtr param1, IntPtr param2, IntPtr param3, IntPtr param4)
        {
            try
            {
                string        rtps          = Marshal.PtrToStringAnsi(param3);
                RtpParameters rtpParameters = JsonConvert.DeserializeObject <RtpParameters>(rtps);

                string strappdata = Marshal.PtrToStringAnsi(param4);
                Dictionary <string, object> appdata = JsonConvert.DeserializeObject <Dictionary <string, object> >(strappdata);

                ProduceRequest request = new ProduceRequest();
                //request.TransportId = Marshal.PtrToStringAnsi(param1);
                request.Kind          = (Marshal.PtrToStringAnsi(param2) == "audio") ? MediaKind.Audio : MediaKind.Video;
                request.RtpParameters = rtpParameters;
                request.AppData       = appdata;

                var result = await connection.InvokeAsync <dynamic>("Produce", request);

                int i = 0;
            }
            catch (Exception ex)
            {
                messagesList.Items.Add(ex.ToString());
            }
        }
Пример #2
0
        public RtpParameters GenerateProbatorRtpParameters(RtpParameters inRtpParameters)
        {
            // Clone given reference video RTP parameters.
            var videoRtpParameters = Utils.Clone(inRtpParameters, new RtpParameters());

            // This may throw.
            ValidateRtpParameters(videoRtpParameters);

            RtpCodecParameters codec = videoRtpParameters.Codecs[0];

            codec.PayloadType = RTP_PROBATOR_CODEC_PAYLOAD_TYPE;

            var rtpParameters = new RtpParameters
            {
                Mid              = RTP_PROBATOR_MID, //// new Mid { Id = RTP_PROBATOR_MID },
                Codecs           = new RtpCodecParameters[] { codec },
                HeaderExtensions = videoRtpParameters.HeaderExtensions,
                Encodings        = new RtpEncodingParameters[] { new RtpEncodingParameters {
                                                                     Ssrc = RTP_PROBATOR_SSRC
                                                                 } },
                Rtcp = new RtcpParameters {
                    Cname = RTP_PROBATOR_MID
                }
            };

            return(rtpParameters);
        }
Пример #3
0
        public static void ApplyCodecParameters(RtpParameters offerRtpParameters, MediaObject answerMediaObject)
        {
            foreach (var codec in offerRtpParameters.Codecs)
            {
                var mimeType = codec.MimeType.ToLower();

                // Avoid parsing codec parameters for unhandled codecs.
                if (mimeType != "audio/opus")
                {
                    continue;
                }

                var rtpmap = (answerMediaObject.MediaDescription.Attributes.Rtpmaps ?? new List <Rtpmap>())
                             .FirstOrDefault(r => r.PayloadType == codec.PayloadType);
                if (rtpmap is null)
                {
                    continue;
                }

                var fmtp = (answerMediaObject.MediaDescription.Attributes.Fmtps ?? new List <Fmtp>())
                           .FirstOrDefault(f => f.PayloadType == codec.PayloadType);
                if (fmtp is null)
                {
                    fmtp = new Fmtp {
                        PayloadType = codec.PayloadType, Value = string.Empty
                    };
                    answerMediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                }

                var parameters = fmtp.Value.Split(';').ToList();
                switch (mimeType)
                {
                case "audio/opus":
                {
                    var spropStereo = (int)(codec.Parameters.ContainsKey("sprop - stereo")
                                ? codec.Parameters["sprop - stereo"] : -1);
                    if (spropStereo != -1)
                    {
                        var stereo = spropStereo == 1 ? 1 : 0;
                        parameters.Add($"stereo={stereo}");
                    }
                    break;
                }
                }

                // Write the codec fmtp.config back.
                fmtp.Value = string.Join(";", parameters.ToArray());
            }
        }
Пример #4
0
        public bool CanReceive(RtpParameters rtpParameters, ExtendedRtpCapabilities extendedRtpCapabilities)
        {
            // This may throw.
            ValidateRtpParameters(rtpParameters);

            if (rtpParameters.Codecs.Length == 0)
            {
                return(false);
            }

            var firstMediaCodec = rtpParameters.Codecs[0];

            return(extendedRtpCapabilities.Codecs
                   .Any(codec => codec.RemotePayloadType == firstMediaCodec.PayloadType));
        }
Пример #5
0
        public Consumer(string id, string localId, string producerId, IRTCRtpReceiver rtpReceiver,
                        IMediaStreamTrack track, RtpParameters rtpParameters, Dictionary <string, object> appData)
        {
            Id            = id;
            LocalId       = localId;
            ProducerId    = producerId;
            RtpReceiver   = rtpReceiver;
            Track         = track;
            RtpParameters = rtpParameters;
            AppData       = appData;
            Paused        = !track.Enabled;
            Closed        = false;

            HandleTrack();
        }
Пример #6
0
        public Producer(string id, string localId, IRTCRtpSender rtpSender, IMediaStreamTrack track,
                        RtpParameters rtpParameters, bool stopTracks, bool disableTrackOnPause, bool zeroRtpOnPause,
                        Dictionary <string, object> appData)
        {
            Id                   = id;
            LocalId              = localId;
            RtpSender            = rtpSender;
            Track                = track;
            RtpParameters        = rtpParameters;
            _stopTracks          = stopTracks;
            _disableTrackOnPause = disableTrackOnPause;
            _zeroRtpOnPause      = zeroRtpOnPause;
            AppData              = appData;

            Kind   = track.Kind.ToMediaSoup();
            Paused = disableTrackOnPause ? !Track.Enabled : false;

            HandleTrack();
        }
Пример #7
0
        public void ValidateRtpParameters(RtpParameters params_)
        {
            // Codecs is mandatory.
            if (params_.Codecs is null)
            {
                throw new Exception("missing params_.codecs");
            }
            foreach (var codec in params_.Codecs)
            {
                ValidateRtpCodecParameters(codec);
            }

            // HeaderExtensions is optional. If unset, fill with an empty array.
            if (params_.HeaderExtensions is null)
            {
                params_.HeaderExtensions = new RtpHeaderExtensionParameters[] { }
            }
            ;
            foreach (var ext in params_.HeaderExtensions)
            {
                ValidateRtpHeaderExtensionParameters(ext);
            }

            // Encodings is optional. If unset, fill with an empty array.
            if (params_.Encodings is null)
            {
                params_.Encodings = new RtpEncodingParameters[] { }
            }
            ;
            foreach (var encoding in params_.Encodings)
            {
                ValidateRtpEncodingParameters(encoding);
            }

            // Rtcp is optional. If unset, fill with an empty object.
            if (params_.Rtcp is null)
            {
                params_.Rtcp = new RtcpParameters();
            }
            ValidateRtcpParameters(params_.Rtcp);
        }
Пример #8
0
        public AnswerMediaSection(
            IceParameters iceParameters,
            IceCandidate[] iceCandidates,
            DtlsParameters dtlsParameters,
            SctpParameters sctpParameters,
            PlainRtpParameters plainRtpParameters,
            bool planB,
            MediaObject offerMediaObject,
            RtpParameters offerRtpParameters,
            RtpParameters answerRtpParameters,
            ProducerCodecOptions codecOptions,
            bool extmapAllowMixed) : base(iceParameters, iceCandidates, dtlsParameters, planB)
        {
            _mediaObject.MediaDescription.Attributes.Mid = offerMediaObject.MediaDescription.Attributes.Mid;
            _mediaObject.MediaDescription.Media          = offerMediaObject.MediaDescription.Media;
            _mediaObject.MediaDescription.Proto          = offerMediaObject.MediaDescription.Proto;

            if (plainRtpParameters is null)
            {
                _mediaObject.MediaDescription.ConnectionData = new ConnectionData
                {
                    NetType           = NetType.Internet,
                    AddrType          = AddrType.Ip4,
                    ConnectionAddress = "127.0.0.1"
                };
                _mediaObject.MediaDescription.Port = 7;
            }
            else
            {
                _mediaObject.MediaDescription.ConnectionData = new ConnectionData
                {
                    NetType           = NetType.Internet,
                    AddrType          = plainRtpParameters.IpVersion,
                    ConnectionAddress = plainRtpParameters.Ip
                };
                _mediaObject.MediaDescription.Port = plainRtpParameters.Port;
            }


            switch (offerMediaObject.MediaDescription.Media)
            {
            case MediaType.Audio:
            case MediaType.Video:
            {
                _mediaObject.MediaDescription.Attributes.RecvOnly = true;
                _mediaObject.MediaDescription.Attributes.Rtpmaps  = new List <Rtpmap>();
                _mediaObject.MediaDescription.Attributes.RtcpFbs  = new List <RtcpFb>();
                _mediaObject.MediaDescription.Attributes.Fmtps    = new List <Fmtp>();

                foreach (var codec in answerRtpParameters.Codecs)
                {
                    var    xxx    = codec;
                    Rtpmap rtpmap = new()
                    {
                        PayloadType  = codec.PayloadType,
                        EncodingName = GetCodecName(codec),
                        ClockRate    = codec.ClockRate,
                        Channels     = codec.Channels > 1 ? codec.Channels : null
                    };
                    _mediaObject.MediaDescription.Attributes.Rtpmaps.Add(rtpmap);

                    var codecParameters = Utils.Clone(codec.Parameters, new Dictionary <string, object>()
                        {
                        });
                    if (codecOptions is not null)
                    {
                        var offerCodec = offerRtpParameters.Codecs
                                         .First(c => c.PayloadType == codec.PayloadType);
                        switch (codec.MimeType.ToLower())
                        {
                        case "audio/opus":
                        {
                            if (codecOptions.OpusStereo.HasValue)
                            {
                                offerCodec.Parameters["sprop-stereo"] =
                                    codecOptions.OpusStereo == true ? 1 : 0;
                                codecParameters["stereo"] =
                                    codecOptions.OpusStereo == true ? 1 : 0;
                            }
                            if (codecOptions.OpusFec.HasValue)
                            {
                                offerCodec.Parameters["useinbandfec"] =
                                    codecOptions.OpusFec == true ? 1 : 0;
                                codecParameters["useinbandfec"] =
                                    codecOptions.OpusFec == true ? 1 : 0;
                            }
                            if (codecOptions.OpusDtx.HasValue)
                            {
                                offerCodec.Parameters["usedtx"] =
                                    codecOptions.OpusDtx == true ? 1 : 0;
                                codecParameters["usedtx"] =
                                    codecOptions.OpusDtx == true ? 1 : 0;
                            }
                            if (codecOptions.OpusMaxPlaybackRate.HasValue)
                            {
                                codecParameters["maxplaybackrate"] =
                                    codecOptions.OpusMaxPlaybackRate;
                            }
                            if (codecOptions.OpusMaxAverageBitrate.HasValue)
                            {
                                codecParameters["maxaveragebitrate"] =
                                    codecOptions.OpusMaxAverageBitrate;
                            }
                            if (codecOptions.OpusPtime.HasValue)
                            {
                                codecParameters["ptime"] =
                                    codecOptions.OpusPtime;
                            }
                        }
                        break;

                        case "video/vp8":
                        case "video/vp9":
                        case "video/h264":
                        case "video/h265":
                        {
                            if (codecOptions.VideoGoogleStartBitrate.HasValue)
                            {
                                codecParameters["x-google-start-bitrate"] =
                                    codecOptions.VideoGoogleStartBitrate;
                            }
                            if (codecOptions.VideoGoogleMaxBitrate.HasValue)
                            {
                                codecParameters["x-google-max-bitrate"] =
                                    codecOptions.VideoGoogleMaxBitrate;
                            }
                            if (codecOptions.VideoGoogleMinBitrate.HasValue)
                            {
                                codecParameters["x-google-min-bitrate"] =
                                    codecOptions.VideoGoogleMinBitrate;
                            }
                        }
                        break;
                        }
                    }

                    Fmtp fmtp = codecParameters.ToFmtp(codec.PayloadType);

                    if (!string.IsNullOrEmpty(fmtp.Value))
                    {
                        _mediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                    }

                    foreach (var fb in codec.RtcpFeedback)
                    {
                        RtcpFb rtcpFb = new()
                        {
                            PayloadType = codec.PayloadType,
                            Type        = fb.Type,
                            SubType     = fb.Parameter
                        };
                        _mediaObject.MediaDescription.Attributes.RtcpFbs.Add(rtcpFb);
                    }
                }

                _mediaObject.MediaDescription.Fmts ??= new List <string>();
                ((List <string>)_mediaObject.MediaDescription.Fmts).AddRange(answerRtpParameters.Codecs
                                                                             .Select(codec => codec.PayloadType.ToString()));

                _mediaObject.MediaDescription.Attributes.Extmaps = new List <Extmap>();
                foreach (var headerExtension in answerRtpParameters.HeaderExtensions)
                {
                    // Don't add a header extension if not present in the offer.
                    var found = offerMediaObject.MediaDescription.Attributes.Extmaps
                                .Any(localExt => localExt.Uri.ToString() == headerExtension.Uri);
                    if (!found)
                    {
                        continue;
                    }

                    Extmap ext = new()
                    {
                        Uri   = new Uri(headerExtension.Uri),
                        Value = headerExtension.Id
                    };
                    _mediaObject.MediaDescription.Attributes.Extmaps.Add(ext);
                }

                // Allow both 1 byte and 2 bytes length header extensions.
                if (extmapAllowMixed && offerMediaObject.MediaDescription.Attributes.ExtmapAllowMixed.HasValue &&
                    offerMediaObject.MediaDescription.Attributes.ExtmapAllowMixed == true)
                {
                    _mediaObject.MediaDescription.Attributes.ExtmapAllowMixed = true;
                }

                // Simulcast.

                //// DEBUG HACK
                if (offerMediaObject.MediaDescription.Media == MediaType.Video)
                {
                    Console.WriteLine("PUT A BP HERE...");
                }
                if (offerMediaObject.MediaDescription.Attributes.Simulcast is not null)
                {
                    _mediaObject.MediaDescription.Attributes.Simulcast = new()
                    {
                        Direction = RidDirection.Recv,
                        IdList    = offerMediaObject.MediaDescription.Attributes.Simulcast.IdList
                    };

                    _mediaObject.MediaDescription.Attributes.Rids = new List <Rid>();
                    foreach (var rid in offerMediaObject.MediaDescription.Attributes.Rids)
                    {
                        if (rid.Direction != RidDirection.Send)
                        {
                            continue;
                        }

                        _mediaObject.MediaDescription.Attributes.Rids.Add(new Rid
                            {
                                Id        = rid.Id,
                                Direction = RidDirection.Recv
                            });
                    }
                }

                // Simulcast (draft version 03).
                else if (offerMediaObject.Simulcast03 is not null)
                {
                    _mediaObject.Simulcast03 = new()
                    {
                        Value = offerMediaObject.Simulcast03.Value.Replace(RidDirection.Send.DisplayName(),
                                                                           RidDirection.Recv.DisplayName())
                    };

                    _mediaObject.MediaDescription.Attributes.Rids = new List <Rid>();
                    foreach (var rid in offerMediaObject.MediaDescription.Attributes.Rids)
                    {
                        if (rid.Direction != RidDirection.Send)
                        {
                            continue;
                        }

                        _mediaObject.MediaDescription.Attributes.Rids.Add(new Rid
                            {
                                Id        = rid.Id,
                                Direction = RidDirection.Recv
                            });
                    }
                }

                _mediaObject.MediaDescription.Attributes.RtcpMux   = true;
                _mediaObject.MediaDescription.Attributes.RtcpRsize = true;

                if (_planB && _mediaObject.MediaDescription.Media == MediaType.Video)
                {
                    _mediaObject.XGoogleFlag = "conference";
                }

                break;
            }

            case MediaType.Application:
            {
                // New spec.
                if (offerMediaObject.MediaDescription.Attributes.SctpPort.Port.GetType() == typeof(int))
                {
                    _mediaObject.MediaDescription.Fmts = new List <string> {
                        "webrtc-datachannel"
                    };
                    _mediaObject.MediaDescription.Attributes.SctpPort = new SctpPort
                    {
                        Port = sctpParameters.Port
                    };
                    _mediaObject.MediaDescription.Attributes.MaxMessageSize = new MaxMessageSize
                    {
                        Size = sctpParameters.MaxMessageSize
                    };
                }
                // Old spec.
                else if (offerMediaObject.SctpMap is not null)
                {
                    _mediaObject.MediaDescription.Fmts = new List <string> {
                        sctpParameters.Port.ToString()
                    };
                    _mediaObject.SctpMap = new()
                    {
                        App            = "webrtc-datachannel",
                        SctpMapNumber  = sctpParameters.Port,
                        MaxMessageSize = sctpParameters.MaxMessageSize
                    };
                }

                break;
            }
            }
        }
Пример #9
0
 public RtpParametersNative(RtpParameters parameters) : base(parameters)
 {
     _parameters = parameters;
 }
Пример #10
0
        //readonly SctpParameters _sctpParameters;
        //readonly PlainRtpParameters _plainRtpParameters;
        //readonly Mid _mid;
        //readonly MediaKind _kind;
        //RtpParameters

        public OfferMediaSection(
            IceParameters iceParameters,
            IceCandidate[] iceCandidates,
            DtlsParameters dtlsParameters,
            SctpParameters sctpParameters,
            PlainRtpParameters plainRtpParameters,
            bool planB,
            Mid mid,
            MediaKind kind,
            RtpParameters offerRtpParameters,
            string streamId,
            string trackId,
            bool oldDataChannelSpec) : base(iceParameters, iceCandidates, dtlsParameters, planB)
        {
            _mediaObject.MediaDescription.Attributes.Mid = mid;
            _mediaObject.MediaDescription.Media          = kind.ToSdp();

            if (plainRtpParameters is null)
            {
                _mediaObject.MediaDescription.ConnectionData = new ConnectionData
                {
                    NetType           = NetType.Internet,
                    AddrType          = AddrType.Ip4,
                    ConnectionAddress = "127.0.0.1"
                };
                if (sctpParameters is null)
                {
                    _mediaObject.MediaDescription.Proto = "UDP/TLS/RTP/SAVPF";
                }
                else
                {
                    _mediaObject.MediaDescription.Proto = "UDP/DTLS/SCTP";
                }
                _mediaObject.MediaDescription.Port = 7;
            }
            else
            {
                _mediaObject.MediaDescription.ConnectionData = new ConnectionData
                {
                    NetType           = NetType.Internet,
                    AddrType          = plainRtpParameters.IpVersion,
                    ConnectionAddress = plainRtpParameters.Ip
                };
                _mediaObject.MediaDescription.Proto = "RTP/AVP";
                _mediaObject.MediaDescription.Port  = plainRtpParameters.Port;
            }

            switch (kind)
            {
            case MediaKind.Audio:
            case MediaKind.Video:
            {
                _mediaObject.MediaDescription.Attributes.SendOnly = true;
                _mediaObject.MediaDescription.Attributes.Rtpmaps  = new List <Rtpmap>();
                _mediaObject.MediaDescription.Attributes.RtcpFbs  = new List <RtcpFb>();
                _mediaObject.MediaDescription.Attributes.Fmtps    = new List <Fmtp>();

                if (!_planB)
                {
                    _mediaObject.MediaDescription.Attributes.Msid = new()
                    {
                        Id      = streamId ?? "-",
                        AppData = trackId
                    }
                }
                ;

                foreach (var codec in offerRtpParameters.Codecs)
                {
                    Rtpmap rtpmap = new()
                    {
                        PayloadType  = codec.PayloadType,
                        EncodingName = GetCodecName(codec),
                        ClockRate    = codec.ClockRate,
                        Channels     = codec.Channels > 1 ? codec.Channels : null
                    };
                    _mediaObject.MediaDescription.Attributes.Rtpmaps.Add(rtpmap);

                    Fmtp fmtp = codec.Parameters.ToFmtp(codec.PayloadType);
                    if (!string.IsNullOrEmpty(fmtp.Value))
                    {
                        _mediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                    }

                    foreach (var fb in codec.RtcpFeedback)
                    {
                        RtcpFb rtcpFb = new()
                        {
                            PayloadType = codec.PayloadType,
                            Type        = fb.Type,
                            SubType     = fb.Parameter
                        };
                        _mediaObject.MediaDescription.Attributes.RtcpFbs.Add(rtcpFb);
                    }
                }

                _mediaObject.MediaDescription.Fmts ??= new List <string>();
                ((List <string>)_mediaObject.MediaDescription.Fmts).AddRange(offerRtpParameters.Codecs
                                                                             .Select(codec => codec.PayloadType.ToString()));


                _mediaObject.MediaDescription.Attributes.Extmaps = new List <Extmap>();
                foreach (var headerExtension in offerRtpParameters.HeaderExtensions)
                {
                    Extmap ext = new()
                    {
                        Uri   = new Uri(headerExtension.Uri),
                        Value = headerExtension.Id
                    };
                    _mediaObject.MediaDescription.Attributes.Extmaps.Add(ext);
                }

                _mediaObject.MediaDescription.Attributes.RtcpMux   = true;
                _mediaObject.MediaDescription.Attributes.RtcpRsize = true;

                var encoding = offerRtpParameters.Encodings[0];
                var ssrc     = encoding.Ssrc;
                var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue) ?
                               encoding.Rtx.Ssrc : null;

                _mediaObject.MediaDescription.Attributes.Ssrcs      = new List <Ssrc>();
                _mediaObject.MediaDescription.Attributes.SsrcGroups = new List <SsrcGroup>();
                if (offerRtpParameters.Rtcp.Cname is not null)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                        {
                            Id        = (uint)ssrc,
                            Attribute = "cname",
                            Value     = offerRtpParameters.Rtcp.Cname
                        });
                }

                if (_planB)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                        {
                            Id        = (uint)ssrc,
                            Attribute = "msid",
                            Value     = $"{streamId ?? "-"} {trackId}"
                        });
                }

                if (rtxSsrc.HasValue)
                {
                    if (offerRtpParameters.Rtcp.Cname is not null)
                    {
                        _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                            {
                                Id        = (uint)rtxSsrc,
                                Attribute = "cname",
                                Value     = offerRtpParameters.Rtcp.Cname
                            });
                    }

                    if (_planB)
                    {
                        _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                            {
                                Id        = (uint)rtxSsrc,
                                Attribute = "msid",
                                Value     = $"{streamId ?? "-"} {trackId}"
                            });
                    }

                    // Associate original and retransmission SSRCs.
                    _mediaObject.MediaDescription.Attributes.SsrcGroups.Add(new SsrcGroup
                        {
                            Semantics = "FID",
                            SsrcIds   = new string[] { $"{ssrc} {rtxSsrc}" }
                        });
                }

                break;
            }

            case MediaKind.Application:
            {
                // New spec.
                if (!oldDataChannelSpec)
                {
                    _mediaObject.MediaDescription.Fmts = new List <string> {
                        "webrtc-datachannel"
                    };
                    _mediaObject.MediaDescription.Attributes.SctpPort = new SctpPort
                    {
                        Port = sctpParameters.Port
                    };
                    _mediaObject.MediaDescription.Attributes.MaxMessageSize = new MaxMessageSize
                    {
                        Size = sctpParameters.MaxMessageSize
                    };
                }
                // Old spec.
                else
                {
                    _mediaObject.MediaDescription.Fmts = new List <string> {
                        sctpParameters.Port.ToString()
                    };
                    _mediaObject.SctpMap = new()
                    {
                        App            = "webrtc-datachannel",
                        SctpMapNumber  = sctpParameters.Port,
                        MaxMessageSize = sctpParameters.MaxMessageSize
                    };
                }

                break;
            }
            }
        }

        void PlanBReceive(RtpParameters offerRtpParameters, string streamId, IMediaStream trackId)
        {
            var encoding = offerRtpParameters.Encodings[0];
            var ssrc     = encoding.Ssrc;
            var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue) ?
                           encoding.Rtx.Ssrc : null;
            var payloads = _mediaObject.MediaDescription.Fmts.ToArray();


            foreach (var codec in offerRtpParameters.Codecs)
            {
                if (payloads.Any(payload => payload.Contains(codec.PayloadType.ToString())))
                {
                    continue;
                }

                Rtpmap rtpmap = new()
                {
                    PayloadType  = codec.PayloadType,
                    EncodingName = GetCodecName(codec),
                    ClockRate    = codec.ClockRate,
                    Channels     = codec.Channels > 1 ? codec.Channels : null
                };
                _mediaObject.MediaDescription.Attributes.Rtpmaps.Add(rtpmap);

                Fmtp fmtp = codec.Parameters.ToFmtp(codec.PayloadType);
                if (!string.IsNullOrEmpty(fmtp.Value))
                {
                    _mediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                }

                foreach (var fb in codec.RtcpFeedback)
                {
                    RtcpFb rtcpFb = new()
                    {
                        PayloadType = codec.PayloadType,
                        Type        = fb.Type,
                        SubType     = fb.Parameter
                    };
                    _mediaObject.MediaDescription.Attributes.RtcpFbs.Add(rtcpFb);
                }
            }

            ((List <string>)_mediaObject.MediaDescription.Fmts).AddRange(offerRtpParameters.Codecs
                                                                         .Select(codec => codec.PayloadType.ToString()));


            if (offerRtpParameters.Rtcp.Cname is not null)
            {
                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)ssrc,
                    Attribute = "cname",
                    Value     = offerRtpParameters.Rtcp.Cname
                });
            }

            _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
            {
                Id        = (uint)ssrc,
                Attribute = "msid",
                Value     = $"{streamId ?? "-"} {trackId}"
            });

            if (rtxSsrc.HasValue)
            {
                if (offerRtpParameters.Rtcp.Cname is not null)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                    {
                        Id        = (uint)rtxSsrc,
                        Attribute = "cname",
                        Value     = offerRtpParameters.Rtcp.Cname
                    });
                }

                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)rtxSsrc,
                    Attribute = "msid",
                    Value     = $"{streamId ?? "-"} {trackId}"
                });

                // Associate original and retransmission SSRCs.
                _mediaObject.MediaDescription.Attributes.SsrcGroups.Add(new SsrcGroup
                {
                    Semantics = "FID",
                    SsrcIds   = new string[] { $"{ssrc} {rtxSsrc}" }
                });
            }
        }

        public void PlanBReceive(RtpParameters offerRtpParameters, string streamId, string trackId)
        {
            var encoding = offerRtpParameters.Encodings[0];
            var ssrc     = encoding.Ssrc;
            var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue)
                ? encoding.Rtx.Ssrc
                : null;
            var payloads = _mediaObject.MediaDescription.Fmts.ToArray();

            foreach (var codec in offerRtpParameters.Codecs)
            {
                if (payloads.Contains(codec.PayloadType.ToString()))
                {
                    continue;
                }

                Rtpmap rtpmap = new()
                {
                    PayloadType  = codec.PayloadType,
                    EncodingName = GetCodecName(codec),
                    ClockRate    = codec.ClockRate,
                    Channels     = codec.Channels > 1 ? codec.Channels : null
                };
                _mediaObject.MediaDescription.Attributes.Rtpmaps.Add(rtpmap);

                Fmtp fmtp = codec.Parameters.ToFmtp(codec.PayloadType);

                if (!string.IsNullOrEmpty(fmtp.Value))
                {
                    _mediaObject.MediaDescription.Attributes.Fmtps.Add(fmtp);
                }

                foreach (var fb in codec.RtcpFeedback)
                {
                    _mediaObject.MediaDescription.Attributes.RtcpFbs.Add(new RtcpFb
                    {
                        PayloadType = codec.PayloadType,
                        Type        = fb.Type,
                        SubType     = fb.Parameter
                    });
                }
            }

            ((List <string>)_mediaObject.MediaDescription.Fmts).AddRange(offerRtpParameters.Codecs
                                                                         .Where(codec => !_mediaObject.MediaDescription.Fmts.Contains(codec.PayloadType.ToString()))
                                                                         .Select(codec => codec.PayloadType.ToString())
                                                                         .ToArray());

            if (offerRtpParameters.Rtcp.Cname is not null)
            {
                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)ssrc,
                    Attribute = "cname",
                    Value     = offerRtpParameters.Rtcp.Cname
                });
            }

            _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
            {
                Id        = (uint)ssrc,
                Attribute = "msid",
                Value     = $"{ streamId ?? "-"} { trackId}"
            });

            if (rtxSsrc is not null)
            {
                if (offerRtpParameters.Rtcp.Cname is not null)
                {
                    _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                    {
                        Id        = (uint)rtxSsrc,
                        Attribute = "cname",
                        Value     = offerRtpParameters.Rtcp.Cname
                    });
                }

                _mediaObject.MediaDescription.Attributes.Ssrcs.Add(new Ssrc
                {
                    Id        = (uint)rtxSsrc,
                    Attribute = "msid",
                    Value     = $"{ streamId ?? "-"} {trackId}"
                });

                // Associate original and retransmission SSRCs.
                _mediaObject.MediaDescription.Attributes.SsrcGroups.Add(new SsrcGroup
                {
                    Semantics = "FID",
                    SsrcIds   = new string[] { $"{ ssrc }${rtxSsrc}" }
                });
            }
        }

        void PlanBStopReceiving(RtpParameters offerRtpParameters)
        {
            var encoding = offerRtpParameters.Encodings[0];
            var ssrc     = encoding.Ssrc;
            var rtxSsrc  = (encoding.Rtx is not null && encoding.Rtx.Ssrc.HasValue) ?
                           encoding.Rtx.Ssrc : null;

            ((List <Ssrc>)_mediaObject.MediaDescription.Attributes.Ssrcs).RemoveAll(s => s.Id != ssrc && s.Id != rtxSsrc);

            if (rtxSsrc is not null)
            {
                ((List <SsrcGroup>)_mediaObject.MediaDescription.Attributes.SsrcGroups).RemoveAll(g => g.SsrcIds.Any(id => id != $"{ssrc} {rtxSsrc}"));
            }
        }

        public override void SetDtlsRole(DtlsRole?dtlsRole)
 public PlatformRtpParameters(RtpParameters rtpParameters) : base(rtpParameters) => _rtpParameters = rtpParameters;