Пример #1
0
        public void ParsePionDataChannelOnlyOfferSDPUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                @"v=0
o=- 87119400 1595185172 IN IP4 0.0.0.0
s=-
t=0 0
a=fingerprint:sha-256 81:5C:47:85:9C:3D:CC:E6:B5:94:0B:3B:65:D5:39:1A:CD:8F:48:2D:78:0F:9F:0B:18:93:BF:C9:F6:C9:8E:F8
a=group:BUNDLE 0
m=application 9 DTLS/SCTP 5000
c=IN IP4 0.0.0.0
a=setup:active
a=mid:0
a=sendrecv
a=sctpmap:5000 webrtc-datachannel 1024";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            SDP rndTripSdp = SDP.ParseSDPDescription(sdp.ToString());

            Assert.Equal("BUNDLE 0", rndTripSdp.Group);
            Assert.Single(rndTripSdp.Media);
            Assert.Equal("sha-256 81:5C:47:85:9C:3D:CC:E6:B5:94:0B:3B:65:D5:39:1A:CD:8F:48:2D:78:0F:9F:0B:18:93:BF:C9:F6:C9:8E:F8", rndTripSdp.DtlsFingerprint);
            Assert.Single(rndTripSdp.Media.Single().ApplicationMediaFormats);
            Assert.Equal(5000, rndTripSdp.Media.Single().SctpPort.Value);
            Assert.Equal(1024, rndTripSdp.Media.Single().MaxMessageSize);
        }
Пример #2
0
        public void ParseMediaFormatWithHyphenNameUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                @"v=0
o=- 1970544282 0 IN IP4 127.0.0.1
s=-
c=IN IP4 10.10.1.8
t=0 0
m=audio 57982 RTP/AVP 0 8
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=sendrecv
m=video 57984 RTP/AVP 96
a=rtpmap:96 H263-1998/90000
a=fmtp:96 QCIF=3
a=sendrecv";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            SDP rndTripSdp = SDP.ParseSDPDescription(sdp.ToString());

            Assert.Equal(96, rndTripSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().MediaFormats.Single().Key);
            Assert.Equal("H263-1998", rndTripSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().MediaFormats.Single().Value.Name());
        }
Пример #3
0
        public void ParseSDPUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                "v=0" + m_CRLF +
                "o=root 3285 3285 IN IP4 10.0.0.4" + m_CRLF +
                "s=session" + m_CRLF +
                "c=IN IP4 10.0.0.4" + m_CRLF +
                "t=0 0" + m_CRLF +
                "m=audio 12228 RTP/AVP 0 101" + m_CRLF +
                "a=rtpmap:0 PCMU/8000" + m_CRLF +
                "a=rtpmap:101 telephone-event/8000" + m_CRLF +
                "a=fmtp:101 0-16" + m_CRLF +
                "a=silenceSupp:off - - - -" + m_CRLF +
                "a=ptime:20" + m_CRLF +
                "a=sendrecv";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            Assert.True(sdp.Connection.ConnectionAddress == "10.0.0.4", "The connection address was not parsed  correctly.");
            Assert.True(sdp.Media[0].Media == SDPMediaTypesEnum.audio, "The media type not parsed correctly.");
            Assert.True(sdp.Media[0].Port == 12228, "The connection port was not parsed correctly.");
            Assert.True(sdp.Media[0].GetFormatListToString() == "0 101", "The media format list was incorrect.");
            Assert.True(sdp.Media[0].MediaFormats[0].ID == 0, "The highest priority media format ID was incorrect.");
            Assert.True(sdp.Media[0].MediaFormats[0].Name() == "PCMU", "The highest priority media format name was incorrect.");
            Assert.Equal(SDPWellKnownMediaFormatsEnum.PCMU.ToString(), sdp.Media[0].MediaFormats[0].Name());
            Assert.True(sdp.Media[0].MediaFormats[0].Rtpmap == "PCMU/8000", "The highest priority media format rtpmap was incorrect.");
        }
Пример #4
0
        public void ParseBriaSDPUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);
            string sdpStr =
                "v=0" +
                "o=- 5 2 IN IP4 10.1.1.2" + m_CRLF +
                "s=CounterPath Bria" + m_CRLF +
                "c=IN IP4 144.137.16.240" + m_CRLF +
                "t=0 0" + m_CRLF +
                "m=audio 34640 RTP/AVP 0 8 101" + m_CRLF +
                "a=sendrecv" + m_CRLF +
                "a=rtpmap:101 telephone-event/8000" + m_CRLF +
                "a=fmtp:101 0-15" + m_CRLF +
                "a=alt:1 1 : STu/ZtOu 7hiLQmUp 10.1.1.2 34640";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            Assert.True(sdp.Connection.ConnectionAddress == "144.137.16.240", "The connection address was not parsed correctly.");
            Assert.True(sdp.Media[0].Port == 34640, "The connection port was not parsed correctly.");
            Assert.True(sdp.Media[0].MediaFormats[0].Name() == "PCMU", "The highest priority media format name was incorrect.");
            Assert.Equal(SDPWellKnownMediaFormatsEnum.PCMU.ToString(), sdp.Media[0].MediaFormats[0].Name());
        }
Пример #5
0
        public void ParseMcpttTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                @"v=0
