Browse Source

- Create the JingletoSDP library

- Fix some little issues
- Continue the Jingle implementation
- Change the default background of the popup
pull/16/head
Jaussoin Timothée 12 years ago
parent
commit
3f22ac21f1
  1. 9
      app/assets/js/movim_rpc.js
  2. 2
      app/widgets/Chat/Chat.php
  3. 2
      app/widgets/Roster/Roster.php
  4. 80
      app/widgets/Visio/adapter.js
  5. BIN
      app/widgets/Visio/img/background.png
  6. 28
      app/widgets/Visio/visio.css
  7. 8
      app/widgets/Visio/visio.tpl
  8. 134
      app/widgets/Visio/webrtc.js
  9. 47
      app/widgets/VisioExt/VisioExt.php
  10. 37
      app/widgets/VisioExt/visioext.js
  11. 3
      bootstrap.php
  12. 86
      lib/JingletoSDP.php
  13. 6
      lib/SDPtoJingle.php
  14. 2
      system/UtilsPicture.php
  15. 1
      themes/movim/css/submitform.css

9
app/assets/js/movim_rpc.js

@ -101,9 +101,14 @@ function MovimRPC()
for(h = 0; h < funcalls.length; h++) {
var funcall = funcalls[h];
if(funcall.func) {
if(funcall.func != null) {
var funcs = funcall.func.split('.');
try {
window[funcall.func](funcall.params);
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);

2
app/widgets/Chat/Chat.php

@ -131,6 +131,8 @@ class Chat extends WidgetBase
RPC::call('scrollTalk',
'messages'.$message->jidfrom);
}
RPC::commit();
}
function onMessagePublished($jid)

2
app/widgets/Roster/Roster.php

@ -172,7 +172,7 @@ class Roster extends WidgetBase
$html .= '<div class="infoicon bot"></div>';
if($jingle_video )
$html .= '<div class="infoicon jingle" onclick="openPopup(\''.$contact->jid.'/'.$contact->ressource.'\')"></div>';
$html .= '<div class="infoicon jingle" onclick="Popup.open(\''.$contact->jid.'/'.$contact->ressource.'\')"></div>';
if(($contact->tuneartist != null && $contact->tuneartist != '') ||
($contact->tunetitle != null && $contact->tunetitle != ''))

80
app/widgets/Visio/adapter.js

@ -41,15 +41,25 @@ if (navigator.mozGetUserMedia) {
if (url_parts[0].indexOf('stun') === 0) {
// Create iceServer with stun url.
iceServer = { 'url': url };
} else if (url_parts[0].indexOf('turn') === 0 &&
(url.indexOf('transport=udp') !== -1 ||
url.indexOf('?transport') === -1)) {
// Create iceServer with turn url.
// Ignore the transport parameter from TURN url.
var turn_url_parts = url.split("?");
iceServer = { 'url': turn_url_parts[0],
'credential': password,
'username': username };
} else if (url_parts[0].indexOf('turn') === 0) {
if (webrtcDetectedVersion < 27) {
// Create iceServer with turn url.
// Ignore the transport parameter from TURN url for FF version <=27.
var turn_url_parts = url.split("?");
// Return null for createIceServer if transport=tcp.
if (turn_url_parts.length === 1 ||
turn_url_parts[1].indexOf('transport=udp') === 0) {
iceServer = { 'url': turn_url_parts[0],
'credential': password,
'username': username };
}
} else {
// FF 27 and above supports transport parameters in TURN url,
// So passing in the full url to create iceServer.
iceServer = { 'url': url,
'credential': password,
'username': username };
}
}
return iceServer;
};
@ -68,13 +78,17 @@ if (navigator.mozGetUserMedia) {
};
// Fake get{Video,Audio}Tracks
MediaStream.prototype.getVideoTracks = function() {
return [];
};
if (!MediaStream.prototype.getVideoTracks) {
MediaStream.prototype.getVideoTracks = function() {
return [];
};
}
MediaStream.prototype.getAudioTracks = function() {
return [];
};
if (!MediaStream.prototype.getAudioTracks) {
MediaStream.prototype.getAudioTracks = function() {
return [];
};
}
} else if (navigator.webkitGetUserMedia) {
console.log("This appears to be Chrome");
@ -90,17 +104,10 @@ if (navigator.mozGetUserMedia) {
// Create iceServer with stun url.
iceServer = { 'url': url };
} else if (url_parts[0].indexOf('turn') === 0) {
if (webrtcDetectedVersion < 28) {
// For pre-M28 chrome versions use old TURN format.
var url_turn_parts = url.split("turn:");
iceServer = { 'url': 'turn:' + username + '@' + url_turn_parts[1],
'credential': password };
} else {
// For Chrome M28 & above use new TURN format.
iceServer = { 'url': url,
'credential': password,
'username': username };
}
// Chrome M28 & above uses below TURN format.
iceServer = { 'url': url,
'credential': password,
'username': username };
}
return iceServer;
};
@ -128,27 +135,6 @@ if (navigator.mozGetUserMedia) {
reattachMediaStream = function(to, from) {
to.src = from.src;
};
// The representation of tracks in a stream is changed in M26.
// Unify them for earlier Chrome versions in the coexisting period.
if (!webkitMediaStream.prototype.getVideoTracks) {
webkitMediaStream.prototype.getVideoTracks = function() {
return this.videoTracks;
};
webkitMediaStream.prototype.getAudioTracks = function() {
return this.audioTracks;
};
}
// New syntax of getXXXStreams method in M26.
if (!webkitRTCPeerConnection.prototype.getLocalStreams) {
webkitRTCPeerConnection.prototype.getLocalStreams = function() {
return this.localStreams;
};
webkitRTCPeerConnection.prototype.getRemoteStreams = function() {
return this.remoteStreams;
};
}
} else {
console.log("Browser does not appear to be WebRTC-capable");
}

