updated and connected webview to C#
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -1,9 +1,41 @@
|
||||
<html lang="enus">
|
||||
<head>
|
||||
<link rel="stylesheet" href="test.css">
|
||||
<script src="test.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<Button id="header" onclick="onClicked()">Hello World!</Button>
|
||||
</body>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>WebRTC Demo</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>1. Start your Webcam</h2>
|
||||
<div class="videos">
|
||||
<span>
|
||||
<h3>Local Stream</h3>
|
||||
<video id="webcamVideo" autoplay playsinline></video>
|
||||
</span>
|
||||
<span>
|
||||
<h3>Remote Stream</h3>
|
||||
<video id="remoteVideo" autoplay playsinline></video>
|
||||
</span>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<button id="webcamButton">Start webcam</button>
|
||||
<h2>2. Create a new Call</h2>
|
||||
<button id="callButton" disabled>Create Call (offer)</button>
|
||||
|
||||
<h2>3. Join a Call</h2>
|
||||
<p>Answer the call from a different browser window or device</p>
|
||||
|
||||
<input id="callInput" />
|
||||
<button id="answerButton" disabled>Answer</button>
|
||||
|
||||
<h2>4. Hangup</h2>
|
||||
|
||||
<button id="hangupButton" disabled>Hangup</button>
|
||||
|
||||
<script type="module" src="test.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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;
|
||||
}
|
||||
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));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
0
RelayClient/Resources/Raw/wwwroot/index.css
Normal file
0
RelayClient/Resources/Raw/wwwroot/index.css
Normal file
124
RelayClient/Resources/Raw/wwwroot/index.html
Normal file
124
RelayClient/Resources/Raw/wwwroot/index.html
Normal file
@@ -0,0 +1,124 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title></title>
|
||||
<link rel="icon" href="data:,">
|
||||
<link rel="stylesheet" href="styles/app.css">
|
||||
<link rel="stylesheet" href="index.css">
|
||||
<script src="_framework/hybridwebview.js"></script>
|
||||
<script>
|
||||
function LogMessage(msg) {
|
||||
var messageLog = document.getElementById("messageLog");
|
||||
messageLog.value += '\r\n' + msg;
|
||||
}
|
||||
|
||||
window.addEventListener(
|
||||
"HybridWebViewMessageReceived",
|
||||
function (e) {
|
||||
LogMessage("Raw message: " + e.detail.message);
|
||||
});
|
||||
|
||||
function AddNumbers(a, b) {
|
||||
var result = {
|
||||
"result": a + b,
|
||||
"operationName": "Addition"
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
|
||||
async function EvaluateMeWithParamsAndAsyncReturn(s1, s2) {
|
||||
const response = await fetch("/asyncdata.txt");
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error: ${response.status}`);
|
||||
}
|
||||
var jsonData = await response.json();
|
||||
|
||||
jsonData[s1] = s2;
|
||||
|
||||
const msg = 'JSON data is available: ' + JSON.stringify(jsonData);
|
||||
window.HybridWebView.SendRawMessage(msg)
|
||||
|
||||
return jsonData;
|
||||
}
|
||||
|
||||
async function InvokeDoSyncWork() {
|
||||
LogMessage("Invoking DoSyncWork");
|
||||
await window.HybridWebView.InvokeDotNet('DoSyncWork');
|
||||
LogMessage("Invoked DoSyncWork");
|
||||
}
|
||||
|
||||
async function InvokeDoSyncWorkParams() {
|
||||
LogMessage("Invoking DoSyncWorkParams");
|
||||
await window.HybridWebView.InvokeDotNet('DoSyncWorkParams', [123, 'hello']);
|
||||
LogMessage("Invoked DoSyncWorkParams");
|
||||
}
|
||||
|
||||
async function InvokeDoSyncWorkReturn() {
|
||||
LogMessage("Invoking DoSyncWorkReturn");
|
||||
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkReturn');
|
||||
LogMessage("Invoked DoSyncWorkReturn, return value: " + retValue);
|
||||
}
|
||||
|
||||
async function InvokeDoSyncWorkParamsReturn() {
|
||||
LogMessage("Invoking DoSyncWorkParamsReturn");
|
||||
const retValue = await window.HybridWebView.InvokeDotNet('DoSyncWorkParamsReturn', [123, 'hello']);
|
||||
LogMessage("Invoked DoSyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value);
|
||||
}
|
||||
|
||||
async function InvokeDoAsyncWork() {
|
||||
LogMessage("Invoking DoAsyncWork");
|
||||
await window.HybridWebView.InvokeDotNet('DoAsyncWork');
|
||||
LogMessage("Invoked DoAsyncWork");
|
||||
}
|
||||
|
||||
async function InvokeDoAsyncWorkParams() {
|
||||
LogMessage("Invoking DoAsyncWorkParams");
|
||||
await window.HybridWebView.InvokeDotNet('DoAsyncWorkParams', [123, 'hello']);
|
||||
LogMessage("Invoked DoAsyncWorkParams");
|
||||
}
|
||||
|
||||
async function InvokeDoAsyncWorkReturn() {
|
||||
LogMessage("Invoking DoAsyncWorkReturn");
|
||||
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkReturn');
|
||||
LogMessage("Invoked DoAsyncWorkReturn, return value: " + retValue);
|
||||
}
|
||||
|
||||
async function InvokeDoAsyncWorkParamsReturn() {
|
||||
LogMessage("Invoking DoAsyncWorkParamsReturn");
|
||||
const retValue = await window.HybridWebView.InvokeDotNet('DoAsyncWorkParamsReturn', [123, 'hello']);
|
||||
LogMessage("Invoked DoAsyncWorkParamsReturn, return value: message=" + retValue.Message + ", value=" + retValue.Value);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Hybrid sample!
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="window.HybridWebView.SendRawMessage('Message from JS! ' + (count++))">Send message to C#</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="InvokeDoSyncWork()">Call C# sync method (no params)</button>
|
||||
<button onclick="InvokeDoSyncWorkParams()">Call C# sync method (params)</button>
|
||||
<button onclick="InvokeDoSyncWorkReturn()">Call C# method (no params) and get simple return value</button>
|
||||
<button onclick="InvokeDoSyncWorkParamsReturn()">Call C# method (params) and get complex return value</button>
|
||||
</div>
|
||||
<div>
|
||||
<button onclick="InvokeDoAsyncWork()">Call C# async method (no params)</button>
|
||||
<button onclick="InvokeDoAsyncWorkParams()">Call C# async method (params)</button>
|
||||
<button onclick="InvokeDoAsyncWorkReturn()">Call C# async method (no params) and get simple return value</button>
|
||||
<button onclick="InvokeDoAsyncWorkParamsReturn()">Call C# async method (params) and get complex return value</button>
|
||||
</div>
|
||||
<div>
|
||||
Log: <textarea readonly id="messageLog" style="width: 80%; height: 10em;"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
Consider checking out this PDF: <a href="docs/sample.pdf">sample.pdf</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user