o=root 5936658357711814578 0 IN IP4 0.0.0.0
s=-
t=0 0
m=audio 55316 RTP/AVP 0 101
a=rtpmap:0 PCMU/8000
a=label:1
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=ptime:20
a=sendrecv
m=application 55317 udp MCPTT
a=fmtp:MCPTT mc_queueing;mc_priority=4";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            SDP rndTripSdp = SDP.ParseSDPDescription(sdp.ToString());

            Assert.Equal("MCPTT", rndTripSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.application).Single().ApplicationMediaFormats.Single().Key);
            Assert.Equal("mc_queueing;mc_priority=4", rndTripSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.application).Single().ApplicationMediaFormats.Single().Value.Fmtp);
        }
Пример #6
0
        public void TIASBandwidthAttributeRoundTripTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                @"v=0
o=root 5936658357711814578 0 IN IP4 0.0.0.0
s=-
t=0 0
m=audio 55316 RTP/AVP 0 101
a=rtpmap:0 PCMU/8000
a=label:1
a=rtpmap:101 telephone-event/8000
a=fmtp:101 0-15
a=ptime:20
a=sendrecv
m=video 61682 UDP/TLS/RTP/SAVPF 96
c=IN IP4 192.168.11.50
b=TIAS:256000
a=rtpmap:96 VP8/90000
a=label:2
a=sendrecv
";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            SDP rndTripSdp = SDP.ParseSDPDescription(sdp.ToString());

            Assert.Equal(256000U, rndTripSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().TIASBandwidth);
        }
Пример #7
0
        public void ParseOfferWithFmtpPreceedingRtmapTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                @"v=0
o=mozilla...THIS_IS_SDPARTA-80.0.1 5936658357711814578 0 IN IP4 0.0.0.0
s=-
t=0 0
a=sendrecv
a=fingerprint:sha-256 46:7C:4B:FD:47:E1:22:16:28:FC:52:94:C8:9D:7D:24:2F:C3:A8:66:02:17:0D:41:DF:34:99:1C:48:CB:9F:D5
a=group:BUNDLE 0
a=ice-options:trickle
a=msid-semantic:WMS *
m=video 9 UDP/TLS/RTP/SAVP 96
c=IN IP4 0.0.0.0
a=recvonly
a=fmtp:96 max-fs=12288;max-fr=60
a=ice-pwd:8136ef42e22d9d6b31d23b39a662bf8d
a=ice-ufrag:2cbeec1e
a=mid:0
a=rtcp-mux
a=rtpmap:96 VP8/90000
a=setup:active
a=ssrc:2404235415 cname:{7c06c5db-d3db-4891-b729-df4919014c3f}";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            Assert.Equal(96, sdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().MediaFormats.Single().Key);
            Assert.Equal("VP8", sdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().MediaFormats.Single().Value.Name());
            Assert.Equal("VP8/90000", sdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().MediaFormats.Single().Value.Rtpmap);
            Assert.Equal("max-fs=12288;max-fr=60", sdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().MediaFormats.Single().Value.Fmtp);
        }
Пример #8
0
        private static void SDPAnswerReceived(WebRtcSession webRtcSession, string sdpAnswer)
        {
            try
            {
                logger.LogDebug("Answer SDP: " + sdpAnswer);

                var answerSDP = SDP.ParseSDPDescription(sdpAnswer);

                webRtcSession.SdpSessionID      = answerSDP.SessionId;
                webRtcSession.RemoteIceUser     = answerSDP.IceUfrag;
                webRtcSession.RemoteIcePassword = answerSDP.IcePwd;

                // All browsers seem to have gone to trickling ICE candidates now but just
                // in case one or more are given we can start the STUN dance immediately.
                if (answerSDP.IceCandidates != null)
                {
                    foreach (var iceCandidate in answerSDP.IceCandidates)
                    {
                        webRtcSession.AppendRemoteIceCandidate(iceCandidate);
                    }
                }

                OnMediaSampleReady += webRtcSession.SendMedia;

                if (_mfSampleGrabber.Paused)
                {
                    _mfSampleGrabber.Start();
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception SDPAnswerReceived. " + excp.Message);
            }
        }
Пример #9
0
        private static async void MessageReceived(WebSocketContext context, string msg)
        {
            //Console.WriteLine($"websocket recv: {msg}");
            var offerSDP = SDP.ParseSDPDescription(msg);

            Console.WriteLine($"offer sdp: {offerSDP}");

            var webRtcSession = new WebRtcSession(
                AddressFamily.InterNetwork,
                DTLS_CERTIFICATE_FINGERPRINT,
                null,
                null);

            webRtcSession.setRemoteDescription(new RTCSessionDescription {
                sdp = offerSDP, type = RTCSdpType.offer
            });

            webRtcSession.OnReceiveReport     += RtpSession_OnReceiveReport;
            webRtcSession.OnSendReport        += RtpSession_OnSendReport;
            webRtcSession.OnRtpPacketReceived += RtpSession_OnRtpPacketReceived;
            webRtcSession.OnClose             += (reason) =>
            {
                Console.WriteLine($"webrtc session closed: {reason}");
                _webRtcSessions.Remove(webRtcSession);
            };

            // Add local recvonly tracks. This ensures that the SDP answer includes only
            // the codecs we support.
            MediaStreamTrack audioTrack = new MediaStreamTrack(null, SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
            });

            audioTrack.Transceiver.SetStreamStatus(MediaStreamStatusEnum.RecvOnly);
            webRtcSession.addTrack(audioTrack);
            MediaStreamTrack videoTrack = new MediaStreamTrack(null, SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> {
                new SDPMediaFormat(SDPMediaFormatsEnum.VP8)
            });

            videoTrack.Transceiver.SetStreamStatus(MediaStreamStatusEnum.RecvOnly);
            webRtcSession.addTrack(videoTrack);

            var answerSdp = await webRtcSession.createAnswer();

            webRtcSession.setLocalDescription(new RTCSessionDescription {
                sdp = answerSdp, type = RTCSdpType.answer
            });

            Console.WriteLine($"answer sdp: {answerSdp}");

            context.WebSocket.Send(answerSdp.ToString());

            if (DoDtlsHandshake(webRtcSession))
            {
                _webRtcSessions.Add(webRtcSession);
            }
            else
            {
                webRtcSession.Close("dtls handshake failed.");
            }
        }
