264 lines
7.6 KiB
JavaScript
264 lines
7.6 KiB
JavaScript
var LivekitClient = window.LivekitClient;
|
|
var room;
|
|
var myUserName;
|
|
var myRoomName;
|
|
var isScreenShared = false;
|
|
var screenSharePublication;
|
|
|
|
/* OPENVIDU METHODS */
|
|
|
|
function joinRoom() {
|
|
myRoomName = document.getElementById('roomName').value;
|
|
myUserName = document.getElementById('userName').value;
|
|
|
|
// --- 1) Get a Room object ---
|
|
|
|
room = new LivekitClient.Room();
|
|
|
|
// --- 2) Specify the actions when events take place in the room ---
|
|
|
|
// On every new Track received...
|
|
room.on(
|
|
LivekitClient.RoomEvent.TrackSubscribed,
|
|
(track, publication, participant) => {
|
|
const element = track.attach();
|
|
element.id = track.sid;
|
|
element.className = 'removable';
|
|
document.getElementById('video-container').appendChild(element);
|
|
if (track.kind === 'video' || track.kind === 'screen') {
|
|
appendUserData(element, participant.identity);
|
|
}
|
|
}
|
|
);
|
|
|
|
// On every new Track destroyed...
|
|
room.on(
|
|
LivekitClient.RoomEvent.TrackUnsubscribed,
|
|
(track, publication, participant) => {
|
|
track.detach();
|
|
document.getElementById(track.sid)?.remove();
|
|
if (track.kind === 'video' || track.kind === 'screen') {
|
|
removeUserData(participant);
|
|
}
|
|
}
|
|
);
|
|
|
|
// --- 3) Connect to the room with a valid access token ---
|
|
|
|
// Get a token from the application backend
|
|
getToken(myRoomName, myUserName).then((token) => {
|
|
const livekitUrl = getLivekitUrlFromMetadata(token);
|
|
|
|
// First param is the LiveKit server URL. Second param is the access token
|
|
room
|
|
.connect(livekitUrl, token)
|
|
.then(() => {
|
|
// --- 4) Set page layout for active call ---
|
|
|
|
document.getElementById('room-title').innerText = myRoomName;
|
|
document.getElementById('join').style.display = 'none';
|
|
document.getElementById('room').style.display = 'block';
|
|
|
|
// --- 5) Publish your local tracks ---
|
|
|
|
room.localParticipant.setMicrophoneEnabled(true);
|
|
room.localParticipant.setCameraEnabled(true).then((publication) => {
|
|
const element = publication.track.attach();
|
|
document.getElementById('video-container').appendChild(element);
|
|
initMainVideo(element, myUserName);
|
|
appendUserData(element, myUserName);
|
|
element.className = 'removable';
|
|
});
|
|
})
|
|
.catch((error) => {
|
|
console.log(
|
|
'There was an error connecting to the room:',
|
|
error.code,
|
|
error.message
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
function leaveRoom() {
|
|
// --- 6) Leave the room by calling 'disconnect' method over the Room object ---
|
|
|
|
room.disconnect();
|
|
|
|
// Removing all HTML elements with user's nicknames.
|
|
// HTML videos are automatically removed when leaving a Room
|
|
removeAllUserData();
|
|
|
|
// Back to 'Join room' page
|
|
document.getElementById('join').style.display = 'block';
|
|
document.getElementById('room').style.display = 'none';
|
|
}
|
|
|
|
async function toggleScreenShare() {
|
|
console.log('Toggling screen share');
|
|
const enabled = !isScreenShared;
|
|
|
|
if (enabled) {
|
|
// Enable screen sharing
|
|
try {
|
|
screenSharePublication =
|
|
await room.localParticipant?.setScreenShareEnabled(enabled);
|
|
} catch (error) {
|
|
console.error('Error enabling screen sharing', error);
|
|
}
|
|
|
|
if (screenSharePublication) {
|
|
console.log('Screen sharing enabled', screenSharePublication);
|
|
isScreenShared = enabled;
|
|
|
|
// Attach the screen share track to the video container
|
|
const element = screenSharePublication.track.attach();
|
|
element.id = screenSharePublication.trackSid;
|
|
element.className = 'removable';
|
|
document.getElementById('video-container').appendChild(element);
|
|
|
|
// Add user data for the screen share
|
|
appendUserData(element, `${myUserName}_SCREEN`);
|
|
|
|
// Listen for the 'ended' event to handle screen sharing stop
|
|
screenSharePublication.addListener('ended', async () => {
|
|
console.debug('Clicked native stop button. Stopping screen sharing');
|
|
await stopScreenSharing();
|
|
});
|
|
}
|
|
} else {
|
|
// Disable screen sharing
|
|
await stopScreenSharing();
|
|
}
|
|
}
|
|
|
|
async function stopScreenSharing() {
|
|
try {
|
|
await room.localParticipant?.setScreenShareEnabled(false);
|
|
isScreenShared = false;
|
|
const trackSid = screenSharePublication?.trackSid;
|
|
|
|
if (trackSid) {
|
|
document.getElementById(trackSid)?.remove();
|
|
removeUserData({ identity: `${myUserName}_SCREEN` });
|
|
}
|
|
screenSharePublication = undefined;
|
|
} catch (error) {
|
|
console.error('Error stopping screen sharing', error);
|
|
}
|
|
}
|
|
|
|
window.onbeforeunload = function () {
|
|
if (room) room.disconnect();
|
|
};
|
|
|
|
/* APPLICATION SPECIFIC METHODS */
|
|
|
|
window.addEventListener('load', function () {
|
|
generateParticipantInfo();
|
|
});
|
|
|
|
function generateParticipantInfo() {
|
|
document.getElementById('roomName').value = 'RoomA';
|
|
document.getElementById('userName').value =
|
|
'Participant' + Math.floor(Math.random() * 100);
|
|
}
|
|
|
|
function appendUserData(videoElement, participantIdentity) {
|
|
var dataNode = document.createElement('div');
|
|
dataNode.className = 'removable';
|
|
dataNode.id = 'data-' + participantIdentity;
|
|
dataNode.innerHTML = '<p>' + participantIdentity + '</p>';
|
|
videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling);
|
|
addClickListener(videoElement, participantIdentity);
|
|
}
|
|
|
|
function removeUserData(participant) {
|
|
var dataNode = document.getElementById('data-' + participant.identity);
|
|
dataNode?.parentNode.removeChild(dataNode);
|
|
}
|
|
|
|
function removeAllUserData() {
|
|
var elementsToRemove = document.getElementsByClassName('removable');
|
|
while (elementsToRemove[0]) {
|
|
elementsToRemove[0].parentNode.removeChild(elementsToRemove[0]);
|
|
}
|
|
}
|
|
|
|
function addClickListener(videoElement, userData) {
|
|
videoElement.addEventListener('click', function () {
|
|
var mainVideo = $('#main-video video').get(0);
|
|
if (mainVideo.srcObject !== videoElement.srcObject) {
|
|
$('#main-video').fadeOut('fast', () => {
|
|
$('#main-video p').html(userData);
|
|
mainVideo.srcObject = videoElement.srcObject;
|
|
$('#main-video').fadeIn('fast');
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function initMainVideo(videoElement, userData) {
|
|
document.querySelector('#main-video video').srcObject =
|
|
videoElement.srcObject;
|
|
document.querySelector('#main-video p').innerHTML = userData;
|
|
document.querySelector('#main-video video')['muted'] = true;
|
|
}
|
|
|
|
function getLivekitUrlFromMetadata(token) {
|
|
if (!token) throw new Error('Trying to get metadata from an empty token');
|
|
try {
|
|
const base64Url = token.split('.')[1];
|
|
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
|
|
const jsonPayload = decodeURIComponent(
|
|
window
|
|
.atob(base64)
|
|
.split('')
|
|
.map((c) => {
|
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|
})
|
|
.join('')
|
|
);
|
|
|
|
const payload = JSON.parse(jsonPayload);
|
|
if (!payload?.metadata) throw new Error('Token does not contain metadata');
|
|
const metadata = JSON.parse(payload.metadata);
|
|
return metadata.livekitUrl;
|
|
} catch (error) {
|
|
throw new Error('Error decoding and parsing token: ' + error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* --------------------------------------------
|
|
* GETTING A TOKEN FROM YOUR APPLICATION SERVER
|
|
* --------------------------------------------
|
|
* The methods below request the creation of a Token to
|
|
* your application server. This keeps your OpenVidu deployment secure.
|
|
*
|
|
* In this sample code, there is no user control at all. Anybody could
|
|
* access your application server endpoints! In a real production
|
|
* environment, your application server must identify the user to allow
|
|
* access to the endpoints.
|
|
*
|
|
*/
|
|
|
|
var APPLICATION_SERVER_URL = 'http://localhost:5000/';
|
|
|
|
function getToken(roomName, participantName) {
|
|
return new Promise((resolve, reject) => {
|
|
$.ajax({
|
|
type: 'POST',
|
|
url: APPLICATION_SERVER_URL + 'token',
|
|
data: JSON.stringify({
|
|
roomName,
|
|
participantName,
|
|
permissions: {},
|
|
}),
|
|
headers: { 'Content-Type': 'application/json' },
|
|
success: (token) => resolve(token),
|
|
error: (error) => reject(error),
|
|
});
|
|
});
|
|
}
|