All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.graphenee.vaadin.meeting.meeting-host.js Maven / Gradle / Ivy

let offerOptions = {
    offerToReceiveAudio: 1,
    offerToReceiveVideo: 1
};

let ws = null;
let peers = new Map();
let videos = new Map();
let localUserId = null;
let _meetingId = null;
let _stunUrl = null;
let _turnUrl = null;
let _turnUsername = null;
let _turnCredentials = null;

// initialize websockets...
function initializeWebSocket(wsurl, userId, meetingId, stunUrl, turnUrl, turnUsername, turnCredentials) {
    localUserId = userId;
    _meetingId = meetingId;
    _stunUrl = stunUrl;
    _turnUrl = turnUrl;
    _turnUsername = turnUsername;
    _turnCredentials = turnCredentials;
    if (ws)
        return;
    ws = new WebSocket(wsurl);
    // when a new message will arrive...
    ws.onmessage = function (m) {
        let message = JSON.parse(m.data);
        if (message.event == "joining") {
            handleJoining(message.uid);
        } else if (message.event == "leaving") {
            handleLeaving(message.uid);
        } else if (message.event == "candidate") {
            handleCandidate(message.data, message.uid);
        } else if (message.event == "offer") {
            handleOffer(message.data, message.uid);
        } else if (message.event == "answer") {
            handleAnswer(message.data, message.uid);
        } else {
            console.log("Invalid Message", message);
        }
    };
    // when socket will be closed...
    ws.onclose = function (e) {
        console.log('Socket is closed. Reconnect will be attempted in 5 second.', e.reason);
        setTimeout(function () {
            ws = null;
            initializeWebSocket(wsurl, userId);
        }, 5000);
    };
    // when an error will occur...
    ws.onerror = function (err) {
        console.error('Socket encountered error: ', err.message);
        // ws.close();
    };
}

function handleJoining(userId) {
    let pc = createPeer(userId);
    console.log(userId, "joined");
    createOffer(userId);
    try {
        document.getElementById(createVideoId(userId) + "_container").style.display = 'inline-block';
    } catch (err) {

    }
}

function handleLeaving(userId) {
    let pc = peers.get(userId);
    videos.delete(pc);
    peers.delete(userId);
    console.log(userId, "left");
    pc.close();
    try {
        document.getElementById(createVideoId(userId) + "_container").style.display = 'none';
    } catch (err) {

    }
}

// called when websocket will receive candidate
async function handleCandidate(candidate, userId) {
    let pc = createPeer(userId);
    try {
        await pc.addIceCandidate(new RTCIceCandidate(candidate));
    } catch (error) {
        console.log(error);
    }
}

// called when join button is clicked on the meeting component.
async function createOffer(userId) {
    pc = createPeer(userId);
    try {
        let offer = await pc.createOffer(offerOptions);
        await pc.setLocalDescription(offer);
        ws.send(JSON.stringify({
            event: "offer",
            uid: localUserId,
            mid: _meetingId,
            data: offer
        }));
    } catch (error) {
        console.log("Error", error);
    }
}

// called when websocket will receive offer from any of the connected peer,
// typically from the host peer.
async function handleOffer(offer, userId) {
    try {
        document.getElementById(createVideoId(userId)).srcObject = null;
    } catch (error) {
        console.log(error);
    }
    let pc = createPeer(userId);
    let session = new RTCSessionDescription(offer);
    await pc.setRemoteDescription(session);
    try {
        let answer = await pc.createAnswer();
        await pc.setLocalDescription(answer);
        ws.send(JSON.stringify({
            event: "answer",
            uid: localUserId,
            mid: _meetingId,
            data: answer
        }));
    } catch (error) {
        console.log("Error", error);
    }
}

// called when websocket will receive answer from any of the connected peer whom
// offer was sent, typically from a joining peer.
async function handleAnswer(answer, userId) {
    pc = createPeer(userId);
    try {
        await pc.setRemoteDescription(new RTCSessionDescription(answer));
    } catch (error) {
        console.log(error);
    }
}