Пример #10
0
        public void ParseMediaFormatWithFowardSlashUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                @"v=0
o=- 1970544282 0 IN IP4 127.0.0.1
s=-
c=IN IP4 10.10.1.8
t=0 0
m=audio 57982 RTP/AVP 111
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10;useinbandfec=1
a=sendrecv";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            SDP rndTripSdp = SDP.ParseSDPDescription(sdp.ToString());

            Assert.Equal(111, rndTripSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.audio).Single().MediaFormats.Single().Key);
            Assert.Equal("opus", rndTripSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.audio).Single().MediaFormats.Single().Value.Name());
        }
        public void GenerateLocalOfferWithAudioTrackUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            RTCPeerConnection pc = new RTCPeerConnection(null);

            pc.IceSession.StartGathering();
            var audioTrack = new MediaStreamTrack(null, SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
            });

            pc.addTrack(audioTrack);
            var offer = pc.createOffer(new RTCOfferOptions());

            SDP offerSDP = SDP.ParseSDPDescription(offer.sdp);

            Assert.NotNull(offer);
            Assert.NotNull(offer.sdp);
            Assert.Equal(RTCSdpType.offer, offer.type);
            Assert.Single(offerSDP.Media);
            Assert.Contains(offerSDP.Media, x => x.Media == SDPMediaTypesEnum.audio);

            logger.LogDebug(offer.sdp);
        }
Пример #12
0
        private static async Task StartFfmpegListener(string sdpPath, CancellationToken cancel)
        {
            while (!File.Exists(FFMPEG_SDP_FILE) && !cancel.IsCancellationRequested)
            {
                await Task.Delay(500);
            }

            if (!cancel.IsCancellationRequested)
            {
                var sdp = SDP.ParseSDPDescription(File.ReadAllText(FFMPEG_SDP_FILE));

                // The SDP is only expected to contain a single video media announcement.
                var videoAnn = sdp.Media.Single(x => x.Media == SDPMediaTypesEnum.video);
                _ffmpegVideoFormat = videoAnn.MediaFormats.Values.First();

                _ffmpegListener = new RTPSession(false, false, false, IPAddress.Loopback, FFMPEG_DEFAULT_RTP_PORT);
                _ffmpegListener.AcceptRtpFromAny = true;
                MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPAudioVideoMediaFormat> {
                    _ffmpegVideoFormat
                }, MediaStreamStatusEnum.RecvOnly);
                _ffmpegListener.addTrack(videoTrack);

                _ffmpegListener.SetRemoteDescription(SIP.App.SdpType.answer, sdp);

                // Set a dummy destination end point or the RTP session will end up sending RTCP reports
                // to itself.
                var dummyIPEndPoint = new IPEndPoint(IPAddress.Loopback, 0);
                _ffmpegListener.SetDestination(SDPMediaTypesEnum.video, dummyIPEndPoint, dummyIPEndPoint);

                await _ffmpegListener.Start();
            }
        }
Пример #13
0
        public void CheckSelectedAudioForamtAttributeUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string remoteSdp =
                @"v=0
o=- 1986548327 0 IN IP4 127.0.0.1
s=-
c=IN IP4 127.0.0.1
t=0 0
m=audio 60640 RTP/AVP 0 111 8
a=rtpmap:0 PCMU/8000
a=rtpmap:111 OPUS/48000/2";

            // Create a local session with an audio track.
            RTPSession       rtpSession      = new RTPSession(false, false, false);
            MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPWellKnownMediaFormatsEnum.PCMA, SDPWellKnownMediaFormatsEnum.G723);

            rtpSession.addTrack(localAudioTrack);

            var offer = SDP.ParseSDPDescription(remoteSdp);

            logger.LogDebug($"Remote offer: {offer}");

            var result = rtpSession.SetRemoteDescription(SIP.App.SdpType.offer, offer);

            logger.LogDebug($"Set remote description on local session result {result}.");

            Assert.Equal(SetDescriptionResultEnum.OK, result);
            Assert.Equal(8, rtpSession.AudioLocalTrack.Capabilities.Single(x => x.Name() == "PCMA").ID);
            Assert.Equal("PCMA", rtpSession.GetSendingFormat(SDPMediaTypesEnum.audio).Name());

            rtpSession.Close("normal");
        }
Пример #14
0
        /// <summary>
        /// Answers an incoming SIP call.
        /// </summary>
        public async Task <bool> Answer()
        {
            if (m_pendingIncomingCall == null)
            {
                StatusMessage(this, $"There was no pending call available to answer.");
                return(false);
            }
            else
            {
                var sipRequest = m_pendingIncomingCall.ClientTransaction.TransactionRequest;

                // Assume that if the INVITE request does not contain an SDP offer that it will be an
                // audio only call.
                bool hasAudio = true;
                bool hasVideo = false;

                if (sipRequest.Body != null)
                {
                    SDP offerSDP = SDP.ParseSDPDescription(sipRequest.Body);
                    hasAudio = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.MediaStreamStatus != MediaStreamStatusEnum.Inactive);
                    hasVideo = offerSDP.Media.Any(x => x.Media == SDPMediaTypesEnum.video && x.MediaStreamStatus != MediaStreamStatusEnum.Inactive);
                }

                MediaSession = CreateMediaSession();

                m_userAgent.RemotePutOnHold   += OnRemotePutOnHold;
                m_userAgent.RemoteTookOffHold += OnRemoteTookOffHold;

                bool result = await m_userAgent.Answer(m_pendingIncomingCall, MediaSession);

                m_pendingIncomingCall = null;

                return(result);
            }
        }
