Example #1
0
        private static async Task WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection pc, string message)
        {
            try
            {
                if (pc.localDescription == null)
                {
                    //logger.LogDebug("Offer SDP: " + message);
                    logger.LogDebug("Offer SDP received.");

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

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

                    if (string.IsNullOrWhiteSpace(message) || message.Trim().ToLower() == SDP.END_ICE_CANDIDATES_ATTRIBUTE)
                    {
                        logger.LogDebug("End of candidates message received.");
                    }
                    else
                    {
                        var candInit = Newtonsoft.Json.JsonConvert.DeserializeObject <RTCIceCandidateInit>(message);
                        pc.addIceCandidate(candInit);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
            }
        }
Example #2
0
        private static async Task WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection pc, string message)
        {
            try
            {
                if (pc.localDescription == null)
                {
                    //logger.LogDebug("Offer SDP: " + message);
                    logger.LogDebug("Offer SDP received.");

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

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

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

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

                    Console.WriteLine(answer.sdp);

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

                    if (string.IsNullOrWhiteSpace(message) || message.Trim().ToLower() == SDP.END_ICE_CANDIDATES_ATTRIBUTE)
                    {
                        logger.LogDebug("End of candidates message received.");
                    }
                    else
                    {
                        var candInit = Newtonsoft.Json.JsonConvert.DeserializeObject <RTCIceCandidateInit>(message);
                        pc.addIceCandidate(candInit);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
            }
        }
Example #3
0
        private static async Task WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection peerConnection, string message)
        {
            try
            {
                if (peerConnection.localDescription == null)
                {
                    logger.LogDebug("Offer SDP: " + message);

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

                    var remoteAudioAnn = remoteSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.audio).FirstOrDefault();
                    var remoteVideoAnn = remoteSdp.Media.Where(x => x.Media == SDPMediaTypesEnum.video).FirstOrDefault();

                    if (remoteAudioAnn != null)
                    {
                        MediaStreamTrack audioTrack = new MediaStreamTrack(remoteAudioAnn.MediaID, SDPMediaTypesEnum.audio, false, new List <SDPMediaFormat> {
                            new SDPMediaFormat(SDPMediaFormatsEnum.PCMU)
                        }, MediaStreamStatusEnum.RecvOnly);
                        peerConnection.addTrack(audioTrack);
                    }

                    if (remoteVideoAnn != null)
                    {
                        MediaStreamTrack videoTrack = new MediaStreamTrack(remoteVideoAnn.MediaID, SDPMediaTypesEnum.video, false, new List <SDPMediaFormat> {
                            new SDPMediaFormat(SDPMediaFormatsEnum.VP8)
                        }, MediaStreamStatusEnum.RecvOnly);
                        peerConnection.addTrack(videoTrack);
                    }

                    // After local media tracks have been added the remote description can be set.
                    await peerConnection.setRemoteDescription(new RTCSessionDescriptionInit { sdp = message, type = RTCSdpType.offer });

                    var answer = await peerConnection.createAnswer(null);

                    await peerConnection.setLocalDescription(answer);

                    context.WebSocket.Send(answer.sdp);
                }
                else if (peerConnection.remoteDescription == null)
                {
                    logger.LogDebug("Answer SDP: " + message);
                    await peerConnection.setRemoteDescription(new RTCSessionDescriptionInit { sdp = message, type = RTCSdpType.answer });
                }
                else
                {
                    logger.LogDebug("ICE Candidate: " + message);
                    await peerConnection.addIceCandidate(new RTCIceCandidateInit { candidate = message });
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
            }
        }
Example #4
0
        private static async void WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection peerConnection, string msg)
        {
            if (peerConnection.RemoteDescription != null)
            {
                Console.WriteLine($"ICE Candidate: {msg}.");
                //await _peerConnections[0].addIceCandidate(new RTCIceCandidateInit { candidate = msg });

                //  await peerConnection.addIceCandidate(new RTCIceCandidateInit { candidate = msg });
                Console.WriteLine("add ICE candidate complete.");
            }
            else
            {
                //Console.WriteLine($"websocket recv: {msg}");
                //var offerSDP = SDP.ParseSDPDescription(msg);
                Console.WriteLine($"offer sdp: {msg}");

                peerConnection.setRemoteDescription(new RTCSessionDescriptionInit {
                    sdp = msg, type = RTCSdpType.offer
                });

                var answerInit = peerConnection.createAnswer(null);
                await peerConnection.setLocalDescription(answerInit);

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

                context.WebSocket.Send(answerInit.sdp);
            }
        }
Example #5
0
        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);
        }