// creates a peer connection if does not already exist. Each peer on the meeting
// must have a connection for other peers.
function createPeer(userId) {
    let pc = peers.get(userId);
    if (pc != null) {
        console.log("Using peer for: " + userId)
        return pc;
    }
    console.log("Creating peer for: " + userId)
    pc = initializePeerConnection(userId);
    peers.set(userId, pc);
    videos.set(pc, createVideoId(userId));
    return pc;
}

// creates a new peer connection for userId and videoId
function initializePeerConnection(userId) {
    let pc = new RTCPeerConnection({
        'iceServers': [
          {
            'url': _stunUrl
          },
          {
            'url': _turnUrl,
            'credential': _turnCredentials,
            'username': _turnUsername
          }
        ]
    });
    let isNegotiating = false;
    // is called when new candidate is discovered
    pc.onicecandidate = function (event) {
        if (event.candidate) {
            ws.send(JSON.stringify({
                event: "candidate",
                uid: localUserId,
                mid: _meetingId,
                data: event.candidate
            }));
        }
    };
    // called when allow button is clicked on any of the media stream.
    pc.onnegotiationneeded = function (event) {
        if (isNegotiating)
            return;
        isNegotiating = true;
        createOffer(userId);
    };
    // Workaround for Chrome: skip nested negotiations
    pc.onsignalingstatechange = (e) => {
        isNegotiating = (pc.signalingState != "stable");
    }
    // called when a stream is added on the remote peer whose ice candidate is
    // known.
    pc.onaddstream = function (event) {
        if (event.stream) {
            let remoteVideo = document.getElementById(videos.get(pc));
            remoteVideo.srcObject = event.stream;
        }
    }

    pc.ontrack = function (event) {
        if (event.track) {
            let remoteVideo = document.getElementById(videos.get(pc));
            let remoteStream = remoteVideo.srcObject;
            if (remoteStream == null) {
                remoteStream = new MediaStream();
                remoteVideo.srcObject = remoteStream;
            }
            remoteStream.addTrack(event.track, remoteStream);
        }
    }

    return pc;
}

// called when camera button is clicked on the meeting component.
async function startCamera() {
    let constraints = {
        video: true,
        audio: true
    }
    try {
        navigator.mediaDevices.getUserMedia(constraints).then(stream => {
            let myVideo = document.getElementById('localVideo');
            if (myVideo.srcObject != null) {
                myVideo.srcObject.getTracks().forEach(track => track.stop());
            }
            myVideo.muted = true;
            myVideo.srcObject = stream;
            stream.getTracks().forEach(track => {
                peers.forEach(pc => pc.addTrack(track, stream));
            });
        }).catch(reason => {
            console.log(reason);
        });
    } catch (error) {
        console.log(error);
    }
}

// called when screen button is clicked on the meeting component.
async function startScreen() {
    try {
        navigator.mediaDevices.getDisplayMedia().then(stream => {
            let myVideo = document.getElementById('localVideo');
            if (myVideo.srcObject != null) {
                myVideo.srcObject.getTracks().forEach(track => track.stop());
            }
            myVideo.muted = true;
            myVideo.srcObject = stream;
            stream.getTracks().forEach(track => {
                peers.forEach(pc => pc.addTrack(track, stream));
            });
        }).catch(reason => {
            console.log(reason);
        });
    } catch (error) {
        console.log(error);
    }
}

// mute all attendees...
function muteAllAttendees() {
    videos.forEach(v => {
        document.getElementById(v).muted = true;
    });
}

// unmute all attendees...
function unmuteAllAttendees() {
    videos.forEach(v => {
        document.getElementById(v).muted = false;
    });
}

// mute single attendee...
function muteAttendee(userId) {
    document.getElementById(createVideoId(userId)).muted = true;
}

//unmute single attendee...
function unmuteAttendee(userId) {
    document.getElementById(createVideoId(userId)).muted = false;
}

function createVideoId(userId) {
    return 'vid_' + userId.replace('-', '_').replace('.', '_');
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy