Client prototype, signaling server & PWA client

This commit is contained in:
gltron
2020-03-26 00:46:45 +01:00
commit ea013cd6fd
52 changed files with 16140 additions and 0 deletions

220
clientV/scripts/rtc.js Normal file
View File

@@ -0,0 +1,220 @@
var name;
var connectedUser;
var loginInput = document.querySelector('#loginInput');
var loginBt = document.querySelector('#loginBt');
var callToUsernameInput = document.querySelector('#callInput');
var callBtn = document.querySelector('#callBt');
var hangUpBtn = document.querySelector('#disconnectBt');
const remoteVideo = document.querySelector('#video');
const remoteVideo2 = document.querySelector('#video2');
var videoInput = document.querySelector('#videoInput');
var yourConn;
var stream;
var reader;
const configuration = {
iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
};
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
videoInput.addEventListener("change", function (event) {
var file = this.files[0]
var type = file.type
var videoNode = remoteVideo2
var canPlay = videoNode.canPlayType(type)
if (canPlay === '') canPlay = 'no'
var message = 'Can play type "' + type + '": ' + canPlay
var isError = canPlay === 'no'
//displayMessage(message, isError)
if (isError) {
return
}
var fileURL = URL.createObjectURL(file)
videoNode.src = fileURL
});
remoteVideo.onplay = function() {
if(remoteVideo.mozCaptureStream()) stream = remoteVideo.mozCaptureStream();
else stream = remoteVideo.captureStream();
//remoteVideo2.srcObject = stream;
stream.getTracks().forEach((track) => {
console.log("ADDED TRACK");
yourConn.addTrack(track, stream);
});
//yourConn.addStream(stream);
}
// Login when the user clicks the button
loginBt.addEventListener("click", function (event) {
name = loginInput.value;
if (name.length > 0) {
send({
type: "login",
name: name
});
}
});
function handleLogin(success) {
if (success === false) {
alert("Ooops...try a different username");
} else {
//**********************
//Starting a peer connection
//**********************
yourConn = new RTCPeerConnection(configuration);
yourConn.ontrack = function (event) {
console.log("GOT TRACK");
remoteVideo.srcObject = event.streams[0];
}
if (stream) {
remoteVideo2.srcObject = stream;
stream.getTracks().forEach((track) => {
yourConn.addTrack(track, stream);
});
}
// setup stream listening
//yourConn.addStream(stream);
// Setup ice handling
yourConn.onicecandidate = function (event) {
if (event.candidate) {
send({
type: "candidate",
candidate: event.candidate
});
}
};
yourConn.onnegotiationneeded = handleNegotiationNeededEvent;
window.setInterval(getConnectionStats, 1000);
}
};
function getConnectionStats() {
/*yourConn.getStats().then(stats => {
var statsOutput = "";
stats.forEach(report => {
statsOutput += report;
});
console.log(statsOutput);
});*/
console.log(yourConn.connectionState);
}
//initiating a call
callBtn.addEventListener("click", function () {
var callToUsername = callToUsernameInput.value;
if (callToUsername.length > 0) {
connectedUser = callToUsername;
// create an offer
yourConn.createOffer(function (offer) {
send({
type: "offer",
offer: offer
});
yourConn.setLocalDescription(offer);
}, function (error) {
alert("Error when creating an offer");
},
offerOptions);
}
});
//when somebody sends us an offer
function handleOffer(offer, name) {
console.log("GOT OFFER");
connectedUser = name;
yourConn.setRemoteDescription(new RTCSessionDescription(offer));
//create an answer to an offer
yourConn.createAnswer(function (answer) {
yourConn.setLocalDescription(answer);
send({
type: "answer",
answer: answer
});
}, function (error) {
alert("Error when creating an answer");
});
};
//when we got an answer from a remote user
function handleAnswer(answer) {
console.log("GOT ANSWER");
yourConn.setRemoteDescription(new RTCSessionDescription(answer));
};
//when we got an ice candidate from a remote user
function handleCandidate(candidate) {
console.log("GOT CANDIDATE");
yourConn.addIceCandidate(new RTCIceCandidate(candidate));
};
function handleNegotiationNeededEvent() {
console.log("NEGOTIATION NEEDED");
yourConn.createOffer().then(function(offer) {
return yourConn.setLocalDescription(offer);
})
.then(function() {
send({
type: "video-offer",
sdp: yourConn.localDescription
});
});
}
//hang up
hangUpBtn.addEventListener("click", function () {
send({
type: "leave"
});
handleLeave();
});
function handleLeave() {
connectedUser = null;
remoteVideo.src = null;
yourConn.close();
yourConn.onicecandidate = null;
//yourConn.onaddstream = null;
};

