Commit dd5f8282 authored by Dele Olajide's avatar Dele Olajide Committed by dele

Jitsi Videobridge plugin code refresh

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13841 b35dd754-fafc-0310-a699-88a17e54d16e
parent ff3733c7
...@@ -47,7 +47,7 @@ Jitsi Video Bridge Plugin Changelog ...@@ -47,7 +47,7 @@ Jitsi Video Bridge Plugin Changelog
<p><b>1.1</b> -- Dec 4th, 2013</p> <p><b>1.1</b> -- Dec 4th, 2013</p>
<ul> <ul>
<li>OF-716 Added to Openfire plugins with webrtc demo video application</li> <li>OF-716 Added to Openfire plugins with jitmeet web video conference application</li>
</ul> </ul>
<p><b>1.0</b> -- Apr 12, 2013</p> <p><b>1.0</b> -- Apr 12, 2013</p>
......
...@@ -63,7 +63,7 @@ Jitsi Videobridge does not mix the video channels into a composite video stream, ...@@ -63,7 +63,7 @@ Jitsi Videobridge does not mix the video channels into a composite video stream,
but only relays the received video channels to all call participants. but only relays the received video channels to all call participants.
Therefore, while it does need to run on a server with good network bandwidth, Therefore, while it does need to run on a server with good network bandwidth,
CPU horsepower is not that critical for performance. CPU horsepower is not that critical for performance.
A demo video conference application using WebRTC is included. The JitMeet video conference application using WebRTC is included.
</p> </p>
...@@ -79,7 +79,7 @@ Under Server settings -> Jitsi Videobridge tab you can configure it. ...@@ -79,7 +79,7 @@ Under Server settings -> Jitsi Videobridge tab you can configure it.
<h2>How to use</h2> <h2>How to use</h2>
To run the demo video conference application, point your browser at https://your_server:7443/videobridge To run the demo video conference application, point your browser at https://your_server:7443/jitmeet
</body> </body>
</html> </html>
...@@ -168,6 +168,7 @@ $(document).bind('callterminated.jingle', function (event, sid, reason) { ...@@ -168,6 +168,7 @@ $(document).bind('callterminated.jingle', function (event, sid, reason) {
$(document).bind('joined.muc', function (event, jid, info) { $(document).bind('joined.muc', function (event, jid, info) {
console.log('onJoinComplete', info); console.log('onJoinComplete', info);
updateRoomUrl(window.location.href); updateRoomUrl(window.location.href);
showToolbar();
if (Object.keys(connection.emuc.members).length < 1) { if (Object.keys(connection.emuc.members).length < 1) {
focus = new ColibriFocus(connection, config.hosts.bridge); focus = new ColibriFocus(connection, config.hosts.bridge);
return; return;
...@@ -177,6 +178,7 @@ $(document).bind('joined.muc', function (event, jid, info) { ...@@ -177,6 +178,7 @@ $(document).bind('joined.muc', function (event, jid, info) {
$(document).bind('entered.muc', function (event, jid, info) { $(document).bind('entered.muc', function (event, jid, info) {
console.log('entered', jid, info); console.log('entered', jid, info);
console.log(focus); console.log(focus);
if (focus !== null) { if (focus !== null) {
// FIXME: this should prepare the video // FIXME: this should prepare the video
if (focus.confid === null) { if (focus.confid === null) {
...@@ -428,6 +430,10 @@ function openChat() { ...@@ -428,6 +430,10 @@ function openChat() {
$('#usermsg').focus(); $('#usermsg').focus();
} }
function showToolbar() {
$('#toolbar').css({visibility:"visible"});
}
function updateRoomUrl(newRoomUrl) { function updateRoomUrl(newRoomUrl) {
roomUrl = newRoomUrl; roomUrl = newRoomUrl;
} }
Sorry, this currently only works with chrome because it uses "Plan B". <html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" media="screen" href="css/chromeonly.css" />
</head>
<body>
<!-- wrap starts here -->
<div id="wrap">
<a href="http://google.com/chrome"><div id="left"></div></a>
<div id="middle"></div>
<div id="text">
<p>This service only works with Chrome.</p>
<p><a href="http://google.com/chrome">Download Chrome</a></p>
</div>
<!-- wrap ends here -->
</div>
</body>
</html>
body {
width:100%;
height:100%;
background-color: white;
color: #424242;
font-family:'YanoneKaffeesatzLight',Verdana,Tahoma,Arial;
margin:0;
padding:0;
}
#wrap{
display: block;
position: absolute;
width:766px;
height: 262px;
overflow:hidden;
text-align: center;
margin: auto;
top: 0; left: 0; bottom: 0; right: 0;
}
#left{
display:inline-block;
background-image:url(../images/chromelogo.png);
background-repeat:no-repeat;
width:246px;
height:262px;
float: left;
}
#middle{
display:inline-block;
background-image:url(../images/chromepointer.png);
background-repeat:no-repeat;
width:53px;
height:262px;
float: left;
}
#text{
display:inline-block;
font-size: 20pt;
width: 400px;
vertical-align:middle;
padding-top: 30px;
}
a {
color: #087dba;
text-decoration:none;
}
...@@ -92,11 +92,11 @@ html, body{ ...@@ -92,11 +92,11 @@ html, body{
visibility:hidden; visibility:hidden;
} }
div.localuser { .localuser {
color: #087dba; color: #087dba;
} }
div.remoteuser { .remoteuser {
color: #424242; color: #424242;
} }
...@@ -128,7 +128,7 @@ div.remoteuser { ...@@ -128,7 +128,7 @@ div.remoteuser {
font-size: 14; font-size: 14;
} }
div#spacer { #spacer {
height:5px; height:5px;
} }
...@@ -140,17 +140,15 @@ div#spacer { ...@@ -140,17 +140,15 @@ div#spacer {
display:none; display:none;
} }
div#header{ #header{
display:block; display:block;
position:relative;
width:100%;
height:39px; height:39px;
z-index: 1; z-index: 1;
text-align:center; text-align:center;
background-color:#087dba; background-color:#087dba;
} }
div#left { #left {
display:block; display:block;
position: absolute; position: absolute;
left: 0px; left: 0px;
...@@ -163,7 +161,7 @@ div#left { ...@@ -163,7 +161,7 @@ div#left {
padding: 0; padding: 0;
} }
div#leftlogo { #leftlogo {
position:absolute; position:absolute;
top: 5px; top: 5px;
left: 15px; left: 15px;
...@@ -174,13 +172,14 @@ div#leftlogo { ...@@ -174,13 +172,14 @@ div#leftlogo {
z-index:1; z-index:1;
} }
div#link { #toolbar {
display:block; display:block;
position:relative; position:relative;
height:39px; height:39px;
width:auto; width:auto;
overflow: hidden; overflow: hidden;
z-index:0; z-index:0;
visibility: hidden;
} }
.button { .button {
...@@ -189,10 +188,9 @@ div#link { ...@@ -189,10 +188,9 @@ div#link {
color: #FFFFFF; color: #FFFFFF;
top: 0; top: 0;
padding: 10px 0px; padding: 10px 0px;
height: 19px;
width: 39px; width: 39px;
cursor: pointer; cursor: pointer;
font-size: 19px; font-size: 11pt;
text-align: center; text-align: center;
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7); text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
} }
...@@ -227,7 +225,7 @@ a.button:hover { ...@@ -227,7 +225,7 @@ a.button:hover {
background: -webkit-gradient(linear, 0 0, 0 100%, from(#087dba), to(#087dba), color-stop(50%, white)); background: -webkit-gradient(linear, 0 0, 0 100%, from(#087dba), to(#087dba), color-stop(50%, white));
} }
div#right { #right {
display:block; display:block;
position:absolute; position:absolute;
right: 0px; right: 0px;
...@@ -239,7 +237,8 @@ div#right { ...@@ -239,7 +237,8 @@ div#right {
width:100px; width:100px;
height:39px; height:39px;
} }
div#rightlogo {
#rightlogo {
position:absolute; position:absolute;
top: 6px; top: 6px;
right: 15px; right: 15px;
......
.jqistates {
font-size: 14px;
}
.jqistates h2 { .jqistates h2 {
padding-bottom: 10px; padding-bottom: 10px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
...@@ -5,18 +9,18 @@ ...@@ -5,18 +9,18 @@
line-height: 25px; line-height: 25px;
text-align: center; text-align: center;
color: #424242; color: #424242;
} }
.jqistates input { .jqistates input {
width: 100%; width: 100%;
margin: 20px 0; margin: 20px 0;
} }
.jqibuttons button { .jqibuttons button {
margin-right: 5px; margin-right: 5px;
float:right; float:right;
} }
button.jqidefaultbutton #inviteLinkRef { button.jqidefaultbutton #inviteLinkRef {
color: #2c8ad2; color: #2c8ad2;
} }
\ No newline at end of file \ No newline at end of file
<html> <html>
<head> <head>
<title>WebRTC, meet the Jitsi Videobridge</title> <title>JitMeet</title>
<script src="jquery.min.js"></script> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script src="libs/strophejingle.bundle.js"></script><!-- strophe.jingle bundle --> <script src="libs/strophejingle.bundle.js"></script><!-- strophe.jingle bundle -->
<script src="libs/colibri.js"></script><!-- colibri focus implementation --> <script src="libs/colibri.js"></script><!-- colibri focus implementation -->
<script src="muc.js"></script><!-- simple MUC library --> <script src="muc.js"></script><!-- simple MUC library -->
<script src="app.js"></script><!-- application logic --> <script src="app.js"></script><!-- application logic -->
<link href="font-awesome-4.0.3/css/font-awesome.css" rel="stylesheet"> <link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css" /> <link rel="stylesheet" type="text/css" media="screen" href="css/main.css" />
<link rel="stylesheet" href="css/jquery-impromptu.css"> <link rel="stylesheet" href="css/jquery-impromptu.css">
<link rel="stylesheet" href="css/modaldialog.css"> <link rel="stylesheet" href="css/modaldialog.css">
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<div id="header"> <div id="header">
<a href="http://jitsi.org" target="_blank"><div id="leftlogo"></div></a> <a href="http://jitsi.org" target="_blank"><div id="leftlogo"></div></a>
<a href="http://www.estos.com/" target="_blank"><div id="rightlogo"></div></a> <a href="http://www.estos.com/" target="_blank"><div id="rightlogo"></div></a>
<div id="link"> <div id="toolbar">
<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>
......
package com.rayo.core.verb; package com.rayo.core.verb;
import org.xmpp.packet.*;
import org.dom4j.*;
public class AddSourceEvent extends AbstractVerbEvent { public class AddSourceEvent extends AbstractVerbEvent {
private JID muc;
private String nickname;
private JID participant;
private Element conference;
public AddSourceEvent() {} public AddSourceEvent() {}
public AddSourceEvent(Verb verb) { public AddSourceEvent(Verb verb) {
super(verb); super(verb);
} }
public Element getConference() {
return conference;
}
public void setConference(Element conference) {
this.conference = conference;
}
public JID getParticipant() {
return participant;
}
public void setParticipant(JID participant) {
this.participant = participant;
}
public JID getMuc() {
return muc;
}
public void setMuc(JID muc) {
this.muc = muc;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
} }
package com.rayo.core.verb; package com.rayo.core.verb;
import org.dom4j.Element;
public class ColibriCommand extends AbstractVerbCommand { public class ColibriCommand extends AbstractVerbCommand {
private String videobridge; private String videobridge;
private Element conference;
public void setVideobridge(String videobridge) { public ColibriCommand(String videobridge, Element conference)
{
this.videobridge = videobridge; this.videobridge = videobridge;
this.conference = conference;
} }
public String getVideobridge() { public String getVideobridge()
{
return this.videobridge; return this.videobridge;
} }
public Element getConference()
{
return this.conference;
}
} }
package com.rayo.core.verb;
import org.xmpp.packet.*;
import org.dom4j.*;
public class ColibriOfferEvent extends AbstractVerbEvent {
private JID muc;
private String nickname;
private JID participant;
private Element conference;
public ColibriOfferEvent() {}
public ColibriOfferEvent(Verb verb) {
super(verb);
}
public Element getConference() {
return conference;
}
public void setConference(Element conference) {
this.conference = conference;
}
public JID getParticipant() {
return participant;
}
public void setParticipant(JID participant) {
this.participant = participant;
}
public JID getMuc() {
return muc;
}
public void setMuc(JID muc) {
this.muc = muc;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
}
package com.rayo.core.verb; package com.rayo.core.verb;
import org.xmpp.packet.*;
public class RemoveSourceEvent extends AbstractVerbEvent { public class RemoveSourceEvent extends AbstractVerbEvent {
private JID muc;
private String nickname;
private JID participant;
private boolean active;
public RemoveSourceEvent() {} public RemoveSourceEvent() {}
public RemoveSourceEvent(Verb verb) { public RemoveSourceEvent(Verb verb) {
super(verb); super(verb);
} }
public JID getParticipant() {
return participant;
}
public void setParticipant(JID participant) {
this.participant = participant;
}
public JID getMuc() {
return muc;
}
public void setMuc(JID muc) {
this.muc = muc;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public void setActive(boolean active) {
this.active = active;
}
public boolean isActive() {
return active;
}
} }
...@@ -46,8 +46,9 @@ public class ColibriProvider extends BaseProvider { ...@@ -46,8 +46,9 @@ public class ColibriProvider extends BaseProvider {
private Object buildColibriCommand(Element element) throws URISyntaxException private Object buildColibriCommand(Element element) throws URISyntaxException
{ {
String videobridge = element.attributeValue("videobridge"); String videobridge = element.attributeValue("videobridge");
ColibriCommand command = new ColibriCommand(); Element conference = element.element("conference").createCopy();
command.setVideobridge(videobridge);
ColibriCommand command = new ColibriCommand(videobridge, conference);
return command; return command;
} }
...@@ -62,6 +63,9 @@ public class ColibriProvider extends BaseProvider { ...@@ -62,6 +63,9 @@ public class ColibriProvider extends BaseProvider {
if (object instanceof ColibriCommand) { if (object instanceof ColibriCommand) {
createColibriCommand((ColibriCommand) object, document); createColibriCommand((ColibriCommand) object, document);
} else if (object instanceof ColibriOfferEvent) {
createColibriOfferEvent((ColibriOfferEvent) object, document);
} else if (object instanceof AddSourceEvent) { } else if (object instanceof AddSourceEvent) {
createAddSourceEvent((AddSourceEvent) object, document); createAddSourceEvent((AddSourceEvent) object, document);
...@@ -82,14 +86,34 @@ public class ColibriProvider extends BaseProvider { ...@@ -82,14 +86,34 @@ public class ColibriProvider extends BaseProvider {
root.addAttribute("videobridge", command.getVideobridge()); root.addAttribute("videobridge", command.getVideobridge());
} }
private void createColibriOfferEvent(ColibriOfferEvent event, Document document)
{
Element root = document.addElement(new QName("offer", NAMESPACE));
root.addAttribute("muc", event.getMuc().toString());
root.addAttribute("videobridge", event.getMuc().getNode());
root.addAttribute("nickname", event.getNickname());
root.addAttribute("participant", event.getParticipant().toString());
root.add(event.getConference().createCopy());
}
private void createAddSourceEvent(AddSourceEvent event, Document document) private void createAddSourceEvent(AddSourceEvent event, Document document)
{ {
document.addElement(new QName("addsource", NAMESPACE)); Element root = document.addElement(new QName("addsource", NAMESPACE));
root.addAttribute("muc", event.getMuc().toString());
root.addAttribute("videobridge", event.getMuc().getNode());
root.addAttribute("nickname", event.getNickname());
root.addAttribute("participant", event.getParticipant().toString());
root.add(event.getConference().createCopy());
} }
private void createRemoveSourceEvent(RemoveSourceEvent event, Document document) private void createRemoveSourceEvent(RemoveSourceEvent event, Document document)
{ {
document.addElement(new QName("removesource", NAMESPACE)); Element root = document.addElement(new QName("removesource", NAMESPACE));
root.addAttribute("muc", event.getMuc().toString());
root.addAttribute("videobridge", event.getMuc().getNode());
root.addAttribute("nickname", event.getNickname());
root.addAttribute("participant", event.getParticipant().toString());
root.addAttribute("active", event.isActive() ? "true" : "false");
} }
private void createMutedEvent(MutedEvent muted, Document document) private void createMutedEvent(MutedEvent muted, Document document)
......
...@@ -18,6 +18,7 @@ import org.jitsi.service.neomedia.*; ...@@ -18,6 +18,7 @@ import org.jitsi.service.neomedia.*;
import org.jitsi.util.*; import org.jitsi.util.*;
import org.jitsi.videobridge.*; import org.jitsi.videobridge.*;
import org.jivesoftware.openfire.container.*; import org.jivesoftware.openfire.container.*;
import org.jivesoftware.openfire.muc.*;
import org.jivesoftware.util.*; import org.jivesoftware.util.*;
import org.jivesoftware.openfire.http.HttpBindManager; import org.jivesoftware.openfire.http.HttpBindManager;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
...@@ -158,7 +159,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -158,7 +159,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
component = null; component = null;
} }
destroyIQHandlers(); //destroyIQHandlers();
} }
/** /**
...@@ -188,7 +189,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -188,7 +189,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
WebAppContext context = new WebAppContext(contexts, pluginDirectory.getPath(), "/" + appName); WebAppContext context = new WebAppContext(contexts, pluginDirectory.getPath(), "/" + appName);
context.setWelcomeFiles(new String[]{"index.html"}); context.setWelcomeFiles(new String[]{"index.html"});
createIQHandlers(); //createIQHandlers();
} }
catch(Exception e) { catch(Exception e) {
...@@ -496,14 +497,18 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -496,14 +497,18 @@ public class PluginImpl implements Plugin, PropertyEventListener
* *
*/ */
private class ColibriIQHandler extends IQHandler private class ColibriIQHandler extends IQHandler implements MUCEventListener
{ {
private ConcurrentHashMap<String, LocalClientSession> sessions; private ConcurrentHashMap<String, FocusAgent> sessions;
private MultiUserChatManager mucManager;
public ColibriIQHandler() public ColibriIQHandler()
{ {
super("Rayo: XEP 0327 - Colibri"); super("Rayo: XEP 0327 - Colibri");
sessions = new ConcurrentHashMap<String, LocalClientSession>(); sessions = new ConcurrentHashMap<String, FocusAgent>();
MUCEventDispatcher.addListener(this);
mucManager = XMPPServer.getInstance().getMultiUserChatManager();
} }
/** /**
...@@ -523,6 +528,10 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -523,6 +528,10 @@ public class PluginImpl implements Plugin, PropertyEventListener
if (object instanceof ColibriCommand) { if (object instanceof ColibriCommand) {
ColibriCommand command = (ColibriCommand) object; ColibriCommand command = (ColibriCommand) object;
reply = handleColibriCommand(command, iq); reply = handleColibriCommand(command, iq);
} else {
reply = IQ.createResultIQ(iq);
reply.setError(PacketError.Condition.not_allowed);
} }
return reply; return reply;
...@@ -549,10 +558,28 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -549,10 +558,28 @@ public class PluginImpl implements Plugin, PropertyEventListener
IQ reply = IQ.createResultIQ(iq); IQ reply = IQ.createResultIQ(iq);
String vBridge = command.getVideobridge(); String vBridge = command.getVideobridge();
Element conference = command.getConference();
if (vBridge != null) if (vBridge != null)
{ {
createColibriFocus(vBridge); String focusAgentName = "colibri.focus.agent." + vBridge;
JID user = iq.getFrom();
if (sessions.containsKey(focusAgentName))
{
FocusAgent focusAgent = sessions.get(focusAgentName);
if (focusAgent.isUser(user))
{
focusAgent.handleColibriCommand(reply, user, conference);
} else {
reply.setError(PacketError.Condition.item_not_found);
}
} else {
reply.setError(PacketError.Condition.not_allowed);
}
} }
return reply; return reply;
...@@ -561,26 +588,102 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -561,26 +588,102 @@ public class PluginImpl implements Plugin, PropertyEventListener
* *
* *
*/ */
private void createColibriFocus(String vBridge) public void roomCreated(JID roomJID)
{
}
/**
*
*
*/
public void roomDestroyed(JID roomJID)
{ {
Log.info("ColibriIQHandler createColibriFocus " + vBridge);
LocalClientSession session;
if (sessions.containsKey(vBridge)) }
/**
*
*
*/
public void occupantJoined(JID roomJID, JID user, String nickname)
{
MUCRoom mucRoom = mucManager.getMultiUserChatService(roomJID).getChatRoom(roomJID.getNode());
Log.info("ColibriIQHandler occupantJoined " + roomJID + " " + user + " " + nickname);
String focusAgentName = "colibri.focus.agent." + roomJID.getNode();
FocusAgent focusAgent;
Participant participant = new Participant(nickname, user);
if (sessions.containsKey(focusAgentName))
{ {
session = sessions.get(vBridge); focusAgent = sessions.get(focusAgentName);
} else { } else {
WSConnection wsConnection = new WSConnection(vBridge, vBridge); focusAgent = new FocusAgent(focusAgentName, roomJID);
session = SessionManager.getInstance().createClientSession(wsConnection, new BasicStreamID(vBridge + "-" + System.currentTimeMillis() ) ); LocalClientSession session = SessionManager.getInstance().createClientSession(focusAgent, new BasicStreamID(focusAgentName + "-" + System.currentTimeMillis() ) );
wsConnection.setRouter( new SessionPacketRouter(session)); focusAgent.setRouter( new SessionPacketRouter(session));
AuthToken authToken = new AuthToken(vBridge, true); AuthToken authToken = new AuthToken(focusAgentName, true);
session.setAuthToken(authToken, vBridge); session.setAuthToken(authToken, focusAgentName);
sessions.put(vBridge, session); sessions.put(focusAgentName, focusAgent);
Presence presence = new Presence(); Presence presence = new Presence();
wsConnection.getRouter().route(presence); focusAgent.getRouter().route(presence);
} }
focusAgent.createColibriChannel(participant);
}
/**
*
*
*/
public void occupantLeft(JID roomJID, JID user)
{
MUCRoom mucRoom = mucManager.getMultiUserChatService(roomJID).getChatRoom(roomJID.getNode());
Log.info("ColibriIQHandler occupantLeft " + roomJID + " " + user);
String focusAgentName = "colibri.focus.agent." + roomJID.getNode();
if (sessions.containsKey(focusAgentName))
{
FocusAgent focusAgent = sessions.get(focusAgentName);
focusAgent.removeColibriChannel(user);
}
}
/**
*
*
*/
public void nicknameChanged(JID roomJID, JID user, String oldNickname, String newNickname)
{
}
/**
*
*
*/
public void messageReceived(JID roomJID, JID user, String nickname, Message message)
{
}
/**
*
*
*/
public void roomSubjectChanged(JID roomJID, JID user, String newSubject)
{
}
/**
*
*
*/
public void privateMessageRecieved(JID a, JID b, Message message)
{
} }
} }
...@@ -626,22 +729,240 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -626,22 +729,240 @@ public class PluginImpl implements Plugin, PropertyEventListener
} }
} }
public class WSConnection extends VirtualConnection /**
*
*
*/
public class Participant
{
/**
*
*
*/
public String audioChannelId;
/**
*
*
*/
public String videoChannelId;
/**
*
*
*/
private String nickname;
/**
*
*
*/
private JID user;
/**
*
*
*/
public Participant(String nickname, JID user) {
this.nickname = nickname;
this.user = user;
}
/**
*
*
*/
public String getNickname() {
return nickname;
}
/**
*
*
*/
public String toString() {
return user + " " + nickname;
}
/**
*
*
*/
public JID getUser() {
return user;
}
}
public class FocusAgent extends VirtualConnection
{ {
private SessionPacketRouter router; private SessionPacketRouter router;
private String remoteAddr; private String focusName;
private String hostName; private JID roomJid;
private String focusId = null;
private int count = 0;
private LocalClientSession session; private LocalClientSession session;
private Participant firstParticipant;
private String domainName = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
public ConcurrentHashMap<String, Participant> users = new ConcurrentHashMap<String, Participant>();
public ConcurrentHashMap<String, Participant> ids = new ConcurrentHashMap<String, Participant>();
public ConcurrentHashMap<String, Participant> channels = new ConcurrentHashMap<String, Participant>();
/** /**
* *
* *
*/ */
public WSConnection( String remoteAddr, String hostName ) { public FocusAgent(String focusName, JID roomJid) {
this.remoteAddr = remoteAddr; this.focusName = focusName;
this.hostName = hostName; this.roomJid = roomJid;
} }
/**
*
*
*/
public void notifyUser(Participant participant, Element conference)
{
routeColibriEvent(participant, conference, true);
}
/**
*
*
*/
public void answerUser(Participant participant, Element conference)
{
String nickname = participant.getNickname();
IQ iq = new IQ(IQ.Type.set);
iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName));
iq.setTo("jitsi-videobridge." + domainName);
String id = nickname + "-" + System.currentTimeMillis();
ids.put(id, participant);
iq.setID(id);
iq.setChildElement(conference.createCopy());
router.route(iq);
routeColibriEvent(participant, conference, true);
}
/**
*
*
*/
public void addUser(Participant participant, Element conference)
{
users.put(participant.getUser().toString(), participant);
Presence presence = new Presence();
presence.setFrom(XMPPServer.getInstance().createJID(focusName, focusName));
presence.setTo(participant.getUser());
ColibriOfferEvent event = new ColibriOfferEvent();
event.setMuc(roomJid);
event.setNickname(participant.getNickname());
event.setParticipant(participant.getUser());
event.setConference(conference);
presence.getElement().add(colibriProvider.toXML(event));
router.route(presence);
for ( Iterator i = conference.elementIterator("content"); i.hasNext(); )
{
Element content = (Element) i.next();
Element channel = content.element("channel");
if ("audio".equals(content.attributeValue("name")))
{
participant.audioChannelId = channel.attributeValue("id");
channels.put(participant.audioChannelId, participant);
} else {
participant.videoChannelId = channel.attributeValue("id");
channels.put(participant.videoChannelId, participant);
}
}
}
/**
*
*
*/
public void updateUser(Participant participant, Element conference)
{
}
/**
*
*
*/
public boolean isUser(JID user)
{
return users.containsKey(user.toString());
}
/**
*
*
*/
public void createColibriChannel(Participant participant)
{
Log.info("createColibriChannel " + participant + " " + count);
count++;
if (count == 1)
{
firstParticipant = participant; // can't start until we have at least 2 people
return;
}
String nickname = participant.getNickname();
IQ iq = new IQ(IQ.Type.get);
iq.setFrom(XMPPServer.getInstance().createJID(focusName, focusName));
iq.setTo("jitsi-videobridge." + domainName);
String id = nickname + "-" + System.currentTimeMillis();
ids.put(id, participant);
iq.setID(id);
Element conferenceIq = iq.setChildElement("conference", "http://jitsi.org/protocol/colibri");
if (focusId != null)
{
conferenceIq.addAttribute("id", focusId);
}
Element audioContent = conferenceIq.addElement("content").addAttribute("name", "audio");
audioContent.addElement("channel").addAttribute("initiator", "true").addAttribute("expire", "15");
Element videoContent = conferenceIq.addElement("content").addAttribute("name", "video");
videoContent.addElement("channel").addAttribute("initiator", "true").addAttribute("expire", "15");
router.route(iq);
}
/**
*
*
*/
public void removeColibriChannel(JID user)
{
String username = user.toString();
count--;
if (count <= 1)
{
focusId = null; // invalidate current focus session
count = 0;
}
if (users.containsKey(username))
{
Participant participant = users.remove(username);
routeColibriEvent(participant, null, false);
}
Log.info("removeColibriChannel " + count);
}
/**
*
*
*/
public void handleColibriCommand(IQ reply, JID user, Element conference)
{
reply.setError(PacketError.Condition.not_allowed); // not implemented
}
/** /**
* *
* *
...@@ -664,7 +985,7 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -664,7 +985,7 @@ public class PluginImpl implements Plugin, PropertyEventListener
*/ */
public void closeVirtualConnection() public void closeVirtualConnection()
{ {
Log.debug("WSConnection - close "); Log.debug("FocusAgent - close ");
} }
/** /**
...@@ -672,18 +993,18 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -672,18 +993,18 @@ public class PluginImpl implements Plugin, PropertyEventListener
* *
*/ */
public byte[] getAddress() { public byte[] getAddress() {
return remoteAddr.getBytes(); return focusName.getBytes();
} }
/** /**
* *
* *
*/ */
public String getHostAddress() { public String getHostAddress() {
return remoteAddr; return focusName;
} }
public String getHostName() { public String getHostName() {
return ( hostName != null ) ? hostName : "0.0.0.0"; return "0.0.0.0";
} }
/** /**
* *
...@@ -698,7 +1019,81 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -698,7 +1019,81 @@ public class PluginImpl implements Plugin, PropertyEventListener
*/ */
public void deliver(Packet packet) throws UnauthorizedException public void deliver(Packet packet) throws UnauthorizedException
{ {
deliverRawText(packet.toXML()); Log.info("FocusAgent deliver\n" + packet);
IQ iq = (IQ) packet;
if (iq.getType() == IQ.Type.result)
{
Element conference = iq.getChildElement().createCopy();
focusId = conference.attributeValue("id");
String id = packet.getID();
if (ids.containsKey(id))
{
Participant participant = ids.remove(id);
if (users.containsKey(participant.getUser().toString()))
updateUser(participant, conference);
else
addUser(participant, conference);
Log.info("FocusAgent response for user " + participant + " " + focusId + "\n" + conference);
} else Log.error("FocusAgent deliver cannot find iq owner " + id + "\n" + packet);
if (firstParticipant != null) // send pending channel request for first participant
{
createColibriChannel(firstParticipant);
firstParticipant = null;
}
} else if (iq.getType() == IQ.Type.error) {
focusId = null; // error
count = 0;
Log.error("Videobrideg error \n" + packet);
for (Participant reciepient : users.values())
{
Log.info("routeColibriEvent - E " + reciepient);
sendRayoEvent(reciepient, null, false, reciepient);
}
} else if (iq.getType() == IQ.Type.set || iq.getType() == IQ.Type.get) {
JID user = iq.getFrom();
Element root = iq.getChildElement();
Element conference = null;
if (user.toString().equals("jitsi-videobridge." + domainName)) // SSRC notification from videobridge
{
/*
conference = root.createCopy();
String channelId = conference.element("content").element("channel").attributeValue("id");
if (channels.containsKey(channelId))
notifyUser(channels.get(channelId), conference);
else Log.error("FocusAgent deliver cannot find channel owner " + channelId + "\n" + packet);
*/
} else {
conference = root.element("conference").createCopy(); // rayo from participant
IQ reply = IQ.createResultIQ(iq);
if (users.containsKey(user.toString()))
answerUser(users.get(user.toString()), conference);
else
reply.setError(PacketError.Condition.not_allowed);
router.route(reply);
}
} else {
Log.warn("Unexpected Videobrideg message \n" + packet);
}
} }
/** /**
* *
...@@ -706,7 +1101,56 @@ public class PluginImpl implements Plugin, PropertyEventListener ...@@ -706,7 +1101,56 @@ public class PluginImpl implements Plugin, PropertyEventListener
*/ */
public void deliverRawText(String text) public void deliverRawText(String text)
{ {
Log.debug("FocusAgent deliverRawText\n" + text);
}
/**
*
*
*/
private void routeColibriEvent(Participant participant, Element conference, boolean isAdd)
{
Log.info("routeColibriEvent - P " + participant);
for (Participant reciepient : users.values())
{
if (participant.getUser().toString().equals(reciepient.getUser().toString()) == false)
{
Log.info("routeColibriEvent - R " + reciepient);
sendRayoEvent(reciepient, conference, isAdd, participant);
}
}
}
/**
*
*
*/
private void sendRayoEvent(Participant reciepient, Element conference, boolean isAdd, Participant participant)
{
Presence presence = new Presence();
presence.setFrom(XMPPServer.getInstance().createJID(focusName, focusName));
presence.setTo(reciepient.getUser());
if (isAdd)
{
AddSourceEvent event = new AddSourceEvent();
event.setMuc(roomJid);
event.setNickname(participant.getNickname());
event.setParticipant(participant.getUser());
event.setConference(conference);
presence.getElement().add(colibriProvider.toXML(event));
} else {
RemoveSourceEvent event = new RemoveSourceEvent();
event.setMuc(roomJid);
event.setNickname(participant.getNickname());
event.setParticipant(participant.getUser());
event.setActive(focusId != null);
presence.getElement().add(colibriProvider.toXML(event));
}
router.route(presence);
} }
/** /**
* *
......
...@@ -7,6 +7,7 @@ Strophe.addConnectionPlugin('emuc', { ...@@ -7,6 +7,7 @@ Strophe.addConnectionPlugin('emuc', {
roomjid: null, roomjid: null,
myroomjid: null, myroomjid: null,
members: {}, members: {},
joined: false,
isOwner: false, isOwner: false,
init: function (conn) { init: function (conn) {
this.connection = conn; this.connection = conn;
...@@ -23,7 +24,7 @@ Strophe.addConnectionPlugin('emuc', { ...@@ -23,7 +24,7 @@ Strophe.addConnectionPlugin('emuc', {
} }
var join = $pres({to: this.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'}); var join = $pres({to: this.myroomjid }).c('x', {xmlns: 'http://jabber.org/protocol/muc'});
if (password !== null) { if (password !== undefined) {
join.c('password').t(password); join.c('password').t(password);
} }
this.connection.send(join); this.connection.send(join);
...@@ -47,10 +48,14 @@ Strophe.addConnectionPlugin('emuc', { ...@@ -47,10 +48,14 @@ Strophe.addConnectionPlugin('emuc', {
member.show = $(pres).find('>show').text(); member.show = $(pres).find('>show').text();
member.status = $(pres).find('>status').text(); member.status = $(pres).find('>status').text();
var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item'); var tmp = $(pres).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');
member.affilication = tmp.attr('affiliation'); member.affiliation = tmp.attr('affiliation');
member.role = tmp.attr('role'); member.role = tmp.attr('role');
if (from == this.myroomjid) { if (from == this.myroomjid) {
$(document).trigger('joined.muc', [from, member]); if (member.affiliation == 'owner') this.isOwner = true;
if (!this.joined) {
this.joined = true;
$(document).trigger('joined.muc', [from, member]);
}
} else if (this.members[from] === undefined) { } else if (this.members[from] === undefined) {
// new participant // new participant
this.members[from] = member; this.members[from] = member;
......
Sorry, webrtc is required for this and your browser does not seem to support it. <html>
<head>
<title></title>
<link rel="stylesheet" type="text/css" media="screen" href="css/chromeonly.css" />
</head>
<body>
<!-- wrap starts here -->
<div id="wrap">
<a href="http://google.com/chrome"><div id="left"></div></a>
<div id="middle"></div>
<div id="text">
<p>This service only works with Chrome.</p>
<p><a href="http://google.com/chrome">Download Chrome</a></p>
</div>
<!-- wrap ends here -->
</div>
</body>
</html>
\ 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