DoAsyncWorkParamsReturn(int i, string s)
+ {
+ await Task.Delay(1000);
+ return new SyncReturn
+ {
+ Message = $"Hello from C# ASync! {s}",
+ Value = i
+ };
+ }
+
+ #endregion
+ public class SyncReturn
+ {
+ public string? Message { get; set; }
+ public int Value { get; set; }
+ }
}
\ No newline at end of file
diff --git a/RelayClient/MauiProgram.cs b/RelayClient/MauiProgram.cs
index a5951be..c6d626a 100644
--- a/RelayClient/MauiProgram.cs
+++ b/RelayClient/MauiProgram.cs
@@ -22,7 +22,8 @@ public static class MauiProgram
#if DEBUG
- builder.Logging.AddDebug();
+ builder.Services.AddHybridWebViewDeveloperTools();
+ builder.Logging.AddDebug();
#endif
return builder.Build();
diff --git a/RelayClient/Resources/Raw/test.css b/RelayClient/Resources/Raw/test.css
index b62e775..097b69a 100644
--- a/RelayClient/Resources/Raw/test.css
+++ b/RelayClient/Resources/Raw/test.css
@@ -1,3 +1,23 @@
-#header{
- border: black solid 2px;
+@import url('https://fonts.googleapis.com/css2?family=Syne+Mono&display=swap');
+
+body {
+ font-family: 'Syne Mono', monospace;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-align: center;
+ color: #2c3e50;
+ margin: 80px 10px;
+}
+
+video {
+ width: 40vw;
+ height: 30vw;
+ margin: 2rem;
+ background: #2c3e50;
+}
+
+.videos {
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
\ No newline at end of file
diff --git a/RelayClient/Resources/Raw/test.html b/RelayClient/Resources/Raw/test.html
index 661c7af..a59f831 100644
--- a/RelayClient/Resources/Raw/test.html
+++ b/RelayClient/Resources/Raw/test.html
@@ -1,9 +1,41 @@
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ WebRTC Demo
+
+
+ 1. Start your Webcam
+
+
+ Local Stream
+
+
+
+ Remote Stream
+
+
+
+
+
+
+
+ 2. Create a new Call
+
+
+ 3. Join a Call
+ Answer the call from a different browser window or device
+
+
+
+
+ 4. Hangup
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RelayClient/Resources/Raw/test.js b/RelayClient/Resources/Raw/test.js
index 9d0408f..c005f6b 100644
--- a/RelayClient/Resources/Raw/test.js
+++ b/RelayClient/Resources/Raw/test.js
@@ -1,10 +1,146 @@
-var toggle = true;
+import './test.css';
-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
+import firebase from 'firebase/app';
+import 'firebase/firestore';
+
+const firebaseConfig = {
+ // your config
+};
+
+if (!firebase.apps.length) {
+ firebase.initializeApp(firebaseConfig);
+}
+const firestore = firebase.firestore();
+
+const servers = {
+ iceServers: [
+ {
+ urls: ['stun:stun1.l.google.com:19302', 'stun:stun2.l.google.com:19302'],
+ },
+ ],
+ iceCandidatePoolSize: 10,
+};
+
+// Global State
+const pc = new RTCPeerConnection(servers);
+let localStream = null;
+let remoteStream = null;
+
+// HTML elements
+const webcamButton = document.getElementById('webcamButton');
+const webcamVideo = document.getElementById('webcamVideo');
+const callButton = document.getElementById('callButton');
+const callInput = document.getElementById('callInput');
+const answerButton = document.getElementById('answerButton');
+const remoteVideo = document.getElementById('remoteVideo');
+const hangupButton = document.getElementById('hangupButton');
+
+// 1. Setup media sources
+
+webcamButton.onclick = async () => {
+ localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
+ remoteStream = new MediaStream();
+
+ // Push tracks from local stream to peer connection
+ localStream.getTracks().forEach((track) => {
+ pc.addTrack(track, localStream);
+ });
+
+ // Pull tracks from remote stream, add to video stream
+ pc.ontrack = (event) => {
+ event.streams[0].getTracks().forEach((track) => {
+ remoteStream.addTrack(track);
+ });
+ };
+
+ webcamVideo.srcObject = localStream;
+ remoteVideo.srcObject = remoteStream;
+
+ callButton.disabled = false;
+ answerButton.disabled = false;
+ webcamButton.disabled = true;
+};
+
+// 2. Create an offer
+callButton.onclick = async () => {
+ // Reference Firestore collections for signaling
+ const callDoc = firestore.collection('calls').doc();
+ const offerCandidates = callDoc.collection('offerCandidates');
+ const answerCandidates = callDoc.collection('answerCandidates');
+
+ callInput.value = callDoc.id;
+
+ // Get candidates for caller, save to db
+ pc.onicecandidate = (event) => {
+ event.candidate && offerCandidates.add(event.candidate.toJSON());
+ };
+
+ // Create offer
+ const offerDescription = await pc.createOffer();
+ await pc.setLocalDescription(offerDescription);
+
+ const offer = {
+ sdp: offerDescription.sdp,
+ type: offerDescription.type,
+ };
+
+ await callDoc.set({ offer });
+
+ // Listen for remote answer
+ callDoc.onSnapshot((snapshot) => {
+ const data = snapshot.data();
+ if (!pc.currentRemoteDescription && data?.answer) {
+ const answerDescription = new RTCSessionDescription(data.answer);
+ pc.setRemoteDescription(answerDescription);
+ }
+ });
+
+ // When answered, add candidate to peer connection
+ answerCandidates.onSnapshot((snapshot) => {
+ snapshot.docChanges().forEach((change) => {
+ if (change.type === 'added') {
+ const candidate = new RTCIceCandidate(change.doc.data());
+ pc.addIceCandidate(candidate);
+ }
+ });
+ });
+
+ hangupButton.disabled = false;
+};
+
+// 3. Answer the call with the unique ID
+answerButton.onclick = async () => {
+ const callId = callInput.value;
+ const callDoc = firestore.collection('calls').doc(callId);
+ const answerCandidates = callDoc.collection('answerCandidates');
+ const offerCandidates = callDoc.collection('offerCandidates');
+
+ pc.onicecandidate = (event) => {
+ event.candidate && answerCandidates.add(event.candidate.toJSON());
+ };
+
+ const callData = (await callDoc.get()).data();
+
+ const offerDescription = callData.offer;
+ await pc.setRemoteDescription(new RTCSessionDescription(offerDescription));
+
+ const answerDescription = await pc.createAnswer();
+ await pc.setLocalDescription(answerDescription);
+
+ const answer = {
+ type: answerDescription.type,
+ sdp: answerDescription.sdp,
+ };
+
+ await callDoc.update({ answer });
+
+ offerCandidates.onSnapshot((snapshot) => {
+ snapshot.docChanges().forEach((change) => {
+ console.log(change);
+ if (change.type === 'added') {
+ let data = change.doc.data();
+ pc.addIceCandidate(new RTCIceCandidate(data));
+ }
+ });
+ });
+};
\ No newline at end of file
diff --git a/RelayClient/Resources/Raw/wwwroot/index.css b/RelayClient/Resources/Raw/wwwroot/index.css
new file mode 100644
index 0000000..e69de29
diff --git a/RelayClient/Resources/Raw/wwwroot/index.html b/RelayClient/Resources/Raw/wwwroot/index.html
new file mode 100644
index 0000000..795408d
--- /dev/null
+++ b/RelayClient/Resources/Raw/wwwroot/index.html
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hybrid sample!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Log:
+
+
+
+
\ No newline at end of file