Example #6
0
        private static void WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection pc, string message)
        {
            try
            {
                if (pc.remoteDescription == null)
                {
                    logger.LogDebug("Answer SDP: " + message);
                    pc.setRemoteDescription(new RTCSessionDescriptionInit {
                        sdp = message, type = RTCSdpType.answer
                    });
                }
                else
                {
                    logger.LogDebug("ICE Candidate: " + message);

                    if (string.IsNullOrWhiteSpace(message) || message.Trim().ToLower() == SDP.END_ICE_CANDIDATES_ATTRIBUTE)
                    {
                        logger.LogDebug("End of candidates message received.");
                    }
                    else
                    {
                        var candInit = Newtonsoft.Json.JsonConvert.DeserializeObject <RTCIceCandidateInit>(message);
                        pc.addIceCandidate(candInit);
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
            }
        }
        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();
        }
Example #8
0
        private static async Task WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection peerConnection, string message)
        {
            try
            {
                if (peerConnection.localDescription == null)
                {
                    //logger.LogDebug("Offer SDP: " + message);
                    logger.LogDebug("Offer SDP received.");

                    // Add local media tracks depending on what was offered. Also add local tracks with the same media ID as
                    // the remote tracks so that the media announcement in the SDP answer are in the same order.
                    SDP remoteSdp = SDP.ParseSDPDescription(message);
                    await peerConnection.setRemoteDescription(new RTCSessionDescriptionInit { sdp = message, type = RTCSdpType.offer });

                    var answer = await peerConnection.createAnswer(null);

                    await peerConnection.setLocalDescription(answer);

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

                    if (string.IsNullOrWhiteSpace(message) || message.Trim().ToLower() == SDP.END_ICE_CANDIDATES_ATTRIBUTE)
                    {
                        logger.LogDebug("End of candidates message received.");
                    }
                    else
                    {
                        await peerConnection.addIceCandidate(new RTCIceCandidateInit { candidate = message });
                    }
                }
            }
            catch (Exception excp)
            {
                logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
            }
        }
        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
                    {
                        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);
        }
        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);
        }
Example #12
0
        private static async Task SDPAnswerReceived(RTCPeerConnection peerConnection, string sdpAnswer)
        {
            try
            {
                Log.LogDebug("Answer SDP: " + sdpAnswer);

                await peerConnection.setRemoteDescription(new RTCSessionDescriptionInit { type = RTCSdpType.answer, sdp = sdpAnswer });

                // Forward audio samples from the SIP session to the WebRTC session (one way).
                //OnMediaFromSIPSampleReady += webRtcSession.SendMedia;
            }
            catch (Exception excp)
            {
                Log.LogError("Exception SDPAnswerReceived. " + excp.Message);
            }
        }
Example #13
0
 private static void WebSocketMessageReceived(RTCPeerConnection pc, string message)
 {
     if (pc.remoteDescription == null)
     {
         logger.LogDebug("Answer SDP: " + message);
         pc.setRemoteDescription(new RTCSessionDescriptionInit {
             sdp = message, type = RTCSdpType.answer
         });
     }
     else
     {
         logger.LogDebug("ICE Candidate: " + message);
         pc.addIceCandidate(new RTCIceCandidateInit {
             candidate = message
         });
     }
 }
Example #14
0
 private static async Task WebSocketMessageReceived(RTCPeerConnection peerConnection, string message)
 {
     try
     {
         if (peerConnection.remoteDescription == null)
         {
             logger.LogDebug("Answer SDP: " + message);
             await peerConnection.setRemoteDescription(new RTCSessionDescriptionInit { sdp = message, type = RTCSdpType.answer });
         }
         else
         {
             logger.LogDebug("ICE Candidate: " + message);
             await peerConnection.addIceCandidate(new RTCIceCandidateInit { candidate = message });
         }
     }
     catch (Exception excp)
     {
         logger.LogError("Exception WebSocketMessageReceived. " + excp.Message);
     }
 }
