This commit is contained in:
bel
2021-09-14 06:30:17 -06:00
commit 7ab1723a5e
327 changed files with 127104 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
var RTCPeerConnection = null;
var getUserMedia = null;
var attachMediaStream = null;
var reattachMediaStream = null;
var webrtcDetectedBrowser = null;
if (navigator.mozGetUserMedia) {
console.log("This appears to be Firefox");
webrtcDetectedBrowser = "firefox";
// The RTCPeerConnection object.
RTCPeerConnection = mozRTCPeerConnection;
// The RTCSessionDescription object.
RTCSessionDescription = mozRTCSessionDescription;
// The RTCIceCandidate object.
RTCIceCandidate = mozRTCIceCandidate;
// Get UserMedia (only difference is the prefix).
// Code from Adam Barth.
getUserMedia = navigator.mozGetUserMedia.bind(navigator);
// Attach a media stream to an element.
attachMediaStream = function(element, stream) {
console.log("Attaching media stream");
element.mozSrcObject = stream;
element.play();
};
reattachMediaStream = function(to, from) {
console.log("Reattaching media stream");
to.mozSrcObject = from.mozSrcObject;
to.play();
};
// Fake get{Video,Audio}Tracks
MediaStream.prototype.getVideoTracks = function() {
return [];
};
MediaStream.prototype.getAudioTracks = function() {
return [];
};
} else if (navigator.webkitGetUserMedia) {
console.log("This appears to be Chrome");
webrtcDetectedBrowser = "chrome";
// The RTCPeerConnection object.
RTCPeerConnection = webkitRTCPeerConnection;
// Get UserMedia (only difference is the prefix).
// Code from Adam Barth.
getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
// Attach a media stream to an element.
attachMediaStream = function(element, stream) {
element.src = webkitURL.createObjectURL(stream);
};
reattachMediaStream = function(to, from) {
to.src = from.src;
};
// The representation of tracks in a stream is changed in M26.
// Unify them for earlier Chrome versions in the coexisting period.
if (!webkitMediaStream.prototype.getVideoTracks) {
webkitMediaStream.prototype.getVideoTracks = function() {
return this.videoTracks;
};
webkitMediaStream.prototype.getAudioTracks = function() {
return this.audioTracks;
};
}
// New syntax of getXXXStreams method in M26.
if (!webkitRTCPeerConnection.prototype.getLocalStreams) {
webkitRTCPeerConnection.prototype.getLocalStreams = function() {
return this.localStreams;
};
webkitRTCPeerConnection.prototype.getRemoteStreams = function() {
return this.remoteStreams;
};
}
} else {
console.log("Browser does not appear to be WebRTC-capable");
}

View File

@@ -0,0 +1,36 @@
//http://stackoverflow.com/questions/4583395/calculate-speed-using-javascript
/*
this is only download estimation, obviously, in peer2peer default deployment, you will
have full mesh, n*times both upstream and donwstream stream comming to you (which is bad,
but unless you have a server in between this is what it is). Remark: simetrical upstream/downstream
is not common in DSL deplyoment, where downstream bandwith is much higher.
*/
var BANDWITDH = (function(){
var imageAddr;
var size;
var startTime, endTime;
var downloadSize;
var download = new Image();
return {
/*
don't forget to change the address every time you make a request to avoid browser caching.
For a demo, I will be using the same as in the stackoverflow example
*/
init: function(callback, address, size){
imageAddr = address !== undefined ? address : "http://www.tranquilmusic.ca/images/cats/Cat2.JPG" + "?n=" + Math.random();
downloadSize = size !== undefined ? size : 5616998;
startTime = (new Date()).getTime();
download.src = imageAddr;
download.onload = function() {
endTime = (new Date()).getTime();
var duration = (endTime - startTime) / 1000;
var bitsLoaded = downloadSize * 8;
var speedBps = (bitsLoaded / duration).toFixed(2);
var speedKbps = (speedBps / 1024).toFixed(2);
var speedMbps = (speedKbps / 1024).toFixed(2);
callback(speedMbps);
}
},
}
})();

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,258 @@
var socketAddress = 'ws://localhost:1337/';
var RTCApp = {
name: null,
webRTC: null,
commChannel: null,
message: null
};
RTCApp.commChannel = signaling({webSocketAddress : socketAddress, id: RTCApp.name });
RTCApp.commChannel.addCallback('presence' , presenceCallback);
RTCApp.commChannel.addCallback('s-offer', accept);
RTCApp.commChannel.addCallback('roster', roster);
RTCApp.webRTC = webrtc({sourcevid : document.getElementById('sourceSmallvid'),
stunServer : "stun.l.google.com:19302",
commChannel : RTCApp.commChannel,
onremote : remoteCallback,
constrains : 'dynamic'
});
RTCApp.webRTC.startVideo();
var users = {};
var caller, newUser;
var snd = new Audio("data/ringtone.wav");
var newUsers = {};
$(document).ready(function() {
//List of people and rooms is either retrived by uncommenting lines below,
//or after first successfull login (when the socket server send it to the logged user)
//loadFromJSON("data/people.json", "ajax-modal", ".people-carousel", false);
//loadFromJSON("data/rooms.json", "ajax-room-modal", ".rooms-carousel", true);
$('#people_content').hide();
$('#room_content').hide();
$('#people').fadeIn();
$('[id^="myCarousel"]').carousel({interval: false});
});
$("#form").submit(function(event) {
event.preventDefault();
RTCApp.name = $("#user").val();
RTCApp.commChannel.sendPresence(RTCApp.name, 'on');
$('#user').attr('readonly', true);
$("#submit").hide();
});
$('#accept').bind('click', function() {
RTCApp.commChannel.answer(caller, 'accept');
$('#acceptModal').modal('hide');
});
$('#reject').bind('click', function() {
RTCApp.commChannel.answer(caller, 'reject');
$('#acceptModal').modal('hide');
});
function accept(message){
if(message.from === RTCApp.name){
console.log("can't call yourself ");
return;
}
caller = message.from;
snd.play();
$('#callerTitle').text('Incoming call');
$('#caller').text('Caller id '+ message.from);
$('#acceptModal').modal('show');
}
function talkFunction(flag){
var callback = flag === true? "hide" : "show";
$('#people_content')[callback]();
$('#room_content')[callback]();
}
function remoteCallback(from, added, stream){
var resource = 'resource_'+ from;
if(added){
if (window.webkitURL) {
$('.inner').append('<video class="span4" id=' + resource + ' src=' +
window.webkitURL.createObjectURL(event.stream) + ' autoplay></video>');
} else {
$('.inner').append('<video class="span4" id=' + resource + ' mozSrcObject=' +
event.stream + ' autoplay></video>');
}
} else {
$('#'+resource).attr('src',"");
$('#'+resource).remove();
}
}
$('#acceptNewUser').bind('click', function() {
delete newUsers[newUser];
addNewUser({
"name" : newUser,
"id": newUser,
"img" : "images/person.jpg"
}, "ajax-modal", ".people-carousel");
$('#userModal').modal('hide');
});
$('#rejectNewUser').bind('click', function() {
newUsers[newUser] = false;
$('#userModal').modal('hide');
});
function presenceCallback(message){
roomFlag = message.room !== undefined;
var user_id, room_id;
if(!roomFlag){
$(".ajax-modal").each(function(){
user_id = $(this).attr('user-id');
if(user_id !== undefined && user_id === message.name){
if(message.status === 'on'){
$(this).find('img').attr('src', 'images/online-icon.png');
} else {
$(this).find('img').attr('src', 'images/offline-icon.png');
}
}
});
if(users[message.name] === undefined && message.status === 'on'){
console.log('presence received from the person that is not in the address book ' + message.name);
if(newUsers[message.name] === undefined){
newUsers[message.name] = true;
}
}
}
else{
$(".ajax-room-modal").each(function(){
room_id = $(this).attr('user-id');
if(room_id !== undefined && room_id === message.name){
if(message.status === 'on'){
$(this).find('img').attr('src', 'images/online-icon.png');
}else {
$(this).find('img').attr('src', 'images/offline-icon.png');
}
}
});
}
}
$('#main').delegate('a.ajax-modal', 'click', function() {
event.preventDefault();
var user_id = $(this).attr('user-id');
if(user_id !== undefined && user_id !== RTCApp.name)
RTCApp.commChannel.callOtherParty(user_id);
});
$('#main').delegate('a.ajax-room-modal', 'click', function() {
event.preventDefault();
var room = $(this).attr('user-id');
RTCApp.commChannel.joinRoom(room);
});
$.ajaxSetup({
'beforeSend' : function(xhr) {
xhr.overrideMimeType('text/html; charset=ISO-8859-1');
},
});
window.onbeforeunload = function() {
if(RTCApp.commChannel !== null)
RTCApp.commChannel.sendPresence(RTCApp.name, 'off');
}
function addNewUser(jsonData, class_name, class_div){
users[jsonData.id] = jsonData;
$('.people-carousel').empty();
var array = $.map(users, function (value, key) { return value; });
addDataToDiv(array, class_name, class_div, false);
}
function loadFromJSON(file, class_name, class_div, roomFlag){
$.getJSON(file, function(data) {
addDataToDiv(data, class_name, class_div, roomFlag);
});
}
function roster(message){
addDataToDiv(message.people, "ajax-modal", ".people-carousel", false);
addDataToDiv(message.rooms, "ajax-room-modal", ".rooms-carousel", true);
if(message.people.length > 0)
$('#people_content').show();
if(message.rooms.length > 0)
$('#room_content').show();
if(users[RTCApp.name] === undefined){
console.log('user not known by the system, create an avatar');
addNewUser({
"name" : RTCApp.name,
"id": RTCApp.name,
"img" : "images/person.jpg"
}, "ajax-modal", ".people-carousel");
}
if(users[RTCApp.name].room !== undefined){
for(var i=0; i < users[RTCApp.name].room.length; i ++)
RTCApp.commChannel.joinRoom(users[RTCApp.name].room[i]);
}
}
function addDataToDiv(data, class_name, class_div, roomFlag){
var items = [];
var i = 0;
var groupIndex = 12;
$.each(data, function(key, value) {
var name = this.name;
var image = this.img;
var id = this.id;
if(!roomFlag)
users[id] = value;
if(i % groupIndex === 0){
if(i === 0)
items.push('<div class="item active">');
else
items.push('<div class="item">');
items.push('<ul class="thumbnails">');
}
items.push('<li class="span1"><div class="thumbnail"><img src="' + image
+' " alt=""></a> <h4>' + name + '</h4><p><a href="#" class="btn btn-primary ' +
class_name + ' " user-id="' + id +
'" >Talk<img src="images/offline-icon.png" width="24" height="24" align="left" alt=""> </a></p></div></li>');
if( (i % groupIndex) === (groupIndex - 1) ){
items.push('</ul>');
items.push('</div>');
}
i++;
});
if(items.lenght > 0 && items[items.lenght - 1].indexOf('div') < 0 ){
items.push('</ul>');
items.push('</div>');
}
$(items.join('')).appendTo(class_div);
}
setInterval(function(){
for(newUser in newUsers){
if(newUsers[newUser]){
$('#userTitle').text('Accept a new user?');
$('#callerUser').text('User id '+ newUser);
$('#userModal').modal('show');
break;
}
}
}, 10000);