658
clientV/scripts/rtc2.js Normal file
View File

@@ -0,0 +1,658 @@
"use strict";
// Get our hostname
var myHostname = window.location.hostname;
if (!myHostname) {
myHostname = "localhost";
}
log("Hostname: " + myHostname);
// WebSocket chat/signaling channel variables.
var connection = null;
var clientID = 0;
// The media constraints object describes what sort of stream we want
// to request from the local A/V hardware (typically a webcam and
// microphone). Here, we specify only that we want both audio and
// video; however, you can be more specific. It's possible to state
// that you would prefer (or require) specific resolutions of video,
// whether to prefer the user-facing or rear-facing camera (if available),
// and so on.
//
// See also:
// https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamConstraints
// https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
//
var mediaConstraints = {
audio: true, // We want an audio track
video: {
aspectRatio: {
ideal: 1.333333 // 3:2 aspect is preferred
}
}
};
var myUsername = null;
var targetUsername = null; // To store username of other peer
var myPeerConnection = null; // RTCPeerConnection
var transceiver = null; // RTCRtpTransceiver
var webcamStream = null; // MediaStream from webcam
// Output logging information to console.
function log(text) {
var time = new Date();
console.log("[" + time.toLocaleTimeString() + "] " + text);
}
// Output an error message to console.
function log_error(text) {
var time = new Date();
console.trace("[" + time.toLocaleTimeString() + "] " + text);
}
// Send a JavaScript object by converting it to JSON and sending
// it as a message on the WebSocket connection.
function sendToServer(msg) {
var msgJSON = JSON.stringify(msg);
log("Sending '" + msg.type + "' message: " + msgJSON);
connection.send(msgJSON);
}
// Called when the "id" message is received; this message is sent by the
// server to assign this login session a unique ID number; in response,
// this function sends a "username" message to set our username for this
// session.
function setUsername() {
myUsername = document.getElementById("name").value;
sendToServer({
name: myUsername,
date: Date.now(),
id: clientID,
type: "username"
});
}
// Open and configure the connection to the WebSocket server.
function connect() {
var serverUrl;
var scheme = "ws";
// If this is an HTTPS connection, we have to use a secure WebSocket
// connection too, so add another "s" to the scheme.
if (document.location.protocol === "https:") {
scheme += "s";
}
serverUrl = scheme + "://" + myHostname + ":6503";
log(`Connecting to server: ${serverUrl}`);
connection = new WebSocket(serverUrl, "json");
connection.onopen = function(evt) {
document.getElementById("text").disabled = false;
document.getElementById("send").disabled = false;
};
connection.onerror = function(evt) {
console.dir(evt);
}
connection.onmessage = function(evt) {
var chatBox = document.querySelector(".chatbox");
var text = "";
var msg = JSON.parse(evt.data);
log("Message received: ");
console.dir(msg);
var time = new Date(msg.date);
var timeStr = time.toLocaleTimeString();
switch(msg.type) {
case "id":
clientID = msg.id;
setUsername();
break;
case "username":
text = "<b>User <em>" + msg.name + "</em> signed in at " + timeStr + "</b><br>";
break;
case "message":
text = "(" + timeStr + ") <b>" + msg.name + "</b>: " + msg.text + "<br>";
break;
case "rejectusername":
myUsername = msg.name;
text = "<b>Your username has been set to <em>" + myUsername +
"</em> because the name you chose is in use.</b><br>";
break;
case "userlist": // Received an updated user list
handleUserlistMsg(msg);
break;
// Signaling messages: these messages are used to trade WebRTC
// signaling information during negotiations leading up to a video
// call.
case "video-offer": // Invitation and offer to chat
handleVideoOfferMsg(msg);
break;
case "video-answer": // Callee has answered our offer
handleVideoAnswerMsg(msg);
break;
case "new-ice-candidate": // A new ICE candidate has been received
handleNewICECandidateMsg(msg);
break;
case "hang-up": // The other peer has hung up the call
handleHangUpMsg(msg);
break;
// Unknown message; output to console for debugging.
default:
log_error("Unknown message received:");
log_error(msg);
}
// If there's text to insert into the chat buffer, do so now, then
// scroll the chat panel so that the new text is visible.
if (text.length) {
chatBox.innerHTML += text;
chatBox.scrollTop = chatBox.scrollHeight - chatBox.clientHeight;
}
};
}
// Handles a click on the Send button (or pressing return/enter) by
// building a "message" object and sending it to the server.
function handleSendButton() {
var msg = {
text: document.getElementById("text").value,
type: "message",
id: clientID,
date: Date.now()
};
sendToServer(msg);
document.getElementById("text").value = "";
}
// Handler for keyboard events. This is used to intercept the return and
// enter keys so that we can call send() to transmit the entered text
// to the server.
function handleKey(evt) {
if (evt.keyCode === 13 || evt.keyCode === 14) {
if (!document.getElementById("send").disabled) {
handleSendButton();
}
}
}
// Create the RTCPeerConnection which knows how to talk to our
// selected STUN/TURN server and then uses getUserMedia() to find
// our camera and microphone and add that stream to the connection for
// use in our video call. Then we configure event handlers to get
// needed notifications on the call.
async function createPeerConnection() {
log("Setting up a connection...");
// Create an RTCPeerConnection which knows to use our chosen
// STUN server.
myPeerConnection = new RTCPeerConnection({
iceServers: [ // Information about ICE servers - Use your own!
{
urls: "turn:" + myHostname, // A TURN server
username: "webrtc",
credential: "turnserver"
}
]
});
// Set up event handlers for the ICE negotiation process.
myPeerConnection.onicecandidate = handleICECandidateEvent;
myPeerConnection.oniceconnectionstatechange = handleICEConnectionStateChangeEvent;
myPeerConnection.onicegatheringstatechange = handleICEGatheringStateChangeEvent;
myPeerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
myPeerConnection.onnegotiationneeded = handleNegotiationNeededEvent;
myPeerConnection.ontrack = handleTrackEvent;
}
// Called by the WebRTC layer to let us know when it's time to
// begin, resume, or restart ICE negotiation.
async function handleNegotiationNeededEvent() {
log("*** Negotiation needed");
try {
log("---> Creating offer");
const offer = await myPeerConnection.createOffer();
// If the connection hasn't yet achieved the "stable" state,
// return to the caller. Another negotiationneeded event
// will be fired when the state stabilizes.
if (myPeerConnection.signalingState != "stable") {
log(" -- The connection isn't stable yet; postponing...")
return;
}
// Establish the offer as the local peer's current
// description.
log("---> Setting local description to the offer");
await myPeerConnection.setLocalDescription(offer);
// Send the offer to the remote peer.
log("---> Sending the offer to the remote peer");
sendToServer({
name: myUsername,
target: targetUsername,
type: "video-offer",
sdp: myPeerConnection.localDescription
});
} catch(err) {
log("*** The following error occurred while handling the negotiationneeded event:");
reportError(err);
};
}
// Called by the WebRTC layer when events occur on the media tracks
// on our WebRTC call. This includes when streams are added to and
// removed from the call.
//
// track events include the following fields:
//
// RTCRtpReceiver receiver
// MediaStreamTrack track
// MediaStream[] streams
// RTCRtpTransceiver transceiver
//
// In our case, we're just taking the first stream found and attaching
// it to the <video> element for incoming media.
function handleTrackEvent(event) {
log("*** Track event");
document.getElementById("received_video").srcObject = event.streams[0];
document.getElementById("hangup-button").disabled = false;
}
// Handles |icecandidate| events by forwarding the specified
// ICE candidate (created by our local ICE agent) to the other
// peer through the signaling server.
function handleICECandidateEvent(event) {
if (event.candidate) {
log("*** Outgoing ICE candidate: " + event.candidate.candidate);
sendToServer({
type: "new-ice-candidate",
target: targetUsername,
candidate: event.candidate
});
}
}
// Handle |iceconnectionstatechange| events. This will detect
// when the ICE connection is closed, failed, or disconnected.
//
// This is called when the state of the ICE agent changes.
function handleICEConnectionStateChangeEvent(event) {
log("*** ICE connection state changed to " + myPeerConnection.iceConnectionState);
switch(myPeerConnection.iceConnectionState) {
case "closed":
case "failed":
case "disconnected":
closeVideoCall();
break;
}
}
// Set up a |signalingstatechange| event handler. This will detect when
// the signaling connection is closed.
//
// NOTE: This will actually move to the new RTCPeerConnectionState enum
// returned in the property RTCPeerConnection.connectionState when
// browsers catch up with the latest version of the specification!
function handleSignalingStateChangeEvent(event) {
log("*** WebRTC signaling state changed to: " + myPeerConnection.signalingState);
switch(myPeerConnection.signalingState) {
case "closed":
closeVideoCall();
break;
}
}
// Handle the |icegatheringstatechange| event. This lets us know what the
// ICE engine is currently working on: "new" means no networking has happened
// yet, "gathering" means the ICE engine is currently gathering candidates,
// and "complete" means gathering is complete. Note that the engine can
// alternate between "gathering" and "complete" repeatedly as needs and
// circumstances change.
//
// We don't need to do anything when this happens, but we log it to the
// console so you can see what's going on when playing with the sample.
function handleICEGatheringStateChangeEvent(event) {
log("*** ICE gathering state changed to: " + myPeerConnection.iceGatheringState);
}
// Given a message containing a list of usernames, this function
// populates the user list box with those names, making each item
// clickable to allow starting a video call.
function handleUserlistMsg(msg) {
var i;
var listElem = document.querySelector(".userlistbox");
// Remove all current list members. We could do this smarter,
// by adding and updating users instead of rebuilding from
// scratch but this will do for this sample.
while (listElem.firstChild) {
listElem.removeChild(listElem.firstChild);
}
// Add member names from the received list.
msg.users.forEach(function(username) {
var item = document.createElement("li");
item.appendChild(document.createTextNode(username));
item.addEventListener("click", invite, false);
listElem.appendChild(item);
});
}
// Close the RTCPeerConnection and reset variables so that the user can
// make or receive another call if they wish. This is called both
// when the user hangs up, the other user hangs up, or if a connection
// failure is detected.
function closeVideoCall() {
var localVideo = document.getElementById("local_video");
log("Closing the call");
// Close the RTCPeerConnection
if (myPeerConnection) {
log("--> Closing the peer connection");
// Disconnect all our event listeners; we don't want stray events
// to interfere with the hangup while it's ongoing.
myPeerConnection.ontrack = null;
myPeerConnection.onnicecandidate = null;
myPeerConnection.oniceconnectionstatechange = null;
myPeerConnection.onsignalingstatechange = null;
myPeerConnection.onicegatheringstatechange = null;
myPeerConnection.onnotificationneeded = null;
// Stop all transceivers on the connection
myPeerConnection.getTransceivers().forEach(transceiver => {
transceiver.stop();
});
// Stop the webcam preview as well by pausing the <video>
// element, then stopping each of the getUserMedia() tracks
// on it.
if (localVideo.srcObject) {
localVideo.pause();
localVideo.srcObject.getTracks().forEach(track => {
track.stop();
});
}
// Close the peer connection
myPeerConnection.close();
myPeerConnection = null;
webcamStream = null;
}
// Disable the hangup button
document.getElementById("hangup-button").disabled = true;
targetUsername = null;
}
// Handle the "hang-up" message, which is sent if the other peer
// has hung up the call or otherwise disconnected.
function handleHangUpMsg(msg) {
log("*** Received hang up notification from other peer");
closeVideoCall();
}
// Hang up the call by closing our end of the connection, then
// sending a "hang-up" message to the other peer (keep in mind that
// the signaling is done on a different connection). This notifies
// the other peer that the connection should be terminated and the UI
// returned to the "no call in progress" state.
function hangUpCall() {
closeVideoCall();
sendToServer({
name: myUsername,
target: targetUsername,
type: "hang-up"
});
}
// Handle a click on an item in the user list by inviting the clicked
// user to video chat. Note that we don't actually send a message to
// the callee here -- calling RTCPeerConnection.addTrack() issues
// a |notificationneeded| event, so we'll let our handler for that
// make the offer.
async function invite(evt) {
log("Starting to prepare an invitation");
if (myPeerConnection) {
alert("You can't start a call because you already have one open!");
} else {
var clickedUsername = evt.target.textContent;
// Don't allow users to call themselves, because weird.
if (clickedUsername === myUsername) {
alert("I'm afraid I can't let you talk to yourself. That would be weird.");
return;
}
// Record the username being called for future reference
targetUsername = clickedUsername;
log("Inviting user " + targetUsername);
// Call createPeerConnection() to create the RTCPeerConnection.
// When this returns, myPeerConnection is our RTCPeerConnection
// and webcamStream is a stream coming from the camera. They are
// not linked together in any way yet.
log("Setting up connection to invite user: " + targetUsername);
createPeerConnection();
// Get access to the webcam stream and attach it to the
// "preview" box (id "local_video").
try {
webcamStream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
document.getElementById("local_video").srcObject = webcamStream;
} catch(err) {
handleGetUserMediaError(err);
return;
}
// Add the tracks from the stream to the RTCPeerConnection
try {
webcamStream.getTracks().forEach(
transceiver = track => myPeerConnection.addTransceiver(track, {streams: [webcamStream]})
);
} catch(err) {
handleGetUserMediaError(err);
}
}
}
// Accept an offer to video chat. We configure our local settings,
// create our RTCPeerConnection, get and attach our local camera
// stream, then create and send an answer to the caller.
async function handleVideoOfferMsg(msg) {
targetUsername = msg.name;
// If we're not already connected, create an RTCPeerConnection
// to be linked to the caller.
log("Received video chat offer from " + targetUsername);
if (!myPeerConnection) {
createPeerConnection();
}
// We need to set the remote description to the received SDP offer
// so that our local WebRTC layer knows how to talk to the caller.
var desc = new RTCSessionDescription(msg.sdp);
// If the connection isn't stable yet, wait for it...
if (myPeerConnection.signalingState != "stable") {
log(" - But the signaling state isn't stable, so triggering rollback");
// Set the local and remove descriptions for rollback; don't proceed
// until both return.
await Promise.all([
myPeerConnection.setLocalDescription({type: "rollback"}),
myPeerConnection.setRemoteDescription(desc)
]);
return;
} else {
log (" - Setting remote description");
await myPeerConnection.setRemoteDescription(desc);
}
// Get the webcam stream if we don't already have it
if (!webcamStream) {
try {
webcamStream = await navigator.mediaDevices.getUserMedia(mediaConstraints);
} catch(err) {
handleGetUserMediaError(err);
return;
}
document.getElementById("local_video").srcObject = webcamStream;
// Add the camera stream to the RTCPeerConnection
try {
webcamStream.getTracks().forEach(
transceiver = track => myPeerConnection.addTransceiver(track, {streams: [webcamStream]})
);
} catch(err) {
handleGetUserMediaError(err);
}
}
log("---> Creating and sending answer to caller");
await myPeerConnection.setLocalDescription(await myPeerConnection.createAnswer());
sendToServer({
name: myUsername,
target: targetUsername,
type: "video-answer",
sdp: myPeerConnection.localDescription
});
}
// Responds to the "video-answer" message sent to the caller
// once the callee has decided to accept our request to talk.
async function handleVideoAnswerMsg(msg) {
log("*** Call recipient has accepted our call");
// Configure the remote description, which is the SDP payload
// in our "video-answer" message.
var desc = new RTCSessionDescription(msg.sdp);
await myPeerConnection.setRemoteDescription(desc).catch(reportError);
}
// A new ICE candidate has been received from the other peer. Call
// RTCPeerConnection.addIceCandidate() to send it along to the
// local ICE framework.
async function handleNewICECandidateMsg(msg) {
var candidate = new RTCIceCandidate(msg.candidate);
log("*** Adding received ICE candidate: " + JSON.stringify(candidate));
try {
await myPeerConnection.addIceCandidate(candidate)
} catch(err) {
reportError(err);
}
}
// Handle errors which occur when trying to access the local media
// hardware; that is, exceptions thrown by getUserMedia(). The two most
// likely scenarios are that the user has no camera and/or microphone
// or that they declined to share their equipment when prompted. If
// they simply opted not to share their media, that's not really an
// error, so we won't present a message in that situation.
function handleGetUserMediaError(e) {
log_error(e);
switch(e.name) {
case "NotFoundError":
alert("Unable to open your call because no camera and/or microphone" +
"were found.");
break;
case "SecurityError":
case "PermissionDeniedError":
// Do nothing; this is the same as the user canceling the call.
break;
default:
alert("Error opening your camera and/or microphone: " + e.message);
break;
}
// Make sure we shut down our end of the RTCPeerConnection so we're
// ready to try again.
closeVideoCall();
}
// Handles reporting errors. Currently, we just dump stuff to console but
// in a real-world application, an appropriate (and user-friendly)
// error message should be displayed.
function reportError(errMessage) {
log_error(`Error ${errMessage.name}: ${errMessage.message}`);
}

