| <!DOCTYPE html> | 
| <html lang="en"> | 
| <head> | 
|     <meta charset="utf-8"> | 
|     <title>JSDoc: Source: xmppvideoroom.js</title> | 
|   | 
|     <script src="scripts/prettify/prettify.js"> </script> | 
|     <script src="scripts/prettify/lang-css.js"> </script> | 
|     <!--[if lt IE 9]> | 
|       <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> | 
|     <![endif]--> | 
|     <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> | 
|     <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> | 
| </head> | 
|   | 
| <body> | 
|   | 
| <div id="main"> | 
|   | 
|     <h1 class="page-title">Source: xmppvideoroom.js</h1> | 
|   | 
|      | 
|   | 
|   | 
|   | 
|      | 
|     <section> | 
|         <article> | 
|             <pre class="prettyprint source linenums"><code>//var XMPPVideoRoom = (function() { | 
|   | 
|      | 
|     /**  | 
|      * Interface with Jitsi Video Room and WebRTC-streamer API | 
|      * @constructor | 
|      * @param {string} xmppUrl - url of XMPP server | 
|      * @param {string} srvurl - url of WebRTC-streamer | 
|     */ | 
|     var XMPPVideoRoom = function XMPPVideoRoom (xmppUrl, srvurl, bus) {     | 
|         this.xmppUrl     = xmppUrl; | 
|         this.srvurl      = srvurl || "//"+window.location.hostname+":"+window.location.port; | 
|         this.sessionList = {}; | 
|         this.bus = bus; | 
|     }; | 
|          | 
|   | 
|     /**  | 
|     * Ask to publish a stream from WebRTC-streamer in a XMPP Video Room user | 
|     * @param {string} roomid - id of the XMPP Video Room to join | 
|     * @param {string} url - WebRTC stream to publish | 
|     * @param {string} name - name in Video Room | 
|     */ | 
|     XMPPVideoRoom.prototype.join = function(roomid, url, name) { | 
|         var connection = new Strophe.Connection("https://" + this.xmppUrl + "/http-bind"); | 
|         connection.addHandler(this.OnJingle.bind(this, connection, url), 'urn:xmpp:jingle:1', 'iq', 'set', null, null); | 
|         connection.roomid = roomid; | 
|         connection.name = name; | 
|         this.emit(connection.roomid + '/' + connection.name, "joining"); | 
|   | 
| //        connection.rawInput = function (data) { console.log('RECV: ' + data); }; | 
| //        connection.rawOutput = function (data) { console.log('SEND: ' + data); }; | 
|         // disco stuff | 
|         if (connection.disco) { | 
|             connection.disco.addIdentity('client', 'http://jitsi.org/jitsimeet'); | 
|             connection.disco.addFeature("urn:xmpp:jingle:1");     | 
|             connection.disco.addFeature("urn:xmpp:jingle:apps:rtp:1");     | 
|             connection.disco.addFeature("urn:xmpp:jingle:transports:ice-udp:1");     | 
|             connection.disco.addFeature("urn:xmpp:jingle:apps:dtls:0");     | 
|             connection.disco.addFeature("urn:xmpp:jingle:apps:rtp:audio");     | 
|             connection.disco.addFeature("urn:xmpp:jingle:apps:rtp:video");     | 
|             connection.disco.addFeature("urn:ietf:rfc:5761") | 
|             connection.disco.addFeature("urn:ietf:rfc:5888"); // a=group, e.g. bundle | 
|              | 
|         } | 
|         connection.connect(this.xmppUrl, null, this.onConnect.bind(this, connection, roomid, name)); | 
|     } | 
|   | 
|     /**  | 
|     * Ask to leave a XMPP Video Room user | 
|     * @param {string} roomid - id of the XMPP Video Room to leave | 
|     * @param {string} name - name in Video Room | 
|     */ | 
|     XMPPVideoRoom.prototype.leave = function (roomId, username) { | 
|         Object.entries(this.sessionList).forEach( ([sid, session]) => { | 
|             if ( (session.connection.roomid === roomId) && (session.connection.name === username) ) { | 
|                 this.leaveSession(sid); | 
|             } | 
|         }); | 
|     } | 
|   | 
|     /**  | 
|     * Ask to leave all XMPP Video Room | 
|     */ | 
|     XMPPVideoRoom.prototype.leaveAll = function () { | 
|         Object.keys(this.sessionList).forEach( (sid) => { | 
|             this.leaveSession(sid); | 
|         }); | 
|         this.sessionList = {}; | 
|     } | 
|   | 
|     /* | 
|     /* HTTP callback for /getIceCandidate  | 
|     */ | 
|     XMPPVideoRoom.prototype.onReceiveCandidate = function(connection, answer, candidateList) { | 
|         console.log("============candidateList:" +  JSON.stringify(candidateList)); | 
|         var jingle = answer.querySelector("jingle"); | 
|         var sid = jingle.getAttribute("sid"); | 
|         var from = answer.getAttribute("to"); | 
|         var to = answer.getAttribute("from"); | 
|   | 
|         candidateList.forEach(function (candidate) { | 
|             var jsoncandidate = SDPUtil.parse_icecandidate(candidate.candidate); | 
|             console.log("<=== webrtc candidate:" +  JSON.stringify(jsoncandidate)); | 
|             // get ufrag | 
|             var ufrag = ""; | 
|             var elems = candidate.candidate.split(' '); | 
|             for (var i = 8; i < elems.length; i += 2) { | 
|                 switch (elems[i]) { | 
|                     case 'ufrag': | 
|                         ufrag = elems[i + 1]; | 
|                         break; | 
|                 } | 
|             } | 
|             // get pwd | 
|             var pwd = ""; | 
|             var transports = $(jingle).find('>content>transport'); | 
|             transports.each( (idx,transport) => { | 
|                 if (ufrag == transport.getAttribute('ufrag')) { | 
|                     pwd = transport.getAttribute('pwd'); | 
|                 } | 
|             }); | 
|              | 
|             // convert candidate from webrtc to jingle  | 
|             var iq = $iq({ type: "set",  from, to }) | 
|                     .c('jingle', {xmlns: 'urn:xmpp:jingle:1'}) | 
|                         .attrs({ action: "transport-info",  sid, ufrag, pwd }) | 
|                         .c('candidate', jsoncandidate) | 
|                         .up() | 
|                     .up(); | 
|   | 
|             var id = connection.sendIQ(iq, () => { | 
|                 console.log("===> xmpp transport-info ok sid:" + sid);         | 
|             },() => { | 
|                 console.log("############transport-info error sid:" + sid); | 
|             }); | 
|         });     | 
|   | 
|         var id = connection.sendIQ(answer, () => { | 
|             console.log("===> xmpp session-accept ok sid:" + sid); | 
|             this.emit(connection.roomid + '/' + connection.name, "published");             | 
|         },() => { | 
|             console.log("############session-accept error sid:" + sid); | 
|         }); | 
|     } | 
|   | 
|     /* | 
|     /* HTTP callback for  /call  | 
|     */     | 
|     XMPPVideoRoom.prototype.onCall = function(connection, iq, data) {         | 
|         var jingle = iq.querySelector("jingle"); | 
|         var sid = jingle.getAttribute("sid"); | 
|         console.log("<=== webrtc answer sid:" + sid);         | 
|          | 
|         this.sessionList[sid].state = "UP"; | 
|         var earlyCandidates = this.sessionList[sid].earlyCandidates; | 
|         while (earlyCandidates.length) { | 
|                 var candidate = earlyCandidates.shift(); | 
|                 var method = this.srvurl + "/api/addIceCandidate?peerid="+ sid; | 
|                 request("POST" , method, { body: JSON.stringify(candidate) }).done( function (response) {  | 
|                         if (response.statusCode === 200) { | 
|                             console.log("method:"+method+ " answer:" +response.body); | 
|                         } | 
|                         else { | 
|                             bind.onError(response.statusCode); | 
|                         } | 
|                     } | 
|                 );     | 
|         } | 
|                  | 
|         var sdp = new SDP(data.sdp); | 
|         var iqAnswer = $iq({ type: "set",  from: iq.getAttribute("to"), to: iq.getAttribute("from") }) | 
|         var jingle = iqAnswer.c('jingle', {xmlns: 'urn:xmpp:jingle:1'}) | 
|                         .attrs({ action: "session-accept",  sid, responder:iq.getAttribute("to") }); | 
|   | 
|         var answer = sdp.toJingle(jingle);  | 
|         var bind = this; | 
|         var method = this.srvurl + "/api/getIceCandidate?peerid="+ sid; | 
|         request("GET" , method).done( function (response) {  | 
|                 if (response.statusCode === 200) { | 
|                     bind.onReceiveCandidate(connection, answer.node, JSON.parse(response.body)); | 
|                 } | 
|                 else { | 
|                     bind.onError(response.statusCode); | 
|                 } | 
|             } | 
|         );             | 
|     } | 
|      | 
|     XMPPVideoRoom.prototype.emit = function(name, state) { | 
|         if (this.bus) { | 
|             this.bus.emit('state', name, state); | 
|         } | 
|     } | 
|   | 
|     XMPPVideoRoom.prototype.onError = function (error) { | 
|         console.log("############onError:" + error) | 
|     } | 
|      | 
|     /* | 
|     /* XMPP callback for  jingle  | 
|     */         | 
|     XMPPVideoRoom.prototype.OnJingle = function(connection, url, iq) { | 
|         var jingle = iq.querySelector("jingle"); | 
|         var sid = jingle.getAttribute("sid"); | 
|         var action = jingle.getAttribute("action"); | 
|         const fromJid = iq.getAttribute('from'); | 
|         const toJid = iq.getAttribute('to'); | 
|         console.log("OnJingle from:" + fromJid + " to:" + toJid | 
|                                       + " sid:" + sid + " action:" + action); | 
|         var id = iq.getAttribute("id"); | 
|         var ack = $iq({ type: "result", to: fromJid, id }) | 
|   | 
|         var bind = this; | 
|   | 
|         if (action === "session-initiate")     { | 
|             connection.sendIQ(ack);     | 
|   | 
|             const resource = Strophe.getResourceFromJid(fromJid); | 
|             const isP2P = (resource !== 'focus'); | 
|             console.log("<=== xmpp offer sid:" + sid + " resource:" + resource + " initiator:" + jingle.getAttribute("initiator")); | 
|   | 
|             if (!isP2P) { | 
|                 this.emit(connection.roomid + '/' + connection.name, "publishing"); | 
|   | 
|                 var sdp = new SDP(''); | 
|                 sdp.fromJingle($(jingle)); | 
|                  | 
|                 this.sessionList[sid] = { connection, state: "INIT", earlyCandidates:[] } ; | 
|   | 
|                 var method = this.srvurl + "/api/call?peerid="+ sid +"&url="+encodeURIComponent(url)+"&options="+encodeURIComponent("rtptransport=tcp&timeout=60"); | 
|                 request("POST" , method, {body:JSON.stringify({type:"offer",sdp:sdp.raw})}).done( function (response) {  | 
|                         if (response.statusCode === 200) { | 
|                             bind.onCall(connection, iq, JSON.parse(response.body)); | 
|                         } | 
|                         else { | 
|                             bind.onError(response.statusCode); | 
|                         } | 
|                     } | 
|                 ); | 
|             } | 
|                  | 
|         } else if (action === "transport-info") { | 
|             connection.sendIQ(ack);         | 
|   | 
|             console.log("<=== xmpp candidate sid:" + sid); | 
|              | 
|             if (this.sessionList[sid]) { | 
|                 var contents = $(jingle).find('>content'); | 
|                 contents.each( (contentIdx,content) => { | 
|                     var transports = $(content).find('>transport'); | 
|                     transports.each( (idx,transport) => { | 
|                         var ufrag = transport.getAttribute('ufrag'); | 
|                         var candidates = $(transport).find('>candidate'); | 
|                         candidates.each ( (idx,candidate) => { | 
|                             var sdp = SDPUtil.candidateFromJingle(candidate); | 
|                             sdp = sdp.replace("a=candidate","candidate"); | 
|                             sdp = sdp.replace("\r\n"," ufrag " + ufrag); | 
|                             var candidate = { candidate:sdp, sdpMid:"", sdpMLineIndex:contentIdx } | 
|                             console.log("===> webrtc candidate :" + JSON.stringify(candidate)); | 
|                  | 
|                             if (this.sessionList[sid].state == "INIT") { | 
|                                 this.sessionList[sid].earlyCandidates.push(candidate); | 
|                             } else { | 
|                                 var method = this.srvurl + "/api/addIceCandidate?peerid="+ sid; | 
|                                 request("POST" , method, { body: JSON.stringify(candidate) }).done( function (response) {  | 
|                                         if (response.statusCode === 200) { | 
|                                             console.log("method:"+method+ " answer:" +response.body); | 
|                                         } | 
|                                         else { | 
|                                             bind.onError(response.statusCode); | 
|                                         } | 
|                                     } | 
|                                 );             | 
|                             }                             | 
|                         }); | 
|                     }); | 
|                 });     | 
|             } | 
|         } else if (action === "session-terminate") {             | 
|             connection.sendIQ(ack);         | 
|             console.log("<=== xmpp session-terminate sid:" + sid + " reason:" + jingle.querySelector("reason").textContent); | 
|             this.leaveSession(sid); | 
|         } | 
|                      | 
|         return true;         | 
|     } | 
|      | 
|     /* | 
|     /* XMPP callback for  presence  | 
|     */         | 
|     XMPPVideoRoom.prototype.OnPresence = function(connection,pres) | 
|     { | 
|         const resource = Strophe.getResourceFromJid(pres.getAttribute('from')); | 
|         const xElement = pres.getElementsByTagNameNS('http://jabber.org/protocol/muc#user', 'x')[0]; | 
|         const mucUserItem = xElement && xElement.getElementsByTagName('item')[0]; | 
|         if (mucUserItem) { | 
|             var msg = " jid:" + mucUserItem.getAttribute('jid')  | 
|                         + " role:" + mucUserItem.getAttribute('role') | 
|                         + " affiliation:" + mucUserItem.getAttribute('affiliation'); | 
|   | 
|             const statusEl = pres.getElementsByTagName('status')[0]; | 
|             if (statusEl) { | 
|                 msg += " status:" + statusEl.getAttribute('code');  | 
|             } | 
|   | 
|             const nickEl = pres.getElementsByTagName('nick')[0]; | 
|             if (nickEl) { | 
|                 msg += "OnPresence nick:" + nickEl.textContent;  | 
|             } | 
|             console.log ( "OnPresence " + msg);                              | 
|         } | 
|         if (resource === connection.name) { | 
|             console.log ( "OnPresence " + connection.name + " connected");  | 
|         } | 
|         return true;         | 
|     } | 
|   | 
|     /* | 
|     /* XMPP callback for  connect  | 
|     */         | 
|     XMPPVideoRoom.prototype.onConnect = function(connection, roomid, name, status) | 
|     {         | 
|         if (status === Strophe.Status.CONNECTING) { | 
|             console.log('Strophe is connecting.'); | 
|         } else if (status === Strophe.Status.CONNFAIL) { | 
|             console.log('Strophe failed to connect.'); | 
|         } else if (status === Strophe.Status.DISCONNECTING) { | 
|             console.log('Strophe is disconnecting.'); | 
|         } else if (status === Strophe.Status.DISCONNECTED) { | 
|             console.log('Strophe is disconnected.'); | 
|         } else if (status === Strophe.Status.CONNECTED) { | 
|             console.log('Strophe is connected.'); | 
|              | 
|   | 
|             var roomUrl = roomid + "@" + "conference." + this.xmppUrl;             | 
|             var extPresence = Strophe.xmlElement('nick', {xmlns:'http://jabber.org/protocol/nick'}, name); | 
|             connection.muc.join(roomUrl, name, null, this.OnPresence.bind(this,connection), null, null, null, extPresence);     | 
|              | 
|             this.emit(connection.roomid + '/' + connection.name, "joined"); | 
|         } | 
|     } | 
|          | 
|   | 
|     /* | 
|     /* leave a session | 
|     */     | 
|     XMPPVideoRoom.prototype.leaveSession = function (sid) { | 
|         var session = this.sessionList[sid]; | 
|         if (session) { | 
|             this.emit(session.connection.roomid + '/' + session.connection.name, "leaving"); | 
|   | 
|             var roomUrl = session.connection.roomid + "@" + "conference." + this.xmppUrl; | 
|             // close jingle session | 
|             var iq = $iq({ type: "set",  from: roomUrl +"/" + session.connection.name, to: roomUrl }) | 
|                             .c('jingle', {xmlns: 'urn:xmpp:jingle:1'}) | 
|                                 .attrs({ action: "session-terminate",  sid}) | 
|                             .up(); | 
|             session.connection.sendIQ(iq); | 
|   | 
|             // close WebRTC session | 
|             var bind = this; | 
|             var method = this.srvurl + "/api/hangup?peerid="+ sid; | 
|             request("GET" , method).done( function (response) {  | 
|                     if (response.statusCode === 200) { | 
|                         console.log("method:"+method+ " answer:" +response.body); | 
|                     } | 
|                     else { | 
|                         bind.onError(response.statusCode); | 
|                     } | 
|                 } | 
|             );                     | 
|             session.connection.muc.leave(roomUrl, session.connection.name); | 
|             session.connection.flush(); | 
|             session.connection.disconnect();     | 
|             this.emit(session.connection.roomid + '/' + session.connection.name, "leaved"); | 
|   | 
|             delete this.sessionList[sid]; | 
|         } | 
|     } | 
|   | 
|   | 
| //    return XMPPVideoRoom; | 
| //})(); | 
|   | 
| //module.exports = XMPPVideoRoom; | 
| </code></pre> | 
|         </article> | 
|     </section> | 
|   | 
|   | 
|   | 
|   | 
| </div> | 
|   | 
| <nav> | 
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="JanusVideoRoom.html">JanusVideoRoom</a></li><li><a href="WebRtcStreamer.html">WebRtcStreamer</a></li><li><a href="XMPPVideoRoom.html">XMPPVideoRoom</a></li></ul> | 
| </nav> | 
|   | 
| <br class="clear"> | 
|   | 
| <footer> | 
|     Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.3</a> on Sat Aug 25 2018 15:00:27 GMT+0200 (CEST) | 
| </footer> | 
|   | 
| <script> prettyPrint(); </script> | 
| <script src="scripts/linenumber.js"> </script> | 
| </body> | 
| </html> |