Commit 6a0f4df2 authored by Dele Olajide's avatar Dele Olajide Committed by dele

Jitsi Videobridge - latest changes

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13824 b35dd754-fafc-0310-a699-88a17e54d16e
parent 6de694bf
/*
Copyright (c) 2013 ESTOS GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* jshint -W117 */
var bridgejid = 'jitsi-videobridge.' + window.location.hostname;
// static offer taken from chrome M31
var staticoffer = '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 103 104 0 8 106 105 13 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:103 ISAC/16000\r\na=rtpmap:104 ISAC/32000\r\na=rtpmap:0 PCMU/8000\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:106 CN/32000\r\na=rtpmap:105 CN/16000\r\na=rtpmap:13 CN/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';
......@@ -7,6 +27,7 @@ function Colibri(connection, bridgejid) {
this.connection = connection;
this.bridgejid = bridgejid;
this.peers = [];
this.confid = null;
this.peerconnection = null;
......@@ -63,9 +84,9 @@ Colibri.prototype.createdConference = function(result) {
var tmp;
this.confid = $(result).find('>conference').attr('id');
this.remotecontents = $(result).find('>conference>content').get();
for (var i = 0; i < this.remotecontents.length; i++) {
tmp = $(this.remotecontents[i]).find('>channel').get();
var remotecontents = $(result).find('>conference>content').get();
for (var i = 0; i < remotecontents.length; i++) {
tmp = $(remotecontents[i]).find('>channel').get();
this.mychannel.push($(tmp.shift()));
for (j = 0; j < tmp.length; j++) {
if (this.channels[j] === undefined) {
......@@ -250,12 +271,14 @@ Colibri.prototype.updateChannel = function (remoteSDP, participant) {
change.c('payload-type', rtpmap);
//
// put any 'a=fmtp:' + mline.fmt[j] lines into <param name=foo value=bar/>
/*
if (SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id)) {
tmp = SDPUtil.parse_fmtp(SDPUtil.find_line(remoteSDP.media[channel], 'a=fmtp:' + rtpmap.id));
for (var k = 0; k < tmp.length; k++) {
change.c('parameter', tmp[k]).up();
}
}
*/
change.up();
});
......@@ -647,6 +670,7 @@ Colibri.prototype.terminate = function (session, reason) {
Colibri.prototype.modifySources = function() {
var ob = this;
if (!(this.addssrc.length || this.removessrc.length)) return;
if (this.peerconnection.signalingState == 'closed') return;
// FIXME: this is a big hack
if (!(this.peerconnection.signalingState == 'stable' && this.peerconnection.iceConnectionState == 'connected')) {
......
var config = {
hosts: {
domain: window.location.hostname,
muc: 'conference.' + window.location.hostname, // FIXME: use XEP-0030
bridge: 'jitsi-videobridge.' + window.location.hostname // FIXME: use XEP-0030
},
useNicks: false,
bosh: window.location.protocol + '//' + window.location.host + '/http-bind/', // FIXME: use xep-0156 for that
type: 'bosh' // use websockets if openfire websockets plugin is installed
};
function urlParam(name)
{
var results = new RegExp('[\\?&]' + name + '=([^&#]*)').exec(window.location.href);
......@@ -55,7 +66,7 @@ Openfire.Connection = function(url)
this.host = url.indexOf("/") < 0 ? url : url.split("/")[2];
this.protocol = url.indexOf("/") < 0 ? "wss:" : (url.split("/")[0] == "http:") ? "ws:" : "wss:";
this.jid = "";
this.resource = "ofchat";
this.resource = "jitsi-videobridge";
this.streamId = null;
// handler lists
......
<html>
<head>
<title>Jitsi Videobridge</title>
<script src="config.js"></script><!-- adapt to your needs, i.e. set hosts and bosh path -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="strophejingle.bundle.js"></script>
<script src="colibri.js"></script>
<script src="videobridge.js"></script>
<script src="muc.js"></script>
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<style type='text/css'>
html,body{margin:0px;}
#largeVideo {width:1280px;height:720px;margin-left:auto;margin-right:auto;display:block}
#localVideo {-webkit-transform: scale(-1,1);}
#remoteVideos {text-align:center;height:180px;}
#remoteVideos video {width:320;height:180;}
#spacer {height:40px;}
#settings {display:none;}
#header {text-align:center;visibility:hidden;}
#nowebrtc {display:none;}
html,body{margin:0px;}
#largeVideo {width:1280px;height:720px;margin-left:auto;margin-right:auto;display:block}
#localVideo {-webkit-transform: scale(-1,1);}
#remoteVideos {text-align:center;height:180px;}
#remoteVideos video {width:320;height:180;}
#spacer {height:40px;}
#settings {display:none;}
#header {text-align:center;visibility:hidden;}
#nowebrtc {display:none;}
</style>
</head>
<body>
......@@ -28,6 +28,7 @@
<form id="loginInfo">
<label>JID: <input id="jid" type="text" name="jid" placeholder="me@example.com"/></label>
<label>Password: <input id="password" type="password" name="password" placeholder="secret"/></label>
<label>BOSH URL: <input id="boshURL" type="text" name="boshURL" placeholder="/http-bind/"/></label>
<input id="connect" type="submit" value="Connect" />
</form>
</div>
......@@ -41,41 +42,47 @@
<script>
var connection = null;
var master = null;
var RTC = setupRTC();
if (RTC == null) {
alert('Sorry, your browser is not WebRTC enabled!'); // BAO
window.location.href = 'about:blank';
} else if (RTC.browser != 'chrome') {
alert('Sorry, only Chrome supported for now!'); // BAO
window.location.href = 'about:blank';
}
var RTCPeerconnection = RTC.peerconnection;
document.getElementById('jid').value = window.location.hostname;
var RTC;
var RTCPeerConnection = null;
var connType = "BOSH"; // change this line to WEBSOCKETS for Openfire websockets
function init() {
RTC = setupRTC();
if (RTC == null) {
alert('Sorry, your browser is not WebRTC enabled!'); // BAO
return;
} else if (RTC.browser != 'chrome') {
alert('Sorry, only Chrome supported for now!'); // BAO
return;
}
RTCPeerconnection = RTC.peerconnection;
if (connType == "WEBSOCKETS")
connection = new Openfire.Connection(window.location.protocol + '//' + window.location.host + '/http-bind/'); // BAO
else
connection = new Strophe.Connection(window.location.protocol + '//' + window.location.host + '/http-bind/'); // BAO
if (config.type == "bosh") // BAO
connection = new Strophe.Connection(document.getElementById('boshURL').value || config.bosh || '/http-bind/');
else
connection = new Openfire.Connection(config.bosh);
window.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); };
connection.jingle.pc_constraints = RTC.pc_constraints;
window.connection.resource = Math.random().toString(36).substr(2, 20); // BAO
var jid = document.getElementById('jid').value || window.location.hostname;
/*
connection.rawInput = function (data) { console.log('RECV: ' + data); };
connection.rawOutput = function (data) { console.log('SEND: ' + data); };
*/
connection.jingle.pc_constraints = RTC.pc_constraints;
connection.emuc.setDomain(config.hosts.muc);
connection.connect(jid, document.getElementById('password').value, function(status) {
if (status == Strophe.Status.CONNECTED) {
console.log('connected');
connection.send($pres()); // BAO
getUserMediaWithConstraints(['audio','video'], '360');
document.getElementById('connect').disabled = true;
} else {
console.log('status', status);
}
});
var jid = document.getElementById('jid').value || config.hosts.domain || window.location.hostname;
connection.connect(jid, document.getElementById('password').value, function(status) {
if (status == Strophe.Status.CONNECTED) {
console.log('connected');
connection.send($pres()); // BAO
getUserMediaWithConstraints(['audio','video'], '360');
document.getElementById('connect').disabled = true;
} else {
console.log('status', status);
}
});
}
$(document).bind('mediaready.jingle', function(event, stream) {
connection.emuc.doJoin();
......@@ -128,6 +135,7 @@
document.getElementById('largeVideo').volume = pick.volume;
document.getElementById('largeVideo').src = pick.src;
}
resizeThumbnails();
}
// FIXME: hover is bad, this causes flicker. How about moving this?
// remember that moving this in the DOM requires to play() again
......@@ -155,6 +163,7 @@
if (videoelem.attr('id').indexOf('mixedmslabel') == -1) {
// ignore mixedmslabela0 and v0
videoelem.show();
resizeThumbnails();
document.getElementById('largeVideo').volume = 1;
$('#largeVideo').attr('src', videoelem.attr('src'));
......@@ -170,17 +179,33 @@
var availableWidth = window.innerWidth;
var aspectRatio = 16.0 / 9.0;
if (availableHeight < availableWidth / aspectRatio) {
availableWidth = availableHeight * aspectRatio;
availableWidth = Math.floor(availableHeight * aspectRatio);
}
if (availableWidth < 0 || availableHeight < 0) return;
$('#largeVideo').width(availableWidth);
$('#largeVideo').height(availableWidth/aspectRatio);
}
function resizeThumbnails() {
var availableWidth = $('#remoteVideos').width();
var numvids = $('#remoteVideos>video:visible').length;
// size videos so that while keeping AR and max height, we have a nice fit
console.log('fit', numvids, 'videos into', availableWidth);
}
$(document).ready(function() {
resizeLarge();
$(window).resize(function() {
resizeLarge();
});
if (!$('#settings').is(':visible')) {
console.log('init');
init();
} else {
loginInfo.onsubmit = function (e) {
if (e.preventDefault) e.preventDefault();
$('#settings').hide();
init();
};
}
});
</script>
</body>
......
......@@ -44,6 +44,12 @@ public class PluginImpl
*/
private static final Logger Log = LoggerFactory.getLogger(PluginImpl.class);
/**
* The name of the property that contains the name of video conference application
*/
public static final String CHECKREPLAY_PROPERTY_NAME
= "org.jitsi.videobridge.video.srtpcryptocontext.checkreplay";
/**
* The name of the property that contains the name of video conference application
*/
......@@ -134,6 +140,7 @@ public class PluginImpl
System.setProperty("net.java.sip.communicator.SC_HOME_DIR_LOCATION", pluginDirectory.getPath());
System.setProperty("net.java.sip.communicator.SC_HOME_DIR_NAME", ".");
System.setProperty("org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay", JiveGlobals.getProperty(CHECKREPLAY_PROPERTY_NAME, "false"));
// start video conference web application
......
var CONFERENCEDOMAIN = 'conference.' + window.location.hostname; // BAO
Strophe.addConnectionPlugin('emuc', {
connection: null,
roomjid: null,
myroomjid: null,
list_members: [],
isOwner: false,
mucDomain: config.hosts.muc,
init: function (conn) {
this.connection = conn;
},
setDomain: function(domain) {
this.mucDomain = domain;
},
doJoin: function () {
var roomnode = urlParam("r"); // BAO
console.log("roomnode = " + roomnode);
if (!roomnode) {
roomnode = Math.random().toString(36).substr(2, 20);
window.history.pushState('VideoChat', 'Room: ' + roomnode, window.location.pathname + "?r=" + roomnode);
}
if (this.roomjid == null) {
this.roomjid = roomnode + '@' + CONFERENCEDOMAIN;
this.roomjid = roomnode + '@' + this.mucDomain;
}
this.myroomjid = this.roomjid + '/' + Strophe.getNodeFromJid(this.connection.jid);
console.log('joining', this.roomjid);
// muc stuff
this.connection.addHandler(this.onPresence.bind(this), null, 'presence', null, null, this.roomjid, {matchBare: true});
this.connection.addHandler(this.onPresenceUnavailable.bind(this), null, 'presence', 'unavailable', null, this.roomjid, {matchBare: true});
this.connection.addHandler(this.onPresenceError.bind(this), null, 'presence', 'error', null, this.roomjid, {matchBare: true});
this.connection.send($pres({to: this.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'}));
this.connection.addHandler(this.onMessage.bind(this), null, 'message', null, null, this.roomjid, {matchBare: true});
if (config.useNicks) {
var nick = window.prompt('Your nickname (optional)');
if (nick) {
this.myroomjid = this.roomjid + '/' + nick;
} else {
this.myroomjid = this.roomjid + '/' + Strophe.getNodeFromJid(this.connection.jid);
}
this.connection.send($pres({to: this.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'}));
} else {
this.myroomjid = this.roomjid + '/' + Strophe.getNodeFromJid(this.connection.jid);
console.log('joining', this.roomjid);
this.connection.send($pres({to: this.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'}));
}
},
onPresence: function (pres) {
var from = pres.getAttribute('from'),
......@@ -52,7 +67,7 @@ Strophe.addConnectionPlugin('emuc', {
// FIXME: belongs into an event so we can separate emuc and colibri
if (master !== null) {
// FIXME: this should prepare the video
if (master.peers.length == 0) {
if (master.confid === null) {
console.log('make new conference with', from);
master.makeConference(this.list_members);
} else {
......@@ -83,6 +98,10 @@ Strophe.addConnectionPlugin('emuc', {
}
if (this.list_members.length == 0) {
console.log('everyone left');
if (master !== null) {
if (master.peerconnection !== null) master.peerconnection.close();
master = new Colibri(connection, config.hosts.bridge);
}
}
return true;
},
......@@ -108,10 +127,23 @@ Strophe.addConnectionPlugin('emuc', {
$('#header').css('visibility', 'visible');
if (this.list_members.length < 1) {
// FIXME: belongs into an event so we can separate emuc and colibri
master = new Colibri(connection, bridgejid);
master = new Colibri(connection, config.hosts.bridge);
return;
}
},
sendMessage: function(body) {
msg = $msg({to: this.roomjid, type: 'groupchat'});
msg.c('body', body);
this.connection.send(msg);
},
onMessage: function (msg) {
var txt = $(msg).find('>body').text();
// TODO: <subject/>
if (txt) {
//console.log('chat', Strophe.getResourceFromJid($(msg).attr('from')), txt);
}
return true;
},
lockRoom: function(key) {
//http://xmpp.org/extensions/xep-0045.html#roomconfig
var ob = this;
......@@ -144,21 +176,6 @@ Strophe.addConnectionPlugin('emuc', {
$(window).bind('beforeunload', function() {
if (connection && connection.connected) {
// ensure signout
$.ajax({
type: 'POST',
url: '/http-bind',
async: false,
cache: false,
contentType: 'application/xml',
data: "<body rid='" + connection.rid + "' xmlns='http://jabber.org/protocol/httpbind' sid='" + connection.sid + "' type='terminate'><presence xmlns='jabber:client' type='unavailable'/></body>",
success: function(data) {
console.log('signed out');
console.log(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log('signout error', textStatus + ' (' + errorThrown + ')');
}
});
connection.disconnect();
}
})
\ No newline at end of file
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