View File

@@ -0,0 +1,76 @@
var signaling = function(options){
var socket = new WebSocket(options.webSocketAddress);
var logg = function(s) { console.log(s); };
var myId = options.id;
var that = {};
var callbacks = {};
function getCallback(type){
return callbacks[type] !== undefined ? callbacks[type] : function(){
console.log("Callback of type " + type + " not found");
};
}
that.addCallback = function(type, f){
callbacks[type] = f;
}
that.sendMessage = function(message, to) {
sendMsg(message, to);
}
that.sendPresence = function(_name, stat){
_status = stat || 'on';
myId = _name;
sendMsg({type: 'presence', name: _name, status: _status});
}
that.joinRoom = function(room){
sendMsg({type: 'room'}, room);
}
that.callOtherParty = function(to){
sendMsg({type: 's-offer'}, to);
};
that.answer = function(to, _answer){
sendMsg({type: 's-answer', answer: _answer}, to);
};
function sendMsg(message, to){
message.from = myId;
if(to !== undefined)
message.to = to;
var mymsg = JSON.stringify(message);
logg("SOCKET Send: " + mymsg);
socket.send(mymsg);
}
socket.addEventListener("message", onMessage, false);
socket.addEventListener("error", function(event) {
logg("SOCKET Error: " + event);
});
socket.addEventListener("close", function(event) {
logg("SOCKET Close: " + event);
});
function onMessage(evt) {
logg("RECEIVED: " + evt.data);
processSignalingMessage(evt.data);
}
//message comes as a JSON from the websocket server
function processSignalingMessage(message) {
var msg = JSON.parse(message);
logg("processSignalingMessage type(" + msg.type + ")= " + message);
getCallback(msg.type)(msg);
}
return that;
}

View File

