Commit 5cec4267 authored by Dele Olajide's avatar Dele Olajide Committed by dele

Jitsi Videobridge - Added screen/desktop sharing to ofmeet and candy

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13866 b35dd754-fafc-0310-a699-88a17e54d16e
parent 8f7385bf
...@@ -31,6 +31,29 @@ ...@@ -31,6 +31,29 @@
return results[1] || undefined; return results[1] || undefined;
}; };
function toggleFullScreen()
{
var videoElement = document.getElementById("largeVideo");
if (!document.mozFullScreen && !document.webkitFullScreen)
{
if (videoElement.mozRequestFullScreen) {
videoElement.mozRequestFullScreen();
} else {
videoElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else {
document.webkitCancelFullScreen();
}
}
}
$(document).ready(function() { $(document).ready(function() {
Candy.init('/http-bind/', { Candy.init('/http-bind/', {
core: { debug: false, websockets: true}, core: { debug: false, websockets: true},
...@@ -59,11 +82,10 @@ ...@@ -59,11 +82,10 @@
CandyShop.Videobridge.init(); CandyShop.Videobridge.init();
CandyShop.Fastpath.init(); CandyShop.Fastpath.init();
Candy.Core.connect(); if (username)
Candy.Core.connect(username + "@" + window.location.hostname, password, username);
// for testing only else
// Candy.Core.connect(username + "@" + window.location.hostname, password, username); Candy.Core.connect();
}); });
</script> </script>
...@@ -72,7 +94,7 @@ ...@@ -72,7 +94,7 @@
<div class="fade_line"></div> <div class="fade_line"></div>
<div id="candy"></div> <div id="candy"></div>
<div class="fade_line"></div> <div class="fade_line"></div>
<video id="largeVideo" autoplay oncontextmenu="return false;"></video> <video id="largeVideo" onDblClick="toggleFullScreen();" autoplay oncontextmenu="return false;"></video>
<div id="remoteVideos"> <div id="remoteVideos">
<video id="localVideo" autoplay oncontextmenu="return false;" muted/> <video id="localVideo" autoplay oncontextmenu="return false;" muted/>
</div> </div>
......
...@@ -38,6 +38,13 @@ body{ ...@@ -38,6 +38,13 @@ body{
#mic-control.muted { #mic-control.muted {
background-image: url(mic_muted.png); background-image: url(mic_muted.png);
} }
#screen-control {
background-image: url(desktop_off.png);
}
#screen-control.active {
background-image: url(desktop_on.png);
}
#remoteVideos { #remoteVideos {
display:block; display:block;
......
...@@ -14,6 +14,7 @@ CandyShop.Videobridge = (function(self, Candy, $) { ...@@ -14,6 +14,7 @@ CandyShop.Videobridge = (function(self, Candy, $) {
var room = null; var room = null;
var previousRoom = null; var previousRoom = null;
var videoOn = false; var videoOn = false;
var screenShare = false;
self.init = function() self.init = function()
{ {
...@@ -57,7 +58,8 @@ CandyShop.Videobridge = (function(self, Candy, $) { ...@@ -57,7 +58,8 @@ CandyShop.Videobridge = (function(self, Candy, $) {
var html = ""; var html = "";
html += '<li id="videobridge-control" data-tooltip="add or remove video"></li>'; html += '<li id="videobridge-control" data-tooltip="add or remove video"></li>';
html += '<li id="webcam-control" data-tooltip="mute/toggle video mute"></li>'; html += '<li id="webcam-control" data-tooltip="mute/toggle video mute"></li>';
html += '<li id="mic-control" data-tooltip="toggle audio mute"></li>'; html += '<li id="mic-control" data-tooltip="toggle audio mute"></li>';
html += '<li id="screen-control" data-tooltip="toggle desktop screen share"></li>';
$('#chat-toolbar').prepend(html); $('#chat-toolbar').prepend(html);
...@@ -92,12 +94,29 @@ CandyShop.Videobridge = (function(self, Candy, $) { ...@@ -92,12 +94,29 @@ CandyShop.Videobridge = (function(self, Candy, $) {
} }
} }
}); });
$('#largeVideo').click(function() { $('#screen-control').click(function() {
$("#largeVideo").css("visibility", "hidden"); var videobridge = Strophe.getNodeFromJid(roomJid);
});
if (screenShare)
{
var screenDIV = document.getElementById("screenshare");
screenDIV.parentElement.removeChild(screenDIV);
$(this).removeClass('active');
} else {
var url = "../../publish.html?r=" + videobridge + "&screen=true";
//var url = "/ofmeet/publish.html?r=" + videobridge + "&screen=true";
$("body").append("<div id='screenshare'><iframe style='display:none' src='" + url + "'></iframe></div>");
$("#screen").addClass("fa-border");
$(this).addClass('active');
}
screenShare = !screenShare;
});
}; };
...@@ -186,7 +205,12 @@ CandyShop.Videobridge = (function(self, Candy, $) { ...@@ -186,7 +205,12 @@ CandyShop.Videobridge = (function(self, Candy, $) {
setTimeout(function() setTimeout(function()
{ {
if (window.RTC.rayo.pc) window.RTC.rayo.pc.close(); if (window.RTC.rayo.pc)
{
console.log("sendExpireCommand close peer conection");
window.RTC.rayo.pc.close();
window.RTC.rayo.pc = null;
}
}, 2000); }, 2000);
}, },
...@@ -338,7 +362,7 @@ CandyShop.Videobridge = (function(self, Candy, $) { ...@@ -338,7 +362,7 @@ CandyShop.Videobridge = (function(self, Candy, $) {
window.RTC.rayo.pc.onicecandidate = function(event) window.RTC.rayo.pc.onicecandidate = function(event)
{ {
//console.log('candidate', event.candidate); console.log('candidate', event.candidate);
if (!event.candidate) if (!event.candidate)
{ {
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
<a href="http://community.igniterealtime.org/community/support/jitsi_videobridge_plugin" target="_blank"><div id="leftlogo"><nobr>OfMeet</nobr></div></a> <a href="http://community.igniterealtime.org/community/support/jitsi_videobridge_plugin" target="_blank"><div id="leftlogo"><nobr>OfMeet</nobr></div></a>
<a href="http://jitsi.org" target="_blank"><div id="rightlogo"></div></a> <a href="http://jitsi.org" target="_blank"><div id="rightlogo"></div></a>
<div id="toolbar"> <div id="toolbar">
<a class="button" onclick='toggleScreenShare();'><i id="screen" title="share/unshare desktop" class="fa fa-desktop fa-lg"></i></a>
<div class="header_button_separator"></div>
<a class="button" onclick='buttonClick("#mute", "fa fa-microphone fa-lg fa fa-microphone-slash fa-lg");toggleAudio();'><i id="mute" title="Mute / unmute" class="fa fa-microphone fa-lg"></i></a> <a class="button" onclick='buttonClick("#mute", "fa fa-microphone fa-lg fa fa-microphone-slash fa-lg");toggleAudio();'><i id="mute" title="Mute / unmute" class="fa fa-microphone fa-lg"></i></a>
<div class="header_button_separator"></div> <div class="header_button_separator"></div>
<a class="button" onclick='buttonClick("#video", "fa fa-video-camera fa-lg fa fa-video-camera no-fa-video-camera fa-lg");toggleVideo();'><i id="video" title="Start / stop camera" class="fa fa-video-camera fa-lg"></i></a> <a class="button" onclick='buttonClick("#video", "fa fa-video-camera fa-lg fa fa-video-camera no-fa-video-camera fa-lg");toggleVideo();'><i id="video" title="Start / stop camera" class="fa fa-video-camera fa-lg"></i></a>
...@@ -45,6 +47,7 @@ ...@@ -45,6 +47,7 @@
<div id="remoteVideos"> <div id="remoteVideos">
<video id="localVideo" autoplay oncontextmenu="return false;" muted/> <video id="localVideo" autoplay oncontextmenu="return false;" muted/>
</div> </div>
<video id="screenVideo" autoplay oncontextmenu="return false;" muted style="display:block"/>
</div> </div>
<div id="chatspace"> <div id="chatspace">
<div id="nickname"> <div id="nickname">
...@@ -57,7 +60,6 @@ ...@@ -57,7 +60,6 @@
<div id="ofmeet"><div id="ofmeet-messaging"><div id="ofmeet-log"></div></div></div> <div id="ofmeet"><div id="ofmeet-messaging"><div id="ofmeet-log"></div></div></div>
<textarea id="usermsg" class= "animated" placeholder='Enter text...' autofocus></textarea> <textarea id="usermsg" class= "animated" placeholder='Enter text...' autofocus></textarea>
</div> </div>
<script> <script>
</script> </script>
</body> </body>
......
...@@ -894,7 +894,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -894,7 +894,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName)); iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName));
iq.setTo("jitsi-videobridge." + domainName); iq.setTo("jitsi-videobridge." + domainName);
String id = nickname + "-" + System.currentTimeMillis(); String id = "answer-" + nickname + "-" + System.currentTimeMillis();
ids.put(id, participant); ids.put(id, participant);
iq.setID(id); iq.setID(id);
...@@ -1004,7 +1004,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1004,7 +1004,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName)); iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName));
iq.setTo("jitsi-videobridge." + domainName); iq.setTo("jitsi-videobridge." + domainName);
String id = nickname + "-" + System.currentTimeMillis(); String id = "offer-" + nickname + "-" + System.currentTimeMillis();
ids.put(id, participant); ids.put(id, participant);
iq.setID(id); iq.setID(id);
...@@ -1039,7 +1039,8 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1039,7 +1039,8 @@ public class PluginImpl implements Plugin, PropertyEventListener
iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName)); iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName));
iq.setTo("jitsi-videobridge." + domainName); iq.setTo("jitsi-videobridge." + domainName);
String id = nickname + "-" + System.currentTimeMillis(); String id = "expire-" + nickname + "-" + System.currentTimeMillis();
ids.put(id, participant);
iq.setID(id); iq.setID(id);
Element conferenceIq = iq.setChildElement("conference", "http://jitsi.org/protocol/colibri"); Element conferenceIq = iq.setChildElement("conference", "http://jitsi.org/protocol/colibri");
...@@ -1286,13 +1287,17 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -1286,13 +1287,17 @@ public class PluginImpl implements Plugin, PropertyEventListener
Participant participant = ids.remove(id); Participant participant = ids.remove(id);
String username = participant.getUser().toString(); String username = participant.getUser().toString();
if (users.containsKey(username) == false) Log.info("FocusAgent response for user " + participant + " " + focusId + "\n" + conference);
if (id.startsWith("offer-") && users.containsKey(username) == false)
{ {
addUser(participant, conference); addUser(participant, conference);
} }
Log.info("FocusAgent response for user " + participant + " " + focusId + "\n" + conference); if (id.startsWith("answer-"))
broadcastSSRC(participant); {
broadcastSSRC(participant);
}
} else Log.error("FocusAgent deliver cannot find iq owner " + id + "\n" + packet); } else Log.error("FocusAgent deliver cannot find iq owner " + id + "\n" + packet);
......
...@@ -5,6 +5,6 @@ var config = { ...@@ -5,6 +5,6 @@ var config = {
bridge: 'jitsi-videobridge.' + window.location.hostname // FIXME: use XEP-0030 bridge: 'jitsi-videobridge.' + window.location.hostname // FIXME: use XEP-0030
}, },
useNicks: false, useNicks: false,
useWebsockets: false, useWebsockets: true,
bosh: window.location.protocol + "//" + window.location.host + '/http-bind/' // FIXME: use xep-0156 for that bosh: window.location.protocol + "//" + window.location.host + '/http-bind/' // FIXME: use xep-0156 for that
}; };
...@@ -3,6 +3,7 @@ var roomjid; ...@@ -3,6 +3,7 @@ var roomjid;
var nickname = null; var nickname = null;
var roomUrl = null; var roomUrl = null;
var sharedKey = ''; var sharedKey = '';
var screenShare = false;
$(document).ready(function () $(document).ready(function ()
{ {
...@@ -77,12 +78,17 @@ $(document).ready(function () ...@@ -77,12 +78,17 @@ $(document).ready(function ()
{ {
console.log('connected'); console.log('connected');
connection.send($pres()); connection.send($pres());
registerRayoEvents(); doJoin();
if (urlParam("screen")) if (urlParam("screen"))
{
getConstraints(['screen']); getConstraints(['screen']);
else $("#screen").addClass("fa-border");
} else {
getConstraints(['audio', 'video'], '720'); getConstraints(['audio', 'video'], '720');
$("#screen").removeClass("fa-border");
}
getUserMedia(); getUserMedia();
...@@ -113,7 +119,8 @@ $(document).bind('mediaready.rayo', function(event, stream) ...@@ -113,7 +119,8 @@ $(document).bind('mediaready.rayo', function(event, stream)
document.getElementById('largeVideo').src = document.getElementById('localVideo').src; document.getElementById('largeVideo').src = document.getElementById('localVideo').src;
console.log("mediaready.rayo"); console.log("mediaready.rayo");
doJoin();
registerRayoEvents();
}); });
$(document).bind('mediafailure.rayo', function(error) { $(document).bind('mediafailure.rayo', function(error) {
...@@ -180,31 +187,6 @@ $(document).bind('remotestreamadded.rayo', function(event, data, sid) ...@@ -180,31 +187,6 @@ $(document).bind('remotestreamadded.rayo', function(event, data, sid)
); );
}); });
function registerRayoEvents()
{
connection.sendIQ($iq({to: connection.domain, type: 'set'}).c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', action: 'register'}),
function (res) {
console.log('rayo colibri register set ok');
},
function (err) {
console.log('rayo colibri register got error', err);
}
);
}
function unregisterRayoEvents()
{
connection.sendIQ($iq({to: connection.domain, type: 'set'}).c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', action: 'unregister'}),
function (res) {
console.log('rayo colibri unregister set ok');
},
function (err) {
console.log('rayo colibri unregister got error', err);
}
);
}
function setupRTC() function setupRTC()
{ {
...@@ -217,7 +199,7 @@ function setupRTC() ...@@ -217,7 +199,7 @@ function setupRTC()
rayo: { rayo: {
channels: {}, channels: {},
confid: {}, confid: {},
pc: null, pc: {},
addssrc: {}, addssrc: {},
localStream: null, localStream: null,
constraints: {audio: false, video: false} constraints: {audio: false, video: false}
...@@ -244,7 +226,7 @@ function setupRTC() ...@@ -244,7 +226,7 @@ function setupRTC()
rayo: { rayo: {
channels: {}, channels: {},
confid: {}, confid: {},
pc: null, pc: {},
addssrc: {}, addssrc: {},
localStream: null, localStream: null,
constraints: {audio: false, video: false} constraints: {audio: false, video: false}
...@@ -462,7 +444,7 @@ function doJoin() { ...@@ -462,7 +444,7 @@ function doJoin() {
} }
connection.addHandler(rayoCallback, 'urn:xmpp:rayo:colibri:1'); connection.addHandler(rayoCallback, 'urn:xmpp:rayo:colibri:1');
connection.emuc.doJoin(myroomjid); connection.emuc.doJoin(myroomjid);
} }
function rayoCallback(presence) function rayoCallback(presence)
...@@ -490,13 +472,12 @@ function rayoCallback(presence) ...@@ -490,13 +472,12 @@ function rayoCallback(presence)
}; };
function removeSSRC(from, removesource) function removeSSRC(from, removesource)
{ {
console.log("removeSSRC input ssrc ", removesource); console.log("removeSSRC input ssrc ", removesource);
var videobridge = $(removesource).attr('videobridge'); var videobridge = $(removesource).attr('videobridge');
var sdp = new SDP(window.RTC.rayo.pc.remoteDescription.sdp); var sdp = new SDP(window.RTC.rayo.pc[videobridge].remoteDescription.sdp);
console.log("removeSSRC unmodified SDP", videobridge); console.log("removeSSRC unmodified SDP", videobridge);
...@@ -521,26 +502,29 @@ function removeSSRC(from, removesource) ...@@ -521,26 +502,29 @@ function removeSSRC(from, removesource)
//console.log("removeSSRC modified SDP", sdp.raw); //console.log("removeSSRC modified SDP", sdp.raw);
window.RTC.rayo.pc.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw} window.RTC.rayo.pc[videobridge].setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}
), function() { ), function() {
console.log('modify ok'); console.log('removeSSRC modify ok');
}, function(error) { }, function(error) {
console.log('handleSSRC modify failed'); console.log('removeSSRC modify failed');
}); });
}; };
function handleAddSSRC(from, addsource) function handleAddSSRC(from, addsource)
{ {
console.log("handleSSRC input ssrc ", addsource); console.log("handleSSRC input ssrc ", addsource);
var videobridge = $(addsource).attr('videobridge'); var videobridge = $(addsource).attr('videobridge');
var sdp = new SDP(window.RTC.rayo.pc.remoteDescription.sdp); var sdp = new SDP(window.RTC.rayo.pc[videobridge].remoteDescription.sdp);
$(addsource).find('content').each(function() $(addsource).find('content').each(function()
{ {
var name = $(this).attr('name'); var name = $(this).attr('name');
if (name == "audio") return;
var lines = ''; var lines = '';
...@@ -566,10 +550,10 @@ function handleAddSSRC(from, addsource) ...@@ -566,10 +550,10 @@ function handleAddSSRC(from, addsource)
window.RTC.rayo.addssrc = true; window.RTC.rayo.addssrc = true;
window.RTC.rayo.pc.setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw} window.RTC.rayo.pc[videobridge].setRemoteDescription(new RTCSessionDescription({type: 'offer', sdp: sdp.raw}
), function() { ), function() {
console.log('modify ok', window.RTC.rayo.pc.signalingState); console.log('handleAddSSRC modify ok', window.RTC.rayo.pc[videobridge].signalingState);
}, function(error) { }, function(error) {
console.log('handleSSRC modify failed'); console.log('handleSSRC modify failed');
...@@ -667,9 +651,10 @@ function handleOffer (from, offer) ...@@ -667,9 +651,10 @@ function handleOffer (from, offer)
//console.log("bridgeSDP.raw", bridgeSDP.raw); //console.log("bridgeSDP.raw", bridgeSDP.raw);
window.RTC.rayo.pc = new window.RTC.peerconnection(null, {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]}); window.RTC.rayo.pc[videobridge] = new window.RTC.peerconnection(null, {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]});
window.RTC.rayo.pc.onicecandidate = function(event)
window.RTC.rayo.pc[videobridge].onicecandidate = function(event)
{ {
//console.log('candidate', event.candidate); //console.log('candidate', event.candidate);
...@@ -680,22 +665,22 @@ function handleOffer (from, offer) ...@@ -680,22 +665,22 @@ function handleOffer (from, offer)
} }
window.RTC.rayo.pc.onaddstream = function(e) window.RTC.rayo.pc[videobridge].onaddstream = function(e)
{ {
console.log("onstream", e, window.RTC.rayo.addssrc); console.log("onstream", e, window.RTC.rayo.addssrc);
if (window.RTC.rayo.pc.signalingState == "have-remote-offer") if (window.RTC.rayo.pc[videobridge].signalingState == "have-remote-offer")
$(document).trigger('remotestreamadded.rayo', [e, nick]); $(document).trigger('remotestreamadded.rayo', [e, nick]);
window.RTC.rayo.pc.createAnswer(function(desc) window.RTC.rayo.pc[videobridge].createAnswer(function(desc)
{ {
if (!window.RTC.rayo.addssrc) if (!window.RTC.rayo.addssrc)
window.RTC.rayo.pc.setLocalDescription(desc); window.RTC.rayo.pc[videobridge].setLocalDescription(desc);
}); });
}; };
window.RTC.rayo.pc.addStream(window.RTC.rayo.localStream); window.RTC.rayo.pc[videobridge].addStream(window.RTC.rayo.localStream);
window.RTC.rayo.pc.setRemoteDescription(new RTCSessionDescription({type: "offer", sdp : bridgeSDP.raw})); window.RTC.rayo.pc[videobridge].setRemoteDescription(new RTCSessionDescription({type: "offer", sdp : bridgeSDP.raw}));
showToolbar(); showToolbar();
updateRoomUrl(window.location.href); updateRoomUrl(window.location.href);
...@@ -706,9 +691,9 @@ function sendAnswer(from, videobridge, confid, channelId) ...@@ -706,9 +691,9 @@ function sendAnswer(from, videobridge, confid, channelId)
{ {
console.log("sendAnswer"); console.log("sendAnswer");
var remoteSDP = new SDP(window.RTC.rayo.pc.localDescription.sdp); var remoteSDP = new SDP(window.RTC.rayo.pc[videobridge].localDescription.sdp);
//console.log("remoteSDP ", window.RTC.rayo.pc.localDescription.sdp); //console.log("remoteSDP ", window.RTC.rayo.pc[videobridge].localDescription.sdp);
var change = $iq({to: from, type: 'set'}); var change = $iq({to: from, type: 'set'});
change.c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', videobridge: videobridge}); change.c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', videobridge: videobridge});
...@@ -718,7 +703,6 @@ function sendAnswer(from, videobridge, confid, channelId) ...@@ -718,7 +703,6 @@ function sendAnswer(from, videobridge, confid, channelId)
{ {
if (remoteSDP.media[channel]) if (remoteSDP.media[channel])
{ {
var indx =
change.c('content', {name: remoteSDP.media[channel].indexOf('m=audio') > -1 ? 'audio' : 'video'}); change.c('content', {name: remoteSDP.media[channel].indexOf('m=audio') > -1 ? 'audio' : 'video'});
change.c('channel', {id: remoteSDP.media[channel].indexOf('m=audio') > -1 ? channelId[0] : channelId[1]}); change.c('channel', {id: remoteSDP.media[channel].indexOf('m=audio') > -1 ? channelId[0] : channelId[1]});
...@@ -793,7 +777,7 @@ function sendAnswer(from, videobridge, confid, channelId) ...@@ -793,7 +777,7 @@ function sendAnswer(from, videobridge, confid, channelId)
connection.sendIQ(change, connection.sendIQ(change,
function (res) { function (res) {
console.log('rayo colibri answer set ok', window.RTC.rayo.pc.signalingState); console.log('rayo colibri answer set ok', window.RTC.rayo.pc[videobridge].signalingState);
}, },
function (err) { function (err) {
...@@ -821,6 +805,56 @@ function toggleAudio() ...@@ -821,6 +805,56 @@ function toggleAudio()
} }
} }
function registerRayoEvents()
{
connection.sendIQ($iq({to: connection.domain, type: 'set'}).c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', action: 'offer', muc: roomjid}),
function (res) {
console.log('rayo colibri register set ok');
},
function (err) {
console.log('rayo colibri register got error', err);
}
);
}
function unregisterRayoEvents()
{
window.RTC.rayo.localStream.stop();
connection.sendIQ($iq({to: connection.domain, type: 'set'}).c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', action: 'expire', muc: roomjid}),
function (res) {
console.log('rayo colibri unregister set ok');
},
function (err) {
console.log('rayo colibri unregister got error', err);
}
);
}
function toggleScreenShare()
{
console.log('toggleScreenShare', window.RTC.rayo.constraints);
var videobridge = Strophe.getNodeFromJid(roomjid);
if (screenShare)
{
var screenDIV = document.getElementById("screenshare");
screenDIV.parentElement.removeChild(screenDIV);
$("#screen").removeClass("fa-border");
} else {
var url = "publish.html?r=" + videobridge + "&screen=true";
$("body").append("<div id='screenshare'><iframe style='display:none' src='" + url + "'></iframe></div>");
$("#screen").addClass("fa-border");
}
screenShare = !screenShare;
}
function updateChatConversation(nick, message) function updateChatConversation(nick, message)
{ {
var timestamp = new Date(); var timestamp = new Date();
......
var connection = null;
var roomjid;
var nickname = null;
var roomUrl = null;
var sharedKey = '';
var screenShare = false;
$(document).ready(function ()
{
window.RTC = setupRTC();
if (window.RTC === null) {
return;
} else if (window.RTC.browser != 'chrome') {
return;
}
RTCPeerconnection = RTC.peerconnection;
if (config.useWebsockets)
connection = new Openfire.Connection(config.bosh);
else
connection = new Strophe.Connection(config.bosh);
connection.resource = Math.random().toString(36).substr(2, 20);
/*
connection.rawInput = function (data) { console.log('RECV: ' + data); };
connection.rawOutput = function (data) { console.log('SEND: ' + data); };
*/
var jid = config.hosts.domain;
connection.connect(jid, null, function (status)
{
if (status == Strophe.Status.CONNECTED)
{
console.log('connected');
connection.send($pres());
doJoin();
getConstraints(['screen']);
getUserMedia();
} else {
console.log('status', status);
}
});
});
$(window).bind('beforeunload', function ()
{
if (connection && connection.connected) {
unregisterRayoEvents();
connection.disconnect();
}
});
$(document).bind('mediaready.rayo', function(event, stream)
{
console.log("mediaready.rayo");
window.RTC.rayo.localStream = stream;
registerRayoEvents();
});
$(document).bind('mediafailure.rayo', function(error) {
console.error('mediafailure.rayo ' + error);
});
function setupRTC()
{
var RTC = null;
if (navigator.mozGetUserMedia) {
console.log('This appears to be Firefox');
var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
if (version >= 22) {
RTC = {
rayo: {
channels: {},
confid: {},
pc: {},
addssrc: {},
localStream: null,
constraints: {audio: false, video: false}
},
peerconnection: mozRTCPeerConnection,
browser: 'firefox',
getUserMedia: navigator.mozGetUserMedia.bind(navigator),
attachMediaStream: function (element, stream) {
element[0].mozSrcObject = stream;
element[0].play();
},
pc_constraints: {}
};
if (!MediaStream.prototype.getVideoTracks)
MediaStream.prototype.getVideoTracks = function () { return []; };
if (!MediaStream.prototype.getAudioTracks)
MediaStream.prototype.getAudioTracks = function () { return []; };
RTCSessionDescription = mozRTCSessionDescription;
RTCIceCandidate = mozRTCIceCandidate;
}
} else if (navigator.webkitGetUserMedia) {
console.log('This appears to be Chrome');
RTC = {
rayo: {
channels: {},
confid: {},
pc: {},
addssrc: {},
localStream: null,
constraints: {audio: false, video: false}
},
peerconnection: webkitRTCPeerConnection,
browser: 'chrome',
getUserMedia: navigator.webkitGetUserMedia.bind(navigator),
attachMediaStream: function (element, stream) {
element.attr('src', webkitURL.createObjectURL(stream));
},
pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]} // enable dtls support in canary
};
if (navigator.userAgent.indexOf('Android') != -1) {
RTC.pc_constraints = {}; // disable DTLS on Android
}
if (!webkitMediaStream.prototype.getVideoTracks) {
webkitMediaStream.prototype.getVideoTracks = function () {
return this.videoTracks;
};
}
if (!webkitMediaStream.prototype.getAudioTracks) {
webkitMediaStream.prototype.getAudioTracks = function () {
return this.audioTracks;
};
}
}
if (RTC === null) {
try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
}
return RTC;
}
function getUserMedia()
{
console.log("getUserMedia", window.RTC.rayo.constraints);
try {
window.RTC.getUserMedia(window.RTC.rayo.constraints,
function (stream) {
console.log('onUserMediaSuccess');
$(document).trigger('mediaready.rayo', [stream]);
},
function (error) {
console.warn('Failed to get access to local media. Error ', error);
$(document).trigger('mediafailure.rayo');
});
} catch (e) {
console.error('GUM failed: ', e);
$(document).trigger('mediafailure.rayo');
}
};
function getConstraints(um, resolution, bandwidth, fps)
{
console.log("getConstraints", um, resolution, bandwidth, fps);
window.RTC.rayo.constraints = {audio: false, video: false};
if (um.indexOf('video') >= 0) {
window.RTC.rayo.constraints.video = {mandatory: {}};// same behaviour as true
}
if (um.indexOf('audio') >= 0) {
window.RTC.rayo.constraints.audio = {};// same behaviour as true
}
if (um.indexOf('screen') >= 0) {
window.RTC.rayo.constraints.video = {
"mandatory": {
"chromeMediaSource": "screen"
}
};
}
if (resolution && window.RTC.rayo.constraints.video)
{
window.RTC.rayo.constraints.video = {mandatory: {}};// same behaviour as true
// see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions
switch (resolution) {
// 16:9 first
case '1080':
case 'fullhd':
window.RTC.rayo.constraints.video.mandatory.minWidth = 1920;
window.RTC.rayo.constraints.video.mandatory.minHeight = 1080;
window.RTC.rayo.constraints.video.mandatory.minAspectRatio = 1.77;
break;
case '720':
case 'hd':
window.RTC.rayo.constraints.video.mandatory.minWidth = 1280;
window.RTC.rayo.constraints.video.mandatory.minHeight = 720;
window.RTC.rayo.constraints.video.mandatory.minAspectRatio = 1.77;
break;
case '360':
window.RTC.rayo.constraints.video.mandatory.minWidth = 640;
window.RTC.rayo.constraints.video.mandatory.minHeight = 360;
window.RTC.rayo.constraints.video.mandatory.minAspectRatio = 1.77;
break;
case '180':
window.RTC.rayo.constraints.video.mandatory.minWidth = 320;
window.RTC.rayo.constraints.video.mandatory.minHeight = 180;
window.RTC.rayo.constraints.video.mandatory.minAspectRatio = 1.77;
break;
// 4:3
case '960':
window.RTC.rayo.constraints.video.mandatory.minWidth = 960;
window.RTC.rayo.constraints.video.mandatory.minHeight = 720;
break;
case '640':
case 'vga':
window.RTC.rayo.constraints.video.mandatory.minWidth = 640;
window.RTC.rayo.constraints.video.mandatory.minHeight = 480;
break;
case '320':
window.RTC.rayo.constraints.video.mandatory.minWidth = 320;
window.RTC.rayo.constraints.video.mandatory.minHeight = 240;
break;
default:
if (navigator.userAgent.indexOf('Android') != -1) {
window.RTC.rayo.constraints.video.mandatory.minWidth = 320;
window.RTC.rayo.constraints.video.mandatory.minHeight = 240;
window.RTC.rayo.constraints.video.mandatory.maxFrameRate = 15;
}
break;
}
}
if (bandwidth) { // doesn't work currently, see webrtc issue 1846
if (!window.RTC.rayo.constraints.video) window.RTC.rayo.constraints.video = {mandatory: {}};//same behaviour as true
window.RTC.rayo.constraints.video.optional = [{bandwidth: bandwidth}];
}
if (fps) { // for some cameras it might be necessary to request 30fps
// so they choose 30fps mjpg over 10fps yuy2
if (!window.RTC.rayo.constraints.video) window.RTC.rayo.constraints.video = {mandatory: {}};// same behaviour as tru;
window.RTC.rayo.constraints.video.mandatory.minFrameRate = fps;
}
}
function urlParam(name)
{
var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (!results) { return undefined; }
return results[1] || undefined;
}
function doJoin() {
var roomnode = urlParam("r");
console.log("doJoin", roomnode);
if (!roomnode) {
roomnode = Math.random().toString(36).substr(2, 20);
window.history.pushState('VideoChat', 'Room: ' + roomnode, window.location.pathname + "?r=" + roomnode);
}
roomjid = roomnode + '@' + config.hosts.muc;
var myroomjid = roomjid;
myroomjid += '/' + Strophe.getNodeFromJid(connection.jid) + "(Desktop)";
connection.addHandler(rayoCallback, 'urn:xmpp:rayo:colibri:1');
connection.emuc.doJoin(myroomjid);
}
function rayoCallback(presence)
{
console.log("rayoCallback", presence);
var from = $(presence).attr('from');
$(presence).find('offer').each(function()
{
handleOffer(from, this);
});
return true;
};
function handleOffer (from, offer)
{
console.log("handleOffer", offer);
var bridgeSDP = new SDP('v=0\r\no=- 5151055458874951233 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\nm=audio 1 RTP/SAVPF 111 0 126\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:audio\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=sendrecv\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:126 telephone-event/8000\r\na=maxptime:60\r\nm=video 1 RTP/SAVPF 100 116 117\r\nc=IN IP4 0.0.0.0\r\na=rtcp:1 IN IP4 0.0.0.0\r\na=mid:video\r\na=extmap:2 urn:ietf:params:rtp-hdrext:toffset\r\na=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=sendrecv\r\na=rtpmap:100 VP8/90000\r\na=rtcp-fb:100 ccm fir\r\na=rtcp-fb:100 nack\r\na=rtcp-fb:100 goog-remb\r\na=rtpmap:116 red/90000\r\na=rtpmap:117 ulpfec/90000\r\n');
var muc = $(offer).attr('muc');
var nick = $(offer).attr('nickname');
var participant = $(offer).attr('participant');
var videobridge = $(offer).attr('videobridge');
var confid = null;
var channelId = [];
window.RTC.rayo.channels = {}
window.RTC.rayo.addssrc = false;
$(offer).find('conference').each(function()
{
confid = $(this).attr('id');
window.RTC.rayo.channels.id = confid;
$(this).find('content').each(function()
{
var name = $(this).attr('name');
var channel = name == "audio" ? 0 : 1;
if ((window.RTC.rayo.localStream.getVideoTracks().length > 0 && name == "video") || (window.RTC.rayo.localStream.getAudioTracks().length > 0 && name == "audio"))
{
console.log("handleOffer track", name);
$(this).find('channel').each(function()
{
channelId[channel] = $(this).attr('id');
$(this).find('source').each(function()
{
var ssrc = $(this).attr('ssrc');
if (ssrc)
{
bridgeSDP.media[channel] += 'a=ssrc:' + ssrc + ' ' + 'cname:mixed' + '\r\n';
bridgeSDP.media[channel] += 'a=ssrc:' + ssrc + ' ' + 'label:mixedlabela0' + '\r\n';
bridgeSDP.media[channel] += 'a=ssrc:' + ssrc + ' ' + 'msid:mixedmslabela0 mixedlabela0' + '\r\n';
bridgeSDP.media[channel] += 'a=ssrc:' + ssrc + ' ' + 'mslabel:mixedmslabela0' + '\r\n';
} else {
// make chrome happy... '3735928559' == 0xDEADBEEF
bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'cname:mixed' + '\r\n';
bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'label:mixedlabelv0' + '\r\n';
bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'msid:mixedmslabelv0 mixedlabelv0' + '\r\n';
bridgeSDP.media[channel] += 'a=ssrc:' + '3735928559' + ' ' + 'mslabel:mixedmslabelv0' + '\r\n';
}
});
$(this).find('transport').each(function()
{
var pwd = $(this).attr('pwd');
var ufrag = $(this).attr('ufrag');
if (ufrag) bridgeSDP.media[channel] += 'a=ice-ufrag:' + ufrag + '\r\n';
if (pwd) bridgeSDP.media[channel] += 'a=ice-pwd:' + pwd + '\r\n';
$(this).find('candidate').each(function()
{
bridgeSDP.media[channel] += SDPUtil.candidateFromJingle(this);
});
$(this).find('fingerprint').each(function()
{
var hash = $(this).attr('hash');
var setup = $(this).attr('setup');
var fingerprint = $(this).text();
if (hash && fingerprint) bridgeSDP.media[channel] += 'a=fingerprint:' + hash + ' ' + fingerprint + '\r\n';
if (setup) bridgeSDP.media[channel] += 'a=setup:' + setup + '\r\n';
});
});
});
} else {
bridgeSDP.media[channel] = null;
}
});
});
bridgeSDP.raw = bridgeSDP.session + bridgeSDP.media.join('');
window.RTC.rayo.channels.sdp = bridgeSDP.raw;
//console.log("bridgeSDP.raw", bridgeSDP.raw);
window.RTC.rayo.pc[videobridge] = new window.RTC.peerconnection(null, {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]});
window.RTC.rayo.pc[videobridge].onicecandidate = function(event)
{
//console.log('candidate', event.candidate);
if (!event.candidate)
{
sendAnswer(from, videobridge, confid, channelId);
}
}
window.RTC.rayo.pc[videobridge].onaddstream = function(e)
{
console.log("onstream", e, window.RTC.rayo.addssrc);
if (window.RTC.rayo.pc[videobridge].signalingState == "have-remote-offer")
$(document).trigger('remotestreamadded.rayo', [e, nick]);
window.RTC.rayo.pc[videobridge].createAnswer(function(desc)
{
if (!window.RTC.rayo.addssrc)
window.RTC.rayo.pc[videobridge].setLocalDescription(desc);
});
};
window.RTC.rayo.pc[videobridge].addStream(window.RTC.rayo.localStream);
window.RTC.rayo.pc[videobridge].setRemoteDescription(new RTCSessionDescription({type: "offer", sdp : bridgeSDP.raw}));
};
function sendAnswer(from, videobridge, confid, channelId)
{
console.log("sendAnswer");
var remoteSDP = new SDP(window.RTC.rayo.pc[videobridge].localDescription.sdp);
//console.log("remoteSDP ", window.RTC.rayo.pc[videobridge].localDescription.sdp);
var change = $iq({to: from, type: 'set'});
change.c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', videobridge: videobridge});
change.c('conference', {xmlns: 'http://jitsi.org/protocol/colibri', id: confid});
for (channel = 0; channel < 2; channel++)
{
if (remoteSDP.media[channel])
{
change.c('content', {name: remoteSDP.media[channel].indexOf('m=audio') > -1 ? 'audio' : 'video'});
change.c('channel', {id: remoteSDP.media[channel].indexOf('m=audio') > -1 ? channelId[0] : channelId[1]});
tmp = SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:');
change.c('source', { xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
tmp.forEach(function (line) {
var idx = line.indexOf(' ');
var linessrc = line.substr(0, idx).substr(7);
change.attrs({ssrc: linessrc});
var kv = line.substr(idx + 1);
change.c('parameter');
if (kv.indexOf(':') == -1) {
change.attrs({ name: kv });
} else {
change.attrs({ name: kv.split(':', 2)[0] });
change.attrs({ value: kv.split(':', 2)[1] });
}
change.up();
});
change.up(); // end of source
var rtpmap = SDPUtil.find_lines(remoteSDP.media[channel], 'a=rtpmap:');
rtpmap.forEach(function (val) {
var rtpmap = SDPUtil.parse_rtpmap(val);
change.c('payload-type', rtpmap);
change.up();
});
change.c('transport', {xmlns: 'urn:xmpp:jingle:transports:ice-udp:1'});
var fingerprints = SDPUtil.find_lines(remoteSDP.media[channel], 'a=fingerprint:', remoteSDP.session);
fingerprints.forEach(function (line)
{
var tmp = SDPUtil.parse_fingerprint(line);
tmp.xmlns = 'urn:xmpp:jingle:apps:dtls:0';
change.c('fingerprint').t(tmp.fingerprint);
delete tmp.fingerprint;
var line = SDPUtil.find_line(remoteSDP.media[channel], 'a=setup:', remoteSDP.session);
if (line) {
tmp.setup = line.substr(8);
}
change.attrs(tmp);
change.up();
});
var candidates = SDPUtil.find_lines(remoteSDP.media[channel], 'a=candidate:', remoteSDP.session);
candidates.forEach(function (line) {
var tmp = SDPUtil.candidateToJingle(line);
change.c('candidate', tmp).up();
});
tmp = SDPUtil.iceparams(remoteSDP.media[channel], remoteSDP.session);
if (tmp) {
change.attrs(tmp);
}
change.up(); // end of transport
change.up(); // end of channel
change.up(); // end of content
}
}
connection.sendIQ(change,
function (res) {
console.log('rayo colibri answer set ok', window.RTC.rayo.pc[videobridge].signalingState);
},
function (err) {
console.log('rayo colibri answer got error ' + err);
}
);
};
function registerRayoEvents()
{
connection.sendIQ($iq({to: connection.domain, type: 'set'}).c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', action: 'offer', muc: roomjid}),
function (res) {
console.log('rayo colibri register set ok');
},
function (err) {
console.log('rayo colibri register got error', err);
}
);
}
function unregisterRayoEvents()
{
window.RTC.rayo.localStream.stop();
connection.sendIQ($iq({to: connection.domain, type: 'set'}).c('colibri', {xmlns: 'urn:xmpp:rayo:colibri:1', action: 'expire', muc: roomjid}),
function (res) {
console.log('rayo colibri unregister set ok');
},
function (err) {
console.log('rayo colibri unregister got error', err);
}
);
}
<html>
<head>
<title>Openfire Meet</title>
<script src="js/jquery.min.js"></script>
<script src="js/md5.js"></script>
<script src="js/base64.js"></script>
<script src="js/strophe.js"></script>
<script src="js/muc.js"></script>
<script src="js/config.js"></script>
<script src="js/webrtc.sdp.js"></script>
<script src="js/strophe-openfire.js"></script>
<script src="js/publish.js"></script>
<link rel="shortcut icon" href="favicon.ico"/>
</head>
<body>
</body>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment