private static Task <RTCPeerConnection> CreatePeerConnection() { RTCConfiguration config = new RTCConfiguration { iceServers = new List <RTCIceServer> { new RTCIceServer { urls = STUN_URL } } }; var pc = new RTCPeerConnection(config); var testPatternSource = new VideoTestPatternSource(); var videoEncoderEndPoint = new VideoEncoderEndPoint(); var audioSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.Music }); MediaStreamTrack videoTrack = new MediaStreamTrack(videoEncoderEndPoint.GetVideoSourceFormats(), MediaStreamStatusEnum.SendRecv); pc.addTrack(videoTrack); MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendRecv); pc.addTrack(audioTrack); testPatternSource.OnVideoSourceRawSample += videoEncoderEndPoint.ExternalVideoSourceRawSample; videoEncoderEndPoint.OnVideoSourceEncodedSample += pc.SendVideo; audioSource.OnAudioSourceEncodedSample += pc.SendAudio; pc.OnVideoFormatsNegotiated += (formats) => videoEncoderEndPoint.SetVideoSourceFormat(formats.First()); pc.onconnectionstatechange += async(state) => { logger.LogDebug($"Peer connection state change to {state}."); if (state == RTCPeerConnectionState.connected) { await audioSource.StartAudio(); await testPatternSource.StartVideo(); } else if (state == RTCPeerConnectionState.failed) { pc.Close("ice disconnection"); } else if (state == RTCPeerConnectionState.closed) { await testPatternSource.CloseVideo(); await audioSource.CloseAudio(); } }; // Diagnostics. pc.OnReceiveReport += (re, media, rr) => logger.LogDebug($"RTCP Receive for {media} from {re}\n{rr.GetDebugSummary()}"); pc.OnSendReport += (media, sr) => logger.LogDebug($"RTCP Send for {media}\n{sr.GetDebugSummary()}"); pc.GetRtpChannel().OnStunMessageReceived += (msg, ep, isRelay) => logger.LogDebug($"STUN {msg.Header.MessageType} received from {ep}."); pc.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}."); return(Task.FromResult(pc)); }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // Set up media sources. _maxSource = new FFmpegFileSource(MP4_PATH, true, new AudioEncoder()); _maxSource.Initialise(); _testPatternSource = new VideoTestPatternSource(); _testPatternEncoder = new FFmpegVideoEndPoint(); _testPatternSource.OnVideoSourceRawSample += _testPatternEncoder.ExternalVideoSourceRawSample; _musicSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.Music }); // The same sources are used for all connected peers (broadcast) so the codecs need // to be restricted to a single well supported option. _musicSource.RestrictCodecs(new List <AudioCodecsEnum> { AudioCodecsEnum.PCMU }); _maxSource.RestrictCodecs(new List <VideoCodecsEnum> { VIDEO_CODEC }); _testPatternEncoder.RestrictCodecs(new List <VideoCodecsEnum> { VIDEO_CODEC }); // Start web socket. _logger.LogInformation("Starting web socket server..."); WebSocketServer webSocketServer = null; if (!string.IsNullOrWhiteSpace(_certificatePath)) { if (!File.Exists(_certificatePath)) { throw new ApplicationException($"Certificate path could not be found {_certificatePath}."); } webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT, true); webSocketServer.SslConfiguration.ServerCertificate = new X509Certificate2(_certificatePath); webSocketServer.SslConfiguration.CheckCertificateRevocation = false; webSocketServer.SslConfiguration.EnabledSslProtocols = System.Security.Authentication.SslProtocols.Tls12; } else { webSocketServer = new WebSocketServer(IPAddress.Any, WEBSOCKET_PORT); } webSocketServer.AddWebSocketService <WebRTCWebSocketPeer>("/", (peer) => peer.CreatePeerConnection = () => CreatePeerConnection(null)); webSocketServer.AddWebSocketService <WebRTCWebSocketPeer>($"/{MAX_URL}", (peer) => peer.CreatePeerConnection = () => CreatePeerConnection(MAX_URL)); webSocketServer.Start(); _logger.LogInformation($"Waiting for web socket connections on {webSocketServer.Address}:{webSocketServer.Port}..."); await Task.Delay(Timeout.Infinite, stoppingToken); }
/// <summary> /// An asynchronous task that attempts to initiate a new call to a listening UAS. /// </summary> /// <param name="sipTransport">The transport object to use for the send.</param> /// <param name="dst">The destination end point to send the request to.</param> /// <returns>True if the expected response was received, false otherwise.</returns> private static async Task <bool> InitiateCallTaskAsync(SIPTransport sipTransport, SIPURI dst) { //UdpClient hepClient = new UdpClient(0, AddressFamily.InterNetwork); try { //sipTransport.SIPRequestOutTraceEvent += (localEP, remoteEP, req) => //{ // logger.LogDebug($"Request sent: {localEP}->{remoteEP}"); // logger.LogDebug(req.ToString()); // //var hepBuffer = HepPacket.GetBytes(localEP, remoteEP, DateTimeOffset.Now, 333, "myHep", req.ToString()); // //hepClient.SendAsync(hepBuffer, hepBuffer.Length, "192.168.11.49", 9060); //}; //sipTransport.SIPResponseInTraceEvent += (localEP, remoteEP, resp) => //{ // logger.LogDebug($"Response received: {localEP}<-{remoteEP}"); // logger.LogDebug(resp.ToString()); // //var hepBuffer = HepPacket.GetBytes(remoteEP, localEP, DateTimeOffset.Now, 333, "myHep", resp.ToString()); // //hepClient.SendAsync(hepBuffer, hepBuffer.Length, "192.168.11.49", 9060); //}; var ua = new SIPUserAgent(sipTransport, null); ua.ClientCallTrying += (uac, resp) => logger.LogInformation($"{uac.CallDescriptor.To} Trying: {resp.StatusCode} {resp.ReasonPhrase}."); ua.ClientCallRinging += (uac, resp) => logger.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}."); ua.ClientCallFailed += (uac, err, resp) => logger.LogWarning($"{uac.CallDescriptor.To} Failed: {err}"); ua.ClientCallAnswered += (uac, resp) => logger.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); var audioOptions = new AudioSourceOptions { AudioSource = AudioSourcesEnum.Silence }; var audioExtrasSource = new AudioExtrasSource(new AudioEncoder(), audioOptions); audioExtrasSource.RestrictCodecs(new List <AudioCodecsEnum> { AudioCodecsEnum.PCMU }); var voipMediaSession = new VoIPMediaSession(new MediaEndPoints { AudioSource = audioExtrasSource }); var result = await ua.Call(dst.ToString(), null, null, voipMediaSession); ua.Hangup(); await Task.Delay(200); return(result); } catch (Exception excp) { logger.LogError($"Exception InitiateCallTaskAsync. {excp.Message}"); return(false); } }
private Task <SIPSorcery.Net.RTCPeerConnection> CreatePeerConnection() { var pc = new SIPSorcery.Net.RTCPeerConnection(null); // Set up sources and hook up send events to peer connection. AudioExtrasSource audioSrc = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.None }); audioSrc.OnAudioSourceEncodedSample += pc.SendAudio; //var testPatternSource = new VideoTestPatternSource(); //testPatternSource.SetMaxFrameRate(true); //testPatternSource.OnVideoSourceRawSample += VideoEncoderEndPoint.ExternalVideoSourceRawSample; VideoEncoderEndPoint.OnVideoSourceEncodedSample += pc.SendVideo; // Add tracks. var audioTrack = new SIPSorcery.Net.MediaStreamTrack(audioSrc.GetAudioSourceFormats(), SIPSorcery.Net.MediaStreamStatusEnum.SendOnly); pc.addTrack(audioTrack); var videoTrack = new SIPSorcery.Net.MediaStreamTrack(VideoEncoderEndPoint.GetVideoSourceFormats(), SIPSorcery.Net.MediaStreamStatusEnum.SendOnly); pc.addTrack(videoTrack); // Handlers to set the codecs to use on the sources once the SDP negotiation is complete. pc.OnVideoFormatsNegotiated += (sdpFormat) => VideoEncoderEndPoint.SetVideoSourceFormat(SIPSorcery.Net.SDPMediaFormatInfo.GetVideoCodecForSdpFormat(sdpFormat.First().FormatCodec)); pc.OnAudioFormatsNegotiated += (sdpFormat) => audioSrc.SetAudioSourceFormat(SIPSorcery.Net.SDPMediaFormatInfo.GetAudioCodecForSdpFormat(sdpFormat.First().FormatCodec)); pc.OnTimeout += (mediaType) => logger.LogDebug($"Timeout on media {mediaType}."); pc.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state changed to {state}."); pc.onconnectionstatechange += async(state) => { logger.LogDebug($"Peer connection connected changed to {state}."); if (state == SIPSorcery.Net.RTCPeerConnectionState.connected) { await audioSrc.StartAudio(); //await testPatternSource.StartVideo(); } else if (state == SIPSorcery.Net.RTCPeerConnectionState.closed || state == SIPSorcery.Net.RTCPeerConnectionState.failed) { await audioSrc.CloseAudio(); //await testPatternSource.CloseVideo(); } }; pc.GetRtpChannel().OnStunMessageReceived += (msg, ep, isRelay) => { bool hasUseCandidate = msg.Attributes.Any(x => x.AttributeType == SIPSorcery.Net.STUNAttributeTypesEnum.UseCandidate); logger.LogDebug($"STUN {msg.Header.MessageType} received from {ep}, use candidate {hasUseCandidate}."); }; return(Task.FromResult(pc)); }
static async Task Main() { Console.WriteLine("SIPSorcery Convert Audio"); Log = AddConsoleLogger(); //WaveFormatConversionStream converter = new WaveFormatConversionStream(_format_s16le48k, ) _waveFile = new WaveFileWriter("output_s16le48k.mp3", _format_s16le48k); var sipTransport = new SIPTransport(); var userAgent = new SIPUserAgent(sipTransport, null); userAgent.OnCallHungup += (dialog) => { Console.WriteLine("Call hungup."); _waveFile?.Close(); }; //EnableTraceLogs(sipTransport); var audioOptions = new AudioSourceOptions { AudioSource = AudioSourcesEnum.Silence }; AudioExtrasSource audioExtrasSource = new AudioExtrasSource(new AudioEncoder(), audioOptions); audioExtrasSource.RestrictFormats((format) => format.Codec == AudioCodecsEnum.PCMU); var rtpSession = new VoIPMediaSession(new MediaEndPoints { AudioSource = audioExtrasSource }); rtpSession.OnAudioFormatsNegotiated += (formats) => { _ratio = (double)(OUT_SAMPLE_RATE / formats.First().RtpClockRate); }; rtpSession.OnRtpPacketReceived += RtpSession_OnRtpPacketReceived; // Place the call and wait for the result. bool callResult = await userAgent.Call(DESTINATION, null, null, rtpSession); Console.WriteLine($"Call result {((callResult) ? "success" : "failure")}."); Console.WriteLine("press any key to exit..."); Console.Read(); if (userAgent.IsCallActive) { Console.WriteLine("Hanging up."); userAgent.Hangup(); } // Clean up. sipTransport.Shutdown(); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); var pc = new RTCPeerConnection(null); AudioExtrasSource audioSource = new AudioExtrasSource(new AudioEncoder()); audioSource.OnAudioSourceEncodedSample += pc.SendAudio; MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendOnly); pc.addTrack(audioTrack); pc.OnAudioFormatsNegotiated += (sdpFormat) => audioSource.SetAudioSourceFormat(SDPMediaFormatInfo.GetAudioCodecForSdpFormat(sdpFormat.First().FormatCodec)); pc.OnReceiveReport += RtpSession_OnReceiveReport; pc.OnSendReport += RtpSession_OnSendReport; pc.OnTimeout += (mediaType) => pc.Close("remote timeout"); pc.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}."); pc.onconnectionstatechange += (state) => { logger.LogDebug($"Peer connection state change to {state}."); if (state == RTCPeerConnectionState.connected) { audioSource.SetSource(new AudioSourceOptions { AudioSource = AudioSourcesEnum.SineWave }); } else if (state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { pc.Close("remote disconnection"); } else if (state == RTCPeerConnectionState.closed) { audioSource?.CloseAudio(); pc.OnReceiveReport -= RtpSession_OnReceiveReport; pc.OnSendReport -= RtpSession_OnSendReport; } }; var offerSdp = pc.createOffer(null); await pc.setLocalDescription(offerSdp); logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); logger.LogDebug(offerSdp.sdp); context.WebSocket.Send(offerSdp.sdp); return(pc); }
private VoIPMediaSession CreateMockVoIPMediaEndPoint(Func <AudioFormat, bool> audioFormatFilter = null) { var audioSource = new AudioExtrasSource(); audioSource.RestrictFormats(audioFormatFilter); MediaEndPoints mockEndPoints = new MediaEndPoints { AudioSource = audioSource }; return(new VoIPMediaSession(mockEndPoints)); }
private VoIPMediaSession CreateMockVoIPMediaEndPoint(List <AudioCodecsEnum> supportedCodecs = null) { var audioSource = new AudioExtrasSource(); audioSource.RestrictCodecs(supportedCodecs); MediaEndPoints mockEndPoints = new MediaEndPoints { AudioSource = audioSource }; return(new VoIPMediaSession(mockEndPoints)); }
private static Task <RTCPeerConnection> CreatePeerConnection() { RTCConfiguration config = new RTCConfiguration { iceServers = new List <RTCIceServer> { new RTCIceServer { urls = STUN_URL } } }; var pc = new RTCPeerConnection(config); AudioExtrasSource audioSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.SineWave }); audioSource.OnAudioSourceEncodedSample += pc.SendAudio; MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendOnly); pc.addTrack(audioTrack); pc.OnAudioFormatsNegotiated += (sdpFormat) => audioSource.SetAudioSourceFormat(SDPMediaFormatInfo.GetAudioCodecForSdpFormat(sdpFormat.First().FormatCodec)); pc.onconnectionstatechange += async(state) => { logger.LogDebug($"Peer connection state change to {state}."); if (state == RTCPeerConnectionState.connected) { await audioSource.StartAudio(); } else if (state == RTCPeerConnectionState.failed) { pc.Close("ice disconnection"); } else if (state == RTCPeerConnectionState.closed) { await audioSource.CloseAudio(); } }; // Diagnostics. pc.OnReceiveReport += (re, media, rr) => logger.LogDebug($"RTCP Receive for {media} from {re}\n{rr.GetDebugSummary()}"); pc.OnSendReport += (media, sr) => logger.LogDebug($"RTCP Send for {media}\n{sr.GetDebugSummary()}"); pc.GetRtpChannel().OnStunMessageReceived += (msg, ep, isRelay) => logger.LogDebug($"STUN {msg.Header.MessageType} received from {ep}."); pc.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}."); return(Task.FromResult(pc)); }
/// <summary> /// Example of how to create a basic RTP session object and hook up the event handlers. /// </summary> /// <param name="ua">The user agent the RTP session is being created for.</param> /// <param name="dst">THe destination specified on an incoming call. Can be used to /// set the audio source.</param> /// <returns>A new RTP session object.</returns> private static VoIPMediaSession CreateRtpSession(SIPUserAgent ua, string dst) { List <AudioCodecsEnum> codecs = new List <AudioCodecsEnum> { AudioCodecsEnum.PCMU, AudioCodecsEnum.PCMA, AudioCodecsEnum.G722 }; var audioSource = AudioSourcesEnum.SineWave; if (string.IsNullOrEmpty(dst) || !Enum.IsDefined(typeof(AudioSourcesEnum), dst) || !Enum.TryParse(dst, out audioSource)) { audioSource = AudioSourcesEnum.Music; } Log.LogInformation($"RTP audio session source set to {audioSource}."); AudioExtrasSource audioExtrasSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = audioSource }); audioExtrasSource.RestrictFormats(formats => codecs.Contains(formats.Codec)); var rtpAudioSession = new VoIPMediaSession(new MediaEndPoints { AudioSource = audioExtrasSource }); rtpAudioSession.AcceptRtpFromAny = true; // Wire up the event handler for RTP packets received from the remote party. rtpAudioSession.OnRtpPacketReceived += (ep, type, rtp) => OnRtpPacketReceived(ua, type, rtp); rtpAudioSession.OnTimeout += (mediaType) => { if (ua?.Dialogue != null) { Log.LogWarning($"RTP timeout on call with {ua.Dialogue.RemoteTarget}, hanging up."); } else { Log.LogWarning($"RTP timeout on incomplete call, closing RTP session."); } ua.Hangup(); }; return(rtpAudioSession); }
static async Task Main() { Console.WriteLine("SIPSorcery Asterisk + ICE Demo"); AddConsoleLogger(); CancellationTokenSource exitCts = new CancellationTokenSource(); var sipTransport = new SIPTransport(); EnableTraceLogs(sipTransport); var userAgent = new SIPUserAgent(sipTransport, null); userAgent.ClientCallFailed += (uac, error, sipResponse) => Console.WriteLine($"Call failed {error}."); userAgent.OnCallHungup += async(dialog) => { // Give time for the BYE response before exiting. await Task.Delay(1000); exitCts.Cancel(); }; var audioExtras = new AudioExtrasSource(); audioExtras.SetSource(AudioSourcesEnum.PinkNoise); var testPattern = new VideoTestPatternSource(new VpxVideoEncoder()); var pc = new RTCPeerConnection(null); pc.OnAudioFormatsNegotiated += (formats) => audioExtras.SetAudioSourceFormat(formats.First()); pc.OnVideoFormatsNegotiated += (formats) => testPattern.SetVideoSourceFormat(formats.First()); var audioTrack = new MediaStreamTrack(audioExtras.GetAudioSourceFormats(), MediaStreamStatusEnum.SendOnly); pc.addTrack(audioTrack); audioExtras.OnAudioSourceEncodedSample += pc.SendAudio; var videoTrack = new MediaStreamTrack(testPattern.GetVideoSourceFormats(), MediaStreamStatusEnum.SendOnly); pc.addTrack(videoTrack); testPattern.OnVideoSourceEncodedSample += pc.SendVideo; // Diagnostics. pc.OnReceiveReport += (re, media, rr) => Console.WriteLine($"RTCP Receive for {media} from {re}\n{rr.GetDebugSummary()}"); pc.OnSendReport += (media, sr) => Console.WriteLine($"RTCP Send for {media}\n{sr.GetDebugSummary()}"); pc.GetRtpChannel().OnStunMessageReceived += (msg, ep, isRelay) => Console.WriteLine($"STUN {msg.Header.MessageType} received from {ep}."); pc.oniceconnectionstatechange += (state) => Console.WriteLine($"ICE connection state change to {state}."); // ICE connection state handler. pc.onconnectionstatechange += (state) => { Console.WriteLine($"Peer connection state change to {state}."); if (state == RTCPeerConnectionState.connected) { audioExtras.StartAudio(); testPattern.StartVideo(); } else if (state == RTCPeerConnectionState.failed) { if (userAgent.IsCallActive) { Console.WriteLine("ICE connection failed, hanging up active call."); userAgent.Hangup(); } } }; // Place the call and wait for the result. var callTask = userAgent.Call(DESTINATION, USERNAME, PASSWORD, pc); Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; if (userAgent != null) { if (userAgent.IsCalling || userAgent.IsRinging) { Console.WriteLine("Cancelling in progress call."); userAgent.Cancel(); } else if (userAgent.IsCallActive) { Console.WriteLine("Hanging up established call."); userAgent.Hangup(); } } ; exitCts.Cancel(); }; Console.WriteLine("press ctrl-c to exit..."); bool callResult = await callTask; if (callResult) { Console.WriteLine($"Call to {DESTINATION} succeeded."); exitCts.Token.WaitHandle.WaitOne(); } else { Console.WriteLine($"Call to {DESTINATION} failed."); } Console.WriteLine("Exiting..."); if (userAgent?.IsHangingUp == true) { Console.WriteLine("Waiting 1s for the call hangup or cancel to complete..."); await Task.Delay(1000); } // Clean up. sipTransport.Shutdown(); }
private static async Task <RTCPeerConnection> CreatePeerConnection() { RTCConfiguration config = new RTCConfiguration { iceServers = new List <RTCIceServer> { new RTCIceServer { urls = STUN_URL } } }; var pc = new RTCPeerConnection(config); WindowsVideoEndPoint winVideoEP = new WindowsVideoEndPoint(new VpxVideoEncoder(), WEBCAM_NAME); //WindowsVideoEndPoint winVideoEP = new WindowsVideoEndPoint(new FFmpegVideoEncoder(), WEBCAM_NAME); //WindowsVideoEndPoint winVideoEP = new WindowsVideoEndPoint(WEBCAM_NAME, 1920, 1080, 30); //winVideoEP.RestrictFormats(x => x.Codec == SIPSorceryMedia.Abstractions.V1.VideoCodecsEnum.H264); bool initResult = await winVideoEP.InitialiseVideoSourceDevice(); if (!initResult) { throw new ApplicationException("Could not initialise video capture device."); } var audioSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.Music }); MediaStreamTrack videoTrack = new MediaStreamTrack(winVideoEP.GetVideoSourceFormats(), MediaStreamStatusEnum.SendRecv); pc.addTrack(videoTrack); MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendRecv); pc.addTrack(audioTrack); winVideoEP.OnVideoSourceEncodedSample += pc.SendVideo; audioSource.OnAudioSourceEncodedSample += pc.SendAudio; pc.OnVideoFormatsNegotiated += (videoFormats) => winVideoEP.SetVideoSourceFormat(videoFormats.First()); pc.OnAudioFormatsNegotiated += (audioFormats) => audioSource.SetAudioSourceFormat(audioFormats.First()); pc.onconnectionstatechange += async(state) => { logger.LogDebug($"Peer connection state change to {state}."); if (state == RTCPeerConnectionState.connected) { await audioSource.StartAudio(); await winVideoEP.StartVideo(); } else if (state == RTCPeerConnectionState.failed) { pc.Close("ice disconnection"); } else if (state == RTCPeerConnectionState.closed) { await winVideoEP.CloseVideo(); await audioSource.CloseAudio(); } }; // Diagnostics. pc.OnReceiveReport += (re, media, rr) => logger.LogDebug($"RTCP Receive for {media} from {re}\n{rr.GetDebugSummary()}"); pc.OnSendReport += (media, sr) => logger.LogDebug($"RTCP Send for {media}\n{sr.GetDebugSummary()}"); pc.GetRtpChannel().OnStunMessageReceived += (msg, ep, isRelay) => logger.LogDebug($"STUN {msg.Header.MessageType} received from {ep}."); pc.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}."); return(pc); }
static void Main(string[] args) { Console.WriteLine("SIPSorcery user agent server example."); Console.WriteLine("Press h to hangup a call or ctrl-c to exit."); Log = AddConsoleLogger(); IPAddress listenAddress = IPAddress.Any; IPAddress listenIPv6Address = IPAddress.IPv6Any; if (args != null && args.Length > 0) { if (!IPAddress.TryParse(args[0], out var customListenAddress)) { Log.LogWarning($"Command line argument could not be parsed as an IP address \"{args[0]}\""); listenAddress = IPAddress.Any; } else { if (customListenAddress.AddressFamily == AddressFamily.InterNetwork) { listenAddress = customListenAddress; } if (customListenAddress.AddressFamily == AddressFamily.InterNetworkV6) { listenIPv6Address = customListenAddress; } } } // Set up a default SIP transport. var sipTransport = new SIPTransport(); var localhostCertificate = new X509Certificate2(SIPS_CERTIFICATE_PATH); // IPv4 channels. sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenAddress, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenAddress, SIPS_LISTEN_PORT))); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_WEBSOCKET_LISTEN_PORT)); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate)); // IPv6 channels. sipTransport.AddSIPChannel(new SIPUDPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTCPChannel(new IPEndPoint(listenIPv6Address, SIP_LISTEN_PORT))); sipTransport.AddSIPChannel(new SIPTLSChannel(localhostCertificate, new IPEndPoint(listenIPv6Address, SIPS_LISTEN_PORT))); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_WEBSOCKET_LISTEN_PORT)); //sipTransport.AddSIPChannel(new SIPWebSocketChannel(IPAddress.IPv6Any, SIP_SECURE_WEBSOCKET_LISTEN_PORT, localhostCertificate)); EnableTraceLogs(sipTransport); string executableDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); // To keep things a bit simpler this example only supports a single call at a time and the SIP server user agent // acts as a singleton SIPServerUserAgent uas = null; CancellationTokenSource rtpCts = null; // Cancellation token to stop the RTP stream. VoIPMediaSession rtpSession = null; // Because this is a server user agent the SIP transport must start listening for client user agents. sipTransport.SIPTransportRequestReceived += async(SIPEndPoint localSIPEndPoint, SIPEndPoint remoteEndPoint, SIPRequest sipRequest) => { try { if (sipRequest.Method == SIPMethodsEnum.INVITE) { Log.LogInformation($"Incoming call request: {localSIPEndPoint}<-{remoteEndPoint} {sipRequest.URI}."); // Check there's a codec we support in the INVITE offer. var offerSdp = SDP.ParseSDPDescription(sipRequest.Body); IPEndPoint dstRtpEndPoint = SDP.GetSDPRTPEndPoint(sipRequest.Body); if (offerSdp.Media.Any(x => x.Media == SDPMediaTypesEnum.audio && x.MediaFormats.Any(x => x.Key == (int)SDPWellKnownMediaFormatsEnum.PCMU))) { Log.LogDebug($"Client offer contained PCMU audio codec."); AudioExtrasSource extrasSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.Music }); rtpSession = new VoIPMediaSession(new MediaEndPoints { AudioSource = extrasSource }); rtpSession.AcceptRtpFromAny = true; var setResult = rtpSession.SetRemoteDescription(SdpType.offer, offerSdp); if (setResult != SetDescriptionResultEnum.OK) { // Didn't get a match on the codecs we support. SIPResponse noMatchingCodecResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.NotAcceptableHere, setResult.ToString()); await sipTransport.SendResponseAsync(noMatchingCodecResponse); } else { // If there's already a call in progress hang it up. Of course this is not ideal for a real softphone or server but it // means this example can be kept simpler. if (uas?.IsHungup == false) { uas?.Hangup(false); } rtpCts?.Cancel(); rtpCts = new CancellationTokenSource(); UASInviteTransaction uasTransaction = new UASInviteTransaction(sipTransport, sipRequest, null); uas = new SIPServerUserAgent(sipTransport, null, uasTransaction, null); uas.CallCancelled += (uasAgent) => { rtpCts?.Cancel(); rtpSession.Close(null); }; rtpSession.OnRtpClosed += (reason) => uas?.Hangup(false); uas.Progress(SIPResponseStatusCodesEnum.Trying, null, null, null, null); await Task.Delay(100); uas.Progress(SIPResponseStatusCodesEnum.Ringing, null, null, null, null); await Task.Delay(100); var answerSdp = rtpSession.CreateAnswer(null); uas.Answer(SDP.SDP_MIME_CONTENTTYPE, answerSdp.ToString(), null, SIPDialogueTransferModesEnum.NotAllowed); await rtpSession.Start(); } } } else if (sipRequest.Method == SIPMethodsEnum.BYE) { Log.LogInformation("Call hungup."); SIPResponse byeResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); await sipTransport.SendResponseAsync(byeResponse); uas?.Hangup(true); rtpSession?.Close(null); rtpCts?.Cancel(); } else if (sipRequest.Method == SIPMethodsEnum.SUBSCRIBE) { SIPResponse notAllowededResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.MethodNotAllowed, null); await sipTransport.SendResponseAsync(notAllowededResponse); } else if (sipRequest.Method == SIPMethodsEnum.OPTIONS || sipRequest.Method == SIPMethodsEnum.REGISTER) { SIPResponse optionsResponse = SIPResponse.GetResponse(sipRequest, SIPResponseStatusCodesEnum.Ok, null); await sipTransport.SendResponseAsync(optionsResponse); } } catch (Exception reqExcp) { Log.LogWarning($"Exception handling {sipRequest.Method}. {reqExcp.Message}"); } }; ManualResetEvent exitMre = new ManualResetEvent(false); Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; Log.LogInformation("Exiting..."); Hangup(uas).Wait(); rtpSession?.Close(null); rtpCts?.Cancel(); if (sipTransport != null) { Log.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } exitMre.Set(); }; // Task to handle user key presses. Task.Run(() => { try { while (!exitMre.WaitOne(0)) { var keyProps = Console.ReadKey(); if (keyProps.KeyChar == 'h' || keyProps.KeyChar == 'q') { Console.WriteLine(); Console.WriteLine("Hangup requested by user..."); Hangup(uas).Wait(); rtpSession?.Close(null); rtpCts?.Cancel(); } if (keyProps.KeyChar == 'q') { Log.LogInformation("Quitting..."); if (sipTransport != null) { Log.LogInformation("Shutting down SIP transport..."); sipTransport.Shutdown(); } exitMre.Set(); } } } catch (Exception excp) { Log.LogError($"Exception Key Press listener. {excp.Message}."); } }); exitMre.WaitOne(); }
private static Task <RTCPeerConnection> Createpc(WebSocketContext context, RTCIceServer stunServer, bool relayOnly) { if (_peerConnection != null) { _peerConnection.Close("normal"); } List <RTCCertificate> presetCertificates = null; if (File.Exists(LOCALHOST_CERTIFICATE_PATH)) { var localhostCert = new X509Certificate2(LOCALHOST_CERTIFICATE_PATH, (string)null, X509KeyStorageFlags.Exportable); presetCertificates = new List <RTCCertificate> { new RTCCertificate { Certificate = localhostCert } }; } RTCConfiguration pcConfiguration = new RTCConfiguration { //certificates = presetCertificates, //X_RemoteSignallingAddress = (context != null) ? context.UserEndPoint.Address : null, iceServers = stunServer != null ? new List <RTCIceServer> { stunServer } : null, iceTransportPolicy = relayOnly ? RTCIceTransportPolicy.relay : RTCIceTransportPolicy.all, //X_BindAddress = IPAddress.Any, // NOTE: Not reqd. Using this to filter out IPv6 addresses so can test with Pion. }; _peerConnection = new RTCPeerConnection(pcConfiguration); //_peerConnection.GetRtpChannel().MdnsResolve = (hostname) => Task.FromResult(NetServices.InternetDefaultAddress); _peerConnection.GetRtpChannel().MdnsResolve = MdnsResolve; //_peerConnection.GetRtpChannel().OnStunMessageReceived += (msg, ep, isrelay) => logger.LogDebug($"STUN message received from {ep}, message type {msg.Header.MessageType}."); //var dc = _peerConnection.createDataChannel(DATA_CHANNEL_LABEL, null); //dc.onmessage += (msg) => logger.LogDebug($"data channel receive ({dc.label}-{dc.id}): {msg}"); // Add a send-only audio track (this doesn't require any native libraries for encoding so is good for x-platform testing). AudioExtrasSource audioSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.Music }); audioSource.OnAudioSourceEncodedSample += _peerConnection.SendAudio; MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendOnly); _peerConnection.addTrack(audioTrack); _peerConnection.OnAudioFormatsNegotiated += (formats) => audioSource.SetAudioSourceFormat(formats.First()); _peerConnection.onicecandidateerror += (candidate, error) => logger.LogWarning($"Error adding remote ICE candidate. {error} {candidate}"); _peerConnection.onconnectionstatechange += async(state) => { logger.LogDebug($"Peer connection state changed to {state}."); if (state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { _peerConnection.Close("remote disconnection"); } if (state == RTCPeerConnectionState.connected) { await audioSource.StartAudio(); } else if (state == RTCPeerConnectionState.closed) { await audioSource.CloseAudio(); } }; _peerConnection.OnReceiveReport += (re, media, rr) => logger.LogDebug($"RTCP Receive for {media} from {re}\n{rr.GetDebugSummary()}"); _peerConnection.OnSendReport += (media, sr) => logger.LogDebug($"RTCP Send for {media}\n{sr.GetDebugSummary()}"); _peerConnection.OnRtcpBye += (reason) => logger.LogDebug($"RTCP BYE receive, reason: {(string.IsNullOrWhiteSpace(reason) ? "<none>" : reason)}."); // Peer ICE connection state changes are for ICE events such as the STUN checks completing. _peerConnection.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}."); _peerConnection.ondatachannel += (dc) => { logger.LogDebug($"Data channel opened by remote peer, label {dc.label}, stream ID {dc.id}."); dc.onmessage += (msg) => { logger.LogDebug($"data channel ({dc.label}:{dc.id}): {msg}."); }; }; _peerConnection.onsignalingstatechange += () => { if (_peerConnection.signalingState == RTCSignalingState.have_remote_offer) { logger.LogDebug("Remote SDP:"); logger.LogDebug(_peerConnection.remoteDescription.sdp.ToString()); } else if (_peerConnection.signalingState == RTCSignalingState.have_local_offer) { logger.LogDebug("Local SDP:"); logger.LogDebug(_peerConnection.localDescription.sdp.ToString()); } }; return(Task.FromResult(_peerConnection)); }
/// <summary> /// An asynchronous task that attempts to initiate a new call to a listening UAS. /// </summary> /// <param name="sipTransport">The transport object to use for the send.</param> /// <param name="dst">The destination end point to send the request to.</param> /// <returns>True if the expected response was received, false otherwise.</returns> private static async Task <bool> InitiateCallTaskAsync(SIPTransport sipTransport, SIPURI dst, Scenarios scenario) { //UdpClient hepClient = new UdpClient(0, AddressFamily.InterNetwork); try { //sipTransport.SIPRequestOutTraceEvent += (localEP, remoteEP, req) => //{ // logger.LogDebug($"Request sent: {localEP}->{remoteEP}"); // logger.LogDebug(req.ToString()); // //var hepBuffer = HepPacket.GetBytes(localEP, remoteEP, DateTimeOffset.Now, 333, "myHep", req.ToString()); // //hepClient.SendAsync(hepBuffer, hepBuffer.Length, "192.168.11.49", 9060); //}; //sipTransport.SIPResponseInTraceEvent += (localEP, remoteEP, resp) => //{ // logger.LogDebug($"Response received: {localEP}<-{remoteEP}"); // logger.LogDebug(resp.ToString()); // //var hepBuffer = HepPacket.GetBytes(remoteEP, localEP, DateTimeOffset.Now, 333, "myHep", resp.ToString()); // //hepClient.SendAsync(hepBuffer, hepBuffer.Length, "192.168.11.49", 9060); //}; var ua = new SIPUserAgent(sipTransport, null); ua.ClientCallTrying += (uac, resp) => logger.LogInformation($"{uac.CallDescriptor.To} Trying: {resp.StatusCode} {resp.ReasonPhrase}."); ua.ClientCallRinging += (uac, resp) => logger.LogInformation($"{uac.CallDescriptor.To} Ringing: {resp.StatusCode} {resp.ReasonPhrase}."); ua.ClientCallFailed += (uac, err, resp) => logger.LogWarning($"{uac.CallDescriptor.To} Failed: {err}"); ua.ClientCallAnswered += (uac, resp) => logger.LogInformation($"{uac.CallDescriptor.To} Answered: {resp.StatusCode} {resp.ReasonPhrase}."); var audioOptions = new AudioSourceOptions { AudioSource = AudioSourcesEnum.Silence }; var audioExtrasSource = new AudioExtrasSource(new AudioEncoder(), audioOptions); audioExtrasSource.RestrictFormats(format => format.Codec == AudioCodecsEnum.PCMU); var voipMediaSession = new VoIPMediaSession(new MediaEndPoints { AudioSource = audioExtrasSource }); var result = await ua.Call(dst.ToString(), null, null, voipMediaSession); if (scenario == Scenarios.uacw) { // Wait for the remote party to hangup the call. logger.LogDebug("Waiting for the remote party to hangup the call..."); ManualResetEvent hangupMre = new ManualResetEvent(false); ua.OnCallHungup += (dialog) => hangupMre.Set(); hangupMre.WaitOne(); logger.LogDebug("Call hungup by remote party."); } else { // We hangup the call after 1s. await Task.Delay(1000); ua.Hangup(); } // Need a bit of time for the BYE to complete. await Task.Delay(500); return(result); } catch (Exception excp) { logger.LogError($"Exception InitiateCallTaskAsync. {excp.Message}"); return(false); } }
private static RTCPeerConnection CreatePeerConnection() { RTCConfiguration config = new RTCConfiguration { iceServers = new List <RTCIceServer> { new RTCIceServer { urls = STUN_URL } } }; var pc = new RTCPeerConnection(config); WindowsVideoEndPoint winVideoEP = new WindowsVideoEndPoint(); //WindowsVideoEndPoint winVideoEP = new WindowsVideoEndPoint(false, 640, 480, 30); //WindowsVideoEndPoint winVideoEP = new WindowsVideoEndPoint(false, 1920, 1080, 30); //await winVideoEP.InitialiseVideoSourceDevice(); var audioSource = new AudioExtrasSource(new AudioEncoder(), new AudioSourceOptions { AudioSource = AudioSourcesEnum.Music }); MediaStreamTrack videoTrack = new MediaStreamTrack(winVideoEP.GetVideoSourceFormats(), MediaStreamStatusEnum.SendRecv); pc.addTrack(videoTrack); MediaStreamTrack audioTrack = new MediaStreamTrack(audioSource.GetAudioSourceFormats(), MediaStreamStatusEnum.SendRecv); pc.addTrack(audioTrack); winVideoEP.OnVideoSourceEncodedSample += pc.SendVideo; audioSource.OnAudioSourceEncodedSample += pc.SendAudio; pc.OnVideoFormatsNegotiated += (sdpFormat) => winVideoEP.SetVideoSourceFormat(SDPMediaFormatInfo.GetVideoCodecForSdpFormat(sdpFormat.First().FormatCodec)); pc.onconnectionstatechange += async(state) => { logger.LogDebug($"Peer connection state change to {state}."); if (state == RTCPeerConnectionState.connected) { await audioSource.StartAudio(); await winVideoEP.StartVideo(); } else if (state == RTCPeerConnectionState.failed) { pc.Close("ice disconnection"); } else if (state == RTCPeerConnectionState.closed) { await winVideoEP.CloseVideo(); await audioSource.CloseAudio(); } }; // Diagnostics. pc.OnReceiveReport += (re, media, rr) => logger.LogDebug($"RTCP Receive for {media} from {re}\n{rr.GetDebugSummary()}"); pc.OnSendReport += (media, sr) => logger.LogDebug($"RTCP Send for {media}\n{sr.GetDebugSummary()}"); pc.GetRtpChannel().OnStunMessageReceived += (msg, ep, isRelay) => logger.LogDebug($"STUN {msg.Header.MessageType} received from {ep}."); pc.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state change to {state}."); return(pc); }