diff --git a/RelayClient/MainPage.xaml b/RelayClient/MainPage.xaml index ee2be68..86a54b9 100644 --- a/RelayClient/MainPage.xaml +++ b/RelayClient/MainPage.xaml @@ -53,6 +53,14 @@ Spacing="8" /> + + + + + + + + \ No newline at end of file diff --git a/RelayClient/Resources/Raw/test.js b/RelayClient/Resources/Raw/test.js new file mode 100644 index 0000000..9d0408f --- /dev/null +++ b/RelayClient/Resources/Raw/test.js @@ -0,0 +1,10 @@ +var toggle = true; + +function onClicked() +{ + if (toggle) + document.getElementById("header").style.color = "green"; + else + document.getElementById("header").style.color = "red"; + toggle = !toggle; +} \ No newline at end of file diff --git a/RelayClient/WebRTC.cs b/RelayClient/WebRTC.cs new file mode 100644 index 0000000..20b2ab5 --- /dev/null +++ b/RelayClient/WebRTC.cs @@ -0,0 +1,170 @@ +using System; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Microsoft.Maui.Dispatching; +using Microsoft.AspNetCore.SignalR.Client; + +namespace RelayClient; + +public static class NativeWebRtc +{ + [DllImport("webrtc_wrapper.dll")] + public static extern IntPtr CreatePeerConnection(); + + [DllImport("webrtc_wrapper.dll")] + public static extern string CreateOffer(IntPtr pc); + + [DllImport("webrtc_wrapper.dll")] + public static extern string CreateAnswer(IntPtr pc); + + [DllImport("webrtc_wrapper.dll")] + public static extern void SetLocalDescription(IntPtr pc, string type, string sdp); + + [DllImport("webrtc_wrapper.dll")] + public static extern void SetRemoteDescription(IntPtr pc, string type, string sdp); + + [DllImport("webrtc_wrapper.dll")] + public static extern void AddIceCandidate(IntPtr pc, string candidate); +} + +public enum RTCSdpType { Offer, Answer } + +public class RTCSessionDescription +{ + public RTCSdpType Type { get; set; } + public string Sdp { get; set; } +} + +public class RTCIceCandidate +{ + public string Candidate { get; set; } +} + +public class PeerConnection +{ + private readonly IntPtr _nativeHandle; + public string RemoteId { get; set; } + + public PeerConnection() + { + _nativeHandle = NativeWebRtc.CreatePeerConnection(); + } + + public Task CreateOffer(Action onOfferCreated) + { + var sdp = NativeWebRtc.CreateOffer(_nativeHandle); + onOfferCreated?.Invoke(new RTCSessionDescription { Type = RTCSdpType.Offer, Sdp = sdp }); + return Task.CompletedTask; + } + + public Task CreateAnswer(Action onAnswerCreated) + { + var sdp = NativeWebRtc.CreateAnswer(_nativeHandle); + onAnswerCreated?.Invoke(new RTCSessionDescription { Type = RTCSdpType.Answer, Sdp = sdp }); + return Task.CompletedTask; + } + + public Task SetLocalDescription(RTCSessionDescription desc) + { + NativeWebRtc.SetLocalDescription(_nativeHandle, desc.Type.ToString(), desc.Sdp); + return Task.CompletedTask; + } + + public Task SetRemoteDescription(RTCSessionDescription desc) + { + NativeWebRtc.SetRemoteDescription(_nativeHandle, desc.Type.ToString(), desc.Sdp); + return Task.CompletedTask; + } + + public Task AddIceCandidate(RTCIceCandidate candidate) + { + NativeWebRtc.AddIceCandidate(_nativeHandle, candidate.Candidate); + return Task.CompletedTask; + } +} +public class WebRtcClient +{ + private readonly PeerConnection _peerConnection = new(); + private readonly HubConnection _signal; + private string _myId; + + public WebRtcClient(string serverUrl) + { + _signal = new HubConnectionBuilder() + .WithUrl($"{serverUrl}/webrtc") + .WithAutomaticReconnect() + .Build(); + + _signal.On("ReceiveOffer", (fromId, sdp) => + { + MainThread.BeginInvokeOnMainThread(async () => + { + await HandleOffer(fromId, sdp); + }); + }); + + _signal.On("ReceiveAnswer", (fromId, sdp) => + { + MainThread.BeginInvokeOnMainThread(async () => + { + await HandleAnswer(sdp); + }); + }); + + _signal.On("ReceiveIceCandidate", (fromId, candidate) => + { + MainThread.BeginInvokeOnMainThread(async () => + { + await HandleIceCandidate(candidate); + }); + }); + } + + public async Task ConnectAsync() + { + await _signal.StartAsync(); + _myId = _signal.ConnectionId; + } + + public async Task CallAsync(string targetId) + { + _peerConnection.RemoteId = targetId; + await _peerConnection.CreateOffer(async offer => + { + await _peerConnection.SetLocalDescription(offer); + await _signal.InvokeAsync("SendOffer", targetId, offer.Sdp); + }); + } + + public async Task HandleOffer(string fromId, string sdp) + { + _peerConnection.RemoteId = fromId; + var remoteDesc = new RTCSessionDescription { Type = RTCSdpType.Offer, Sdp = sdp }; + await _peerConnection.SetRemoteDescription(remoteDesc); + + await _peerConnection.CreateAnswer(async answer => + { + await _peerConnection.SetLocalDescription(answer); + await _signal.InvokeAsync("SendAnswer", fromId, answer.Sdp); + }); + } + + public async Task HandleAnswer(string sdp) + { + var remoteDesc = new RTCSessionDescription { Type = RTCSdpType.Answer, Sdp = sdp }; + await _peerConnection.SetRemoteDescription(remoteDesc); + } + + public async Task HandleIceCandidate(string candidate) + { + await _peerConnection.AddIceCandidate(new RTCIceCandidate { Candidate = candidate }); + } + + public async Task SendIceCandidate(string candidate) + { + if (!string.IsNullOrEmpty(_peerConnection.RemoteId)) + { + await _signal.InvokeAsync("SendIceCandidate", _peerConnection.RemoteId, candidate); + } + } +} \ No newline at end of file diff --git a/RelayServer/Program.cs b/RelayServer/Program.cs index cbdf928..008e16e 100644 --- a/RelayServer/Program.cs +++ b/RelayServer/Program.cs @@ -1,6 +1,8 @@ using System.Text.Json; using RelayServer.Services; using WebSocketSharp.Server; +using Microsoft.AspNetCore.SignalR; +using Microsoft.AspNetCore.Builder; using RelayServer.Models; var surrealService = new SurrealService(); @@ -12,6 +14,14 @@ await using var db = await surrealService.ConnectAsync(); ChatTest.ClientKeyService = new ClientKeyService(db); ChatTest.Db = db; +var builder = WebApplication.CreateBuilder(args); +builder.Services.AddSignalR(); + +var app = builder.Build(); +app.MapGet("/", () => "Server Running!"); +app.MapHub("/webrtc"); +app.Run(); + var wssv = new WebSocketServer("ws://localhost:1337"); wssv.AddWebSocketService("/"); wssv.Start(); @@ -146,4 +156,25 @@ static string GetRecordId(object? id) var table = root.GetProperty("Table").GetString() ?? string.Empty; return $"{table}:{recordId}"; +} + +public class WebRtcHub : Hub +{ + public async Task SendOffer(string targetConnectionId, string sdp) + { + await Clients.Client(targetConnectionId) + .SendAsync("ReceiveOffer", Context.ConnectionId, sdp); + } + + public async Task SendAnswer(string targetConnectionId, string sdp) + { + await Clients.Client(targetConnectionId) + .SendAsync("ReceiveAnswer", Context.ConnectionId, sdp); + } + + public async Task SendIceCandidate(string targetConnectionId, string candidate) + { + await Clients.Client(targetConnectionId) + .SendAsync("ReceiveIceCandidate", Context.ConnectionId, candidate); + } } \ No newline at end of file diff --git a/RelayServer/RelayServer.csproj b/RelayServer/RelayServer.csproj index 78b8450..1a2c504 100644 --- a/RelayServer/RelayServer.csproj +++ b/RelayServer/RelayServer.csproj @@ -1,4 +1,4 @@ - + Exe @@ -8,6 +8,7 @@ +