From 15d71452295d1e0a54ecd2ba3c080dc0c1a05ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaussoin=20Timoth=C3=A9e?= Date: Sat, 18 Jan 2014 00:14:24 +0100 Subject: [PATCH] - Adding ICE Candidate support in Movim (receive and send) - Fixing some little issues in the Javascript --- app/assets/js/movim_rpc.js | 6 +- app/assets/js/movim_tpl.js | 3 +- app/widgets/Explore/Explore.php | 3 +- app/widgets/Visio/visio.css | 1 - app/widgets/Visio/webrtc.js | 58 ++++++-- app/widgets/VisioExt/VisioExt.php | 24 ++- app/widgets/VisioExt/visioext.js | 3 +- lib/JingletoSDP.php | 239 +++++++++++++++++------------- lib/SDPtoJingle.php | 12 +- themes/movim/css/posts.css | 4 + 10 files changed, 224 insertions(+), 129 deletions(-) diff --git a/app/assets/js/movim_rpc.js b/app/assets/js/movim_rpc.js index f5ab8c33f..5393b9cf9 100644 --- a/app/assets/js/movim_rpc.js +++ b/app/assets/js/movim_rpc.js @@ -104,15 +104,15 @@ function MovimRPC() if(funcall.func != null) { var funcs = funcall.func.split('.'); - try { + //try { if(funcs.length == 1) window[funcs[0]](funcall.params); else if(funcs.length == 2) window[funcs[0]][funcs[1]](funcall.params); - } + /*} catch(err) { console.log("Error caught: " + err.toString() + " - " +funcall.func); - } + }*/ } } } diff --git a/app/assets/js/movim_tpl.js b/app/assets/js/movim_tpl.js index 8691adbab..24ca16e27 100644 --- a/app/assets/js/movim_tpl.js +++ b/app/assets/js/movim_tpl.js @@ -62,5 +62,6 @@ function movim_fill(params) function movim_delete(params) { target = document.getElementById(params[0]); - target.parentNode.removeChild(target); + if(target) + target.parentNode.removeChild(target); } diff --git a/app/widgets/Explore/Explore.php b/app/widgets/Explore/Explore.php index e0f00f65b..974294dd6 100644 --- a/app/widgets/Explore/Explore.php +++ b/app/widgets/Explore/Explore.php @@ -121,7 +121,8 @@ class Explore extends WidgetCommon {
'.prepareString($user->description).'
- + + '; } diff --git a/app/widgets/Visio/visio.css b/app/widgets/Visio/visio.css index ab7f311a2..48786bb31 100644 --- a/app/widgets/Visio/visio.css +++ b/app/widgets/Visio/visio.css @@ -80,7 +80,6 @@ body { } #visio #log { - display: none; color: white; position: absolute; top: 0; diff --git a/app/widgets/Visio/webrtc.js b/app/widgets/Visio/webrtc.js index 84e0e4803..fc3e0a05c 100644 --- a/app/widgets/Visio/webrtc.js +++ b/app/widgets/Visio/webrtc.js @@ -15,13 +15,6 @@ var sdpConstraints = {'mandatory': { 'OfferToReceiveAudio': true, 'OfferToReceiveVideo': true }}; -function onIceCandidate(event) { - Visio.log('onIceCandidate'); - //Visio.log(event); - - //pc.addIceCandidate(event.candidate, onIceCandidateAdded, onDomError); -} - function onIceConnectionStateChanged(event) { Visio.log('onIceConnectionStateChanged'); Visio.log(event); @@ -33,7 +26,17 @@ function onSignalingStateChanged(event) { } function onIceCandidateAdded(event) { - Visio.log('onIceCandateAdded'); + Visio.log('onIceCandidateAdded'); + Visio.log(event); +} + +function onRemoteIceCandidateAdded(event) { + Visio.log('onRemoteIceCandidateAdded'); + Visio.log(event); +} + +function onRemoteIceCandidateError(event) { + Visio.log('onRemoteIceCandidateError'); Visio.log(event); } @@ -75,6 +78,25 @@ function onAnswerCreated(offer) { sendMessage(offer, true); } +function onIceCandidate(event) { + Visio.log('onIceCandidate'); + console.log(event); + candidate = {}; + + if(event.candidate != null) { + candidate.sdp = event.candidate.candidate; + candidate.mid = event.candidate.sdpMid; + candidate.line = event.candidate.sdpMLineIndex; + + candidate.jid = VISIO_JID; + candidate.ressource = VISIO_RESSOURCE; + + var msgString = JSON.stringify(candidate); + + Visio.call(['VisioExt_ajaxSendCandidate', msgString]); + } +} + function sendMessage(msg, accept) { offer = {}; offer.sdp = msg.sdp; @@ -115,7 +137,6 @@ function sendMessage(msg, accept) { } else { Visio.log('Send the proposal.'); Visio.log('PROPOSAL ' + msg.sdp); - Visio.call(['VisioExt_ajaxSendProposal', msgString]); } } @@ -151,18 +172,18 @@ function onOffer(offer) { init(false); if(offer != null) { - /* + var message = {}; message.sdp = offer; message.type = 'offer'; console.log(message); var desc = new RTCSessionDescription(message); console.log(desc); - */ + /* var desc = new RTCSessionDescription(); desc.sdp = offer; desc.type = 'offer'; - + */ pc.setRemoteDescription(desc, onSetRemoteSessionDescriptionSuccess, onSetRemoteSessionDescriptionError); } @@ -190,6 +211,19 @@ function onAccept(offer) { } } +function onCandidate(message) { + var label = { + 'audio' : 0, + 'video' : 1 + }; + + var candidate = new RTCIceCandidate({sdpMLineIndex: label[message[1]], + candidate: message[0]}); + + //console.log(candidate); + pc.addIceCandidate(candidate, onRemoteIceCandidateAdded, onRemoteIceCandidateError); +} + function init(isCaller) { var configuration = {"iceServers":[{"url": "stun:23.21.150.121:3478"}]}; diff --git a/app/widgets/VisioExt/VisioExt.php b/app/widgets/VisioExt/VisioExt.php index 7d9f401eb..19659ac36 100644 --- a/app/widgets/VisioExt/VisioExt.php +++ b/app/widgets/VisioExt/VisioExt.php @@ -53,11 +53,11 @@ class VisioExt extends WidgetBase function onTransportInfo($jingle) { $jts = new \JingletoSDP($jingle); - $sdp = $jts->generate(); + + RPC::call('Popup.call', 'onCandidate', $jts->generate(), $jts->media); } function onSessionTerminate($jingle) { - //call webrtc.js terminate() RPC::call('Popup.call', 'terminate'); } @@ -109,6 +109,26 @@ class VisioExt extends WidgetBase ->request(); } + function ajaxSendCandidate($candidate) { + $p = json_decode($candidate); + $sd = Sessionx::start(); + + $sdp = + 'm='.$p->mid."\n". + $p->sdp; + + $stj = new SDPtoJingle( + $sdp, + $this->user->getLogin().'/'.$sd->ressource, + $p->jid.'/'.$p->ressource, + 'transport-info'); + + $r = new moxl\JingleSessionInitiate(); + $r->setTo($p->jid.'/'.$p->ressource) + ->setOffer($stj->generate()) + ->request(); + } + function build() { } diff --git a/app/widgets/VisioExt/visioext.js b/app/widgets/VisioExt/visioext.js index 7214fc7d6..1cde9ad9e 100644 --- a/app/widgets/VisioExt/visioext.js +++ b/app/widgets/VisioExt/visioext.js @@ -39,12 +39,13 @@ var Popup = { }, open: function(jid) { - console.log('Opening the Popup'); + console.log('Popup already opened'); var url = BASE_URI + PAGE_KEY_URI + "visio&f="+jid this.setJid(jid); if( !this.win || this.win.closed ) { + console.log('Opening the Popup'); this.win = window.open( url, "win", "height=480,width=640,directories=0,titlebar=0,toolbar=0,location=0,status=0, personalbar=0,menubar=0,resizable=0" ); } else this.win.focus(); }, diff --git a/lib/JingletoSDP.php b/lib/JingletoSDP.php index 89f3040e0..fcca11298 100644 --- a/lib/JingletoSDP.php +++ b/lib/JingletoSDP.php @@ -3,6 +3,11 @@ class JingletoSDP { private $sdp = ''; private $jingle; + + private $action; + + // Only used for ICE Candidate (Jingle transport-info) + public $media; private $values = array( 'session_id' => 1, @@ -14,6 +19,8 @@ class JingletoSDP { function __construct($jingle) { $this->jingle = $jingle; + + $this->action = (string)$this->jingle->attributes()->action; } function getSessionId(){ @@ -78,119 +85,123 @@ class JingletoSDP { if(isset($content->transport->attributes()->pwd)) $sdp_media .= "\na=ice-pwd:".$content->transport->attributes()->pwd; - - foreach($content->description->children() as $payload) { - switch($payload->getName()) { - case 'rtp-hdrext': - $sdp_media .= - "\na=extmap:". - $payload->attributes()->id; - - if(isset($payload->attributes()->senders)) - $sdp_media .= ' '.$payload->attributes()->senders; - $sdp_media .= ' '.$payload->attributes()->uri; - break; - - case 'rtcp-mux': - $sdp_media .= - "\na=rtcp-mux"; - - case 'encryption': - if(isset($payload->crypto)) { + if(isset($content->description)) { + foreach($content->description->children() as $payload) { + switch($payload->getName()) { + case 'rtp-hdrext': $sdp_media .= - "\na=crypto:". - $payload->crypto->attributes()->tag.' '. - $payload->crypto->attributes()->{'crypto-suite'}.' '. - $payload->crypto->attributes()->{'key-params'}; + "\na=extmap:". + $payload->attributes()->id; + + if(isset($payload->attributes()->senders)) + $sdp_media .= ' '.$payload->attributes()->senders; - // TODO session params ? - } - break; + $sdp_media .= ' '.$payload->attributes()->uri; + break; + + case 'rtcp-mux': + $sdp_media .= + "\na=rtcp-mux"; + + case 'encryption': + if(isset($payload->crypto)) { + $sdp_media .= + "\na=crypto:". + $payload->crypto->attributes()->tag.' '. + $payload->crypto->attributes()->{'crypto-suite'}.' '. + $payload->crypto->attributes()->{'key-params'}; - case 'payload-type': - $sdp_media .= - "\na=rtpmap:". - $payload->attributes()->id; + // TODO session params ? + } + break; - array_push($media_header_ids, $payload->attributes()->id); + case 'payload-type': + $sdp_media .= + "\na=rtpmap:". + $payload->attributes()->id; + + array_push($media_header_ids, $payload->attributes()->id); - if(isset($payload->attributes()->name)) { - $sdp_media .= ' '.$payload->attributes()->name; + if(isset($payload->attributes()->name)) { + $sdp_media .= ' '.$payload->attributes()->name; - if(isset($payload->attributes()->clockrate)) { - $sdp_media .= '/'.$payload->attributes()->clockrate; + if(isset($payload->attributes()->clockrate)) { + $sdp_media .= '/'.$payload->attributes()->clockrate; - if(isset($payload->attributes()->channels)) { - $sdp_media .= '/'.$payload->attributes()->channels; + if(isset($payload->attributes()->channels)) { + $sdp_media .= '/'.$payload->attributes()->channels; + } } } - } - $first_fmtp = true; + $first_fmtp = true; - foreach($payload->children() as $param) { - switch($param->getName()) { - case 'rtcp-fb' : - $sdp_media .= - "\na=rtcp-fb:". - $param->attributes()->id.' '. - $param->attributes()->type; + foreach($payload->children() as $param) { + switch($param->getName()) { + case 'rtcp-fb' : + $sdp_media .= + "\na=rtcp-fb:". + $param->attributes()->id.' '. + $param->attributes()->type; - if(isset($param->attributes()->subtype)) { - $sdp_media .= ' '.$param->attributes()->subtype; - } + if(isset($param->attributes()->subtype)) { + $sdp_media .= ' '.$param->attributes()->subtype; + } - break; + break; - // http://xmpp.org/extensions/xep-0167.html#format - case 'parameter' : - if($first_fmtp) { - $sdp_media .= - "\na=fmtp:". - $payload->attributes()->id. - ' '; - } else { - $sdp_media .= '; '; - } + // http://xmpp.org/extensions/xep-0167.html#format + case 'parameter' : + if($first_fmtp) { + $sdp_media .= + "\na=fmtp:". + $payload->attributes()->id. + ' '; + } else { + $sdp_media .= '; '; + } + + if(isset($param->attributes()->name)) { + $sdp_media .= + $param->attributes()->name. + '='; + } - if(isset($param->attributes()->name)) { $sdp_media .= - $param->attributes()->name. - '='; - } + $param->attributes()->value; - $sdp_media .= - $param->attributes()->value; + $first_fmtp = false; + + break; + } - $first_fmtp = false; - - break; + // TODO rtcp_fb_trr_int ? } + + break; - // TODO rtcp_fb_trr_int ? - } - - break; - - case 'source': - foreach($payload->children() as $s) { - $sdp_media .= - "\na=ssrc:".$payload->attributes()->id.' '. - $s->attributes()->name.':'. - $s->attributes()->value; - } - break; + case 'source': + foreach($payload->children() as $s) { + $sdp_media .= + "\na=ssrc:".$payload->attributes()->id.' '. + $s->attributes()->name.':'. + $s->attributes()->value; + } + break; + } + // TODO sendrecv ? } - // TODO sendrecv ? } - if(isset($content->description->attributes()->ptime)) { + if(isset($content->description) + && isset($content->description->attributes()->ptime)) { $sdp_media .= "\na=ptime:".$content->description->attributes()->ptime; } - if(isset($content->description->attributes()->maxptime)) { + if(isset($content->description) + && isset($content->description->attributes()->maxptime)) { $sdp_media .= "\na=maxptime:".$content->description->attributes()->maxptime; } @@ -260,32 +271,50 @@ class JingletoSDP { } } - $sdp_media_header = - "\nm=".$content->description->attributes()->media. - ' '.$media_header_first_port.' '; + if($media_header_first_port == null) + $media_header_first_port = 1; - if(isset($content->transport->sctpmap)) { - $sdp_media_header .= 'DTLS/SCTP'; - } elseif(isset($content->description->crypto) - || isset($content->transport->fingerprint)) { - $sdp_media_header .= 'RTP/SAVPF'; + if($media_header_last_ip == null) + $media_header_last_ip = '0.0.0.0'; + + if(isset($content->description)) + $this->media = (string)$content->description->attributes()->media; + else + $this->media = (string)$content->attributes()->name; + + if($this->action != 'transport-info') { + $sdp_media_header = + "\nm=".$this->media. + ' '.$media_header_first_port.' '; + + if(isset($content->transport->sctpmap)) { + $sdp_media_header .= 'DTLS/SCTP'; + } elseif(isset($content->description->crypto) + || isset($content->transport->fingerprint)) { + $sdp_media_header .= 'RTP/SAVPF'; + } else { + $sdp_media_header .= 'RTP/AVP'; + } + + + $sdp_media_header = $sdp_media_header.' '.implode(' ', $media_header_ids); + + $sdp_medias .= + $sdp_media_header. + "\nc=IN IP4 ".$media_header_last_ip. + $sdp_media; } else { - $sdp_media_header .= 'RTP/AVPF'; + $sdp_medias = $sdp_media; } - - - $sdp_media_header = $sdp_media_header.' '.implode(' ', $media_header_ids); + } - $sdp_medias .= - $sdp_media_header. - "\nc=IN IP4 ".$media_header_last_ip. - $sdp_media; + if($this->action != 'transport-info') { + $this->sdp .= $sdp_version; + $this->sdp .= "\n".$sdp_origin; + $this->sdp .= "\n".$sdp_session_name; + $this->sdp .= "\n".$sdp_timing; } - $this->sdp .= $sdp_version; - $this->sdp .= "\n".$sdp_origin; - $this->sdp .= "\n".$sdp_session_name; - $this->sdp .= "\n".$sdp_timing; $this->sdp .= $sdp_medias; return $this->sdp; diff --git a/lib/SDPtoJingle.php b/lib/SDPtoJingle.php index 16da0cd2a..88563c162 100644 --- a/lib/SDPtoJingle.php +++ b/lib/SDPtoJingle.php @@ -7,6 +7,8 @@ class SDPtoJingle { private $content = null; private $transport = null; + private $action; + // Move the global fingerprint into each medias private $global_fingerprint = array(); @@ -41,6 +43,8 @@ class SDPtoJingle { $this->jingle->addAttribute('action',$action); $this->jingle->addAttribute('initiator',$initiator); $this->jingle->addAttribute('responder',$responder); + + $this->action = $action; } function getSessionId(){ @@ -72,9 +76,11 @@ class SDPtoJingle { $this->content->addAttribute('name', $matches[1]); // The description node - $description = $this->content->addChild('description'); - $description->addAttribute('xmlns', "urn:xmpp:jingle:apps:rtp:1"); - $description->addAttribute('media', $matches[1]); + if($this->action != 'transport-info') { + $description = $this->content->addChild('description'); + $description->addAttribute('xmlns', "urn:xmpp:jingle:apps:rtp:1"); + $description->addAttribute('media', $matches[1]); + } if(!empty($this->global_fingerprint)) { $fingerprint = $this->transport->addChild('fingerprint', $this->global_fingerprint['fingerprint']); diff --git a/themes/movim/css/posts.css b/themes/movim/css/posts.css index 488fd3e5e..49a0ea4e6 100644 --- a/themes/movim/css/posts.css +++ b/themes/movim/css/posts.css @@ -46,6 +46,10 @@ article footer { padding-bottom: 1em; } +article:nth-last-child(-n+2) footer { + border-bottom: none; +} + article footer:after { content: ""; clear: both;