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()); } }
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); }
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()); } }
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)); }
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(); }
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(); }
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); }
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; } } }
public RtpParametersNative(RtpParameters parameters) : base(parameters) { _parameters = parameters; }
//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;