/// <summary> /// Hands the socket handle to the DTLS context and waits for the handshake to complete. /// </summary> /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param> /// <returns>True if the handshake completed successfully or false otherwise.</returns> private static async Task <bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsHandshake dtls) { logger.LogDebug("DoDtlsHandshake started."); int res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle); logger.LogDebug("DtlsContext initialisation result=" + res); if (dtls.IsHandshakeComplete()) { logger.LogDebug("DTLS negotiation complete."); var srtpSendContext = new Srtp(dtls, false); var srtpReceiveContext = new Srtp(dtls, true); peerConnection.SetSecurityContext( srtpSendContext.ProtectRTP, srtpReceiveContext.UnprotectRTP, srtpSendContext.ProtectRTCP, srtpReceiveContext.UnprotectRTCP); await peerConnection.Start(); if (!_isSampling) { _ = Task.Run(StartMedia); } return(true); } else { return(false); } }
/// <summary> /// Hands the socket handle to the DTLS context and waits for the handshake to complete. /// </summary> /// <param name="peerConnection">The WebRTC session to perform the DTLS handshake on.</param> /// <returns>True if the handshake completes successfully. False if not.</returns> private static async Task <bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsHandshake dtls) { Console.WriteLine("DoDtlsHandshake started."); int res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle); Console.WriteLine("DtlsContext initialisation result=" + res); if (dtls.IsHandshakeComplete()) { Console.WriteLine("DTLS negotiation complete."); // TODO fix race condition!!! First RTP packet is not getting decrypted. var srtpSendContext = new Srtp(dtls, false); var srtpReceiveContext = new Srtp(dtls, true); peerConnection.SetSecurityContext( srtpSendContext.ProtectRTP, srtpReceiveContext.UnprotectRTP, srtpSendContext.ProtectRTCP, srtpReceiveContext.UnprotectRTCP); await peerConnection.Start(); return(true); } else { return(false); } }
/// <summary> /// Hands the socket handle to the DTLS context and waits for the handshake to complete. /// </summary> /// <param name="webRtcSession">The WebRTC session to perform the DTLS handshake on.</param> private static async Task <bool> DoDtlsHandshake(RTCPeerConnection peerConnection, DtlsHandshake dtls) { logger.LogDebug("DoDtlsHandshake started."); if (!File.Exists(DTLS_CERTIFICATE_PATH)) { throw new ApplicationException($"The DTLS certificate file could not be found at {DTLS_CERTIFICATE_PATH}."); } else if (!File.Exists(DTLS_KEY_PATH)) { throw new ApplicationException($"The DTLS key file could not be found at {DTLS_KEY_PATH}."); } byte[] clientFingerprint = null; int res = dtls.DoHandshakeAsServer((ulong)peerConnection.GetRtpChannel(SDPMediaTypesEnum.audio).RtpSocket.Handle, ref clientFingerprint); logger.LogDebug("DtlsContext initialisation result=" + res); if (dtls.IsHandshakeComplete()) { logger.LogDebug("DTLS negotiation complete."); // TODO: Check client fingerprint matches one supplied in the SDP. var srtpSendContext = new Srtp(dtls, false); var srtpReceiveContext = new Srtp(dtls, true); peerConnection.SetSecurityContext( srtpSendContext.ProtectRTP, srtpReceiveContext.UnprotectRTP, srtpSendContext.ProtectRTCP, srtpReceiveContext.UnprotectRTCP); await peerConnection.Start(); //dtls.Shutdown(); if (_sendTestPatternTimer == null) { _sendTestPatternTimer = new Timer(SendTestPattern, null, 0, TEST_PATTERN_SPACING_MILLISECONDS); } return(true); } else { return(false); } }
private void InitConnection() { Logger.LogInformation("Initializing connection..."); var servers = IceServers.DistinctBy(x => x.Url).ToList(); foreach (var ice in servers) { Logger.LogInformation($"Adding ice server: '{ice}'..."); } _connection = new RTCPeerConnection(new RTCConfiguration() { iceServers = servers.Select(x => new RTCIceServer() { urls = x.Url, username = x.UserName, credential = x.Credentials, }).ToList() }); _connection.ondatachannel += OnDataChannelInitialized; _connection.onicecandidate += OnIceCandidateAvailable; _connection.onconnectionstatechange += OnConnectionStateChanged; Logger.LogInformation($"Creating data channel {ChannelName}..."); _dataChannel = _connection.createDataChannel(ChannelName).GetAwaiter().GetResult(); _dataChannel.onopen += OnDataChannelOpened; _dataChannel.onclose += OnDataChannelClosed; Logger.LogInformation("Starting connection..."); _connection.Start().GetAwaiter().GetResult(); _connectionInitialized = true; }
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); }
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); }