BIN
app/widgets/Visio/img/background.png

Before

Width: 158  |  Height: 158  |  Size: 16 KiB

After

Width: 100  |  Height: 100  |  Size: 6.8 KiB

28
app/widgets/Visio/visio.css

@ -21,13 +21,28 @@ body {
position: absolute;
bottom: 0;
text-align: center;
background-color: rgba(255, 255, 255, 0.3);
background-color: rgba(0, 0, 0, 0.3);
padding-top: 0.5em;
width: 100%;
}
#visio #camera-stream {
#visio #remote-video,
#visio #local-video {
width: 100%;
position: absolute;
right: 0;
bottom: 0;
transition: width 0.5s ease;
}
#visio #local-video.tiny {
width: 20%;
bottom: 3.5em;
}
#visio #local-video.tiny:hover {
width: 50%;
bottom: 3.5em;
}
#visio #log {
@ -46,10 +61,19 @@ body {
position: absolute;
left: 50%;
top: 50%;
max-width: 200px;
margin-left: -100px;
margin-top: -150px;
}
#visio #avatar img {
width: 100%;
}
#visio #avatar.tiny {
display: none;
}
#visio #avatar .name {
color: white;
display: block;

8
app/widgets/Visio/visio.tpl

@ -2,7 +2,13 @@
<div id="log">
</div>
<video autoplay="true" id="camera-stream">
<audio autoplay="true" id="remote-audio">
</audio>
<video autoplay="true" id="remote-video">
</video>
<video autoplay="true" id="local-video">
</video>

134
app/widgets/Visio/webrtc.js