Example #15
0
        private void DoProcessMessage(Message message)
        {
            switch (message.type)
            {
            case "Offer":
                // var offer = JsonUtility.FromJson<RTCSessionDescriptionInit>(message.args);
                if (RTCSessionDescriptionInit.TryParse(message.args, out RTCSessionDescriptionInit offer))
                {
                    Debug.Log($"Got remote SDP, type {offer.type}");

                    var result = rtcPeerConnection.setRemoteDescription(offer);
                    if (result != SetDescriptionResultEnum.OK)
                    {
                        Debug.Log($"Failed to set remote description, {result}.");
                        rtcPeerConnection.Close("Failed to set remote description");
                    }
                    else
                    {
                        if (rtcPeerConnection.signalingState == RTCSignalingState.have_remote_offer)
                        {
                            var answerSdp = rtcPeerConnection.createAnswer();
                            rtcPeerConnection.setLocalDescription(answerSdp);

                            Debug.Log($"Sending SDP answer");

                            Send("Offer", answerSdp.toJSON());
                        }
                    }
                }
                break;

            case "IceCandidate":
                if (RTCIceCandidateInit.TryParse(message.args, out RTCIceCandidateInit candidate))
                {
                    Debug.Log($"Got remote Ice Candidate, uri {candidate.candidate}");
                    rtcPeerConnection.addIceCandidate(candidate);
                }
                break;
            }
        }
Example #16
0
        /// <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
        }
Example #17
0
        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);
            }
        }