@@ -0,0 +1,210 @@
var webrtc = function(options) {
var my = {};
var commChannel = options.commChannel,
stunServer = options.stunServer,
sourcevid = options.sourcevid,
remoteCallback = options.onremote;
var localStream;
var peerConn = {};
var mediaConstraints = {'mandatory': {'OfferToReceiveAudio':true, 'OfferToReceiveVideo':true }};
if(options.constrains === 'dynamic'){
BANDWITDH.init(function(bandwitdh){
console.log('calculate bandwitdh');
if(!isNaN(bandwitdh) && bandwitdh < 0.5){
mediaConstraints = {'mandatory': {
'OfferToReceiveAudio':true,
'OfferToReceiveVideo':false }};
}
console.log('bandwitdh is ' + bandwitdh + ' [Mbps]');
});
}
//callback to start p2p connection between two parties
commChannel.addCallback('s-answer', call);
commChannel.addCallback('offer', processSignalingMessage);
commChannel.addCallback('answer', processSignalingMessage);
commChannel.addCallback('candidate', processSignalingMessage);
commChannel.addCallback('bye', processSignalingMessage);
function RTCPeer(pc_config, name) {
this.from = name;
this.rtc = new RTCPeerConnection(pc_config);
that = this;
this.rtc.onaddstream = function(event){
logg("Added remote stream");
remoteCallback(that.from, true, event.stream);
};
this.rtc.onremovestream = function(event) {
logg("Remove remote stream");
remoteCallback(that.from, false);
};
this.rtc.onicecandidate = function(event) {
logg("send on Icecandidate");
if (event.candidate) {
commChannel.sendMessage({type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate}, that.from);
} else {
logg("End of candidates.");
}
};
}
function setLocalDescriptionAndMessage(sessionDescription){
logg("setLocalDescriptionAndMessage");
this.rtc.setLocalDescription(sessionDescription);
commChannel.sendMessage(sessionDescription, this.from);
}
RTCPeer.prototype.createOffer = function(callback){
logg("createOffer to " + this.from);
that = this;
this.rtc.createOffer(function(sessionDescription){
callback.call(that, sessionDescription);
}, null, mediaConstraints);
}
RTCPeer.prototype.createAnswer = function(callback){
logg("createAnswer to " + this.from);
that = this;
this.rtc.createAnswer(function(sessionDescription){
callback.call(that, sessionDescription);
}, null, mediaConstraints);
}
RTCPeer.prototype.getRTC = function(){
return this.rtc;
}
RTCPeer.prototype.getFrom = function(){
return this.from;
}
var logg = function(s) { console.log(s); };
my.startVideo = function() {
try {
getUserMedia({audio: true, video: true}, successCallback, errorCallback);
} catch (e) {
getUserMedia("video,audio", successCallback, errorCallback);
}
function successCallback(stream) {
attachMediaStream(sourcevid, stream);
localStream = stream;
logg('local stream started');
}
function errorCallback(error) {
logg('An error occurred: [CODE ' + error.code + ']');
}
}
my.stopVideo = function() {
sourcevid.src = "";
}
my.onHangUp = function() {
logg("Hang up.");
closeSession();
}
// start the connection upon user request
function call(msg) {
if(msg.answer !== 'accept') {
console.log('call not accepted');
return;
}
if (peerConn[msg.from] === undefined && localStream) {
logg("Creating PeerConnection with "+ msg.from);
createPeerConnection(msg.from);
} else if (!localStream){
alert("Please start the video first");
logg("localStream not started");
return;
} else {
logg("peer SDP offer already made");
}
logg("create offer");
peerConn[msg.from].createOffer(setLocalDescriptionAndMessage);
}
function createPeerConnection(from) {
try {
logg("Creating peer connection with " + from);
var servers = [];
servers.push({'url':'stun:' + stunServer});
var pc_config = {'iceServers':servers};
peerConn[from] = new RTCPeer(pc_config, from);
logg("Connected using stun server "+ stunServer);
} catch (e) {
alert("Failed to create PeerConnection, exception: " + e.message);
return;
}
logg('Adding local stream...');
peerConn[from].getRTC().addStream(localStream);
}
function processSignalingMessage(msg) {
logg("processSignalingMessage type(" + msg.type + ")= " + msg);
if (msg.type === 'offer') {
if(peerConn[msg.from] === undefined && localStream) {
createPeerConnection(msg.from);
//set remote description
peerConn[msg.from].getRTC().setRemoteDescription(new RTCSessionDescription(msg));
//create answer
logg("Sending answer to peer.");
peerConn[msg.from].createAnswer(setLocalDescriptionAndMessage);
} else {
logg('peerConnection has already been started');
}
} else if (msg.type === 'answer' && peerConn[msg.from] !== undefined) {
logg("setRemoteDescription...");
peerConn[msg.from].getRTC().setRemoteDescription(new RTCSessionDescription(msg));
} else if (msg.type === 'candidate' && peerConn[msg.from] !== undefined) {
var candidate = new RTCIceCandidate({sdpMLineIndex:msg.label, candidate:msg.candidate});
peerConn[msg.from].getRTC().addIceCandidate(candidate);
} else if (msg.type === 'bye' && peerConn[msg.from] !== undefined) {
onRemoteHangUp(msg.from);
} else {
logg("message unknown:" + msg);
}
}
function onRemoteHangUp(from) {
logg("Remote(" + from + ") Hang up ");
remoteCallback(from, false);
peerConn[from].getRTC().close();
delete peerConn[from];
}
function closeSession() {
for(var index in peerConn){
remoteCallback(peerConn[index].getFrom(), false);
peerConn[index].getRTC().close();
delete peerConn[index];
}
commChannel.sendMessage({type: 'bye'});
}
window.onbeforeunload = function() {
if (Object.keys(peerConn).length > 0) {
closeSession();
}
}
return my;
};