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"); }
private static RTPSession CreateRtpSession(List <SDPAudioVideoMediaFormat> audioFormats, List <SDPAudioVideoMediaFormat> videoFormats) { var rtpSession = new RTPSession(false, false, false, IPAddress.Loopback); bool hasAudio = false; bool hasVideo = false; if (audioFormats != null && audioFormats.Count > 0) { MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, audioFormats, MediaStreamStatusEnum.SendRecv); rtpSession.addTrack(audioTrack); hasAudio = true; } if (videoFormats != null && videoFormats.Count > 0) { MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, videoFormats, MediaStreamStatusEnum.SendRecv); rtpSession.addTrack(videoTrack); hasVideo = true; } var sdpOffer = rtpSession.CreateOffer(null); // Because the SDP being written to the file is the input to ffplay the connection ports need to be changed // to the ones ffplay will be listening on. if (hasAudio) { sdpOffer.Media.Single(x => x.Media == SDPMediaTypesEnum.audio).Port = FFPLAY_DEFAULT_AUDIO_PORT; } if (hasVideo) { sdpOffer.Media.Single(x => x.Media == SDPMediaTypesEnum.video).Port = FFPLAY_DEFAULT_VIDEO_PORT; } Console.WriteLine(sdpOffer); using (StreamWriter sw = new StreamWriter(FFPLAY_DEFAULT_SDP_PATH)) { sw.Write(sdpOffer); } string ffplayCommand = String.Format(FFPLAY_DEFAULT_COMMAND, FFPLAY_DEFAULT_SDP_PATH); Console.WriteLine($"Start ffplay using the command below:"); Console.WriteLine(ffplayCommand); Console.WriteLine($"To request the remote peer to send a video key frame press 'k'"); rtpSession.Start(); rtpSession.SetDestination(SDPMediaTypesEnum.audio, new IPEndPoint(IPAddress.Loopback, FFPLAY_DEFAULT_AUDIO_PORT), new IPEndPoint(IPAddress.Loopback, FFPLAY_DEFAULT_AUDIO_PORT + 1)); rtpSession.SetDestination(SDPMediaTypesEnum.video, new IPEndPoint(IPAddress.Loopback, FFPLAY_DEFAULT_VIDEO_PORT), new IPEndPoint(IPAddress.Loopback, FFPLAY_DEFAULT_VIDEO_PORT + 1)); return(rtpSession); }
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 a=rtpmap:0 PCMU/8000 a=sendrecv"; // Create a local session and add the video track first. RTPSession localSession = new RTPSession(false, false, false); MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); localSession.addTrack(localAudioTrack); MediaStreamTrack localVideoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }); localSession.addTrack(localVideoTrack); var offer = SDP.ParseSDPDescription(remoteSdp); logger.LogDebug($"Remote offer: {offer}"); var result = localSession.SetRemoteDescription(SIP.App.SdpType.offer, offer); logger.LogDebug($"Set remote description on local session result {result}."); Assert.Equal(SetDescriptionResultEnum.OK, result); var answer = localSession.CreateAnswer(null); logger.LogDebug($"Local answer: {answer}"); Assert.Equal(offer.Media.First().Media, answer.Media.First().Media); Assert.Equal(offer.Media.Last().Media, answer.Media.Last().Media); localSession.Close("normal"); }
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(); } }
public void NoLocalTracksTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); // Create two RTP sessions. First one acts as the local session to generate the offer. // Second one acts as the remote session to generate the answer. // A local session is created but NO media tracks are added to it. RTPSession localSession = new RTPSession(false, false, false); // Create a remote session WITH an audio track. RTPSession remoteSession = new RTPSession(false, false, false); // The track for the track for the remote session is still local relative to the session it's being added to. MediaStreamTrack remoteAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); remoteSession.addTrack(remoteAudioTrack); var offer = remoteSession.CreateOffer(IPAddress.Loopback); // Give the offer to the local session that is missing any media tracks. var result = localSession.SetRemoteDescription(SIP.App.SdpType.offer, offer); logger.LogDebug($"Set remote description on local session result {result}."); Assert.Equal(SetDescriptionResultEnum.NoMatchingMediaType, result); localSession.Close("normal"); remoteSession.Close("normal"); }
public async void SortChecklistUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTPSession rtpSession = new RTPSession(true, true, true); MediaStreamTrack dummyTrack = new MediaStreamTrack(null, SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); rtpSession.addTrack(dummyTrack); var iceSession = new IceSession(rtpSession.GetRtpChannel(SDPMediaTypesEnum.audio), RTCIceComponent.rtp, null); iceSession.StartGathering(); Assert.NotNull(iceSession); Assert.NotEmpty(iceSession.Candidates); foreach (var hostCandidate in iceSession.Candidates) { logger.LogDebug(hostCandidate.ToString()); } var remoteCandidate = RTCIceCandidate.Parse("candidate:408132416 1 udp 2113937151 192.168.11.50 51268 typ host generation 0 ufrag CI7o network-cost 999"); await iceSession.AddRemoteCandidate(remoteCandidate); var remoteCandidate2 = RTCIceCandidate.Parse("candidate:408132417 1 udp 2113937150 192.168.11.51 51268 typ host generation 0 ufrag CI7o network-cost 999"); await iceSession.AddRemoteCandidate(remoteCandidate2); foreach (var entry in iceSession._checklist) { logger.LogDebug($"checklist entry priority {entry.Priority}."); } }
public void NoMatchingMediaTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTPSession localSession = new RTPSession(false, false, false); MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); localSession.addTrack(localAudioTrack); RTPSession remoteSession = new RTPSession(false, false, false); // The track for the track for the remote session is still local relative to the session it's being added to. MediaStreamTrack remoteVideoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.video, 96, "VP8", 90000) }); remoteSession.addTrack(remoteVideoTrack); var result = localSession.SetRemoteDescription(SIP.App.SdpType.offer, remoteSession.CreateOffer(IPAddress.Loopback)); logger.LogDebug($"Set remote description on local session result {result}."); Assert.Equal(SetDescriptionResultEnum.NoMatchingMediaType, result); localSession.Close("normal"); remoteSession.Close("normal"); }
public void GetHostCandidatesForRTPBindUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); var localAddress = NetServices.InternetDefaultAddress; RTPSession rtpSession = new RTPSession(true, true, true, localAddress); // Add a track to the session in order to initialise the RTPChannel. MediaStreamTrack dummyTrack = new MediaStreamTrack(null, SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); rtpSession.addTrack(dummyTrack); RTPChannel rtpChannel = rtpSession.GetRtpChannel(SDPMediaTypesEnum.audio); logger.LogDebug($"RTP channel RTP socket local end point {rtpChannel.RTPLocalEndPoint}."); var iceSession = new IceSession(rtpChannel, RTCIceComponent.rtp, null); iceSession.StartGathering(); Assert.NotNull(iceSession); Assert.NotEmpty(iceSession.Candidates); Assert.True(localAddress.Equals(IPAddress.Parse(iceSession.Candidates.Single().address))); foreach (var hostCandidate in iceSession.Candidates) { logger.LogDebug(hostCandidate.ToString()); } }
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"); }
public void CheckDuplicateBindPortFailsUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); // Create two RTP sessions. First one acts as the local session to generate the offer. // Second one acts as the remote session to generate the answer. RTPSession localSession = new RTPSession(false, false, false, IPAddress.Loopback); MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); localSession.addTrack(localAudioTrack); var rtpEndPoint = localSession.GetRtpChannel(SDPMediaTypesEnum.audio).RTPLocalEndPoint; logger.LogDebug($"RTP session local end point {rtpEndPoint}."); // Now attempt to create a second RTP session on the same port as the previous one. RTPSession duplicateSession = new RTPSession(false, false, false, IPAddress.Loopback, rtpEndPoint.Port); MediaStreamTrack duplicateTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); Assert.Throws <ApplicationException>(() => duplicateSession.addTrack(duplicateTrack)); localSession.Close(null); }
public void AudioVideoOfferNoLocalAudioUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); // Create two RTP sessions. First one acts as the local session to generate the offer. // Second one acts as the remote session to generate the answer. // A local session is created but only has a video track added to it. RTPSession localSession = new RTPSession(false, false, false); MediaStreamTrack localVideoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.video, 96, "VP8", 90000) }); localSession.addTrack(localVideoTrack); // Create a remote session with both audio and video tracks. RTPSession remoteSession = new RTPSession(false, false, false); // The track for the track for the remote session is still local relative to the session it's being added to. MediaStreamTrack remoteAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); remoteSession.addTrack(remoteAudioTrack); MediaStreamTrack remoteVideoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.video, 96, "VP8", 90000) }); remoteSession.addTrack(remoteVideoTrack); var offer = remoteSession.CreateOffer(IPAddress.Loopback); // Give the offer to the local session that is missing a video tracks. var result = localSession.SetRemoteDescription(SIP.App.SdpType.offer, offer); logger.LogDebug($"Set remote description on local session result {result}."); Assert.Equal(SetDescriptionResultEnum.OK, result); var answer = localSession.CreateAnswer(null); Assert.Equal(MediaStreamStatusEnum.Inactive, answer.Media.Where(x => x.Media == SDPMediaTypesEnum.audio).Single().MediaStreamStatus); Assert.Equal(MediaStreamStatusEnum.SendRecv, answer.Media.Where(x => x.Media == SDPMediaTypesEnum.video).Single().MediaStreamStatus); localSession.Close("normal"); remoteSession.Close("normal"); }
private static RTPSession CreateRtpSession(List <SDPMediaFormat> videoFormats) { var rtpSession = new RTPSession(false, false, false, IPAddress.Loopback); MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, videoFormats, MediaStreamStatusEnum.SendRecv); rtpSession.addTrack(videoTrack); rtpSession.SetDestination(SDPMediaTypesEnum.video, new IPEndPoint(IPAddress.Loopback, FFPLAY_DEFAULT_VIDEO_PORT), new IPEndPoint(IPAddress.Loopback, FFPLAY_DEFAULT_VIDEO_PORT + 1)); return(rtpSession); }
public void AudioOnlyOfferAnswerTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); // Create two RTP sessions. First one acts as the local session to generate the offer. // Second one acts as the remote session to generate the answer. RTPSession localSession = new RTPSession(false, false, false); MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); localSession.addTrack(localAudioTrack); // Generate the offer to send to the remote party. var offer = localSession.CreateOffer(IPAddress.Loopback); logger.LogDebug("Local offer: " + offer.ToString()); RTPSession remoteSession = new RTPSession(false, false, false); // The track for the track for the remote session is still local relative to the session it's being added to. MediaStreamTrack remoteAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); remoteSession.addTrack(remoteAudioTrack); var result = remoteSession.SetRemoteDescription(SIP.App.SdpType.offer, offer); logger.LogDebug($"Set remote description on remote session result {result}."); Assert.Equal(SetDescriptionResultEnum.OK, result); // Get the answer from the remote session. var answer = remoteSession.CreateAnswer(IPAddress.Loopback); logger.LogDebug("Remote answer: " + offer.ToString()); // Provide the answer back to the local session. result = localSession.SetRemoteDescription(SIP.App.SdpType.answer, answer); logger.LogDebug($"Set remote description on local session result {result}."); Assert.Equal(SetDescriptionResultEnum.OK, result); localSession.Close("normal"); remoteSession.Close("normal"); }
public void CreateInstanceUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTPSession rtpSession = new RTPSession(true, true, true); // Add a track to the session in order to initialise the RTPChannel. MediaStreamTrack dummyTrack = new MediaStreamTrack(null, SDPMediaTypesEnum.audio, false, new List<SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); rtpSession.addTrack(dummyTrack); var iceSession = new IceSession(rtpSession.GetRtpChannel(SDPMediaTypesEnum.audio), RTCIceComponent.rtp); Assert.NotNull(iceSession); }
public async Task HandleInvalidSdpPortOnAnswerUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); SIPTransport transport = new SIPTransport(); SIPUserAgent userAgent = new SIPUserAgent(transport, null); string inviteReqStr = @"INVITE sip:[email protected] SIP/2.0 Via: SIP/2.0/UDP 0.0.0.0;branch=z9hG4bK57441c4980b94e1686a06ae080be2935;rport To: <sip:[email protected]> From: <sip:0.0.0.0:0>;tag=MYILIYPHQD Call-ID: ddf0e5a9687b4745925438da9000445d CSeq: 1 INVITE Max-Forwards: 70 Allow: ACK, BYE, CANCEL, INFO, INVITE, NOTIFY, OPTIONS, PRACK, REFER, REGISTER, SUBSCRIBE Content-Length: 0 v=0 o=- 1838015445 0 IN IP4 127.0.0.1 s=- c=IN IP4 127.0.0.1 t=0 0 m=audio 79762 RTP/AVP 0 a=rtpmap:0 PCMU/8000 a=sendrecv"; SIPEndPoint dummySipEndPoint = new SIPEndPoint(new IPEndPoint(IPAddress.Any, 0)); SIPMessageBuffer sipMessageBuffer = SIPMessageBuffer.ParseSIPMessage(inviteReqStr, dummySipEndPoint, dummySipEndPoint); SIPRequest inviteReq = SIPRequest.ParseSIPRequest(sipMessageBuffer); var uas = userAgent.AcceptCall(inviteReq); RTPSession rtpSession = new RTPSession(false, false, false); MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); rtpSession.addTrack(audioTrack); var result = await userAgent.Answer(uas, rtpSession); Assert.False(result); rtpSession.Close("normal"); }
public void ModifiedWellKnownFormatIDUnitTest() { 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 c=IN IP4 127.0.0.1 m=audio 60640 RTP/AVP 8 12 a=rtpmap:8 OPUS/48000/2 a=rtpmap:12 PCMA/8000"; // Create a local session with an audio track. RTPSession rtpSession = new RTPSession(false, false, false); MediaStreamTrack localAudioTrack = new MediaStreamTrack( SDPWellKnownMediaFormatsEnum.PCMU, SDPWellKnownMediaFormatsEnum.PCMA, SDPWellKnownMediaFormatsEnum.G722); 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(12, rtpSession.AudioLocalTrack.Capabilities.Single(x => x.Name() == "PCMA").ID); Assert.Equal("PCMA", rtpSession.GetSendingFormat(SDPMediaTypesEnum.audio).Name()); var answer = rtpSession.CreateAnswer(null); logger.LogDebug($"Local answer: {answer}"); Assert.Equal(12, answer.Media.Single().MediaFormats.Single().Key); Assert.Equal("PCMA", answer.Media.Single().MediaFormats.Single().Value.Name()); rtpSession.Close("normal"); }
public void SetRemoteSDPNoMediaStreamAttributeUnitTest() { 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 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(new AudioFormat(SDPWellKnownMediaFormatsEnum.PCMU)); 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); var answer = rtpSession.CreateAnswer(null); logger.LogDebug($"Local answer: {answer}"); Assert.Equal(0, rtpSession.AudioLocalTrack.Capabilities.Single(x => x.Name() == "PCMU").ID); Assert.Equal(MediaStreamStatusEnum.SendRecv, rtpSession.AudioLocalTrack.StreamStatus); Assert.NotNull(rtpSession.AudioRemoteTrack); Assert.Equal(MediaStreamStatusEnum.SendRecv, rtpSession.AudioRemoteTrack.StreamStatus); rtpSession.Close("normal"); }
public void NoRemoteMediaTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTPSession localSession = new RTPSession(false, false, false); MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); localSession.addTrack(localAudioTrack); var remoteOffer = new SDP(); var result = localSession.SetRemoteDescription(SIP.App.SdpType.offer, remoteOffer); logger.LogDebug($"Set remote description on local session result {result}."); Assert.Equal(SetDescriptionResultEnum.NoRemoteMedia, result); localSession.Close("normal"); }
public void InvalidPortInRemoteOfferTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTPSession localSession = new RTPSession(false, false, false); MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); localSession.addTrack(localAudioTrack); var remoteOffer = new SDP(); remoteOffer.SessionId = Crypto.GetRandomInt(5).ToString(); remoteOffer.Connection = new SDPConnectionInformation(IPAddress.Loopback); SDPMediaAnnouncement audioAnnouncement = new SDPMediaAnnouncement( SDPMediaTypesEnum.audio, 66000, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); audioAnnouncement.Transport = RTPSession.RTP_MEDIA_PROFILE; remoteOffer.Media.Add(audioAnnouncement); var result = localSession.SetRemoteDescription(SIP.App.SdpType.offer, remoteOffer); logger.LogDebug($"Set remote description on local session result {result}."); Assert.Null(localSession.AudioDestinationEndPoint); localSession.Close("normal"); }
public void CheckCreateOfferWithIPv6BindAddressAnswerTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); // Create two RTP sessions. First one acts as the local session to generate the offer. // Second one acts as the remote session to generate the answer. RTPSession localSession = new RTPSession(false, false, false, IPAddress.IPv6Loopback); MediaStreamTrack localAudioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); localSession.addTrack(localAudioTrack); // Generate the offer to send to the remote party. var offer = localSession.CreateOffer(null); logger.LogDebug("Local offer: " + offer.ToString()); Assert.True(IPAddress.IPv6Loopback.Equals(IPAddress.Parse(offer.Connection.ConnectionAddress))); localSession.Close("normal"); }
static void Main() { Console.WriteLine("SIPSorcery SIP to WebRTC example."); Console.WriteLine("Press ctrl-c to exit."); // Plumbing code to facilitate a graceful exit. CancellationTokenSource exitCts = new CancellationTokenSource(); // Cancellation token to stop the SIP transport and RTP stream. Log = AddConsoleLogger(); //EnableTraceLogs(sipTransport); // Start web socket. Console.WriteLine("Starting web socket server..."); var webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT); webSocketServer.AddWebSocketService <WebRTCWebSocketPeer>("/", (peer) => peer.CreatePeerConnection = CreatePeerConnection); webSocketServer.Start(); // Set up a default SIP transport. var sipTransport = new SIPTransport(); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT))); // Create a SIP user agent to receive a call from a remote SIP client. // Wire up event handlers for the different stages of the call. var userAgent = new SIPUserAgent(sipTransport, null, true); // We're only answering SIP calls, not placing them. userAgent.OnCallHungup += (dialog) => { Log.LogInformation($"Call hungup by remote party."); exitCts.Cancel(); }; userAgent.ServerCallCancelled += (uas) => Log.LogInformation("Incoming call cancelled by caller."); userAgent.OnIncomingCall += async(ua, req) => { Log.LogInformation($"Incoming call request from {req.RemoteSIPEndPoint}: {req.StatusLine}."); var incomingCall = userAgent.AcceptCall(req); var rtpSession = new RTPSession(false, false, false); rtpSession.AcceptRtpFromAny = true; MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }); rtpSession.addTrack(audioTrack); await userAgent.Answer(incomingCall, rtpSession); rtpSession.OnRtpPacketReceived += ForwardMediaToPeerConnection; Log.LogInformation($"Answered incoming call from {req.Header.From.FriendlyDescription()} at {req.RemoteSIPEndPoint}."); _rtpSession = rtpSession; }; Console.WriteLine($"Waiting for browser web socket connection to {webSocketServer.Address}:{webSocketServer.Port}..."); var contactURI = new SIPURI(SIPSchemesEnum.sip, sipTransport.GetSIPChannels().First().ListeningSIPEndPoint); Console.WriteLine($"Waiting for incoming SIP call to {contactURI}."); // Ctrl-c will gracefully exit the call at any point. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; exitCts.Cancel(); }; // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed. exitCts.Token.WaitHandle.WaitOne(); #region Cleanup. Log.LogInformation("Exiting..."); _rtpSession?.Close("app exit"); if (userAgent != null) { if (userAgent.IsCallActive) { Log.LogInformation($"Hanging up call to {userAgent?.CallDescriptor?.To}."); userAgent.Hangup(); } // Give the BYE or CANCEL request time to be transmitted. Log.LogInformation("Waiting 1s for call to clean up..."); Task.Delay(1000).Wait(); } if (sipTransport != null) { Log.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } #endregion }
public async Task HandleInvalidSdpPortOnPlaceCallUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); // This transport will act as the call receiver. It allows the test to provide a // tailored response to an incoming call. SIPTransport calleeTransport = new SIPTransport(); // This transport will be used by the SIPUserAgent being tested to place the call. SIPTransport callerTransport = new SIPTransport(); RTPSession rtpSession = new RTPSession(false, false, false); try { calleeTransport.AddSIPChannel(new SIPUDPChannel(IPAddress.Loopback, 0)); calleeTransport.SIPTransportRequestReceived += async(lep, rep, req) => { if (req.Method != SIPMethodsEnum.INVITE) { SIPResponse notAllowedResponse = SIPResponse.GetResponse(req, SIPResponseStatusCodesEnum.MethodNotAllowed, null); await calleeTransport.SendResponseAsync(notAllowedResponse); } else { UASInviteTransaction uasTransaction = new UASInviteTransaction(calleeTransport, req, null); var uas = new SIPServerUserAgent(calleeTransport, null, null, null, SIPCallDirection.In, null, null, null, uasTransaction); uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); var answerSdp = @" v=0 o=- 1838015445 0 IN IP4 127.0.0.1 s=- c=IN IP4 127.0.0.1 t=0 0 m=audio 79762 RTP/AVP 0 a=rtpmap:0 PCMU/8000 a=sendrecv"; uas.Answer(SDP.SDP_MIME_CONTENTTYPE, answerSdp, null, SIPDialogueTransferModesEnum.NotAllowed); } }; SIPUserAgent userAgent = new SIPUserAgent(callerTransport, null); MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); rtpSession.addTrack(audioTrack); SIPURI dstUri = new SIPURI(SIPSchemesEnum.sip, calleeTransport.GetSIPChannels().First().ListeningSIPEndPoint); var result = await userAgent.Call(dstUri.ToString(), null, null, rtpSession); Assert.False(result); } finally { rtpSession?.Close("normal"); callerTransport?.Shutdown(); calleeTransport?.Shutdown(); } }
//private delegate void MediaSampleReadyDelegate(SDPMediaTypesEnum mediaType, uint duration, byte[] sample); //private static event MediaSampleReadyDelegate OnMediaFromSIPSampleReady; static void Main(string[] args) { Console.WriteLine("SIPSorcery SIP to WebRTC example."); Console.WriteLine("Press ctrl-c to exit."); // Plumbing code to facilitate a graceful exit. CancellationTokenSource exitCts = new CancellationTokenSource(); // Cancellation token to stop the SIP transport and RTP stream. Log = AddConsoleLogger(); // Start web socket. Console.WriteLine("Starting web socket server..."); _webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, true); _webSocketServer.SslConfiguration.ServerCertificate = new X509Certificate2(WEBSOCKET_CERTIFICATE_PATH); _webSocketServer.SslConfiguration.CheckCertificateRevocation = false; //_webSocketServer.Log.Level = WebSocketSharp.LogLevel.Debug; _webSocketServer.AddWebSocketService <SDPExchange>("/", (sdpExchanger) => { sdpExchanger.WebSocketOpened += SendSDPOffer; sdpExchanger.SDPAnswerReceived += SDPAnswerReceived; }); _webSocketServer.Start(); Console.WriteLine($"Waiting for browser web socket connection to {_webSocketServer.Address}:{_webSocketServer.Port}..."); // Set up a default SIP transport. var sipTransport = new SIPTransport(); sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(IPAddress.Any, SIP_LISTEN_PORT))); //EnableTraceLogs(sipTransport); RTPSession rtpSession = null; // Create a SIP user agent to receive a call from a remote SIP client. // Wire up event handlers for the different stages of the call. var userAgent = new SIPUserAgent(sipTransport, null); // We're only answering SIP calls, not placing them. userAgent.OnCallHungup += (dialog) => { Log.LogInformation($"Call hungup by remote party."); exitCts.Cancel(); }; userAgent.ServerCallCancelled += (uas) => Log.LogInformation("Incoming call cancelled by caller."); sipTransport.SIPTransportRequestReceived += async(localEndPoint, remoteEndPoint, sipRequest) => { if (sipRequest.Header.From != null && sipRequest.Header.From.FromTag != null && sipRequest.Header.To != null && sipRequest.Header.To.ToTag != null) { // This is an in-dialog request that will be handled directly by a user agent instance. } else if (sipRequest.Method == SIPMethodsEnum.INVITE) { if (userAgent?.IsCallActive == true) { Log.LogWarning($"Busy response returned for incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}."); // If we are already on a call return a busy response. UASInviteTransaction uasTransaction = new UASInviteTransaction(sipTransport, sipRequest, null); SIPResponse busyResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.BusyHere, null); uasTransaction.SendFinalResponse(busyResponse); } else { Log.LogInformation($"Incoming call request from {remoteEndPoint}: {sipRequest.StatusLine}."); var incomingCall = userAgent.AcceptCall(sipRequest); rtpSession = new RTPSession(false, false, false); rtpSession.AcceptRtpFromAny = true; MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); rtpSession.addTrack(audioTrack); await userAgent.Answer(incomingCall, rtpSession); rtpSession.OnRtpPacketReceived += (ep, mediaType, rtpPacket) => ForwardMedia(mediaType, rtpPacket); Log.LogInformation($"Answered incoming call from {sipRequest.Header.From.FriendlyDescription()} at {remoteEndPoint}."); } } else { Log.LogDebug($"SIP {sipRequest.Method} request received but no processing has been set up for it, rejecting."); SIPResponse notAllowedResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); await sipTransport.SendResponseAsync(notAllowedResponse); } }; // Ctrl-c will gracefully exit the call at any point. Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; exitCts.Cancel(); }; // Wait for a signal saying the call failed, was cancelled with ctrl-c or completed. exitCts.Token.WaitHandle.WaitOne(); #region Cleanup. Log.LogInformation("Exiting..."); rtpSession?.Close("app exit"); if (userAgent != null) { if (userAgent.IsCallActive) { Log.LogInformation($"Hanging up call to {userAgent?.CallDescriptor?.To}."); userAgent.Hangup(); } // Give the BYE or CANCEL request time to be transmitted. Log.LogInformation("Waiting 1s for call to clean up..."); Task.Delay(1000).Wait(); } if (sipTransport != null) { Log.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } #endregion }