195
clientV/scripts/rtc3.js Normal file
View File

@@ -0,0 +1,195 @@
var name;
var connections = {};
const configuration = {
iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
};
const offerOptions = {
offerToReceiveAudio: 1,
offerToReceiveVideo: 1
};
function handleLogin(success) {
if (success === false) {
alert("try a different username");
} else {
}
};
async function createPeerConnection(target) {
console.log("CREATED PEER CONNECTION");
var connection = new RTCPeerConnection(configuration);
connections[target] = connection;
connection.onicecandidate = function(event) {
if (event.candidate) {
send({
type: "candidate",
name: name,
target: target,
candidate: event.candidate
});
}
};
connection.onnegotiationneeded = function() { handleNegotiationNeededEvent(target); };
connection.ontrack = function(event) { handleTrackEvent(event); }
connection.onsignalingstatechange = function() { handleSignalingStateChangeEvent(connection); }
connection.oniceconnectionstatechange = function() { handleICEConnectionStateChangeEvent(connection); }
connection.onicegatheringstatechange = function() { handleICEGatheringStateChangeEvent(connection); }
//window.setInterval(getConnectionStats, 1000);
}
function handleICEConnectionStateChangeEvent(connection) {
console.log("ICE CONNECTION CHANGE "+connection.iceConnectionState);
}
function handleICEGatheringStateChangeEvent(connection) {
console.log("ICE GATHERING CHANGE "+connection.iceGatheringState);
}
async function makeOffer(target) {
createPeerConnection(target);
var connection = connections[target];
var offer = await connection.createOffer();
send({
type: "offer",
name: name,
target: target,
offer: offer
});
await connection.setLocalDescription(offer);
}
async function handleOffer(offer, target) {
console.log("GOT OFFER FROM "+target);
await createPeerConnection(target);
var connection = connections[target];
await connection.setRemoteDescription(new RTCSessionDescription(offer));
if (stream) {
console.log("STREAM DETECTED");
stream.getTracks().forEach((track) => {
console.log("ADDED TRACK");
connection.addTrack(track, stream);
});
}
//create an answer to an offer
var answer = await connection.createAnswer();
await connection.setLocalDescription(answer);
send({
type: "answer",
name: name,
target: target,
answer: answer
});
};
async function handleAnswer(answer, target) {
console.log("GOT ANSWER FROM "+target);
var connection = connections[target];
await connection.setRemoteDescription(new RTCSessionDescription(answer));
};
async function handleCandidate(candidate, target) {
console.log("GOT CANDIDATE FROM "+target);
var connection = connections[target];
await connection.addIceCandidate(new RTCIceCandidate(candidate));
};
async function handleNegotiationNeededEvent(target) {
console.log("NEGOTIATION NEEDED FROM "+target);
var connection = connections[target];
var offer = await connection.createOffer(offerOptions);
await connection.setLocalDescription(offer);
send({
type: "video-offer",
name: name,
target: target,
sdp: connection.localDescription
});
}
function handleLeave() {
connections.foreach( (connection) => {
connection.close();
connection.onicecandidate = null;
//connection.onaddstream = null;
connection = null;
});
connections = {};
remoteVideo.src = null;
};
function handleUserlist(list) {
console.log("GOT USER LIST");
}
async function handleVideoOffer(sdp, target) {
console.log("GOT VIDEO OFFER FROM "+target);
await createPeerConnection(target);
var connection = connections[target];
await connection.setRemoteDescription(new RTCSessionDescription(sdp))
if (stream) {
console.log("STREAM DETECTED");
stream.getTracks().forEach((track) => {
console.log("ADDED TRACK");
connection.addTrack(track, stream);
});
}
var answer = await connection.createAnswer();
await connection.setLocalDescription(answer);
send({
type: "video-answer",
name: name,
target: target,
sdp: answer
});
}
async function handleVideoAnswer(sdp, target) {
console.log("GOT VIDEO ANSWER FROM "+target);
var connection = connections[target];
await connection.setRemoteDescription(new RTCSessionDescription(sdp));
}
async function handleSignalingStateChangeEvent(connection) {
console.log("STATE CHANGED TO : " + connection.signalingState);
switch(connection.signalingState) {
case "closed":
await connection.close();
break;
}
}
function handleTrackEvent(event) {
console.log("GOT TRACK");
remoteVideo.srcObject = event.streams[0];
//document.getElementById("hangup-button").disabled = false;
}
function getConnectionStats() {
for ([connection, target] in connections) {
console.log("[" + target + "] " + connection.connectionState);
}
}