Example #18
0
        static async Task Main()
        {
            Console.WriteLine("Janus Echo Test Demo");

            AddConsoleLogger();

            CancellationTokenSource cts = new CancellationTokenSource();
            bool isFormActivated        = false;

            #region Set up a simple Windows Form with two picture boxes.

            var form = new Form();
            form.AutoSize = true;
            form.BackgroundImageLayout = ImageLayout.Center;
            var localVideoPicBox = new PictureBox
            {
                Size     = new Size(VIDEO_FRAME_WIDTH, VIDEO_FRAME_HEIGHT),
                Location = new Point(0, 0),
                Visible  = true
            };
            var remoteVideoPicBox = new PictureBox
            {
                Size     = new Size(VIDEO_FRAME_WIDTH, VIDEO_FRAME_HEIGHT),
                Location = new Point(0, VIDEO_FRAME_HEIGHT),
                Visible  = true
            };
            form.Controls.Add(localVideoPicBox);
            form.Controls.Add(remoteVideoPicBox);

            Application.EnableVisualStyles();
            ThreadPool.QueueUserWorkItem(delegate { Application.Run(form); });
            form.FormClosing += (sender, e) => isFormActivated = false;
            form.Activated   += (sender, e) => isFormActivated = true;

            #endregion

            Console.WriteLine("Creating peer connection.");
            RTCPeerConnection pc = new RTCPeerConnection(null);

            var videoSource = new VideoTestPatternSource(new VpxVideoEncoder());
            var videoSink   = new VideoEncoderEndPoint();

            MediaStreamTrack videoTrack = new MediaStreamTrack(videoSink.GetVideoSourceFormats(), MediaStreamStatusEnum.SendRecv);
            pc.addTrack(videoTrack);
            pc.OnVideoFrameReceived += videoSink.GotVideoFrame;
            videoSource.OnVideoSourceEncodedSample += pc.SendVideo;

            pc.OnVideoFormatsNegotiated += (formats) =>
            {
                videoSink.SetVideoSourceFormat(formats.First());
                videoSource.SetVideoSourceFormat(formats.First());
            };
            pc.OnTimeout += (mediaType) => Console.WriteLine($"Peer connection timeout on media {mediaType}.");
            pc.oniceconnectionstatechange += (state) => Console.WriteLine($"ICE connection state changed to {state}.");
            pc.onconnectionstatechange    += async(state) =>
            {
                Console.WriteLine($"Peer connection connected changed to {state}.");

                if (state == RTCPeerConnectionState.closed || state == RTCPeerConnectionState.failed)
                {
                    await videoSource.CloseVideo().ConfigureAwait(false);

                    videoSource.Dispose();
                }
            };

            #region Wire up the video source and sink to the picutre boxes.

            videoSource.OnVideoSourceRawSample += (uint durationMilliseconds, int width, int height, byte[] sample, VideoPixelFormatsEnum pixelFormat) =>
            {
                if (isFormActivated)
                {
                    form?.BeginInvoke(new Action(() =>
                    {
                        if (form.Handle != IntPtr.Zero)
                        {
                            unsafe
                            {
                                fixed(byte *s = sample)
                                {
                                    var bmpImage           = new Bitmap(width, height, width * 3, PixelFormat.Format24bppRgb, (IntPtr)s);
                                    localVideoPicBox.Image = bmpImage;
                                }
                            }
                        }
                    }));
                }
            };

            videoSink.OnVideoSinkDecodedSample += (byte[] bmp, uint width, uint height, int stride, VideoPixelFormatsEnum pixelFormat) =>
            {
                if (isFormActivated)
                {
                    form.BeginInvoke(new Action(() =>
                    {
                        unsafe
                        {
                            fixed(byte *s = bmp)
                            {
                                Bitmap bmpImage         = new Bitmap((int)width, (int)height, (int)(bmp.Length / height), PixelFormat.Format24bppRgb, (IntPtr)s);
                                remoteVideoPicBox.Image = bmpImage;
                            }
                        }
                    }));
                }
            };

            #endregion

            var offer = pc.CreateOffer(null);
            await pc.setLocalDescription(new RTCSessionDescriptionInit { type = RTCSdpType.offer, sdp = offer.ToString() }).ConfigureAwait(false);

            Console.WriteLine($"SDP Offer: {pc.localDescription.sdp}");

            await videoSource.StartVideo().ConfigureAwait(false);

            if (_useJanusRest)
            {
                JanusRestClient janusClient = new JanusRestClient(
                    JANUS_BASE_URI,
                    SIPSorcery.LogFactory.CreateLogger <JanusRestClient>(),
                    cts.Token);

                //var serverInfo = await janusClient.GetServerInfo().ConfigureAwait(false);
                //Console.WriteLine($"Name={serverInfo.name}.");
                //Console.WriteLine($"Version={serverInfo.version}.");

                janusClient.OnJanusEvent += async(resp) =>
                {
                    if (resp.jsep != null)
                    {
                        Console.WriteLine($"get event jsep={resp.jsep.type}.");

                        Console.WriteLine($"SDP Answer: {resp.jsep.sdp}");
                        var result = pc.setRemoteDescription(new RTCSessionDescriptionInit {
                            type = RTCSdpType.answer, sdp = resp.jsep.sdp
                        });
                        Console.WriteLine($"SDP Answer: {pc.remoteDescription.sdp}");

                        if (result == SetDescriptionResultEnum.OK)
                        {
                            Console.WriteLine("Starting peer connection.");
                            await pc.Start().ConfigureAwait(false);
                        }
                        else
                        {
                            Console.WriteLine($"Error setting remote SDP description {result}.");
                        }
                    }
                };

                await janusClient.StartSession().ConfigureAwait(false);

                await janusClient.StartEcho(offer.ToString()).ConfigureAwait(false);
            }
            else
            {
                //RestClient signalingClient = new RestClient($"{WEBRTC_SIGNALING_JANUS_URL}?duration=15");
                RestClient signalingClient = new RestClient($"{WEBRTC_SIGNALING_JANUS_URL}");
                var        echoTestReq     = new RestRequest(string.Empty, Method.POST, DataFormat.Json);
                echoTestReq.AddJsonBody(pc.localDescription.sdp.ToString());
                var echoTestResp = await signalingClient.ExecutePostAsync <string>(echoTestReq).ConfigureAwait(false);

                if (echoTestResp.IsSuccessful)
                {
                    var sdpAnswer = echoTestResp.Data;
                    Console.WriteLine($"SDP Answer: {sdpAnswer}");
                    var result = pc.setRemoteDescription(new RTCSessionDescriptionInit {
                        type = RTCSdpType.answer, sdp = sdpAnswer
                    });
                    Console.WriteLine($"SDP Answer: {pc.remoteDescription.sdp}");

                    if (result == SetDescriptionResultEnum.OK)
                    {
                        Console.WriteLine("Starting peer connection.");
                        await pc.Start().ConfigureAwait(false);
                    }
                    else
                    {
                        Console.WriteLine($"Error setting remote SDP description {result}.");
                    }
                }
                else
                {
                    Console.WriteLine($"Janus echo test plugin request failed {echoTestResp.ErrorMessage}.");
                }
            }

            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();

            isFormActivated = false;
            cts.Cancel();

            //await janusClient.DestroySession().ConfigureAwait(false);
        }
