From dff05dd596ba2e126f9293d9b18b3b2cecf8542e Mon Sep 17 00:00:00 2001 From: Cody Larkin Date: Tue, 7 Apr 2026 17:30:34 -0400 Subject: [PATCH] ice candidates gathering working, needs to get public ips --- RelayClient/MainPage.xaml.cs | 23 +++++- RelayClient/Resources/Raw/wwwroot/index.js | 96 ++++++++++++---------- 2 files changed, 72 insertions(+), 47 deletions(-) diff --git a/RelayClient/MainPage.xaml.cs b/RelayClient/MainPage.xaml.cs index 228edb4..439c259 100644 --- a/RelayClient/MainPage.xaml.cs +++ b/RelayClient/MainPage.xaml.cs @@ -417,7 +417,7 @@ public partial class MainPage : ContentPage bool active = await ServerAPI.GetIsChannelActiveAsync(_currentChannelId); - SafeSendRawToWebView($"Rtc Channel {_currentChannelName} | {_currentChannelId} is active: {active}"); + // SafeSendRawToWebView($"Rtc Channel {_currentChannelName} | {_currentChannelId} is active: {active}"); return active; } @@ -457,7 +457,7 @@ public partial class MainPage : ContentPage public async void WriteRtcAnswer(string json) { - SafeSendRawToWebView("WriteRtcAnswer entered with: " + json); + // SafeSendRawToWebView("WriteRtcAnswer entered with: " + json); try { @@ -477,6 +477,23 @@ public partial class MainPage : ContentPage } } + public async void WriteIceCandidate(string json) + { + IceCandidate? candidate = JsonSerializer.Deserialize(json); + await ServerAPI.PostIceCandidateAsync(candidate); + } + + public async void IceCandidateCallback(string json) + { + try + { + await hybridWebView.InvokeJavaScriptAsync("IceCandidateAdded"); + } + catch (Exception ex) + { + SafeSendRawToWebView("WriteIceCandidate failed: " + ex.Message); + } + } public async Task AnswerCallback(RtcDescription answer) { answer.sdp = answer.sdp.Replace("\r\n", "(rn)"); @@ -486,7 +503,7 @@ public partial class MainPage : ContentPage } catch (Exception ex) { - SafeSendRawToWebView("WriteRtcAnswer failed: " + ex.Message); + SafeSendRawToWebView("AnswerCallback failed: " + ex.Message); } } diff --git a/RelayClient/Resources/Raw/wwwroot/index.js b/RelayClient/Resources/Raw/wwwroot/index.js index 466634c..3d6d560 100644 --- a/RelayClient/Resources/Raw/wwwroot/index.js +++ b/RelayClient/Resources/Raw/wwwroot/index.js @@ -20,7 +20,6 @@ window.setUsername = function(name) { currentUsername = name; LogMessage("Username set to: " + currentUsername); }; - window.setChannelId = function(channelId) { currentChannelId = channelId; LogMessage("Channel set to: " + currentChannelId); @@ -166,10 +165,10 @@ async function ensureLocalMedia() { async function joinChannelCall() { LogMessage("Current username: " + currentUsername); LogMessage("Current channel: " + currentChannelId); - LogMessage("Joining RTCChannel"); + // LogMessage("Joining RTCChannel"); let active = await window.HybridWebView.InvokeDotNet("JoinRtcChannel"); await channelCallJoin(active); - LogMessage("Joined RTCChannel"); + // LogMessage("Joined RTCChannel"); // return; // try { // if (!currentChannelId) { @@ -200,28 +199,57 @@ async function ensurePeerConnection2() if (peerConnection) return; peerConnection = new RTCPeerConnection(configuration); - peerConnection.addEventListener('icegatheringstatechange', () => { - console.log( - `ICE gathering state changed: ${peerConnection.iceGatheringState}`); - }); - - peerConnection.addEventListener('connectionstatechange', () => { + peerConnection.onicegatheringstatechange = () => { + console.log(`ICE gathering state changed: ${peerConnection.iceGatheringState}`); + }; + peerConnection.onconnectionstatechange = () => { console.log(`Connection state change: ${peerConnection.connectionState}`); - }); - - peerConnection.addEventListener('signalingstatechange', () => { + }; + peerConnection.onsignalingstatechange = () => { console.log(`Signaling state change: ${peerConnection.signalingState}`); - }); + }; + peerConnection.oniceconnectionstatechange = () => { + console.log(`ICE connection state change: ${peerConnection.iceConnectionState}`); + }; + + peerConnection.onicecandidate = (event) => { + console.log(`Ice Candidate: ${JSON.stringify(event.candidate)}`); + LogMessage(`Ice Candidate: ${JSON.stringify(event.candidate)}`); + }; + + peerConnection.ontrack = (event) => { + LogMessage("Remote track received"); - peerConnection.addEventListener('iceconnectionstatechange ', () => { - console.log( - `ICE connection state change: ${peerConnection.iceConnectionState}`); - }); + const remoteVideo = document.getElementById("remoteVideo"); + const remoteVideoStatus = document.getElementById("remoteVideoStatus"); + const remoteMediaStatus = document.getElementById("remoteMediaStatus"); + + const stream = event.streams[0]; + const hasVideo = stream.getVideoTracks().length > 0; + const hasAudio = stream.getAudioTracks().length > 0; + + if (hasVideo) { + remoteVideo.srcObject = stream; + } else { + remoteVideo.srcObject = null; + } + + if (remoteVideoStatus) { + remoteVideoStatus.textContent = hasVideo + ? "Remote video: active" + : "Remote video: unavailable"; + } + + if (remoteMediaStatus) { + remoteMediaStatus.textContent = `Remote media: audio=${hasAudio} video=${hasVideo}`; + } + }; } async function channelCallJoin(activeCall) { - LogMessage("Active call: " + activeCall); + // LogMessage("Active call: " + activeCall); await ensurePeerConnection2(); + await ensureLocalMedia(); if (activeCall) { @@ -231,8 +259,8 @@ async function channelCallJoin(activeCall) const answer = await peerConnection.createAnswer(); await peerConnection.setLocalDescription(answer); - LogMessage("Joining call with media answer: " + JSON.stringify(answer)); - LogMessage("Calling C# WriteRtcAnswer with: " + JSON.stringify(answer)); + // LogMessage("Joining call with media answer: " + JSON.stringify(answer)); + // LogMessage("Calling C# WriteRtcAnswer with: " + JSON.stringify(answer)); await window.HybridWebView.InvokeDotNet("WriteRtcAnswer", [JSON.stringify(answer)]); LogMessage("C# WriteRtcAnswer invoked"); //TODO: Update offer in SurrealDB to include answer @@ -244,33 +272,14 @@ async function channelCallJoin(activeCall) await window.HybridWebView.InvokeDotNet("WriteRtcOffer", [JSON.stringify(offer)]); LogMessage(`Joining call with media offer: ${JSON.stringify(offer)}`); - - localStream.getTracks().forEach(track => { - peerConnection.addTrack(track, localStream); - }); - - peerConnection.addEventListener('track', event => { - LogMessage("Received track: " + event.streams[0]); - event.streams[0].getTracks().forEach(track => { - LogMessage(`Add a track to the remoteStream: ${track}`); - remoteStream.addTrack(track); - }); - }); } } - -async function CSharpCallTest(value) -{ - LogMessage("Called from C#: " + value); - // let test = JSON.parse(value); - // LogMessage("JSON : " + JSON.stringify(test)); -} async function AnswerCallbackJS(answer) { answer.sdp = answer.sdp.replaceAll("(rn)", "\r\n"); - LogMessage("Answer: " + JSON.stringify(answer)); + // LogMessage("Answer: " + JSON.stringify(answer)); - LogMessage("RemoteDescription: " + peerConnection.currentRemoteDescription); + // LogMessage("RemoteDescription: " + peerConnection.currentRemoteDescription); if (!peerConnection.currentRemoteDescription && answer) { @@ -279,10 +288,9 @@ async function AnswerCallbackJS(answer) await peerConnection.setRemoteDescription(desc); } } - -async function CollectIceCandidates() +async function IceCandidateAdded(candidate) { - //TODO: collect ICE candidates + await peerConnection.addIceCandidate(candidate); } async function handleRtcSignal(rawJson) { try {