Пример #15
0
        public void ParseICESessionAttributesUnitTest()
        {
            Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                "v=0" + m_CRLF +
                "o=jdoe 2890844526 2890842807 IN IP4 10.0.1.1" + m_CRLF +
                "s=" + m_CRLF +
                "c=IN IP4 192.0.2.3" + m_CRLF +
                "t=0 0" + m_CRLF +
                "a=ice-pwd:asd88fgpdd777uzjYhagZg" + m_CRLF +
                "a=ice-ufrag:8hhY" + m_CRLF +
                "m=audio 45664 RTP/AVP 0" + m_CRLF +
                "b=RS:0" + m_CRLF +
                "b=RR:0" + m_CRLF +
                "a=rtpmap:0 PCMU/8000" + m_CRLF +
                "a=candidate:1 1 UDP 2130706431 10.0.1.1 8998 typ host" + m_CRLF +
                "a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr 10.0.1.1 rport 8998";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            Debug.WriteLine(sdp.ToString());

            Assert.IsTrue(sdp.IceUfrag == "8hhY", "The ICE username was not parsed correctly.");
            Assert.IsTrue(sdp.IcePwd == "asd88fgpdd777uzjYhagZg", "The ICE password was not parsed correctly.");
        }
Пример #16
0
        private void WebRtcAnswerReceived(WebSocketSharp.Net.WebSockets.WebSocketContext context, string webSocketID, string sdpAnswer)
        {
            try
            {
                logger.LogDebug("Answer SDP: " + sdpAnswer);

                var answerSDP = SDP.ParseSDPDescription(sdpAnswer);

                var conn = _webRtcConnections.Where(x => x.Key == webSocketID).Select(x => x.Value).SingleOrDefault();

                if (conn.WebRtcSession == null)
                {
                    logger.LogWarning("No WebRTC client entry exists for web socket ID " + webSocketID + ", ignoring.");
                }
                else
                {
                    logger.LogDebug("New WebRTC client SDP answer for web socket ID " + webSocketID + ".");
                    conn.WebRtcSession.setRemoteDescription(SdpType.answer, answerSDP);
                }

                context.WebSocket.CloseAsync();
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebRtcAnswerReceived. " + excp.Message);
            }
        }