Example #19
0
        /// <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;
            }
        }
Example #20
0
        private static async void WebSocketMessageReceived(WebSocketContext context, RTCPeerConnection peerConnection, string msg)
        {
            if (peerConnection.RemoteDescription != null)
            {
                Console.WriteLine($"ICE Candidate: {msg}.");
                //await _peerConnections[0].addIceCandidate(new RTCIceCandidateInit { candidate = msg });

                //  await peerConnection.addIceCandidate(new RTCIceCandidateInit { candidate = msg });
                Console.WriteLine("add ICE candidate complete.");
            }
            else
            {
                //Console.WriteLine($"websocket recv: {msg}");
                //var offerSDP = SDP.ParseSDPDescription(msg);
                Console.WriteLine($"offer sdp: {msg}");

                var dtls = new DtlsHandshake(DTLS_CERTIFICATE_PATH, DTLS_KEY_PATH);

                await peerConnection.setRemoteDescription(new RTCSessionDescriptionInit { sdp = msg, type = RTCSdpType.offer });

                peerConnection.OnReceiveReport         += RtpSession_OnReceiveReport;
                peerConnection.OnSendReport            += RtpSession_OnSendReport;
                peerConnection.onconnectionstatechange += (state) =>
                {
                    if (state == RTCPeerConnectionState.closed)
                    {
                        Console.WriteLine($"webrtc session closed.");
                        //_peerConnections.Remove(peerConnection);
                    }
                };

                peerConnection.oniceconnectionstatechange += (state) =>
                {
                    if (state == RTCIceConnectionState.connected)
                    {
                        Console.WriteLine("Starting DTLS handshake task.");

                        bool dtlsResult = false;
                        Task.Run(async() => dtlsResult = await DoDtlsHandshake(peerConnection, dtls))
                        .ContinueWith((t) =>
                        {
                            Console.WriteLine($"dtls handshake result {dtlsResult}.");

                            if (dtlsResult)
                            {
                                //peerConnection.SetDestination(SDPMediaTypesEnum.audio, peerConnection.IceSession.ConnectedRemoteEndPoint, peerConnection.IceSession.ConnectedRemoteEndPoint);
                                //_peerConnections.Add(peerConnection);
                                peerConnection.OnRtpPacketReceived += RtpSession_OnRtpPacketReceived;
                            }
                            else
                            {
                                dtls.Shutdown();
                                peerConnection.Close("dtls handshake failed.");
                            }
                        });
                    }
                };


                var answerInit = await peerConnection.createAnswer(null);

                await peerConnection.setLocalDescription(answerInit);

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

                context.WebSocket.Send(answerInit.sdp);
            }
        }
Example #21
0
        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);
        }
