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); }
public async Task Connect() { TaskCompletionSource <bool> dcAOpened = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); DC = await PCSrc.createDataChannel($"{DATACHANNEL_LABEL_PREFIX}-{ID}-a"); DC.onopen += () => { Console.WriteLine($"Peer connection pair {Name} A data channel opened."); StreamSendConfirmed.TryAdd(DC.id.Value, new ManualResetEventSlim()); dcAOpened.TrySetResult(true); }; var offer = PCSrc.createOffer(); await PCSrc.setLocalDescription(offer); if (PCDst.setRemoteDescription(offer) != SetDescriptionResultEnum.OK) { throw new ApplicationException($"SDP negotiation failed for peer connection pair {Name}."); } var answer = PCDst.createAnswer(); await PCDst.setLocalDescription(answer); if (PCSrc.setRemoteDescription(answer) != SetDescriptionResultEnum.OK) { throw new ApplicationException($"SDP negotiation failed for peer connection pair {Name}."); } await Task.WhenAll(dcAOpened.Task); }
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); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { Log.LogDebug($"Web socket client connection from {context.UserEndPoint}."); _peerConnection = new RTCPeerConnection(null); _peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; _peerConnection.OnSendReport += RtpSession_OnSendReport; Log.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); _peerConnection.onconnectionstatechange += (state) => { Log.LogDebug($"WebRTC peer connection state changed to {state}."); if (state == RTCPeerConnectionState.closed) { _peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; _peerConnection.OnSendReport -= RtpSession_OnSendReport; } }; MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); _peerConnection.addTrack(audioTrack); var offerInit = _peerConnection.createOffer(null); await _peerConnection.setLocalDescription(offerInit); context.WebSocket.Send(offerInit.sdp); return(_peerConnection); }
public async void CheckPeerConnectionEstablishment() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); var aliceConnected = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var bobConnected = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var alice = new RTCPeerConnection(); alice.onconnectionstatechange += (state) => { if (state == RTCPeerConnectionState.connected) { logger.LogDebug("Alice connected."); aliceConnected.SetResult(true); } }; alice.addTrack(new MediaStreamTrack(SDPWellKnownMediaFormatsEnum.PCMU)); var aliceOffer = alice.createOffer(); await alice.setLocalDescription(aliceOffer); logger.LogDebug($"alice offer: {aliceOffer.sdp}"); var bob = new RTCPeerConnection(); bob.onconnectionstatechange += (state) => { if (state == RTCPeerConnectionState.connected) { logger.LogDebug("Bob connected."); bobConnected.SetResult(true); } }; bob.addTrack(new MediaStreamTrack(SDPWellKnownMediaFormatsEnum.PCMU)); var setOfferResult = bob.setRemoteDescription(aliceOffer); Assert.Equal(SetDescriptionResultEnum.OK, setOfferResult); var bobAnswer = bob.createAnswer(); await bob.setLocalDescription(bobAnswer); var setAnswerResult = alice.setRemoteDescription(bobAnswer); Assert.Equal(SetDescriptionResultEnum.OK, setAnswerResult); logger.LogDebug($"answer: {bobAnswer.sdp}"); await Task.WhenAny(Task.WhenAll(aliceConnected.Task, bobConnected.Task), Task.Delay(2000)); Assert.True(aliceConnected.Task.IsCompleted); Assert.True(aliceConnected.Task.Result); Assert.True(bobConnected.Task.IsCompleted); Assert.True(bobConnected.Task.Result); bob.close(); alice.close(); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); var peerConnection = new RTCPeerConnection(null); // Sink (speaker) only audio end point. WindowsAudioEndPoint windowsAudioEP = new WindowsAudioEndPoint(new AudioEncoder(), -1, -1, true, false); MediaStreamTrack audioTrack = new MediaStreamTrack(windowsAudioEP.GetAudioSinkFormats(), MediaStreamStatusEnum.RecvOnly); peerConnection.addTrack(audioTrack); peerConnection.OnAudioFormatsNegotiated += (audioFormats) => windowsAudioEP.SetAudioSinkFormat(audioFormats.First()); peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; peerConnection.OnSendReport += RtpSession_OnSendReport; peerConnection.OnTimeout += (mediaType) => logger.LogDebug($"Timeout on media {mediaType}."); peerConnection.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state changed to {state}."); peerConnection.onconnectionstatechange += async(state) => { logger.LogDebug($"Peer connection connected changed to {state}."); if (state == RTCPeerConnectionState.connected) { await windowsAudioEP.StartAudio(); } else if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.failed) { peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; peerConnection.OnSendReport -= RtpSession_OnSendReport; await windowsAudioEP.CloseAudio(); } }; peerConnection.OnRtpPacketReceived += (IPEndPoint rep, SDPMediaTypesEnum media, RTPPacket rtpPkt) => { //logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}."); if (media == SDPMediaTypesEnum.audio) { windowsAudioEP.GotAudioRtp(rep, rtpPkt.Header.SyncSource, rtpPkt.Header.SequenceNumber, rtpPkt.Header.Timestamp, rtpPkt.Header.PayloadType, rtpPkt.Header.MarkerBit == 1, rtpPkt.Payload); } }; var offerSdp = peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerSdp); logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); logger.LogDebug(offerSdp.sdp); context.WebSocket.Send(offerSdp.sdp); return(peerConnection); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); var pc = new RTCPeerConnection(null); MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }, MediaStreamStatusEnum.SendOnly); pc.addTrack(videoTrack); 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.disconnected || state == RTCPeerConnectionState.failed) { pc.Close("remote disconnection"); } if (state == RTCPeerConnectionState.closed) { OnTestPatternSampleReady -= pc.SendMedia; pc.OnReceiveReport -= RtpSession_OnReceiveReport; pc.OnSendReport -= RtpSession_OnSendReport; _sendTestPatternTimer?.Dispose(); } else if (state == RTCPeerConnectionState.connected) { OnTestPatternSampleReady += pc.SendMedia; if (_sendTestPatternTimer == null) { _sendTestPatternTimer = new Timer(SendTestPattern, null, 0, TEST_PATTERN_SPACING_MILLISECONDS); } } }; 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); }
public void GenerateLocalOfferUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTCPeerConnection pc = new RTCPeerConnection(null); var offer = pc.createOffer(new RTCOfferOptions()); Assert.NotNull(offer); logger.LogDebug(offer.ToString()); }
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 static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); var peerConnection = new RTCPeerConnection(null); MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.SendOnly); peerConnection.addTrack(audioTrack); MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }, MediaStreamStatusEnum.SendOnly); peerConnection.addTrack(videoTrack); peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; peerConnection.OnSendReport += RtpSession_OnSendReport; peerConnection.OnTimeout += (mediaType) => peerConnection.Close("remote timeout"); peerConnection.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state changed to {state}."); peerConnection.onconnectionstatechange += (state) => { logger.LogDebug($"Peer connection state changed to {state}."); if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { OnVideoSampleReady -= peerConnection.SendVideo; OnAudioSampleReady -= peerConnection.SendAudio; peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; peerConnection.OnSendReport -= RtpSession_OnSendReport; } else if (state == RTCPeerConnectionState.connected) { if (!_isSampling) { _isSampling = true; OnVideoSampleReady += peerConnection.SendVideo; OnAudioSampleReady += peerConnection.SendAudio; _ = Task.Run(StartMedia); } } }; var offerInit = peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerInit); logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); context.WebSocket.Send(offerInit.sdp); return(peerConnection); }
public async void CheckDataChannelEstablishment() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); var aliceDataConnected = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var bobDataOpened = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var alice = new RTCPeerConnection(); var dc = await alice.createDataChannel("dc1", null); dc.onopen += () => aliceDataConnected.TrySetResult(true); var aliceOffer = alice.createOffer(); await alice.setLocalDescription(aliceOffer); logger.LogDebug($"alice offer: {aliceOffer.sdp}"); var bob = new RTCPeerConnection(); RTCDataChannel bobData = null; bob.ondatachannel += (chan) => { bobData = chan; bobDataOpened.TrySetResult(true); }; var setOfferResult = bob.setRemoteDescription(aliceOffer); Assert.Equal(SetDescriptionResultEnum.OK, setOfferResult); var bobAnswer = bob.createAnswer(); await bob.setLocalDescription(bobAnswer); var setAnswerResult = alice.setRemoteDescription(bobAnswer); Assert.Equal(SetDescriptionResultEnum.OK, setAnswerResult); logger.LogDebug($"answer: {bobAnswer.sdp}"); await Task.WhenAny(Task.WhenAll(aliceDataConnected.Task, bobDataOpened.Task), Task.Delay(2000)); Assert.True(aliceDataConnected.Task.IsCompleted); Assert.True(aliceDataConnected.Task.Result); Assert.True(bobDataOpened.Task.IsCompleted); Assert.True(bobDataOpened.Task.Result); Assert.True(dc.IsOpened); Assert.True(bobData.IsOpened); bob.close(); alice.close(); }
public void SendVideoRtcpFeedbackReportUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTCConfiguration pcConfiguration = new RTCConfiguration { certificates = new List <RTCCertificate> { new RTCCertificate { Certificate = DtlsUtils.CreateSelfSignedCert() } }, X_UseRtpFeedbackProfile = true }; RTCPeerConnection pcSrc = new RTCPeerConnection(pcConfiguration); var videoTrackSrc = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }); pcSrc.addTrack(videoTrackSrc); var offer = pcSrc.createOffer(new RTCOfferOptions()); logger.LogDebug($"offer: {offer.sdp}"); RTCPeerConnection pcDst = new RTCPeerConnection(pcConfiguration); var videoTrackDst = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }); pcDst.addTrack(videoTrackDst); var setOfferResult = pcDst.setRemoteDescription(offer); Assert.Equal(SetDescriptionResultEnum.OK, setOfferResult); var answer = pcDst.createAnswer(null); var setAnswerResult = pcSrc.setRemoteDescription(answer); Assert.Equal(SetDescriptionResultEnum.OK, setAnswerResult); logger.LogDebug($"answer: {answer.sdp}"); RTCPFeedback pliReport = new RTCPFeedback(pcDst.VideoLocalTrack.Ssrc, pcDst.VideoRemoteTrack.Ssrc, PSFBFeedbackTypesEnum.PLI); pcDst.SendRtcpFeedback(SDPMediaTypesEnum.video, pliReport); }
public void SendVideoRtcpFeedbackReportUnitTest() { logger.LogDebug("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name); logger.BeginScope(System.Reflection.MethodBase.GetCurrentMethod().Name); RTCConfiguration pcConfiguration = new RTCConfiguration { certificates = new List <RTCCertificate> { new RTCCertificate { X_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" } }, X_UseRtpFeedbackProfile = true }; RTCPeerConnection pcSrc = new RTCPeerConnection(pcConfiguration); var videoTrackSrc = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }); pcSrc.addTrack(videoTrackSrc); var offer = pcSrc.createOffer(new RTCOfferOptions()); logger.LogDebug($"offer: {offer.sdp}"); RTCPeerConnection pcDst = new RTCPeerConnection(pcConfiguration); var videoTrackDst = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }); pcDst.addTrack(videoTrackDst); var setOfferResult = pcDst.setRemoteDescription(offer); Assert.Equal(SetDescriptionResultEnum.OK, setOfferResult); var answer = pcDst.createAnswer(null); var setAnswerResult = pcSrc.setRemoteDescription(answer); Assert.Equal(SetDescriptionResultEnum.OK, setAnswerResult); logger.LogDebug($"answer: {answer.sdp}"); RTCPFeedback pliReport = new RTCPFeedback(pcDst.VideoLocalTrack.Ssrc, pcDst.VideoRemoteTrack.Ssrc, PSFBFeedbackTypesEnum.PLI); pcDst.SendRtcpFeedback(SDPMediaTypesEnum.video, pliReport); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { Log.LogDebug($"Web socket client connection from {context.UserEndPoint}."); _peerConnection = new RTCPeerConnection(null); //AddressFamily.InterNetwork, //DTLS_CERTIFICATE_FINGERPRINT, //null, //null); _peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; _peerConnection.OnSendReport += RtpSession_OnSendReport; Log.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); _peerConnection.onconnectionstatechange += (state) => { if (state == RTCPeerConnectionState.closed) { Log.LogDebug($"RTC peer connection closed."); _peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; _peerConnection.OnSendReport -= RtpSession_OnSendReport; } }; MediaStreamTrack audioTrack = new MediaStreamTrack(null, SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); _peerConnection.addTrack(audioTrack); var offerInit = await _peerConnection.createOffer(null); await _peerConnection.setLocalDescription(offerInit); context.WebSocket.Send(offerInit.sdp); if (DoDtlsHandshake(_peerConnection)) { Log.LogInformation("DTLS handshake completed successfully."); } else { _peerConnection.Close("dtls handshake failed."); } return(_peerConnection); }
public async Task <RTCSessionDescriptionInit> GetOffer(string id) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException("id", "A unique ID parameter must be supplied when creating a new peer connection."); } else if (_peerConnections.ContainsKey(id)) { throw new ArgumentNullException("id", "The specified peer connection ID is already in use."); } var peerConnection = new RTCPeerConnection(null); MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.RecvOnly); peerConnection.addTrack(audioTrack); peerConnection.OnRtpPacketReceived += (IPEndPoint rep, SDPMediaTypesEnum media, RTPPacket rtpPkt) => _logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}, SeqNum {rtpPkt.Header.SequenceNumber}."); //peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; //peerConnection.OnSendReport += RtpSession_OnSendReport; peerConnection.OnTimeout += (mediaType) => _logger.LogWarning($"Timeout for {mediaType}."); peerConnection.onconnectionstatechange += (state) => { _logger.LogDebug($"Peer connection {id} state changed to {state}."); if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { _peerConnections.TryRemove(id, out _); } else if (state == RTCPeerConnectionState.connected) { _logger.LogDebug("Peer connection connected."); } }; var offerSdp = peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerSdp); _peerConnections.TryAdd(id, peerConnection); return(offerSdp); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); var pc = new RTCPeerConnection(null); MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.SendOnly); pc.addTrack(videoTrack); 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.disconnected || state == RTCPeerConnectionState.failed) { pc.Close("remote disconnection"); } else if (state == RTCPeerConnectionState.closed) { 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 static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); var peerConnection = new RTCPeerConnection(null); MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.RecvOnly); peerConnection.addTrack(audioTrack); peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; peerConnection.OnSendReport += RtpSession_OnSendReport; peerConnection.OnTimeout += (mediaType) => logger.LogDebug($"Timeout on media {mediaType}."); peerConnection.oniceconnectionstatechange += (state) => logger.LogDebug($"ICE connection state changed to {state}."); peerConnection.onconnectionstatechange += (state) => { logger.LogDebug($"Peer connection connected changed to {state}."); if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; peerConnection.OnSendReport -= RtpSession_OnSendReport; } else if (state == RTCPeerConnectionState.connected) { peerConnection.OnRtpPacketReceived += (SDPMediaTypesEnum media, RTPPacket rtpPkt) => logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}."); } }; var offerSdp = peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerSdp); logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); logger.LogDebug(offerSdp.sdp); context.WebSocket.Send(offerSdp.sdp); return(peerConnection); }
public async Task <RTCSessionDescriptionInit> GetOffer(string id) { if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException("id", "A unique ID parameter must be supplied when creating a new peer connection."); } else if (_peerConnections.ContainsKey(id)) { throw new ArgumentNullException("id", "The specified peer connection ID is already in use."); } RTCConfiguration pcConfiguration = new RTCConfiguration { certificates = new List <RTCCertificate> { new RTCCertificate { X_CertificatePath = DTLS_CERTIFICATE_PATH, X_KeyPath = DTLS_KEY_PATH, X_Fingerprint = DTLS_CERTIFICATE_FINGERPRINT } } }; var peerConnection = new RTCPeerConnection(pcConfiguration); var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH); MediaStreamTrack audioTrack = new MediaStreamTrack("0", SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.RecvOnly); peerConnection.addTrack(audioTrack); //peerConnection.OnRtpPacketReceived += (SDPMediaTypesEnum media, RTPPacket rtpPkt) => _logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}, SeqNum {rtpPkt.Header.SequenceNumber}."); //peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; //peerConnection.OnSendReport += RtpSession_OnSendReport; peerConnection.OnTimeout += (mediaType) => { peerConnection.Close("remote timeout"); dtls.Shutdown(); }; peerConnection.onconnectionstatechange += (state) => { if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { //peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; //peerConnection.OnSendReport -= RtpSession_OnSendReport; _logger.LogDebug($"Peer connection {id} closed."); _peerConnections.TryRemove(id, out _); } else if (state == RTCPeerConnectionState.connected) { _logger.LogDebug("Peer connection connected."); } }; _ = Task <bool> .Run(() => Task.FromResult(DoDtlsHandshake(peerConnection, dtls))) .ContinueWith((t) => { _logger.LogDebug($"dtls handshake result {t.Result}."); if (t.Result) { var remoteEP = peerConnection.IceSession.ConnectedRemoteEndPoint; peerConnection.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP); } else { dtls.Shutdown(); peerConnection.Close("dtls handshake failed."); } }); var offerSdp = await peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerSdp); _peerConnections.TryAdd(id, peerConnection); return(offerSdp); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); RTCConfiguration pcConfiguration = new RTCConfiguration { certificates = new List <RTCCertificate> { new RTCCertificate { X_CertificatePath = DTLS_CERTIFICATE_PATH, X_KeyPath = DTLS_KEY_PATH, X_Fingerprint = DTLS_CERTIFICATE_FINGERPRINT } } }; var peerConnection = new RTCPeerConnection(pcConfiguration); var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH); MediaStreamTrack audioTrack = new MediaStreamTrack("0", SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.RecvOnly); peerConnection.addTrack(audioTrack); peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; peerConnection.OnSendReport += RtpSession_OnSendReport; peerConnection.OnTimeout += (mediaType) => { peerConnection.Close("remote timeout"); dtls.Shutdown(); }; peerConnection.onconnectionstatechange += (state) => { if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; peerConnection.OnSendReport -= RtpSession_OnSendReport; } else if (state == RTCPeerConnectionState.connected) { logger.LogDebug("Peer connection connected."); peerConnection.OnRtpPacketReceived += (SDPMediaTypesEnum media, RTPPacket rtpPkt) => logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}."); } }; peerConnection.oniceconnectionstatechange += (state) => { if (state == RTCIceConnectionState.connected) { logger.LogDebug("Starting DTLS handshake task."); bool dtlsResult = false; Task.Run(async() => dtlsResult = await DoDtlsHandshake(peerConnection, dtls)) .ContinueWith((t) => { logger.LogDebug($"dtls handshake result {dtlsResult}."); if (dtlsResult) { var remoteEP = peerConnection.IceSession.ConnectedRemoteEndPoint; peerConnection.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP); } else { dtls.Shutdown(); peerConnection.Close("dtls handshake failed."); } }); } }; var offerSdp = await peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerSdp); logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); logger.LogDebug(offerSdp.sdp); context.WebSocket.Send(offerSdp.sdp); return(peerConnection); }
public async Task <RTCSessionDescriptionInit> GotOffer(RTCSessionDescriptionInit offer) { _logger.LogDebug($"SDP offer received."); _logger.LogTrace($"Offer SDP:\n{offer.sdp}"); var pc = new RTCPeerConnection(); if (_publicIPv4 != null) { var rtpPort = pc.GetRtpChannel().RTPPort; var publicIPv4Candidate = new RTCIceCandidate(RTCIceProtocol.udp, _publicIPv4, (ushort)rtpPort, RTCIceCandidateType.host); pc.addLocalIceCandidate(publicIPv4Candidate); } if (_publicIPv6 != null) { var rtpPort = pc.GetRtpChannel().RTPPort; var publicIPv6Candidate = new RTCIceCandidate(RTCIceProtocol.udp, _publicIPv6, (ushort)rtpPort, RTCIceCandidateType.host); pc.addLocalIceCandidate(publicIPv6Candidate); } MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU) }, MediaStreamStatusEnum.SendRecv); pc.addTrack(audioTrack); MediaStreamTrack videoTrack = new MediaStreamTrack(new VideoFormat(VideoCodecsEnum.VP8, VP8_PAYLOAD_ID), MediaStreamStatusEnum.SendRecv); pc.addTrack(videoTrack); pc.OnRtpPacketReceived += (IPEndPoint rep, SDPMediaTypesEnum media, RTPPacket rtpPkt) => { pc.SendRtpRaw(media, rtpPkt.Payload, rtpPkt.Header.Timestamp, rtpPkt.Header.MarkerBit, rtpPkt.Header.PayloadType); //_logger.LogDebug($"RTP {media} pkt received, SSRC {rtpPkt.Header.SyncSource}, SeqNum {rtpPkt.Header.SequenceNumber}."); }; //peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; //peerConnection.OnSendReport += RtpSession_OnSendReport; pc.OnTimeout += (mediaType) => _logger.LogWarning($"Timeout for {mediaType}."); pc.onconnectionstatechange += (state) => { _logger.LogDebug($"Peer connection state changed to {state}."); if (state == RTCPeerConnectionState.failed) { pc.Close("ice failure"); } }; var setResult = pc.setRemoteDescription(offer); if (setResult == SetDescriptionResultEnum.OK) { var offerSdp = pc.createOffer(null); await pc.setLocalDescription(offerSdp); var answer = pc.createAnswer(null); await pc.setLocalDescription(answer); _logger.LogTrace($"Answer SDP:\n{answer.sdp}"); return(answer); } else { return(null); } }
private static async Task <RTCPeerConnection> StartPeerConnection() { RTCConfiguration pcConfiguration = new RTCConfiguration { certificates = new List <RTCCertificate> { new RTCCertificate { X_CertificatePath = DTLS_CERTIFICATE_PATH, X_KeyPath = DTLS_KEY_PATH, X_Fingerprint = DTLS_CERTIFICATE_FINGERPRINT } } }; var peerConnection = new RTCPeerConnection(pcConfiguration); MediaStreamTrack videoTrack = new MediaStreamTrack( SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }, MediaStreamStatusEnum.SendOnly); peerConnection.addTrack(videoTrack); peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; peerConnection.OnSendReport += RtpSession_OnSendReport; //peerConnection.OnRtcpBye += (reason) => //{ // logger.LogInformation("RTCP BYE report received from remote peer."); // peerConnection.Close(reason); // dtls.Shutdown(); //}; peerConnection.OnTimeout += (mediaType) => { peerConnection.Close("remote timeout"); }; peerConnection.onconnectionstatechange += async(state) => { if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { OnTestPatternSampleReady -= peerConnection.SendMedia; peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; peerConnection.OnSendReport -= RtpSession_OnSendReport; } else if (state == RTCPeerConnectionState.connected) { // The DTLS handshake completed. logger.LogDebug("Peer connection connected."); OnTestPatternSampleReady += peerConnection.SendMedia; await peerConnection.Start(); if (_sendTestPatternTimer == null) { _sendTestPatternTimer = new Timer(SendTestPattern, null, 0, TEST_PATTERN_SPACING_MILLISECONDS); } } }; peerConnection.oniceconnectionstatechange += (state) => { logger.LogDebug($"ICE connection state change {state}."); // The ICE connectivity check completed successfully. if (state == RTCIceConnectionState.connected) { var remoteEP = peerConnection.AudioDestinationEndPoint; peerConnection.SetDestination(SDPMediaTypesEnum.audio, remoteEP, remoteEP); } }; var offerSdp = peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerSdp); logger.LogDebug(offerSdp.sdp); return(peerConnection); }
public async Task CreateSDPOfferAsync() { await peerConnection.createOffer(); }
private static async Task <RTCPeerConnection> SendSDPOffer(WebSocketContext context) { logger.LogDebug($"Web socket client connection from {context.UserEndPoint}."); RTCConfiguration pcConfiguration = new RTCConfiguration { certificates = new List <RTCCertificate> { new RTCCertificate { X_CertificatePath = DTLS_CERTIFICATE_PATH, X_KeyPath = DTLS_KEY_PATH, X_Fingerprint = DTLS_CERTIFICATE_FINGERPRINT } } }; var peerConnection = new RTCPeerConnection(pcConfiguration); var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH); MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.PCMU) }); peerConnection.addTrack(audioTrack); MediaStreamTrack videoTrack = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> { new SDPMediaFormat(SDPMediaFormatsEnum.VP8) }); peerConnection.addTrack(videoTrack); peerConnection.OnReceiveReport += RtpSession_OnReceiveReport; peerConnection.OnSendReport += RtpSession_OnSendReport; peerConnection.OnTimeout += (mediaType) => { peerConnection.Close("remote timeout"); dtls.Shutdown(); }; peerConnection.onconnectionstatechange += (state) => { if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.disconnected || state == RTCPeerConnectionState.failed) { logger.LogDebug($"RTC peer connection was closed."); OnMediaSampleReady -= peerConnection.SendMedia; peerConnection.OnReceiveReport -= RtpSession_OnReceiveReport; peerConnection.OnSendReport -= RtpSession_OnSendReport; } else if (state == RTCPeerConnectionState.connected) { logger.LogDebug("Peer connection connected."); OnMediaSampleReady += peerConnection.SendMedia; } }; peerConnection.oniceconnectionstatechange += (state) => { if (state == RTCIceConnectionState.connected) { logger.LogDebug("Starting DTLS handshake task."); bool dtlsResult = false; Task.Run(async() => dtlsResult = await DoDtlsHandshake(peerConnection, dtls, peerConnection.RemotePeerDtlsFingerprint)) .ContinueWith((t) => { logger.LogDebug($"dtls handshake result {dtlsResult}."); if (dtlsResult) { //peerConnection.SetDestination(SDPMediaTypesEnum.audio, peerConnection.IceSession.ConnectedRemoteEndPoint, peerConnection.IceSession.ConnectedRemoteEndPoint); } else { dtls.Shutdown(); peerConnection.Close("dtls handshake failed."); } }); } }; var offerInit = peerConnection.createOffer(null); await peerConnection.setLocalDescription(offerInit); logger.LogDebug($"Sending SDP offer to client {context.UserEndPoint}."); context.WebSocket.Send(offerInit.sdp); return(peerConnection); }
/// <summary> /// This application spits out a lot of log messages. In an attempt to make command entry slightly more usable /// this method attempts to always write the current command input as the bottom line on the console output. /// </summary> private static async Task ProcessInput(CancellationTokenSource cts) { // Local function to write the current command in the process of being entered. Action <int, string> writeCommandPrompt = (lastPromptRow, cmd) => { // The cursor is already at the current row. if (Console.CursorTop == lastPromptRow) { // The command was corrected. Need to re-write the whole line. Console.SetCursorPosition(0, Console.CursorTop); Console.Write(new string(' ', Console.WindowWidth)); Console.SetCursorPosition(0, Console.CursorTop); Console.Write($"{COMMAND_PROMPT}{cmd}"); } else { // The cursor row has changed since the last input. Rewrite the prompt and command // on the current line. Console.Write($"{COMMAND_PROMPT}{cmd}"); } }; string command = null; int lastInputRow = Console.CursorTop; while (!cts.IsCancellationRequested) { var inKey = Console.ReadKey(true); if (inKey.Key == ConsoleKey.Enter) { if (command == null) { Console.WriteLine(); Console.Write(COMMAND_PROMPT); } else { // Attempt to execute the current command. switch (command.ToLower()) { case "c": // Close active peer connection. if (_peerConnection != null) { Console.WriteLine(); Console.WriteLine("Closing peer connection"); _peerConnection.Close("user initiated"); } break; case var x when x.StartsWith("cdc"): // Attempt to create a new data channel. if (_peerConnection != null) { (_, var label) = x.Split(" ", 2, StringSplitOptions.None); if (!string.IsNullOrWhiteSpace(label)) { Console.WriteLine(); Console.WriteLine($"Creating data channel for label {label}."); var dc = _peerConnection.createDataChannel(label, null); dc.onmessage += (msg) => logger.LogDebug($" data channel message received on {label}: {msg}"); } else { Console.WriteLine(); Console.WriteLine($"Send message command was in the wrong format. Needs to be: cdc <label>"); } } break; case var x when x.StartsWith("ldc"): // List data channels. if (_peerConnection != null) { if (_peerConnection.DataChannels.Count > 0) { Console.WriteLine(); foreach (var dc in _peerConnection.DataChannels) { Console.WriteLine($" data channel: label {dc.label}, stream ID {dc.id}, is open {dc.IsOpened}."); } } else { Console.WriteLine(); Console.WriteLine(" no data channels available."); } } break; case var x when x.StartsWith("sdc"): // Send data channel message. if (_peerConnection != null) { (_, var label, var msg) = x.Split(" ", 3, StringSplitOptions.None); if (!string.IsNullOrWhiteSpace(label) && !string.IsNullOrWhiteSpace(msg)) { Console.WriteLine(); Console.WriteLine($"Sending message on channel {label}: {msg}"); var dc = _peerConnection.DataChannels.FirstOrDefault(x => x.label == label && x.IsOpened); if (dc != null) { dc.send(msg); } else { Console.WriteLine($"No data channel was found for label {label}."); } } else { Console.WriteLine(); Console.WriteLine($"Send data channel message command was in the wrong format. Needs to be: sdc <label> <message>"); } } break; case "q": // Quit. Console.WriteLine(); Console.WriteLine("Quitting..."); cts.Cancel(); break; case "isalive": // Check responsiveness. Console.WriteLine(); Console.WriteLine("yep"); Console.Write(COMMAND_PROMPT); break; case var x when x.StartsWith("node"): (_, var sdpType, var myUser, string theirUser) = x.Split(" ", 4, StringSplitOptions.None); if (sdpType == "so") { _peerConnection = Createpc(null, _stunServer); var offerSdp = _peerConnection.createOffer(null); await _peerConnection.setLocalDescription(offerSdp); Console.WriteLine($"Our Offer:\n{offerSdp.sdp}"); var offerJson = JsonConvert.SerializeObject(offerSdp, new Newtonsoft.Json.Converters.StringEnumConverter()); var content = new StringContent(offerJson, Encoding.UTF8, "application/json"); var res = await _nodeDssclient.PostAsync($"{_nodeDssUri}data/{theirUser}", content); Console.WriteLine($"node-dss POST result {res.StatusCode}."); } else if (sdpType == "go") { var res = await _nodeDssclient.GetAsync($"{_nodeDssUri}data/{myUser}"); Console.WriteLine($"node-dss GET result {res.StatusCode}."); if (res.StatusCode == HttpStatusCode.OK) { var content = await res.Content.ReadAsStringAsync(); RTCSessionDescriptionInit offerInit = JsonConvert.DeserializeObject <RTCSessionDescriptionInit>(content); Console.WriteLine($"Remote offer:\n{offerInit.sdp}"); _peerConnection = Createpc(null, _stunServer); var setRes = _peerConnection.setRemoteDescription(offerInit); if (setRes != SetDescriptionResultEnum.OK) { // No point continuing. Something will need to change and then try again. _peerConnection.Close("failed to set remote sdp offer"); } else { var answer = _peerConnection.createAnswer(null); await _peerConnection.setLocalDescription(answer); Console.WriteLine($"Our answer:\n{answer.sdp}"); var answerJson = JsonConvert.SerializeObject(answer, new Newtonsoft.Json.Converters.StringEnumConverter()); var answerContent = new StringContent(answerJson, Encoding.UTF8, "application/json"); var postRes = await _nodeDssclient.PostAsync($"{_nodeDssUri}data/{theirUser}", answerContent); Console.WriteLine($"node-dss POST result {res.StatusCode}."); } } } else if (sdpType == "ga") { var res = await _nodeDssclient.GetAsync($"{_nodeDssUri}data/{myUser}"); Console.WriteLine($"node-dss GET result {res.StatusCode}."); if (res.StatusCode == HttpStatusCode.OK) { var content = await res.Content.ReadAsStringAsync(); RTCSessionDescriptionInit answerInit = JsonConvert.DeserializeObject <RTCSessionDescriptionInit>(content); Console.WriteLine($"Remote answer:\n{answerInit.sdp}"); var setRes = _peerConnection.setRemoteDescription(answerInit); if (setRes != SetDescriptionResultEnum.OK) { // No point continuing. Something will need to change and then try again. _peerConnection.Close("failed to set remote sdp answer"); } } } break; default: // Command not recognised. Console.WriteLine(); Console.WriteLine($"Unknown command: {command}"); Console.Write(COMMAND_PROMPT); break; } command = null; } } else if (inKey.Key == ConsoleKey.UpArrow) { // Convenience mechanism to get the current input prompt without // needing to change the command being entered. writeCommandPrompt(lastInputRow, command); } else if (inKey.Key == ConsoleKey.Escape) { // Escape key clears the current command. command = null; writeCommandPrompt(lastInputRow, command); } else if (inKey.Key == ConsoleKey.Backspace) { // Backspace removes the last character. command = (command?.Length > 0) ? command.Substring(0, command.Length - 1) : null; writeCommandPrompt(lastInputRow, command); } else if (!Char.IsControl(inKey.KeyChar)) { // Non-control character, append to current command. command += inKey.KeyChar; if (Console.CursorTop == lastInputRow) { Console.Write(inKey.KeyChar); } else { writeCommandPrompt(lastInputRow, command); } } lastInputRow = Console.CursorTop; } }
protected override Task OnConnect() { //SIPSorcery.LogFactory.Set(Resonance.ResonanceGlobalSettings.Default.LoggerFactory); _connectionInitialized = false; _rolesReversed = false; _connectionCompleted = false; _receivedSegments = new List <byte[]>(); _expectedSegments = 0; _expectedSegmentsCheckSum = null; _incomingQueue = new ProducerConsumerQueue <byte[]>(); _connectionCompletionSource = new TaskCompletionSource <object>(); Logger.LogInformation("Initializing adapter with role '{Role}'.", Role); Task.Factory.StartNew(async() => { try { Thread.Sleep(50); if (Role == WebRTCAdapterRole.Accept) { if (_offerRequest != null) { Logger.LogInformation("Adapter initialized by an offer request. Sending answer..."); var response = OnWebRTCOfferRequest(_offerRequest); _signalingTransporter.SendResponse(response.Response, _offerRequestToken); } else { Logger.LogInformation("Waiting for offer..."); } } else { InitConnection(); Logger.LogInformation("Creating offer..."); RTCSessionDescriptionInit offer = _connection.createOffer(new RTCOfferOptions()); Logger.LogInformation("Setting local description..."); await _connection.setLocalDescription(offer); Logger.LogInformation("Sending offer request..."); var response = await _signalingTransporter.SendRequestAsync <WebRTCOfferRequest, WebRTCOfferResponse>(new WebRTCOfferRequest() { ChannelName = ChannelName, Offer = WebRTCSessionDescription.FromSessionDescription(offer) }, new ResonanceRequestConfig() { Timeout = TimeSpan.FromSeconds(30) }); if (response.Answer.InternalType == RTCSdpType.answer) { Logger.LogInformation("Answer received, setting remove description..."); var result = _connection.setRemoteDescription(response.Answer.ToSessionDescription()); if (result != SetDescriptionResultEnum.OK) { throw new Exception("Error setting the remote description."); } } else { Logger.LogError($"Invalid answer type received '{response.Answer.InternalType}'."); } FlushIceCandidates(); } } catch (Exception ex) { FailConnection(ex); } }); return(_connectionCompletionSource.Task); }
/// <summary> /// This is a javascript application. /// </summary> /// <param name="page">HTML document rendered by the web server which can now be enhanced.</param> public Application(IApp page) { // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection // https://github.com/cjb/serverless-webrtc // https://github.com/XSockets/WebRTC // jsc when was the last time we tried p2p? // var peer = new PeerConnection(iceServers, optional); where iceServers = null this is working without internet // http://stackoverflow.com/questions/19675165/whether-stun-server-is-needed-within-lan-for-webrtc //var peer = new PeerConnection(iceServers, optional); // https://www.webrtc-experiment.com/docs/WebRTC-PeerConnection.html // http://stackoverflow.com/questions/12848013/what-is-the-replacement-for-the-deprecated-peerconnection-api // http://docs.webplatform.org/wiki/apis/webrtc/RTCPeerConnection // http://w3schools.invisionzone.com/index.php?showtopic=46661 // http://www.html5rocks.com/en/tutorials/webrtc/basics/#toc-rtcpeerconnection // IDL dictionary looks like C# PrimaryCnstructor concept does it not ////var d = new RTCSessionDescription( //// new ////{ ////} ////); // 02000002 TestPeerConnection.Application // script: error JSC1000: You tried to instance a class which seems to be marked as native. // script: error JSC1000: type has no callable constructor: [ScriptCoreLib.JavaScript.DOM.RTCPeerConnection] //Void.ctor() // Uncaught ReferenceError: RTCPeerConnection is not defined // wtf? // {{ RTCPeerConnection = undefined }} //new IHTMLPre { new { w.RTCPeerConnection } }.AttachToDocument(); // {{ webkitRTCPeerConnection = function RTCPeerConnection() { [native code] } }} //new IHTMLPre { new { w.webkitRTCPeerConnection } }.AttachToDocument(); // wtf chrome? stop prefixing var w = Native.window as dynamic; Console.WriteLine(new { w.RTCPeerConnection }); w.RTCPeerConnection = w.webkitRTCPeerConnection; // Uncaught TypeError: Failed to construct 'RTCPeerConnection': 1 argument required, but only 0 present. // http://stackoverflow.com/questions/22470291/rtcdatachannels-readystate-is-not-open // after Chrome 31, you can use SCTP based data channels. // http://stackoverflow.com/questions/21585681/send-image-data-over-rtc-data-channel // https://code.google.com/p/chromium/issues/detail?id=295771 // https://gist.github.com/shacharz/9661930 // http://chimera.labs.oreilly.com/books/1230000000545/ch18.html#_tracking_ice_gathering_and_connectivity_status var peer = new RTCPeerConnection( new { iceServers = new object[0] }, null // https://groups.google.com/forum/#!topic/discuss-webrtc/y2A97iCByTU //constraints: new { // optional = new[] // { // new { RtpDataChannels = true } // } //} ); // how the hell cann I connect two p2p? // i see we need to do data //peer.setLocalDescription // https://groups.google.com/forum/#!topic/discuss-webrtc/zK_5yUqiqsE // X:\jsc.svn\examples\javascript\xml\VBDisplayServerDebuggerPresence\VBDisplayServerDebuggerPresence\ApplicationWebService.vb // https://code.google.com/p/webrtc/source/browse/trunk/samples/js/base/adapter.js // http://www.webrtc.org/faq-recent-topics // http://stackoverflow.com/questions/14134090/how-is-a-webrtc-peer-connection-established peer.onicecandidate = new Action <RTCPeerConnectionIceEvent>( (RTCPeerConnectionIceEvent e) => { if (e.candidate != null) { new IHTMLPre { "onicecandidate: " + new { e.candidate.candidate } }.AttachToDocument(); peer.addIceCandidate(e.candidate, new Action( delegate { new IHTMLPre { "addIceCandidate" }.AttachToDocument(); } )); } } ); // http://stackoverflow.com/questions/15484729/why-doesnt-onicecandidate-work // http://www.skylinetechnologies.com/Blog/Article/48/Peer-to-Peer-Media-Streaming-with-WebRTC-and-SignalR.aspx peer.createOffer( new Action <RTCSessionDescription>( (RTCSessionDescription x) => { new IHTMLPre { "after createOffer " + new { x.sdp } }.AttachToDocument(); peer.setLocalDescription(x, new Action( delegate { // // send the offer to a server that can negotiate with a remote client new IHTMLPre { "after setLocalDescription " }.AttachToDocument(); } ) ); peer.setRemoteDescription(x, new Action( delegate { // // send the offer to a server that can negotiate with a remote client new IHTMLPre { "after setRemoteDescription " }.AttachToDocument(); } ) ); } ) ); peer.createAnswer( new Action <RTCSessionDescription>( (RTCSessionDescription x) => { new IHTMLPre { "after createAnswer " + new { x.sdp } }.AttachToDocument(); } )); // https://groups.google.com/forum/#!topic/discuss-webrtc/wbcgYMrIii4 // https://groups.google.com/forum/#!msg/discuss-webrtc/wbcgYMrIii4/aZ12cENVTxEJ // http://blog.printf.net/articles/2013/05/17/webrtc-without-a-signaling-server/ //peer.onconn // https://github.com/cjb/serverless-webrtc/blob/master/js/serverless-webrtc.js peer.ondatachannel = new Action <RTCDataChannelEvent>( (RTCDataChannelEvent e) => { //Console.WriteLine("ondatachannel"); new IHTMLPre { "ondatachannel" }.AttachToDocument(); var c = e.channel; c.onmessage = new Action <MessageEvent>( (MessageEvent ee) => { new IHTMLPre { new { ee.data } }.AttachToDocument(); } ); } ); // jsc cant the idl generator understand optinal? RTCDataChannel dc = peer.createDataChannel("label1", null); // {{ id = 65535, label = label1, readyState = connecting }} new IHTMLPre { new { dc.id, dc.label, dc.readyState } }.AttachToDocument(); new IHTMLButton { "awaiting to open..." }.AttachToDocument().With( button => { // !!! can our IDL compiler generate events and async at the same time? dc.onopen = new Action <IEvent>( async ee => { button.innerText = "send"; while (true) { await button.async.onclick; new IHTMLPre { "send" }.AttachToDocument(); // Failed to execute 'send' on 'RTCDataChannel': RTCDataChannel.readyState is not 'open' dc.send("data to send"); } } ); } ); //connection.createOffer }
public async Task <RTCSessionDescriptionInit> GotOffer(RTCSessionDescriptionInit offer) { logger.LogTrace($"SDP offer received."); logger.LogTrace(offer.sdp); var pc = new RTCPeerConnection(); if (_presetIceAddresses != null) { foreach (var addr in _presetIceAddresses) { var rtpPort = pc.GetRtpChannel().RTPPort; var publicIPv4Candidate = new RTCIceCandidate(RTCIceProtocol.udp, addr, (ushort)rtpPort, RTCIceCandidateType.host); pc.addLocalIceCandidate(publicIPv4Candidate); } } MediaStreamTrack audioTrack = new MediaStreamTrack(SDPWellKnownMediaFormatsEnum.PCMU); pc.addTrack(audioTrack); MediaStreamTrack videoTrack = new MediaStreamTrack(new VideoFormat(VideoCodecsEnum.VP8, VP8_PAYLOAD_ID)); pc.addTrack(videoTrack); pc.OnRtpPacketReceived += (IPEndPoint rep, SDPMediaTypesEnum media, RTPPacket rtpPkt) => { pc.SendRtpRaw(media, rtpPkt.Payload, rtpPkt.Header.Timestamp, rtpPkt.Header.MarkerBit, rtpPkt.Header.PayloadType); }; pc.OnTimeout += (mediaType) => logger.LogWarning($"Timeout for {mediaType}."); pc.oniceconnectionstatechange += (state) => logger.LogInformation($"ICE connection state changed to {state}."); pc.onsignalingstatechange += () => logger.LogInformation($"Signaling state changed to {pc.signalingState}."); pc.onconnectionstatechange += (state) => { logger.LogInformation($"Peer connection state changed to {state}."); if (state == RTCPeerConnectionState.failed) { pc.Close("ice failure"); } }; var setResult = pc.setRemoteDescription(offer); if (setResult == SetDescriptionResultEnum.OK) { var offerSdp = pc.createOffer(null); await pc.setLocalDescription(offerSdp); var answer = pc.createAnswer(null); logger.LogTrace($"SDP answer created."); logger.LogTrace(answer.sdp); return(answer); } else { logger.LogWarning($"Failed to set remote description {setResult}."); return(null); } }