Пример #17
0
        private static async Task WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection pc, string message)
        {
            try
            {
                if (pc.localDescription == null)
                {
                    //logger.LogDebug("Offer SDP: " + message);
                    logger.LogDebug("Offer SDP received.");

                    // Add local media tracks depending on what was offered. Also add local tracks with the same media ID as
                    // the remote tracks so that the media announcement in the SDP answer are in the same order.
                    SDP remoteSdp = SDP.ParseSDPDescription(message);
                    var res       = pc.setRemoteDescription(new RTCSessionDescriptionInit {
                        sdp = message, type = RTCSdpType.offer
                    });
                    if (res != SetDescriptionResultEnum.OK)
                    {
                        // No point continuing. Something will need to change and then try again.
                        pc.Close("failed to set remote sdp");
                    }
                    else
                    {
                        var answer = pc.createAnswer(null);
                        await pc.setLocalDescription(answer);

                        context.WebSocket.Send(answer.sdp);
                    }
                }
                else if (pc.remoteDescription == null)
                {
                    logger.LogDebug("Answer SDP: " + message);
                    var res = pc.setRemoteDescription(new RTCSessionDescriptionInit {
                        sdp = message, type = RTCSdpType.answer
                    });
                    if (res != SetDescriptionResultEnum.OK)
                    {
                        // No point continuing. Something will need to change and then try again.
                        pc.Close("failed to set remote sdp");
                    }
                }
                else
                {
                    logger.LogDebug("ICE Candidate: " + message);

                    if (string.IsNullOrWhiteSpace(message) || message.Trim().ToLower() == SDP.END_ICE_CANDIDATES_ATTRIBUTE)
                    {
                        logger.LogDebug("End of candidates message received.");
                    }
                    else
                    {
                        var candInit = Newtonsoft.Json.JsonConvert.DeserializeObject <RTCIceCandidateInit>(message);
                        pc.addIceCandidate(candInit);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
            }
        }
Пример #18
0
        public void ParseSDPUnitTest()
        {
            Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                "v=0" + m_CRLF +
                "o=root 3285 3285 IN IP4 10.0.0.4" + m_CRLF +
                "s=session" + m_CRLF +
                "c=IN IP4 10.0.0.4" + m_CRLF +
                "t=0 0" + m_CRLF +
                "m=audio 12228 RTP/AVP 0 101" + m_CRLF +
                "a=rtpmap:0 PCMU/8000" + m_CRLF +
                "a=rtpmap:101 telephone-event/8000" + m_CRLF +
                "a=fmtp:101 0-16" + m_CRLF +
                "a=silenceSupp:off - - - -" + m_CRLF +
                "a=ptime:20" + m_CRLF +
                "a=sendrecv";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            Debug.WriteLine(sdp.ToString());

            Assert.IsTrue(sdp.Connection.ConnectionAddress == "10.0.0.4", "The connection address was not parsed  correctly.");
            Assert.IsTrue(sdp.Media[0].Media == SDPMediaTypesEnum.audio, "The media type not parsed correctly.");
            Assert.IsTrue(sdp.Media[0].Port == 12228, "The connection port was not parsed correctly.");
            Assert.IsTrue(sdp.Media[0].GetFormatListToString() == "0 101", "The media format list was incorrect.");
            Assert.IsTrue(sdp.Media[0].MediaFormats[0].FormatID == 0, "The highest priority media format ID was incorrect.");
            Assert.IsTrue(sdp.Media[0].MediaFormats[0].Name == "PCMU", "The highest priority media format name was incorrect.");
            Assert.IsTrue(sdp.Media[0].MediaFormats[0].ClockRate == 8000, "The highest priority media format clockrate was incorrect.");
        }
Пример #19
0
        public void ParseAudioAndVideoConnectionsUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr = "v=0" + m_CRLF +
                            "o=Cisco-SIPUA 6396 0 IN IP4 101.180.234.134" + m_CRLF +
                            "s=SIP Call" + m_CRLF +
                            "t=0 0" + m_CRLF +
                            "m=audio 19586 RTP/AVP 0" + m_CRLF +
                            "c=IN IP4 101.180.234.134" + m_CRLF +
                            "a=rtpmap:0 PCMU/8000" + m_CRLF +
                            "a=sendrecv" + m_CRLF +
                            "m=video 0 RTP/AVP 96" + m_CRLF +
                            "c=IN IP4 10.0.0.10";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            Assert.True(sdp.Connection.ConnectionAddress == "101.180.234.134", "The connection address was not parsed correctly.");
            Assert.NotEmpty(sdp.Media);
            Assert.True(sdp.Media[0].Media == SDPMediaTypesEnum.audio, "The media type not parsed correctly.");
            Assert.Equal(SDPMediaFormatsEnum.PCMU, sdp.Media[0].MediaFormats[0].FormatCodec);
            Assert.True(sdp.Media[1].Media == SDPMediaTypesEnum.video, "The media type not parsed correctly.");
            Assert.True(sdp.Media[1].Connection.ConnectionAddress == "10.0.0.10", "The connection address was not parsed correctly.");
        }
Пример #20
0
        public void ParseBadFormatSDPUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                " v=0" + m_CRLF +
                " o=root 3285 3285 IN IP4 10.0.0.4" + m_CRLF +
                " s=session" + m_CRLF +
                " c=IN IP4 10.0.0.4" + m_CRLF +
                " t=0 0" + m_CRLF +
                " m=audio 12228 RTP/AVP 0 101" + m_CRLF +
                " a=rtpmap:0 PCMU/8000" + m_CRLF +
                " a=rtpmap:101 telephone-event/8000" + m_CRLF +
                " a=fmtp:101 0-16" + m_CRLF +
                " a=silenceSupp:off - - - -" + m_CRLF +
                " a=ptime:20" + m_CRLF +
                " a=sendrecv";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            logger.LogDebug($"audio format[0]: {sdp.Media[0].MediaFormats[0].ToString()}");
            logger.LogDebug($"audio format[1]: {sdp.Media[0].MediaFormats[1].ToString()}");

            Assert.True(sdp.Connection.ConnectionAddress == "10.0.0.4", "The connection address was not parsed  correctly.");
            Assert.True(sdp.Username == "root", "The owner was not parsed correctly.");
            Assert.True(sdp.SessionName == "session", "The SessionName was not parsed correctly.");
            Assert.True(sdp.Media[0].Media == SDPMediaTypesEnum.audio, "The media type not parsed correctly.");
            Assert.Equal(SDPMediaFormatsEnum.PCMU, sdp.Media[0].MediaFormats[0].FormatCodec);
            Assert.Equal(SDPMediaFormatsEnum.Event, sdp.Media[0].MediaFormats[1].FormatCodec);
        }
Пример #21
0
        public void SessionMediaSteamStatusRoundTripUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                "v=0" + m_CRLF +
                "o=root 3285 3285 IN IP4 10.0.0.4" + m_CRLF +
                "s=session" + m_CRLF +
                "c=IN IP4 10.0.0.4" + m_CRLF +
                "t=0 0" + m_CRLF +
                "a=recvonly" + m_CRLF +
                "m=audio 12228 RTP/AVP 0 101" + m_CRLF +
                "a=rtpmap:0 PCMU/8000" + m_CRLF +
                "a=rtpmap:101 telephone-event/8000" + m_CRLF +
                "a=fmtp:101 0-16" + m_CRLF +
                "a=silenceSupp:off - - - -" + m_CRLF +
                "a=ptime:20";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            SDP sdpRoundTrip = SDP.ParseSDPDescription(sdp.ToString());

            Assert.Equal(MediaStreamStatusEnum.RecvOnly, sdpRoundTrip.SessionMediaStreamStatus);
        }