Example #22
0
        public RTCSessionDescriptionInit GetSdpAnswer(SdpAnswerRequestArgs args)
        {
            ConnectionId = args.ConnectionId;

            RTCConfiguration config = new RTCConfiguration
            {
                X_UseRtpFeedbackProfile = true
            };
            var pc = new RTCPeerConnection(config);

            // Add local receive only tracks. This ensures that the SDP answer includes only the codecs we support.
            if (!_noAudio)
            {
                MediaStreamTrack audioTrack = new MediaStreamTrack(SDPMediaTypesEnum.audio, false,
                                                                   new List <SDPAudioVideoMediaFormat> {
                    new SDPAudioVideoMediaFormat(SDPWellKnownMediaFormatsEnum.PCMU)
                }, MediaStreamStatusEnum.RecvOnly);
                pc.addTrack(audioTrack);
            }
            // MediaStreamTrack videoTrack = new MediaStreamTrack(new VideoFormat(96, "VP8", 90000, "x-google-max-bitrate=5000000"), MediaStreamStatusEnum.RecvOnly);

            // pc.OnVideoFrameReceived += _videoSink.GotVideoFrame;
            // pc.OnVideoFormatsNegotiated += (formats) => _videoSink.SetVideoSinkFormat(formats.First());

            pc.OnVideoFrameReceived += (endpoint, rtpTimestampMs, frame, format) =>
            {
                int    frameLength  = frame.Length;
                string rtpTimestamp = new DateTime(rtpTimestampMs * 100).ToString("dd.MM.yyyy HH:mm:ss,ffff");
                string timestamp    = "none";
                long   timestampMs  = 0;
                if (ExtractTimestampFromFrame)
                {
                    frameLength -= 8;
                    var span = new Span <byte>(frame, frameLength, 8);
                    span.Reverse();
                    timestampMs = BitConverter.ToInt64(span);
                    if (timestampMs > 0 && timestampMs < 4398046511104)
                    {
                        timestamp = DateTime.UnixEpoch.AddMilliseconds(timestampMs).ToString("dd.MM.yyyy HH:mm:ss,ffff");
                    }
                }
                Console.WriteLine($"On frame received: byte[{frame.Length}], rtpTs: {rtpTimestamp} extractedTs: {timestamp})");
                _listener.OnFrameReceived(this, new Memory <byte>(frame, 0, frameLength), rtpTimestampMs, timestampMs);
            };

            pc.onicecandidate += (iceCandidate) =>
            {
                Console.WriteLine("On ice candidate");
                _listener.OnIceCandidate(this, iceCandidate);
            };

            pc.onconnectionstatechange += (state) =>
            {
                Console.WriteLine($"Peer connection state change to {state}.");

                if (state == RTCPeerConnectionState.failed)
                {
                    pc.Close("ice disconnection");
                }
                _listener.OnConnectionStateChanged(this, state);
            };

            pc.OnSendReport += (media, sr) => Console.WriteLine($"RTCP Send for {media}\n{sr.GetDebugSummary()}");
            pc.oniceconnectionstatechange += (state) => Console.WriteLine($"ICE connection state change to {state}.");

            var sdpOffer = SDP.ParseSDPDescription(args.SdpOffer);
            //sdpOffer.Media.FirstOrDefault()?.
            var videoMedia      = sdpOffer.Media.FirstOrDefault(m => m.Media == SDPMediaTypesEnum.video);
            var h264VideoFormat = videoMedia.MediaFormats.Values.First(m => m.Rtpmap == "H264/90000").ToVideoFormat();
            var videoTrack      = new MediaStreamTrack(h264VideoFormat, MediaStreamStatusEnum.RecvOnly);

            pc.addTrack(videoTrack);

            //var track = new MediaStreamTrack(SDPMediaTypesEnum.video, false, new List<SDPAudioVideoMediaFormat> { new SDPAudioVideoMediaFormat(SDPMediaTypesEnum.video, 102, "H264", 90000) });
            //pc.addTrack(track);

            var setRemoteDescriptionResult = pc.setRemoteDescription(new RTCSessionDescriptionInit
            {
                type = RTCSdpType.offer,
                sdp  = args.SdpOffer
            });
            //MediaStreamTrack videoTrack = new MediaStreamTrack(new VideoFormat(VideoCodecsEnum.H264, 102), MediaStreamStatusEnum.RecvOnly);
            //pc.addTrack(videoTrack);

            var answer = pc.createAnswer();
            var setLocalDescriptionResult = pc.setLocalDescription(answer);

            Console.WriteLine("SDP Offer:\n" + args.SdpOffer);
            Console.WriteLine("SDP Answer 2:\n" + answer.sdp);

            _peerConnection = pc;
            return(answer);
        }
Example #23
0
        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);
            }
        }