203
clientV/scripts/rtc_old.js Normal file
View File

@@ -0,0 +1,203 @@
//our username
var name;
var connectedUser;
//connecting to our signaling server
var conn = new WebSocket('ws://localhost:9090');
conn.onopen = function () {
console.log("Connected to the signaling server");
};
//when we got a message from a signaling server
conn.onmessage = function (msg) {
console.log("Got message", msg.data);
var data = JSON.parse(msg.data);
switch(data.type) {
case "login":
handleLogin(data.success);
break;
//when somebody wants to call us
case "offer":
handleOffer(data.offer, data.name);
break;
case "answer":
handleAnswer(data.answer);
break;
//when a remote peer sends an ice candidate to us
case "candidate":
handleCandidate(data.candidate);
break;
case "leave":
handleLeave();
break;
default:
break;
}
};
conn.onerror = function (err) {
console.log("Got error", err);
};
//alias for sending JSON encoded messages
function send(message) {
//attach the other peer username to our messages
if (connectedUser) {
message.name = connectedUser;
}
conn.send(JSON.stringify(message));
};
//******
//UI selectors block
//******
var loginPage = document.querySelector('#loginPage');
var usernameInput = document.querySelector('#usernameInput');
var loginBtn = document.querySelector('#loginBtn');
var callPage = document.querySelector('#callPage');
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn');
var hangUpBtn = document.querySelector('#hangUpBtn');
var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
var yourConn;
var stream;
callPage.style.display = "none";
// Login when the user clicks the button
loginBtn.addEventListener("click", function (event) {
name = usernameInput.value;
if (name.length > 0) {
send({
type: "login",
name: name
});
}
});
function handleLogin(success) {
if (success === false) {
alert("Ooops...try a different username");
} else {
loginPage.style.display = "none";
callPage.style.display = "block";
//**********************
//Starting a peer connection
//**********************
//stream = myStream;
//displaying local video stream on the page
//localVideo.src = window.URL.createObjectURL(stream);
//using Google public stun server
var configuration = {
"iceServers": [{ "url": "stun:stun2.1.google.com:19302" }]
};
yourConn = new RTCPeerConnection(configuration);
// setup stream listening
//yourConn.addStream(stream);
//when a remote user adds stream to the peer connection, we display it
yourConn.onaddstream = function (e) {
//remoteVideo.src = window.URL.createObjectURL(e.stream);
};
// Setup ice handling
yourConn.onicecandidate = function (event) {
if (event.candidate) {
send({
type: "candidate",
candidate: event.candidate
});
}
};
}
};
//initiating a call
callBtn.addEventListener("click", function () {
var callToUsername = callToUsernameInput.value;
if (callToUsername.length > 0) {
connectedUser = callToUsername;
// create an offer
yourConn.createOffer(function (offer) {
send({
type: "offer",
offer: offer
});
yourConn.setLocalDescription(offer);
}, function (error) {
alert("Error when creating an offer");
});
}
});
//when somebody sends us an offer
function handleOffer(offer, name) {
connectedUser = name;
yourConn.setRemoteDescription(new RTCSessionDescription(offer));
//create an answer to an offer
yourConn.createAnswer(function (answer) {
yourConn.setLocalDescription(answer);
send({
type: "answer",
answer: answer
});
}, function (error) {
alert("Error when creating an answer");
});
};
//when we got an answer from a remote user
function handleAnswer(answer) {
yourConn.setRemoteDescription(new RTCSessionDescription(answer));
};
//when we got an ice candidate from a remote user
function handleCandidate(candidate) {
yourConn.addIceCandidate(new RTCIceCandidate(candidate));
};
//hang up
hangUpBtn.addEventListener("click", function () {
send({
type: "leave"
});
handleLeave();
});
function handleLeave() {
connectedUser = null;
remoteVideo.src = null;
yourConn.close();
yourConn.onicecandidate = null;
yourConn.onaddstream = null;
};