Пример #22
0
        private void HandleResponse(SIPResponse response, string sourceIP, string destinationIP)
        {
            VoipCall call = CreateVoipCallFromSipMessage(response, sourceIP, destinationIP);

            // Check if the voip calls hash set already contains this call.
            if (_voipCalls.Contains(call))
            {
                if (response.StatusCode == (int)SipResponses.OK)
                {
                    SDP SDPmessage = SDP.ParseSDPDescription(response.Body);
                    GetCall(call).RTPPort = SDPmessage.Media[0].Port;
                    HandleRTPPortAdded(call);

                    foreach (var m in SDPmessage.Media[0].MediaFormats)
                    {
                        GetCall(call).RTPMediaType += $"{m.Value.Kind}:{m.Value.Rtpmap} ";
                    }

                    HandleRTPMediaTypeAdded(call);
                }
                else if (response.StatusCode >= (int)SipResponses.BadRequest && response.StatusCode < (int)SipResponses.InternalServerError)
                {
                    GetCall(call).CallState = CallState.Rejected;
                    HandleCallStateUpdate(call, CallState.Rejected);
                    HandleFinishedCall(call);
                }
            }
        }
        public void JsonRoundtripUnitTest()
        {
            RTCPeerConnection pcSrc = new RTCPeerConnection(null);
            var videoTrackSrc       = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPAudioVideoMediaFormat> {
                new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.video, 96, "VP8", 90000)
            });

            pcSrc.addTrack(videoTrackSrc);

            var offer = pcSrc.createOffer(new RTCOfferOptions());

            Assert.NotNull(offer.toJSON());

            logger.LogDebug($"offer: {offer.toJSON()}");

            var parseResult = RTCSessionDescriptionInit.TryParse(offer.toJSON(), out var init);

            Assert.True(parseResult);

            Assert.Equal(RTCSdpType.offer, init.type);
            Assert.NotNull(init.sdp);

            SDP sdp = SDP.ParseSDPDescription(init.sdp);

            Assert.Equal(0, sdp.Version);
        }
