mirror of
https://github.com/AlexandreRouma/wiscast.git
synced 2026-04-18 23:52:42 +00:00
progress
This commit is contained in:
@@ -1,39 +1,368 @@
|
||||
// Streaming objects
|
||||
let sock = null;
|
||||
let conn = null;
|
||||
// User API class
|
||||
class WisCastDisplayAPIClient {
|
||||
// Socket to the API endpoint
|
||||
#sock;
|
||||
|
||||
// GUI Objects
|
||||
let idleScreen = document.querySelector('#idleScreen');
|
||||
let dispIDSpan = document.querySelector('#dispID');
|
||||
let pinScreen = document.querySelector('#pinScreen');
|
||||
let pinSpan = document.querySelector('#pin');
|
||||
let playback = document.querySelector('#playback');
|
||||
let credits = document.querySelector('#credits');
|
||||
// Endpoint URL
|
||||
#endpoint;
|
||||
|
||||
// Connect to the server using WebSockets
|
||||
console.log('Connecting to websocket...')
|
||||
sock = new WebSocket(`ws://${location.host}/sig`);
|
||||
sock.addEventListener('open', async (event) => {
|
||||
console.log('Connected to websocket')
|
||||
// Display ID
|
||||
#dispID;
|
||||
|
||||
// // DEBUGGING ONLY
|
||||
// await sock.send(JSON.stringify({
|
||||
// type: 'init',
|
||||
// pin: dispPINTb.value
|
||||
// }))
|
||||
// Initial OTP
|
||||
#initOTP;
|
||||
|
||||
// Called when a config message is received
|
||||
#onconfig = (config) => {};
|
||||
|
||||
/**
|
||||
* Handler called when streaming should be started.
|
||||
*/
|
||||
onstream = () => {};
|
||||
|
||||
/**
|
||||
* Handler called when a WebRTC offer is received.
|
||||
* @param {RTCSessionDescriptionInit} offer The received WebRTC offer.
|
||||
*/
|
||||
onwebrtcoffer = (offer) => { return null; };
|
||||
|
||||
/**
|
||||
* Handler called when an ICE candidate is received
|
||||
* @param {RTCIceCandidateInit} candidate The received ICE candidate.
|
||||
*/
|
||||
onicecandidate = (candidate) => {};
|
||||
|
||||
/**
|
||||
* Handler called when the connection to the user should be reset.
|
||||
*/
|
||||
onreset = () => {};
|
||||
|
||||
/**
|
||||
* Create a User API client instance.
|
||||
* @param {String} endpoint URL of the API endpoint.
|
||||
*/
|
||||
constructor(endpoint) {
|
||||
// Save the endpoint
|
||||
this.#endpoint = endpoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the API server.
|
||||
* @param displayID ID of the display to give to the server.
|
||||
* @param initialOTP Initial OTP to give the server.
|
||||
* @returns {Object} Configuration to use for the session.
|
||||
*/
|
||||
async connect(displayID, initialOTP) {
|
||||
// Save the parameters
|
||||
this.#dispID = displayID;
|
||||
this.#initOTP = initialOTP;
|
||||
|
||||
// Do the rest asynchronously
|
||||
return new Promise(async (res) => {
|
||||
// Register the handler for config messages
|
||||
this.#onconfig = (config) => { res(config); };
|
||||
|
||||
// Connect to the WebSocket endpoint
|
||||
console.log('Connecting to the API...');
|
||||
this.#sock = new WebSocket(this.#endpoint);
|
||||
|
||||
// Handle connection
|
||||
this.#sock.addEventListener('open', async (event) => { await this.#connectHandler(event); });
|
||||
|
||||
// Handle messages
|
||||
this.#sock.addEventListener('message', async (event) => { await this.#messageHandler(event); });
|
||||
|
||||
// Handle disconnection
|
||||
this.#sock.addEventListener('close', async (event) => { await this.#disconnectHandler(event); });
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a new OTP.
|
||||
* @param {String} otp New OTP.
|
||||
*/
|
||||
async setOTP(otp) {
|
||||
// Send the connection command
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'otp',
|
||||
otp: otp
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a WebRTC answer to the user.
|
||||
* @param {RTCSessionDescriptionInit} answer ICE candidate to send to the display.
|
||||
*/
|
||||
async sendWebRTCAnswer(answer) {
|
||||
// Send the connection command
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'webrtc-answer',
|
||||
answer: answer
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an ICE candidate to the display. Must already be connected.
|
||||
* @param {RTCIceCandidateInit} candidate ICE candidate to send to the display.
|
||||
*/
|
||||
async sendICECandidate(candidate) {
|
||||
// Send the connection command
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'ice-candidate',
|
||||
candidate: candidate
|
||||
}))
|
||||
}
|
||||
|
||||
// Disconnect from the display
|
||||
async disconnectDisplay() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
async #connectHandler(event) {
|
||||
console.log('Connected!');
|
||||
|
||||
// Send initialization message
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'init',
|
||||
clientType: 'display',
|
||||
dispID: this.#dispID,
|
||||
otp: this.#initOTP
|
||||
}))
|
||||
}
|
||||
|
||||
async #messageHandler(event) {
|
||||
// Parse the message
|
||||
const msg = JSON.parse(event.data);
|
||||
|
||||
// Handle the message depending on its type
|
||||
switch (msg.type) {
|
||||
case 'config':
|
||||
console.log(msg)
|
||||
// Call the config handler
|
||||
this.#onconfig(msg.config);
|
||||
break;
|
||||
|
||||
case 'stream':
|
||||
this.onstream();
|
||||
break;
|
||||
|
||||
case 'webrtc-offer':
|
||||
// Call the offer handler to get the answer
|
||||
answer = await this.onwebrtcoffer(msg.offer);
|
||||
|
||||
// Send the answer back to the server
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'webrtc-answer',
|
||||
answer: answer
|
||||
}))
|
||||
break;
|
||||
|
||||
case 'ice-candidate':
|
||||
// Call the answer handler
|
||||
this.onicecandidate(msg.candidate);
|
||||
break;
|
||||
|
||||
case 'reset':
|
||||
// Call the reset handler
|
||||
this.onreset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async #disconnectHandler(event) {
|
||||
console.log('Disconnected :/');
|
||||
}
|
||||
}
|
||||
|
||||
async function initWebRTC(client, config) {
|
||||
// Create the WebRTC connection
|
||||
let conn = new RTCPeerConnection({'iceServers': [{'urls': config.iceServers[0]}]});
|
||||
|
||||
// Handle new ice candidates
|
||||
conn.addEventListener('icecandidate', async (event) => {
|
||||
// If there is a new candidate, send it to the peer through websockets
|
||||
if (event.candidate) {
|
||||
await sock.send(JSON.stringify({
|
||||
type: 'ice-candidate',
|
||||
candidate: event.candidate
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
// Handle connection and disconnection of peer
|
||||
conn.addEventListener('connectionstatechange', (event) => {
|
||||
switch (conn.connectionState) {
|
||||
case 'connected':
|
||||
// Switch to playback mode
|
||||
credits.hidden = true;
|
||||
playback.hidden = false;
|
||||
break;
|
||||
|
||||
case 'disconnected':
|
||||
// Reset the display
|
||||
reset();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// Send remote stream to the playback widget
|
||||
conn.addEventListener('track', (event) => {
|
||||
const [remoteStream] = event.streams;
|
||||
playback.srcObject = remoteStream;
|
||||
});
|
||||
}
|
||||
|
||||
function genOTP() {
|
||||
let otp = '';
|
||||
for (let i = 0; i < 6; i++) {
|
||||
otp += Math.floor(Math.random() * 10);
|
||||
}
|
||||
return otp;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Get or generate a display ID
|
||||
let params = new URLSearchParams(document.location.search);
|
||||
let dispID = params.get("dispID");
|
||||
if (dispID === null) {
|
||||
// Generate a random name (TODO)
|
||||
dispID = self.crypto.randomUUID().substring(0, 8).toUpperCase();
|
||||
}
|
||||
|
||||
// Generate the initial OTP
|
||||
let initOTP = genOTP();
|
||||
|
||||
// GUI Objects
|
||||
const idleScreen = document.getElementById('idleScreen');
|
||||
const dispIDSpan = document.getElementById('dispID');
|
||||
const otpSpan = document.getElementById('otp');
|
||||
const playback = document.getElementById('playback');
|
||||
const credits = document.getElementById('credits');
|
||||
const lifespan = document.getElementById('lifespan');
|
||||
|
||||
// Set the ID and OTP spans
|
||||
dispIDSpan.textContent = dispID;
|
||||
otpSpan.textContent = initOTP;
|
||||
|
||||
// Global state
|
||||
let conn = null;
|
||||
|
||||
// Create the API client
|
||||
const client = new WisCastDisplayAPIClient(`wss://${location.host}/sig`);
|
||||
|
||||
// Connect to the server
|
||||
const config = await client.connect(dispID, initOTP);
|
||||
|
||||
// Define the progress bar animation
|
||||
const animKeyframes = [
|
||||
{ width: '100%' },
|
||||
{ width: '0%' },
|
||||
];
|
||||
const animTiming = {
|
||||
duration: config.otpLifespan,
|
||||
iterations: 1
|
||||
};
|
||||
|
||||
// Start the animation
|
||||
lifespan.animate(animKeyframes, animTiming);
|
||||
|
||||
// Generate a new OTP every given interval
|
||||
console.log(lifespan)
|
||||
setInterval(() => {
|
||||
// Generate a new OTP
|
||||
const otp = genOTP();
|
||||
|
||||
// Send it to the server
|
||||
client.setOTP(otp);
|
||||
|
||||
// Update it in the GUI
|
||||
otpSpan.textContent = otp;
|
||||
|
||||
// Restart the animation
|
||||
lifespan.animate(animKeyframes, animTiming);
|
||||
|
||||
}, config.otpLifespan);
|
||||
|
||||
// Define the WebRTC initialization function
|
||||
const initWebRTC = () => {
|
||||
// Create the WebRTC connection
|
||||
conn = new RTCPeerConnection({'iceServers': [{'urls': config.iceServers[0]}]});
|
||||
|
||||
// Handle offers
|
||||
client.onwebrtcoffer = async (offer) => {
|
||||
// Pass on the offer to WebRTC
|
||||
await conn.setRemoteDescription(new RTCSessionDescription(offer));
|
||||
|
||||
// Create an answer
|
||||
answer = await conn.createAnswer();
|
||||
await conn.setLocalDescription(answer);
|
||||
|
||||
// Return the answer to the server
|
||||
return answer;
|
||||
};
|
||||
|
||||
// Handle ice candidate from user to display
|
||||
client.onicecandidate = async (candidate) => {
|
||||
// Add the ice candidate to the WebRTC connection
|
||||
await conn.addIceCandidate(candidate);
|
||||
};
|
||||
|
||||
// Handle ice candidate from display to user
|
||||
conn.onicecandidate = async (event) => {
|
||||
// If there is a new candidate, send it to the peer through websockets
|
||||
if (event.candidate) { await client.sendICECandidate(event.candidate); }
|
||||
};
|
||||
|
||||
// Handle connection and disconnection of peer
|
||||
conn.onconnectionstatechange = async (event) => {
|
||||
switch (conn.connectionState) {
|
||||
case 'connected':
|
||||
// Switch to playback mode
|
||||
credits.hidden = true;
|
||||
playback.hidden = false;
|
||||
break;
|
||||
|
||||
case 'disconnected':
|
||||
// Completely reset the state
|
||||
playback.hidden = true;
|
||||
credits.hidden = false;
|
||||
playback.srcObject = null;
|
||||
conn = null;
|
||||
|
||||
// Initialize WebRTC
|
||||
await initWebRTC();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Send remote stream to the playback widget
|
||||
conn.ontrack = (event) => {
|
||||
const [remoteStream] = event.streams;
|
||||
playback.srcObject = remoteStream;
|
||||
};
|
||||
}
|
||||
|
||||
// Init WebRTC
|
||||
initWebRTC();
|
||||
|
||||
// Register the reset handler
|
||||
client.onreset = async () => {
|
||||
// Completely reset the state
|
||||
playback.hidden = true;
|
||||
credits.hidden = false;
|
||||
playback.srcObject = null;
|
||||
conn = null;
|
||||
|
||||
// Initialize WebRTC
|
||||
await initWebRTC();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
main();
|
||||
|
||||
await sock.send(JSON.stringify({
|
||||
type: 'init',
|
||||
clientType: 'display',
|
||||
dispID: 'TEST',
|
||||
otp: '123456'
|
||||
}));
|
||||
});
|
||||
|
||||
sock.addEventListener('message', (event) => {
|
||||
console.log(event.data)
|
||||
});
|
||||
|
||||
sock.addEventListener('close', (event) => {
|
||||
console.log('Disconnected from websocket')
|
||||
});
|
||||
@@ -1,71 +1,321 @@
|
||||
// Streaming objects
|
||||
let sock = null;
|
||||
let stream = null;
|
||||
let conn = null;
|
||||
|
||||
// GUI Objects
|
||||
let connForm = document.getElementById('connForm');
|
||||
let dispNameTb = document.getElementById('dispName');
|
||||
let connBtn = document.getElementById('connect');
|
||||
let pinValForm = document.getElementById('pinValForm');
|
||||
let dispPINTb = document.getElementById('dispPIN');
|
||||
let validateBtn = document.getElementById('validate');
|
||||
let streamForm = document.getElementById('streamForm');
|
||||
let locPlayback = document.getElementById('localPlayback');
|
||||
|
||||
// User API class
|
||||
class WisCastUserAPI {
|
||||
class WisCastUserAPIClient {
|
||||
// Socket to the API endpoint
|
||||
#sock;
|
||||
|
||||
// Endpoint URL
|
||||
#endpoint;
|
||||
|
||||
// Called when a config message is received
|
||||
#onconfig = (config) => {};
|
||||
|
||||
// Called when a success message is received
|
||||
#onsuccess = () => {};
|
||||
|
||||
// Called when an error message is received
|
||||
#onerror = (err) => {}
|
||||
|
||||
// Called when a WebRTC answer message is received
|
||||
#onwebrtcanswer = (answer) => {}
|
||||
|
||||
/**
|
||||
* Handler called when an ICE candidate is received
|
||||
* @param {RTCIceCandidateInit} candidate The received ICE candidate.
|
||||
*/
|
||||
onicecandidate = (candidate) => {}
|
||||
|
||||
/**
|
||||
* Create a User API client instance.
|
||||
* @param {String} endpoint URL of the API endpoint.
|
||||
*/
|
||||
constructor(endpoint) {
|
||||
// Save the endpoint
|
||||
this.endpoint = endpoint;
|
||||
this.#endpoint = endpoint;
|
||||
}
|
||||
|
||||
// Connect to the API
|
||||
/**
|
||||
* Connect to the API server.
|
||||
* @returns {Object} Configuration to use for the session.
|
||||
*/
|
||||
async connect() {
|
||||
// Connect to the WebSocket endpoint
|
||||
console.log('Connecting to the API...')
|
||||
this.#sock = new WebSocket(endpoint);
|
||||
return new Promise(async (res) => {
|
||||
// Register the handler for config messages
|
||||
this.#onconfig = (config) => { res(config); };
|
||||
|
||||
// Handle connection
|
||||
sock.addEventListener('open', this.#connectHandler);
|
||||
// Connect to the WebSocket endpoint
|
||||
console.log('Connecting to the API...');
|
||||
this.#sock = new WebSocket(this.#endpoint);
|
||||
|
||||
// Handle messages
|
||||
sock.addEventListener('message', this.#messageHandler);
|
||||
// Handle connection
|
||||
this.#sock.addEventListener('open', async (event) => { await this.#connectHandler(event); });
|
||||
|
||||
// Handle disconnection
|
||||
sock.addEventListener('close', this.#disconnectHandler);
|
||||
// Handle messages
|
||||
this.#sock.addEventListener('message', async (event) => { await this.#messageHandler(event); });
|
||||
|
||||
// Handle disconnection
|
||||
this.#sock.addEventListener('close', async (event) => { await this.#disconnectHandler(event); });
|
||||
});
|
||||
}
|
||||
|
||||
// Connect to a display using its ID and OTP
|
||||
/**
|
||||
* Connect to a display.
|
||||
* @param {String} dispID ID of the display.
|
||||
* @param {String} OTP One-Time-Pass currently shown on the display.
|
||||
*/
|
||||
async connectDisplay(dispID, OTP) {
|
||||
return new Promise(async (res) => {
|
||||
// Register the success and error handlers
|
||||
this.#onsuccess = () => { res(null); }
|
||||
this.#onerror = (err) => { res(err); }
|
||||
|
||||
// Send the connection command
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'connect',
|
||||
dispID: dispID,
|
||||
otp: OTP
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
#connectHandler(event) {
|
||||
console.log('Connected!')
|
||||
/**
|
||||
* Send a WebRTC offer to the display. Must already be connected.
|
||||
* @param {RTCSessionDescriptionInit} offer Offer to send to the display.
|
||||
* @returns {RTCSessionDescriptionInit} The answer from the display or null on error.
|
||||
*/
|
||||
async sendWebRTCOffer(offer) {
|
||||
return new Promise(async (res) => {
|
||||
// Register the answer and error handlers
|
||||
this.#onwebrtcanswer = (answer) => { res(answer); }
|
||||
this.#onerror = (err) => { res(err); }
|
||||
|
||||
// Send the connection command
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'webrtc-offer',
|
||||
offer: offer
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
#messageHandler(event) {
|
||||
console.log(event.data)
|
||||
/**
|
||||
* Send an ICE candidate to the display. Must already be connected.
|
||||
* @param {RTCIceCandidateInit} candidate ICE candidate to send to the display.
|
||||
*/
|
||||
async sendICECandidate(candidate) {
|
||||
// Send the connection command
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'ice-candidate',
|
||||
candidate: candidate
|
||||
}))
|
||||
}
|
||||
|
||||
#disconnectHandler(event) {
|
||||
console.log('Disconnected :/')
|
||||
// Disconnect from the display
|
||||
async disconnectDisplay() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
async #connectHandler(event) {
|
||||
console.log('Connected!');
|
||||
|
||||
// Send initialization message
|
||||
await this.#sock.send(JSON.stringify({
|
||||
type: 'init',
|
||||
clientType: 'user'
|
||||
}))
|
||||
}
|
||||
|
||||
async #messageHandler(event) {
|
||||
// Parse the message
|
||||
const msg = JSON.parse(event.data);
|
||||
|
||||
// Handle the message depending on its type
|
||||
switch (msg.type) {
|
||||
case 'success':
|
||||
// Call the success handler
|
||||
this.#onsuccess();
|
||||
break;
|
||||
|
||||
case 'error':
|
||||
// Call the success handler
|
||||
console.log('Error:', msg.code)
|
||||
this.#onerror(msg.code);
|
||||
break;
|
||||
|
||||
case 'config':
|
||||
// Call the config handler
|
||||
this.#onconfig(msg.config);
|
||||
break;
|
||||
|
||||
case 'webrtc-answer':
|
||||
// Call the answer handler
|
||||
this.#onwebrtcanswer(msg.answer);
|
||||
break;
|
||||
|
||||
case 'ice-candidate':
|
||||
// Call the answer handler
|
||||
this.onicecandidate(msg.candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
async #disconnectHandler(event) {
|
||||
console.log('Disconnected :/');
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// Create the API connection
|
||||
const api = new WisCastUserAPI(`ws://${location.host}/sig`);
|
||||
// Get GUI objects from their IDs
|
||||
const connForm = document.getElementById('connForm');
|
||||
const dispIDTb = document.getElementById('dispIDTb');
|
||||
const dispOTPTb = document.getElementById('dispOTPTb');
|
||||
const connBtn = document.getElementById('connectBtn');
|
||||
const streamForm = document.getElementById('streamForm');
|
||||
const locPlayback = document.getElementById('localPlayback');
|
||||
const disconnectBtn = document.getElementById('disconnectBtn');
|
||||
|
||||
// Create the API client
|
||||
const client = new WisCastUserAPIClient(`wss://${location.host}/sig`);
|
||||
|
||||
// Global state
|
||||
let config = null;
|
||||
let conn = null;
|
||||
let stream = null;
|
||||
|
||||
// Register a checking function for the contents of the display ID and OTP
|
||||
check = (event) => {
|
||||
// Only enable the connect button if the content of both is valid
|
||||
console.log('change')
|
||||
connBtn.disabled = (dispIDTb.value === '' || dispOTPTb.value.length !== 6 || !config);
|
||||
}
|
||||
dispIDTb.oninput = check;
|
||||
dispOTPTb.oninput = check;
|
||||
|
||||
// Register a handler for when enter is pressed in the display name textbox
|
||||
dispIDTb.onkeyup = (event) => {
|
||||
// Check that the key was enter
|
||||
if (event.key != 'Enter') { return; }
|
||||
|
||||
// Check that the name textbox is not empty
|
||||
if (dispIDTb.value === '') { return; }
|
||||
|
||||
// Select the OTP textbox
|
||||
dispOTPTb.focus();
|
||||
dispOTPTb.select();
|
||||
};
|
||||
|
||||
// Register a handler for when enter is pressed in the display OTP textbox
|
||||
dispOTPTb.onkeyup = (event) => {
|
||||
// Check that the key was enter
|
||||
if (event.key != 'Enter') { return; }
|
||||
|
||||
// Check that the connect button is enabled
|
||||
if (connBtn.disabled) { return; }
|
||||
|
||||
// Press the connect button
|
||||
connBtn.click();
|
||||
};
|
||||
|
||||
// Connect to the server
|
||||
await api.connect();
|
||||
config = await client.connect();
|
||||
|
||||
// Register a handler for clicking the connection button
|
||||
connBtn.onclick = async (event) => {
|
||||
// Disable the text boxes and the button
|
||||
dispIDTb.disabled = true;
|
||||
dispOTPTb.disabled = true;
|
||||
connBtn.disabled = true;
|
||||
|
||||
// Change the status
|
||||
connBtn.textContent = 'Getting permissions...';
|
||||
|
||||
// Get the stream for the screen
|
||||
stream = await navigator.mediaDevices.getDisplayMedia({ video: { cursor: 'always' } });
|
||||
|
||||
// Disable the text boxes and the button
|
||||
dispIDTb.disabled = true;
|
||||
dispOTPTb.disabled = true;
|
||||
connBtn.disabled = true;
|
||||
|
||||
// Change the status
|
||||
connBtn.textContent = 'Authenticating...';
|
||||
|
||||
// Attempt to connect to the display
|
||||
const err = await client.connectDisplay(dispIDTb.value, dispOTPTb.value);
|
||||
if (err) {
|
||||
// TODO: Show the error
|
||||
console.log(err)
|
||||
|
||||
// Reset the GUI
|
||||
dispIDTb.value = '';
|
||||
dispOTPTb.value = '';
|
||||
connBtn.textContent = 'Connect';
|
||||
dispIDTb.disabled = false;
|
||||
dispOTPTb.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Change the status
|
||||
connBtn.textContent = 'Connecting...';
|
||||
|
||||
// Create the connection
|
||||
conn = new RTCPeerConnection({'iceServers': [{'urls': config.iceServers[0]}]});
|
||||
|
||||
// Handle ice candidates from user to display
|
||||
conn.onicecandidate = async (event) => {
|
||||
// If there is a new candidate, send it to the peer through websockets
|
||||
if (event.candidate) { await client.sendICECandidate(event.candidate); }
|
||||
};
|
||||
|
||||
// Handle ice candidates from display to user
|
||||
client.onicecandidate = (candidate) => {
|
||||
conn.addIceCandidate(candidate);
|
||||
}
|
||||
|
||||
// Handle connection and disconnection of peer
|
||||
conn.onconnectionstatechange = (event) => {
|
||||
switch (conn.connectionState) {
|
||||
case 'connected':
|
||||
// Switch to stream screen
|
||||
connForm.hidden = true;
|
||||
streamForm.hidden = false;
|
||||
locPlayback.srcObject = stream;
|
||||
console.log("Streaming!")
|
||||
break;
|
||||
|
||||
case 'disconnected':
|
||||
console.log("Stream ended.")
|
||||
// Reload the page to ensure the state is complete reset
|
||||
location.reload();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Start streaming the screen
|
||||
stream.getTracks().forEach(track => {
|
||||
conn.addTrack(track, stream);
|
||||
});
|
||||
|
||||
// If the stream ends, reload the page
|
||||
stream.getVideoTracks()[0].onended = (event) => {
|
||||
location.reload();
|
||||
};
|
||||
|
||||
// Create and send an offer
|
||||
const offer = await conn.createOffer();
|
||||
await conn.setLocalDescription(offer);
|
||||
await conn.setRemoteDescription(await client.sendWebRTCOffer(offer));
|
||||
};
|
||||
|
||||
// Register the disconnect button click event
|
||||
disconnectBtn.onclick = (event) => {
|
||||
// Just reload the page
|
||||
location.reload();
|
||||
};
|
||||
|
||||
// Do a check to potentially enable the connection button
|
||||
check(null);
|
||||
}
|
||||
|
||||
// Run the main function
|
||||
|
||||
Reference in New Issue
Block a user