@ -6,21 +6,15 @@ var optional = {
optional: [DtlsSrtpKeyAgreement]
};
var pc;
var remoteStream;
// Set up audio and video regardless of what devices are present.
var sdpConstraints = {'mandatory': {
'OfferToReceiveAudio': true,
'OfferToReceiveVideo': true }};
function onIceCandidate(event) {
/*if (event.candidate) {
sendMessage({type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: event.candidate.candidate});
noteIceCandidate("Local", iceCandidateType(event.candidate.candidate));
} else {
Visio.log('End of candidates.');
}*/
Visio.log('onIceCandidate');
Visio.log(event);
}
@ -35,36 +29,105 @@ function onSignalingStateChanged(event) {
Visio.log(event);
}
function onRemoteStreamAdded(event) {
console.log(event);
var vid = document.getElementById('remote-video');
vid.src = window.URL.createObjectURL(event.stream);
/*remoteStream = event.stream;
audioTracks = remoteStream.getAudioTracks();
for (i = 0; i < audioTracks.length; i++) {
audioTracks[i].enabled = true;
}*/
}
function onError(err) {
window.alert(err.message);
window.alert(err.message);
}
function onOfferCreated(description) {
Visio.log(description);
offer = description;
function onOfferCreated(offer) {
Visio.log(offer);
pc.setLocalDescription(offer,onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
sendMessage(offer);
}
function sendMessage(offer) {
function onAnswerCreated(offer) {
Visio.log(offer);
pc.setLocalDescription(offer,onSetSessionDescriptionSuccess, onSetSessionDescriptionError);
sendMessage(offer, true);
}
function sendMessage(offer, accept) {
offer = offer.toJSON();
offer.jid = VISIO_JID;
offer.ressource = VISIO_RESSOURCE;
var msgString = JSON.stringify(offer);
Visio.log('Send the proposal.');
Visio.call(['VisioExt_ajaxSendProposal', msgString]);
if(accept) {
Visio.log('Send the acceptance.');
Visio.call(['VisioExt_ajaxSendAcceptance', msgString]);
} else {
Visio.log('Send the proposal.');
Visio.call(['VisioExt_ajaxSendProposal', msgString]);
}
}
function onSetSessionDescriptionSuccess() {
Visio.log('Set session description success.');
Visio.log('Set session description success.');
}
function onSetSessionDescriptionError(error) {
Visio.log('Failed to set session description: ' + error.toString());
Visio.log('Failed to set session description: ' + error.toString());
}
function init() {
function onSetRemoteSessionDescriptionSuccess() {
Visio.log('Set remote session description success.');
}
function onSetRemoteSessionDescriptionError(error) {
Visio.log('Failed to set remote session description: ' + error.message);
}
function onOffer(offer) {
offer = offer[0];
if(!pc)
init(false);
if(offer != null) {
var desc = new RTCSessionDescription();
desc.sdp = offer;
desc.type = 'offer';
pc.setRemoteDescription(desc,
onSetRemoteSessionDescriptionSuccess, onSetRemoteSessionDescriptionError);
}
}
function onAccept(offer) {
offer = offer[0];
Visio.log(offer);
if(offer != null) {
var desc = new RTCSessionDescription();
desc.sdp = offer;
desc.type = 'answer';
pc.setRemoteDescription(desc,
onSetRemoteSessionDescriptionSuccess, onSetRemoteSessionDescriptionError);
}
}
function init(isCaller) {
var configuration = {"iceServers":[{"url": "stun:23.21.150.121:3478"}]};
try {
@ -73,6 +136,7 @@ function init() {
pc.onicecandidate = onIceCandidate;
pc.onsignalingstatechange = onSignalingStateChanged;
pc.oniceconnectionstatechange = onIceConnectionStateChanged;
pc.onaddstream = onRemoteStreamAdded;
} catch (e) {
Visio.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object; \
@ -89,29 +153,45 @@ function init() {
getUserMedia(
// Constraints
{
video: true, audio: true
video: false, audio: true
},
// Success Callback
function(localMediaStream) {
// Get a reference to the video element on the page.
var vid = document.getElementById('camera-stream');
var vid = document.getElementById('local-video');
var avatar = document.getElementById('avatar');
// Create an object URL for the video stream and use this
// to set the video source.
vid.src = window.URL.createObjectURL(localMediaStream);
setTimeout(
function() {
vid.className = 'tiny';
avatar.className = 'tiny';
},
3000);
Visio.log(localMediaStream);
pc.addStream(localMediaStream);
channel = pc.createDataChannel("visio");
pc.createOffer(onOfferCreated, onError);
console.log(pc);
if(isCaller)
pc.createOffer(onOfferCreated, onError);
else
pc.createAnswer(onAnswerCreated, onError);
//pc.createOffer(onOfferCreated, onError);
},
// Error Callback
function(err) {
// Log the error to the console.
Visio.log('The following error occurred when trying to use getUserMedia: ' + err);
// Log the error to the console.
Visio.log('The following error occurred when trying to use getUserMedia: ' + err);
}
@ -120,11 +200,7 @@ function init() {
alert('Sorry, your browser does not support getUserMedia');
}
//channel = pc.createDataChannel("visio");
Visio.log(pc);
//Visio.log(channel);
}
init();
init(true);

47
app/widgets/VisioExt/VisioExt.php

@ -18,25 +18,44 @@
* See COPYING for licensing information.
*/
//require_once(APP_PATH . "widgets/Chat/Chat.php");
class VisioExt extends WidgetBase
{
function WidgetLoad() {
$this->addjs('visioext.js');
$this->registerEvent('jinglesessioninitiate', 'onSessionInitiate');
$this->registerEvent('jinglesessionterminate', 'onSessionTerminate');
$this->registerEvent('jinglesessionaccept', 'onSessionAccept');
}
function onSessionInitiate($jingle) {
$jts = new \JingletoSDP($jingle);
$sdp = $jts->generate();
RPC::call('Popup.open', (string)$jingle->attributes()->initiator);
RPC::call('Popup.call', 'onOffer', $sdp);
}
function onSessionAccept($jingle) {
$jts = new \JingletoSDP($jingle);
$sdp = $jts->generate();
RPC::call('Popup.call', 'onAccept', $sdp);
}
function onSessionTerminate($jingle) {
//\movim_log($jingle);
}
function ajaxSendProposal($proposal) {
$p = json_decode($proposal);
$sd = Sessionx::start();
\movim_log($p->sdp);
$stj = new SDPtoJingle(
$p->sdp,
$this->user->getLogin().'/'.$sd->ressource,
$p->jid.'/'.$p->ressource);
$p->jid.'/'.$p->ressource,
'session-initiate');
$r = new moxl\JingleSessionInitiate();
$r->setTo($p->jid.'/'.$p->ressource)
@ -44,6 +63,24 @@ class VisioExt extends WidgetBase
->request();
}
function ajaxSendAcceptance($proposal) {
$p = json_decode($proposal);
$sd = Sessionx::start();
$stj = new SDPtoJingle(
$p->sdp,
$this->user->getLogin().'/'.$sd->ressource,
$p->jid.'/'.$p->ressource,
'session-accept');
$r = new moxl\JingleSessionInitiate();
$r->setTo($p->jid.'/'.$p->ressource)
->setOffer($stj->generate())
->request();
}
function build() {
}

37
app/widgets/VisioExt/visioext.js

@ -1,5 +1,5 @@
// Initialise popup pointer
var popupWin = null;
/*var popupWin = null;
function openPopup(jid) {
var url = BASE_URI + PAGE_KEY_URI + "visio&f="+jid
@ -28,4 +28,39 @@ function popUpEvent(args) {
// The popup is closed so open it
openPopup();
}
}*/
var Popup = {
win : null,
open: function(jid) {
console.log('Opening the Popup');
var url = BASE_URI + PAGE_KEY_URI + "visio&f="+jid
if( !this.win || this.win.closed ) {
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();
},
close: function() {
this.win.close();
},
focus: function() {
this.win.focus();
},
call: function(args) {
if( this.win && !this.win.closed ) {
console.log('Calling the Popup');
// The popup is open so call it
var func = args[0];
args.shift();
var params = args;
this.win[func](params);
} else {
// The popup is closed so open it
}
}
}

3
bootstrap.php

@ -178,8 +178,9 @@ class Bootstrap {
// XMPPtoForm lib
require_once(LIB_PATH . "XMPPtoForm.php");
// SDPtoJingle lib
// SDPtoJingle and JingletoSDP lib :)
require_once(LIB_PATH . "SDPtoJingle.php");
require_once(LIB_PATH . "JingletoSDP.php");
// Markdown lib
require_once(LIB_PATH . "Markdown.php");

86
lib/JingletoSDP.php

@ -0,0 +1,86 @@
<?php
class JingletoSDP {
private $sdp;
private $jingle;
private $jid;
private $iceufrag;
private $icepwd;
function __construct($jingle) {
$this->jingle = $jingle;
}
function generate() {
foreach($this->jingle->children() as $content) {
$this->icepwd = $content->transport->attributes()->pwd;
$this->iceufrag = $content->transport->attributes()->ufrag;
$p = $c = '';
$priority = '';
$port = false;
$ip = false;
foreach($content->description->children() as $payload) {
$p .=
'a=rtpmap'.
':'.$payload->attributes()->id.
' '.$payload->attributes()->name.
'/'.$payload->attributes()->clockrate.
"\n";
$priority .= ' '.$payload->attributes()->id;
}
foreach($content->transport->children() as $candidate) {
$c .=
'a=candidate:'.$candidate->attributes()->component.
' '.$candidate->attributes()->foundation.
' '.$candidate->attributes()->protocol.
' '.$candidate->attributes()->priority.
' '.$candidate->attributes()->ip.
' '.$candidate->attributes()->port.
' typ '.$candidate->attributes()->type;
if($port == false)
$port = $candidate->attributes()->port;
if($ip == false)
$ip = $candidate->attributes()->ip;
if($candidate->attributes()->type == 'srflx') {
$c .=
' raddr '.$candidate->attributes()->{'rel-addr'}.
' rport '.$candidate->attributes()->{'rel-port'};
}
$c .= "\n";
}
$this->sdp .=
'm='.$content->description->attributes()->media.
' '.$port.
' RTP/SAVPF'.
$priority.
"\n".
'c=IN IP4 '.$ip."\n".
$p.
'a=setup:actpass'."\n".
$c.
'a=rtcp-mux'."\n";
}
$this->sdp =
'v=0'."\n".
'o=Mozilla-SIPUA-29.0a1 2019 0 IN IP4 0.0.0.0'."\n".
's=SIP Call'."\n".
't=0 0'."\n".
'a=ice-ufrag:'.$this->iceufrag."\n".
'a=ice-pwd:'.$this->icepwd."\n".
'a=fingerprint:sha-256 D4:E6:DC:30:3F:63:0A:55:8D:65:F6:7C:F7:81:47:F8:3D:45:74:EE:74:61:CB:9A:F5:4F:60:79:F2:2D:D2:20'."\n".
$this->sdp;
return $this->sdp;
}
}

6
lib/SDPtoJingle.php

@ -7,11 +7,11 @@ class SDPtoJingle {
private $iceufrag;
private $icepwd;
function __construct($sdp, $initiator, $responder) {
function __construct($sdp, $initiator, $responder, $action) {
$this->sdp = $sdp;
$this->jingle = new SimpleXMLElement('<jingle></jingle>');
$this->jingle->addAttribute('xmlns', 'urn:xmpp:jingle:1');
$this->jingle->addAttribute('action','session-initiate');
$this->jingle->addAttribute('action',$action);
$this->jingle->addAttribute('initiator',$initiator);
$this->jingle->addAttribute('responder',$responder);
$this->jingle->addAttribute('sid', generateKey(10));
@ -59,7 +59,7 @@ class SDPtoJingle {
$candidate = $transport->addChild('candidate');
$candidate->addAttribute('componenent', $candidexpl[1]);
$candidate->addAttribute('component', $candidexpl[1]);
$candidate->addAttribute('foundation', $expl[1]);
$candidate->addAttribute('generation', 0);
$candidate->addAttribute('protocol', $expl[2]);

2
system/UtilsPicture.php

@ -38,7 +38,7 @@ function createEmailPic($jid, $email) {
$white = imagecolorallocate($thumb, 255, 255, 255);
imagefill($thumb, 0, 0, $white);
$text_color = imagecolorallocate ($im, 0, 0,0);//black text
$text_color = imagecolorallocate ($thumb, 0, 0,0);//black text
imagestring ($thumb, 4, 0, 0, $email, $text_color);
imagejpeg($thumb, DOCUMENT_ROOT.'/cache/'.$jid.'_email.jpg', 95);

1
themes/movim/css/submitform.css

@ -65,7 +65,6 @@ table#feedsubmitform {
#feedsubmitform #feedsubmitrow {
background-color: #4C5A61;
border-radius: 0em 0em 0.1em 0.1em;
width: auto;
overflow: auto;
padding: 0px;

Loading…
Cancel
Save