Пример #24
0
        private void WebRtcAnswerReceived(string webSocketID, string sdpAnswer)
        {
            try
            {
                logger.Debug("Answer SDP: " + sdpAnswer);

                var answerSDP = SDP.ParseSDPDescription(sdpAnswer);

                var peer = _webRtcSessions.Where(x => x.Key == webSocketID).Select(x => x.Value.Peer).SingleOrDefault();

                if (peer == null)
                {
                    logger.Warn("No WebRTC client entry exists for web socket ID " + webSocketID + ", ignoring.");
                }
                else
                {
                    logger.Debug("New WebRTC client SDP answer for web socket ID " + webSocketID + ".");

                    peer.SdpSessionID      = answerSDP.SessionId;
                    peer.RemoteIceUser     = answerSDP.IceUfrag;
                    peer.RemoteIcePassword = answerSDP.IcePwd;

                    foreach (var iceCandidate in answerSDP.IceCandidates)
                    {
                        peer.AppendRemoteIceCandidate(iceCandidate);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.Error("Exception SDPExchangeReceiver_SDPAnswerReceived. " + excp.Message);
            }
        }
Пример #25
0
        /// <summary>
        /// An outgoing call was successfully answered.
        /// </summary>
        /// <param name="uac">The local SIP user agent client that initiated the call.</param>
        /// <param name="sipResponse">The SIP answer response received from the remote party.</param>
        private void CallAnswered(ISIPClientUserAgent uac, SIPResponse sipResponse)
        {
            StatusMessage("Call answered: " + sipResponse.StatusCode + " " + sipResponse.ReasonPhrase + ".");

            if (sipResponse.StatusCode >= 200 && sipResponse.StatusCode <= 299)
            {
                if (sipResponse.Header.ContentType != _sdpMimeContentType)
                {
                    // Payload not SDP, I don't understand :(.
                    StatusMessage("Call was hungup as the answer response content type was not recognised: " + sipResponse.Header.ContentType + ". :(");
                    Hangup();
                }
                else if (sipResponse.Body.IsNullOrBlank())
                {
                    // They said SDP but didn't give me any :(.
                    StatusMessage("Call was hungup as the answer response had an empty SDP payload. :(");
                    Hangup();
                }

                SDP sdpAnswer = SDP.ParseSDPDescription(sipResponse.Body);
                System.Diagnostics.Debug.WriteLine(sipResponse.Body);
                _mediaManager.SetRemoteSDP(sdpAnswer);
                CallAnswer();
            }
            else
            {
                CallFinished();
            }
        }
Пример #26
0
        public void ParseMultipleMediaAnnouncementsUnitTest()
        {
            Console.WriteLine(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr = "v=0" + m_CRLF +
                            "o=- 13064410510996677 3 IN IP4 10.1.1.2" + m_CRLF +
                            "s=Bria 4 release 4.1.1 stamp 74246" + m_CRLF +
                            "c=IN IP4 10.1.1.2" + m_CRLF +
                            "b=AS:2064" + m_CRLF +
                            "t=0 0" + m_CRLF +
                            "m=audio 49290 RTP/AVP 0" + m_CRLF +
                            "a=sendrecv" + m_CRLF +
                            "m=video 56674 RTP/AVP 96" + m_CRLF +
                            "b=TIAS:2000000" + m_CRLF +
                            "a=rtpmap:96 VP8/90000" + m_CRLF +
                            "a=sendrecv" + m_CRLF +
                            "a=rtcp-fb:* nack pli";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            Debug.WriteLine(sdp.ToString());

            Assert.AreEqual(2, sdp.Media.Count);
            Assert.AreEqual(49290, sdp.Media.Where(x => x.Media == SDPMediaTypesEnum.audio).FirstOrDefault().Port);
            Assert.AreEqual(56674, sdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).FirstOrDefault().Port);
        }
Пример #27
0
        public void ParseWebRtcSDPUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sdpStr =
                @"v=0
o=- 1090343221 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
m=audio 11158 RTP/SAVP 0
c=IN IP4 127.0.0.1
a=candidate:1988909849 1 udp 1124657401 192.168.11.50 11158 typ host generation 0
a=candidate:1846148317 1 udp 2094219785 127.0.0.1 11158 typ host generation 0
a=candidate:2012632329 1 udp 2122820711 172.30.224.1 11158 typ host generation 0
a=end-of-candidates 
a=ice-ufrag:UWWAVCUMPZHPCLNIMZYA
a=ice-pwd:IEUVYLWMXMQZKCMLTXQHZZVWXRCBLPPNUYFPCABK
a=fingerprint:sha-256 C6:ED:8C:9D:06:50:77:23:0A:4A:D8:42:68:29:D0:70:2F:BB:C7:72:EC:98:5C:62:07:1B:0C:5D:CB:CE:BE:CD
a=setup:actpass
a=sendonly
a=rtcp-mux
a=mid:audio
a=rtpmap:0 PCMU/8000
m=video 0 RTP/SAVP 100
c=IN IP4 127.0.0.1
a=ice-ufrag:UWWAVCUMPZHPCLNIMZYA
a=ice-pwd:IEUVYLWMXMQZKCMLTXQHZZVWXRCBLPPNUYFPCABK
a=fingerprint:sha-256 C6:ED:8C:9D:06:50:77:23:0A:4A:D8:42:68:29:D0:70:2F:BB:C7:72:EC:98:5C:62:07:1B:0C:5D:CB:CE:BE:CD
a=bundle-only 
a=setup:actpass
a=sendonly
a=rtcp-mux
a=mid:video
a=rtpmap:100 VP8/90000";

            SDP sdp = SDP.ParseSDPDescription(sdpStr);

            logger.LogDebug(sdp.ToString());

            SDP rndTripSdp = SDP.ParseSDPDescription(sdp.ToString());

            Assert.Equal("BUNDLE audio video", sdp.Group);
            Assert.Equal("BUNDLE audio video", rndTripSdp.Group);
            Assert.Equal("UWWAVCUMPZHPCLNIMZYA", sdp.Media[0].IceUfrag);
            Assert.Equal("UWWAVCUMPZHPCLNIMZYA", rndTripSdp.Media[0].IceUfrag);
            Assert.Equal("IEUVYLWMXMQZKCMLTXQHZZVWXRCBLPPNUYFPCABK", sdp.Media[0].IcePwd);
            Assert.Equal("IEUVYLWMXMQZKCMLTXQHZZVWXRCBLPPNUYFPCABK", rndTripSdp.Media[0].IcePwd);
            Assert.Equal(3, sdp.Media[0].IceCandidates.Count());
            Assert.Equal(3, rndTripSdp.Media[0].IceCandidates.Count());
            Assert.Equal("sha-256 C6:ED:8C:9D:06:50:77:23:0A:4A:D8:42:68:29:D0:70:2F:BB:C7:72:EC:98:5C:62:07:1B:0C:5D:CB:CE:BE:CD", sdp.Media[0].DtlsFingerprint);
            Assert.Equal("sha-256 C6:ED:8C:9D:06:50:77:23:0A:4A:D8:42:68:29:D0:70:2F:BB:C7:72:EC:98:5C:62:07:1B:0C:5D:CB:CE:BE:CD", rndTripSdp.Media[0].DtlsFingerprint);
            Assert.Equal("audio", sdp.Media[0].MediaID);
            Assert.Equal("audio", rndTripSdp.Media[0].MediaID);
            Assert.Equal("video", sdp.Media[1].MediaID);
            Assert.Equal("video", rndTripSdp.Media[1].MediaID);
        }
Пример #28
0
        private static async Task WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection pc, string message)
        {
            try
            {
                if (pc.localDescription == null)
                {
                    //logger.LogDebug("Offer SDP: " + message);
                    logger.LogDebug("Offer SDP received.");

                    // Add local media tracks depending on what was offered. Also add local tracks with the same media ID as
                    // the remote tracks so that the media announcement in the SDP answer are in the same order.
                    SDP remoteSdp = SDP.ParseSDPDescription(message);

                    foreach (var ann in remoteSdp.Media)
                    {
                        var capbilities        = FilterCodecs(ann.Media, ann.MediaFormats);
                        MediaStreamTrack track = new MediaStreamTrack(ann.Media, false, capbilities, MediaStreamStatusEnum.RecvOnly);
                        pc.addTrack(track);
                    }

                    pc.setRemoteDescription(new RTCSessionDescriptionInit {
                        sdp = message, type = RTCSdpType.offer
                    });

                    var answer = pc.createAnswer(null);
                    await pc.setLocalDescription(answer);

                    Console.WriteLine(answer.sdp);

                    context.WebSocket.Send(answer.sdp);
                }
                else if (pc.remoteDescription == null)
                {
                    logger.LogDebug("Answer SDP: " + message);
                    pc.setRemoteDescription(new RTCSessionDescriptionInit {
                        sdp = message, type = RTCSdpType.answer
                    });
                }
                else
                {
                    logger.LogDebug("ICE Candidate: " + message);

                    if (string.IsNullOrWhiteSpace(message) || message.Trim().ToLower() == SDP.END_ICE_CANDIDATES_ATTRIBUTE)
                    {
                        logger.LogDebug("End of candidates message received.");
                    }
                    else
                    {
                        var candInit = Newtonsoft.Json.JsonConvert.DeserializeObject <RTCIceCandidateInit>(message);
                        pc.addIceCandidate(candInit);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
            }
        }
Пример #29
0
        public void RFC5118_4_9()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            string sipMsg =
                "INVITE sip:[email protected] SIP/2.0" + CRLF +
                "To: sip:[email protected]" + CRLF +
                "From: sip:[email protected];tag=81x2" + CRLF +
                "Via: SIP/2.0/UDP [::ffff:192.0.2.10]:19823;branch=z9hG4bKbh19" + CRLF +
                "Via: SIP/2.0/UDP [::ffff:192.0.2.2];branch=z9hG4bKas3-111" + CRLF +
                "Call-ID: SSG9559905523997077@hlau_4100" + CRLF +
                "Contact: \"T. desk phone\" <sip:ted@[::ffff:192.0.2.2]>" + CRLF +
                "CSeq: 612 INVITE" + CRLF +
                "Max-Forwards: 70" + CRLF +
                "Content-Type: application/sdp" + CRLF +
                "Content-Length: 236" + CRLF +
                CRLF +
                "v=0" + CRLF +
                "o=assistant 971731711378798081 0 IN IP6 ::ffff:192.0.2.2" + CRLF +
                "s=Call me soon, please!" + CRLF +
                "c=IN IP6 ::ffff:192.0.2.2" + CRLF +
                "t=3338481189 3370017201" + CRLF +
                "m=audio 6000 RTP/AVP 2" + CRLF +
                "a=rtpmap:2 G726-32/8000" + CRLF +
                "m=video 6024 RTP/AVP 107" + CRLF +
                "a=rtpmap:107 H263-1998/90000";


            SIPMessageBuffer sipMessageBuffer = SIPMessageBuffer.ParseSIPMessage(Encoding.UTF8.GetBytes(sipMsg), null, null);

            Assert.True(sipMessageBuffer != null, "The SIP message not parsed correctly.");
            SIPRequest sipRequest = SIPRequest.ParseSIPRequest(sipMessageBuffer);

            Assert.Equal(SIPMethodsEnum.INVITE, sipRequest.Method);
            IPAddress ip6;

            Assert.NotEmpty(sipRequest.Header.Vias.Via);
            Assert.True(IPAddress.TryParse(sipRequest.Header.Vias.TopViaHeader.Host, out ip6));
            Assert.Equal(AddressFamily.InterNetworkV6, ip6.AddressFamily);
            Assert.Equal(19823, sipRequest.Header.Vias.TopViaHeader.Port);
            Assert.True(IPAddress.TryParse(sipRequest.Header.Vias.BottomViaHeader.ReceivedFromAddress, out ip6));
            Assert.Equal(AddressFamily.InterNetworkV6, ip6.AddressFamily);
            Assert.NotEmpty(sipRequest.Header.Contact);
            Assert.True(IPAddress.TryParse(sipRequest.Header.Contact[0].ContactURI.HostAddress, out ip6));
            Assert.Equal(AddressFamily.InterNetworkV6, ip6.AddressFamily);
            Assert.False(IPAddress.TryParse(sipRequest.URI.HostAddress, out ip6));
            Assert.False(string.IsNullOrWhiteSpace(sipRequest.Body));
            SDP sdp = SDP.ParseSDPDescription(sipRequest.Body);

            Assert.NotNull(sdp);
            Assert.NotNull(sdp.Connection);
            Assert.True(IPAddress.TryParse(sdp.Connection.ConnectionAddress, out ip6));
            Assert.Equal(AddressFamily.InterNetworkV6, ip6.AddressFamily);
            Assert.NotEmpty(sdp.Media);

            logger.LogDebug("-----------------------------------------");
        }
Пример #30
0
        public void MediaOrderMatchesRemoteOfferUnitTest()
        {
            logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
            logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name);

            // By default offers made by us always put audio first. Create a remote SDP offer
            // with the video first.
            string remoteSdp =
                @"v=0
o=- 1986548327 0 IN IP4 127.0.0.1
s=-
c=IN IP4 127.0.0.1
t=0 0
m=video 60638 RTP/AVP 100
a=rtpmap:100 VP8/90000
a=sendrecv
m=audio 60640 RTP/AVP 0 111
a=rtpmap:0 PCMU/8000
a=rtpmap:111 OPUS/48000/2
a=sendrecv";

            // Create a local session and add the video track first.
            RTPSession       rtpSession      = new RTPSession(false, false, false);
            MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> {
                new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU),
                new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.audio, 110, "OPUS/48000/2")
            });

            rtpSession.addTrack(localAudioTrack);
            MediaStreamTrack localVideoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPAudioVideoMediaFormat> {
                new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.video, 96, "VP8", 90000)
            });

            rtpSession.addTrack(localVideoTrack);

            var offer = SDP.ParseSDPDescription(remoteSdp);

            logger.LogDebug($"Remote offer: {offer}");

            var result = rtpSession.SetRemoteDescription(SIP.App.SdpType.offer, offer);

            logger.LogDebug($"Set remote description on local session result {result}.");

            Assert.Equal(SetDescriptionResultEnum.OK, result);

            var answer = rtpSession.CreateAnswer(null);

            logger.LogDebug($"Local answer: {answer}");

            Assert.Equal(111, rtpSession.AudioLocalTrack.Capabilities.Single(x => x.Name() == "OPUS").ID);
            Assert.Equal(100, rtpSession.VideoLocalTrack.Capabilities.Single(x => x.Name() == "VP8").ID);

            //Assert.True(SDPAudioVideoMediaFormat.AreMatch(offer.Media.Single(x => x.Media == SDPMediaTypesEnum.audio)., answer.Media.First().Media));
            //Assert.Equal(offer.Media.Last().Media, answer.Media.Last().Media);

            rtpSession.Close("normal");
        }