64
clientV/scripts/script.js Normal file
View File

@@ -0,0 +1,64 @@
var loginInput = document.querySelector('#loginInput');
var loginBt = document.querySelector('#loginBt');
var callInput = document.querySelector('#callInput');
var callBt = document.querySelector('#callBt');
var disconnectBt = document.querySelector('#disconnectBt');
const remoteVideo = document.querySelector('#video');
var videoInput = document.querySelector('#videoInput');
var stream;
loginBt.addEventListener("click", function (event) {
name = loginInput.value;
if (name.length > 0) {
send({
type: "login",
name: name
});
}
});
callBt.addEventListener("click", function () {
var callToUsername = callInput.value;
if (callToUsername.length > 0) {
makeOffer(callToUsername);
}
});
disconnectBt.addEventListener("click", function () {
send({
type: "leave",
name: name
});
handleLeave();
});
videoInput.addEventListener("change", function (event) {
var file = this.files[0]
var type = file.type
var videoNode = remoteVideo2
var canPlay = videoNode.canPlayType(type)
if (canPlay === '') canPlay = 'no'
var message = 'Can play type "' + type + '": ' + canPlay
var isError = canPlay === 'no'
//displayMessage(message, isError)
if (isError) {
return
}
var fileURL = URL.createObjectURL(file)
videoNode.src = fileURL
});
remoteVideo.onplay = function() {
console.log("ADD STREAM");
if(remoteVideo.mozCaptureStream()) stream = remoteVideo.mozCaptureStream();
else stream = remoteVideo.captureStream();
}

60
clientV/scripts/signal.js Normal file
View File

@@ -0,0 +1,60 @@
//connecting to our signaling server
var conn = new WebSocket('ws://localhost:9090');
conn.onopen = function () {
console.log("Connected to the signaling server");
};
//when we got a message from a signaling server
conn.onmessage = function (msg) {
console.log("Got message", msg.data);
var data = JSON.parse(msg.data);
switch(data.type) {
case "login":
handleLogin(data.success);
break;
case "offer":
handleOffer(data.offer, data.name);
break;
case "answer":
handleAnswer(data.answer, data.name);
break;
case "candidate":
handleCandidate(data.candidate, data.name);
break;
case "userlist":
handleUserlist(data);
break;
case "leave":
handleLeave();
break;
case "video-offer":
handleVideoOffer(data.sdp, data.name);
break;
case "video-answer":
handleVideoAnswer(data.sdp, data.name);
break;
default:
break;
}
};
conn.onerror = function (err) {
console.log("Got error", err);
};
//alias for sending JSON encoded messages
function send(message) {
console.log("Sended message", message);
conn.send(JSON.stringify(message));
};