diff --git a/README b/README index e03064b2..41bf5eec 100644 --- a/README +++ b/README @@ -1,4 +1,17 @@ Flashphoner-client is an open-source part of Flashphoner product. You can use it for customization. -Builds: http://flashphoner.com/downloads/builds/flashphoner_client/ \ No newline at end of file +Builds: http://flashphoner.com/downloads/builds/flashphoner_client/ + +requirement: +DETECTING java command... +- Java command found successfully. + +DETECTING JVM architecture... +- 64-Bit architecture detected successfully. + +DETECTING JDK home... +- JDK home detected successfully: /usr/java/default. + +DETECTING ANT... +- Error: ANT is not found diff --git a/client/api/src/com/flashphoner/api/Flash_API.as b/client/api/src/com/flashphoner/api/Flash_API.as index ae4a0788..9d794e93 100644 --- a/client/api/src/com/flashphoner/api/Flash_API.as +++ b/client/api/src/com/flashphoner/api/Flash_API.as @@ -91,6 +91,7 @@ package com.flashphoner.api ExternalInterface.addCallback("notificationResult",notificationResult); ExternalInterface.addCallback("call",call); ExternalInterface.addCallback("callByToken",callByToken); + ExternalInterface.addCallback("msrpCall",msrpCall); ExternalInterface.addCallback("hangup",hangup); ExternalInterface.addCallback("answer",answer); ExternalInterface.addCallback("subscribe",subscribe); @@ -377,6 +378,10 @@ package com.flashphoner.api if (callee.indexOf("@") == -1 || callee.indexOf("@") == callee.length-1){ return 1; } + }else if (callee.indexOf("tel:") == 0){ + if (callee.substring(4).search(reg) != -1){ + return 1; + } }else{ if (callee.search(reg) != -1){ if (callee.indexOf("@") != -1){ @@ -413,6 +418,15 @@ package com.flashphoner.api phoneServerProxy.callByToken(callObject); return 0; } + + /** + * Create new msrp call + * @param call object + **/ + public function msrpCall(callObject:Object):int{ + phoneServerProxy.msrpCall(callObject); + return 0; + } /** * Get information about logged user diff --git a/client/api/src/com/flashphoner/api/PhoneModel.as b/client/api/src/com/flashphoner/api/PhoneModel.as index 3fb6c6d2..f89e2996 100644 --- a/client/api/src/com/flashphoner/api/PhoneModel.as +++ b/client/api/src/com/flashphoner/api/PhoneModel.as @@ -126,6 +126,18 @@ package com.flashphoner.api parameters[node] = String(xml[node]); } + var wcs_server:String = xml.wcs_server; + if (wcs_server!=null && wcs_server.length!=0){ + PhoneConfig.WCS_SERVER = wcs_server; + } + Logger.info("WCS_SERVER: "+PhoneConfig.WCS_SERVER); + + var rtmfp_port:String = xml.rtmfp_port; + if (rtmfp_port!=null && rtmfp_port.length!=0){ + PhoneConfig.RTMFP_PORT = int(rtmfp_port); + } + Logger.info("RTMFP_PORT: "+PhoneConfig.RTMFP_PORT); + var use_enhanced_mic:String = xml.use_enhanced_mic; if (use_enhanced_mic!=null && use_enhanced_mic.length!=0){ diff --git a/client/api/src/com/flashphoner/api/PhoneServerProxy.as b/client/api/src/com/flashphoner/api/PhoneServerProxy.as index 9293df03..03117bd9 100644 --- a/client/api/src/com/flashphoner/api/PhoneServerProxy.as +++ b/client/api/src/com/flashphoner/api/PhoneServerProxy.as @@ -116,8 +116,13 @@ package com.flashphoner.api public function callByToken(callObject:Object):void{ Logger.info("PhoneServerProxy.callByToken()"); - nc.call("call",responder, callObject); - } + nc.call("call", responder, callObject); + } + + public function msrpCall(callObject:Object):void{ + Logger.info("PhoneServerProxy.msrpCall()"); + nc.call("msrpCall", responder, callObject); + } public function disconnect():void { hasDisconnectAttempt = true; diff --git a/client/api/src/com/flashphoner/api/data/PhoneConfig.as b/client/api/src/com/flashphoner/api/data/PhoneConfig.as index 321f872c..57020eb3 100644 --- a/client/api/src/com/flashphoner/api/data/PhoneConfig.as +++ b/client/api/src/com/flashphoner/api/data/PhoneConfig.as @@ -92,6 +92,10 @@ package com.flashphoner.api.data public static var LOAD_BALANCER_URL = null; + public static var WCS_SERVER:String = "192.168.1.5"; + + public static var RTMFP_PORT:int = 1935; + /** * Current version of Flashphoner product **/ diff --git a/client/api/src/com/flashphoner/api/js/APINotifyJS.as b/client/api/src/com/flashphoner/api/js/APINotifyJS.as index 3de2463c..a27e420d 100644 --- a/client/api/src/com/flashphoner/api/js/APINotifyJS.as +++ b/client/api/src/com/flashphoner/api/js/APINotifyJS.as @@ -32,7 +32,7 @@ package com.flashphoner.api.js ExternalInterface.call("notifyConnected"); } public function notifyRegistered(_sipObject:Object):void{ - ExternalInterface.call("notifyRegistered"); + ExternalInterface.call("notifyRegistered", _sipObject); } public function notifyBalance(balance:Number,_sipObject:Object):void{ ExternalInterface.call("notifyBalance",String(balance)); diff --git a/client/client/src/Phone.html b/client/client/src/Phone.html index c5649857..51564766 100644 --- a/client/client/src/Phone.html +++ b/client/client/src/Phone.html @@ -4,7 +4,7 @@ Flash phone - + + Phone @@ -71,7 +72,7 @@ -
+
@@ -216,5 +217,10 @@ + +
Test +
+ + diff --git a/client/client/src/com/flashphoner/phone/views/PhoneButton.mxml b/client/client/src/com/flashphoner/phone/views/PhoneButton.mxml index b3e1c1f3..58bee929 100644 --- a/client/client/src/com/flashphoner/phone/views/PhoneButton.mxml +++ b/client/client/src/com/flashphoner/phone/views/PhoneButton.mxml @@ -22,6 +22,7 @@ This code and accompanying materials also available under LGPL and MPL license f import com.flashphoner.Logger; import com.flashphoner.api.Flash_API; import com.flashphoner.api.SoundControl; + import com.flashphoner.api.data.PhoneConfig; import com.flashphoner.phone.DataPhone; import flash.utils.setTimeout; @@ -73,6 +74,9 @@ This code and accompanying materials also available under LGPL and MPL license f var callObject:Object = new Object(); callObject.callee = calleeNumber; callObject.visibleName = flashAPI.modelLocator.visibleName; + callObject.hasVideo = PhoneConfig.VIDEO_ENABLED; + callObject.inviteParameters = "name=value"; + callObject.isMsrp = false; var result:int = flashAPI.call(callObject); if (result == 0) { delaythisEnabled(3000); diff --git a/client/client/src/com/flashphoner/phone/views/SipAccountView.mxml b/client/client/src/com/flashphoner/phone/views/SipAccountView.mxml index ff0fc1d3..ffc0df1a 100644 --- a/client/client/src/com/flashphoner/phone/views/SipAccountView.mxml +++ b/client/client/src/com/flashphoner/phone/views/SipAccountView.mxml @@ -19,6 +19,7 @@ This code and accompanying materials also available under LGPL and MPL license f import com.adobe.cairngorm.control.CairngormEventDispatcher; import com.flashphoner.Logger; import com.flashphoner.api.Flash_API; + import com.flashphoner.api.data.PhoneConfig; import com.flashphoner.phone.DataPhone; import mx.controls.Alert; @@ -54,12 +55,14 @@ This code and accompanying materials also available under LGPL and MPL license f DataPhone.getInstance().viewController.showConnectingView(); var loginObject:Object = new Object(); - loginObject.username = "sip:"+username.text+"@"+domain.text; + loginObject.login = username.text; loginObject.password = password.text; loginObject.authenticationName = authName.text; loginObject.outboundProxy = outboundProxy.text; + loginObject.domain = domain.text; loginObject.port = port.text; - if (DataPhone.getInstance().flash_API.login(loginObject, "rtmfp://192.168.1.5:1935/phone_app") == 0){ + loginObject.registerRequired = PhoneConfig.REGISTER_REQUIRED; + if (DataPhone.getInstance().flash_API.login(loginObject, "rtmfp://"+PhoneConfig.WCS_SERVER+":"+PhoneConfig.RTMFP_PORT+"/phone_app") == 0){ save(); } } diff --git a/client/client/src/flashphoner.xml b/client/client/src/flashphoner.xml index 012be828..8592f56d 100644 --- a/client/client/src/flashphoner.xml +++ b/client/client/src/flashphoner.xml @@ -12,8 +12,10 @@ This code and accompanying materials also available under LGPL and MPL license for Flashphoner buyers. Other license versions by negatiation. Write us support@flashphoner.com with any questions. --> - 192.168.1.5 + 87.226.225.59 8080 + 8443 + false 1935 true diff --git a/client/client/src/flashphoner_js_api.mxml b/client/client/src/flashphoner_js_api.mxml index cf7ad790..d37fe1f6 100644 --- a/client/client/src/flashphoner_js_api.mxml +++ b/client/client/src/flashphoner_js_api.mxml @@ -58,7 +58,7 @@ This code and accompanying materials also available under LGPL and MPL license f if (flashAPI.isInitialized()){ Logger.info("FlashAPI has been initialized"); flashAPI.initMedia(); - ExternalInterface.call("notifyFlashReady"); + ExternalInterface.call("notifyConfigLoaded"); } else{ Logger.info("Waiting flashAPI initialization..."); setTimeout(wait,500); diff --git a/client/client/src/js/Click2Call.js b/client/client/src/js/Click2Call.js index 6bc5c19a..f9b4af35 100644 --- a/client/client/src/js/Click2Call.js +++ b/client/client/src/js/Click2Call.js @@ -205,8 +205,8 @@ function addLogMessage(message) { trace('addLogMessage', message); } -function notifyFlashReady() { - trace("notifyFlashReady"); +function notifyConfigLoaded() { + trace("notifyConfigLoaded"); flashphoner = flashphonerLoader.getFlashphoner(); flashphoner_UI = flashphonerLoader.getFlashphonerUI(); if (flashphonerLoader.useWebRTC) { diff --git a/client/client/src/js/FlashphonerLoader.js b/client/client/src/js/FlashphonerLoader.js index 82428a1e..cda20c63 100644 --- a/client/client/src/js/FlashphonerLoader.js +++ b/client/client/src/js/FlashphonerLoader.js @@ -22,7 +22,7 @@ FlashphonerLoader = function (config) { this.loadBalancerUrl = null; this.jsonpSuccess = false; this.token = null; - this.registerRequired = false; + this.registerRequired = true; this.videoWidth = 320; this.videoHeight = 240; this.pushLogEnabled = false; @@ -32,10 +32,12 @@ FlashphonerLoader = function (config) { this.finishSound = "sounds/HANGUP.ogg"; this.xcapUrl = null; this.msrpCallee = null; - this.subscribeEvent = "reg"; + this.subscribeEvent = null; this.contactParams = null; this.imdnEnabled = false; - + this.msgContentType = "text/plain"; + this.stripCodecs = new Array(); + this.stunServer = ""; $.ajax({ type: "GET", @@ -61,6 +63,15 @@ FlashphonerLoader.prototype = { if (wsPort.length > 0) { this.wsPort = wsPort[0].textContent; } + var wssPort = $(xml).find("wss_port"); + if (wssPort.length > 0) { + this.wssPort = wssPort[0].textContent; + } + var useWss= $(xml).find("use_wss"); + if (useWss.length > 0) { + this.useWss = "true" == useWss[0].textContent; + } + var flashPort = $(xml).find("flash_port"); if (flashPort.length > 0) { this.flashPort = flashPort[0].textContent; @@ -79,7 +90,7 @@ FlashphonerLoader.prototype = { } var registerRequired = $(xml).find("register_required"); if (registerRequired.length > 0) { - this.registerRequired = registerRequired[0].textContent; + this.registerRequired = (registerRequired[0].textContent === "true"); } var videoWidth = $(xml).find("video_width"); if (videoWidth.length > 0) { @@ -156,6 +167,44 @@ FlashphonerLoader.prototype = { } } + //Message content type by default "text/plain", can be "message/cpim" + var msgContentType = $(xml).find("msg_content_type"); + if (msgContentType.length > 0) { + this.msgContentType = msgContentType.text(); + console.log("Message content type: " + this.msgContentType); + } + + var stripCodecs = $(xml).find("strip_codecs"); + if (stripCodecs.length > 0) { + var tempCodecs = stripCodecs[0].textContent.split(","); + for (i = 0; i < tempCodecs.length; i++) { + if (tempCodecs[i].length) this.stripCodecs[i] = tempCodecs[i]; + console.log("Codec " + tempCodecs[i] + " will be removed from SDP!"); + } + } + + //stun server address + var stunServer = $(xml).find("stun_server"); + if (stunServer.length > 0) { + this.stunServer = stunServer.text(); + console.log("Stun server: " + this.stunServer); + } + + //variable participating in api load, can bee null, webrtc, flash + var streamingType = $(xml).find("streaming"); + if (streamingType.length > 0) { + if (streamingType.text() == "webrtc") { + console.log("Force WebRTC usage!"); + isWebRTCAvailable = true; + } else if (streamingType.text() == "flash") { + console.log("Force Flash usage!"); + isWebRTCAvailable = false; + } else { + console.log("Bad streaming property " + streamingType.text() + + ", can be webrtc or flash. Using default behaviour!") + } + } + //get load balancer url if load balancing enabled if (me.loadBalancerUrl != null) { trace("Retrieve server url from load balancer"); @@ -166,7 +215,7 @@ FlashphonerLoader.prototype = { */ setTimeout(function () { //check status of ajax request - if (!this.jsonpSuccess) { + if (!me.jsonpSuccess) { trace("Error occurred while retrieving load balancer data, please check your load balancer url " + me.loadBalancerUrl); me.loadAPI(); @@ -179,13 +228,15 @@ FlashphonerLoader.prototype = { dataType: "jsonp", data: loadBalancerData, success: function (loadBalancerData) { - this.wcsIP = loadBalancerData.server; - this.wsPort = loadBalancerData.ws; - this.flashPort = loadBalancerData.flash; - this.jsonpSuccess = true; + me.wcsIP = loadBalancerData.server; + me.wsPort = loadBalancerData.ws; + me.wssPort = loadBalancerData.wss; + me.flashPort = loadBalancerData.flash; + me.jsonpSuccess = true; trace("Connection data from load balancer: " + "wcsIP " + loadBalancerData.server + ", wsPort " + loadBalancerData.ws + + ", wssPort " + loadBalancerData.wss + ", flashPort " + loadBalancerData.flash); me.loadAPI(); } @@ -199,10 +250,18 @@ FlashphonerLoader.prototype = { var me = this; if (isWebRTCAvailable) { me.useWebRTC = true; - me.urlServer = "ws://" + this.wcsIP + ":" + this.wsPort; + var protocol = "ws://"; + var port = this.wsPort; + if (this.useWss){ + protocol = "wss://"; + port = this.wssPort; + } + me.urlServer = protocol + this.wcsIP + ":" + port; me.flashphoner = new WebSocketManager(getElement('localVideoPreview'), getElement('remoteVideo')); + if (me.stripCodecs.length) me.flashphoner.setStripCodecs(me.stripCodecs); + if (me.stunServer != "") me.flashphoner.setStunServer(me.stunServer); me.flashphoner_UI = new UIManagerWebRtc(); - notifyFlashReady(); + notifyConfigLoaded(); } else { me.useWebRTC = false; me.urlServer = "rtmfp://" + this.wcsIP + ":" + this.flashPort + "/" + this.appName; @@ -211,7 +270,14 @@ FlashphonerLoader.prototype = { params.swliveconnect = "true"; params.allowfullscreen = "true"; params.allowscriptaccess = "always"; - params.wmode = "transparent"; + //in case of Safari wmode should be "window" + if((navigator.userAgent.indexOf("Safari") > -1) && !(navigator.userAgent.indexOf("Chrome") > -1)) { + params.wmode = "window"; + //workaround for safari browser, FPNR-403 + swfobject.switchOffAutoHideShow(); + } else { + params.wmode = "transparent"; + } var attributes = {}; var flashvars = {}; flashvars.config = "flashphoner.xml"; @@ -221,6 +287,8 @@ FlashphonerLoader.prototype = { me.flashphoner = e.ref; me.flashphoner_UI = new UIManagerFlash(); }); + } else { + notifyFlashNotFound(); } } diff --git a/client/client/src/js/Phone.js b/client/client/src/js/Phone.js index 61abf51d..ae7a0a58 100644 --- a/client/client/src/js/Phone.js +++ b/client/client/src/js/Phone.js @@ -43,8 +43,6 @@ $(document).ready(function () { toLogOffState(); openConnectingView("Loading...", 0); flashphonerLoader = new FlashphonerLoader(); -// openConnectingView("You have old flash player", 0); -// trace("Download flash player from: http://get.adobe.com/flashplayer/"); }); @@ -64,6 +62,7 @@ function login() { loginObject.outboundProxy = $('#outbound_proxy').val(); loginObject.port = $('#port').val(); loginObject.useProxy = $('#checkboxUseProxy').attr("checked") ? true : false; + loginObject.registerRequired = flashphonerLoader.registerRequired; if (flashphonerLoader.contactParams != null && flashphonerLoader.contactParams.length != 0) { loginObject.contactParams = flashphonerLoader.contactParams; } @@ -235,7 +234,13 @@ function addLogMessage(message) { trace(message); } -function notifyFlashReady() { +function notifyFlashNotFound() { + closeConnectingView(); + getElement('phoneScreen2').innerHTML = "Get Adobe Flash player"; +} + +function notifyConfigLoaded() { + notifyReady(); flashphoner = flashphonerLoader.getFlashphoner(); flashphoner_UI = flashphonerLoader.getFlashphonerUI(); messenger = new Messenger(flashphoner); @@ -289,7 +294,7 @@ function notifyConnected() { //flashphoner.setSpeexQuality(10); } -function notifyRegistered() { +function notifyRegistered(sipObject) { trace("notifyRegistered"); if (registerRequired) { toLogState(); @@ -371,6 +376,7 @@ function notifyBalance(balance) { function notify(call) { trace("notify", call); //: callId " + call.id + " --- " + call.anotherSideUser); if (currentCall.id == call.id) { //if we have some call now and notify is about exactly our call + trace("currentCall.id == call.id "+call.id); currentCall = call; if (call.state == STATE_FINISH) { // if that hangup during transfer procedure? @@ -401,6 +407,7 @@ function notify(call) { flashphoner.playSound("BUSY"); } } else if (holdedCall.id == call.id) { + trace("holdedCall.id == call.id "+call.id); if (call.state == STATE_FINISH) { /* that mean if - user1 call user2 @@ -549,21 +556,41 @@ function notifyMessageReceived(messageObject) { function convertMessageBody(messageBody, contentType) { trace("convertMessageBody " + contentType); if (contentType == "application/fsservice+xml") { + var missedCallNotification; var xml = $.parseXML(messageBody); var fsService = $(xml).find("fs-services").find("fs-service"); var action = fsService.attr("action"); if (action == "servicenoti-indicate") { - var cawData = fsService.find("caw").find("caw-data"); - if (cawData) { - var sender = $(cawData).attr("sender"); - trace("cawData: " + sender); - return "Missed call " + sender; + var caw = parseMsn(fsService,"caw"); + if (!!caw){ + missedCallNotification = caw; + }else{ + missedCallNotification = parseMsn(fsService,"mcn"); } + } else if (action == "serviceinfo-confirm") { + //service status confirmation + missedCallNotification = "Service status: " + $(fsService.find("mcn").find("mcn-data")).attr("status"); } + if(missedCallNotification !== undefined) return missedCallNotification; } - return messageBody; +} +function parseMsn(fsService,mcn){ + trace("parseMcn: "+mcn); + var caw = fsService.find(mcn); + var ret = null; + if (!!caw){ + var cawData = caw.find(mcn+"-data"); + if (!!cawData) { + var sender = $(cawData).attr("sender"); + if (!!sender){ + trace("Missed call: " + sender); + ret = "Missed call from " + sender; + } + } + } + return ret; } function addMessageToChat(chatDiv, from, body, className, messageId) { @@ -612,8 +639,10 @@ function notifyAddCall(call) { trace("notifyAddCall", call); // call.id, call.anotherSideUser if (currentCall != null && call.incoming == true) { + trace("currentCall != null && call.incoming == true"); hangup(call.id); } else if (currentCall != null && call.incoming == false) { + trace("currentCall != null && call.incoming == false"); holdedCall = currentCall; currentCall = call; createCallView(currentCall); @@ -783,7 +812,10 @@ function closeInfoView(timeout) { function openIncomingView(call) { trace("openIncomingView", call)// call.caller, call.visibleNameCaller - var displayedCaller = call.caller + " '" + call.visibleNameCaller + "'"; + //form Caller-ID information displayed to user + var displayedCaller = ""; + if (call.caller !== undefined) displayedCaller += call.caller; + if (call.visibleNameCaller !== undefined) displayedCaller += " '" + call.visibleNameCaller + "'"; $('#incomingDiv').show(); $('#callerField').html(displayedCaller); @@ -948,7 +980,7 @@ function createChat(calleeName) { $('#chat' + calleeNameId + ' .messageSend').click(function () { var fullCalleeName = $(this).parent().attr('fullCalleeName'); //parse id of current chatBox, take away chat word from the beginning var messageText = $(this).prev().val(); //parse text from input - sendMessage(calleeName, messageText, 'text/plain'); //send message + sendMessage(calleeName, messageText, flashphonerLoader.msgContentType); //send message $(this).prev().val(''); //clear message input }); @@ -1008,9 +1040,8 @@ function close(element) { element.css('visibility', 'hidden'); } - /* --------------------- On document load we do... ------------------ */ -$(function () { +function notifyReady() { // open login view $("#loginMainButton").click(function () { @@ -1147,6 +1178,10 @@ $(function () { } }); + $(".testButton").click(function () { + startUnitTests(); + }); + // this function set changing in button styles when you press any button $(".button").mousedown(function () { $(this).addClass('pressed'); @@ -1213,4 +1248,4 @@ $(function () { }); -}); +} diff --git a/client/client/src/js/UnitTests.js b/client/client/src/js/UnitTests.js new file mode 100644 index 00000000..9cd05562 --- /dev/null +++ b/client/client/src/js/UnitTests.js @@ -0,0 +1,11 @@ +function startUnitTests() { + testConvertMessageBody(); +} + +function testConvertMessageBody(){ + //var messageBody=""; + var messageBody="" + var contentType="application/fsservice+xml"; + var res = convertMessageBody(messageBody,contentType); + trace("testConvertMessageBody "+res); +} diff --git a/client/client/src/js/rtc/WebRtcMediaManager.js b/client/client/src/js/rtc/WebRtcMediaManager.js index cbc88fbe..228aeefe 100644 --- a/client/client/src/js/rtc/WebRtcMediaManager.js +++ b/client/client/src/js/rtc/WebRtcMediaManager.js @@ -7,9 +7,13 @@ var WebRtcMediaManager = function (localVideoPreview, remoteVideo, hasVideo) { me.remoteVideo = remoteVideo; me.localVideo = localVideoPreview; me.isMuted = 1; + //stun server by default + //commented to speedup WebRTC call establishment + //me.stunServer = "stun.l.google.com:19302"; }; WebRtcMediaManager.prototype.init = function () { + console.debug("WebRtcMediaManager.init"); var me = this; me.hasVideo = false; @@ -23,9 +27,10 @@ WebRtcMediaManager.prototype.close = function () { if (this.peerConnectionState != 'finished') { this.peerConnectionState = 'finished'; if (this.peerConnection) { + console.debug("WebRtcMediaManager:PeerConnection will be closed"); + this.peerConnection.close(); this.remoteVideo.pause(); this.remoteVideo.src = null; - this.peerConnection.close(); } } else { console.log("peerConnection already closed, do nothing!"); @@ -38,15 +43,15 @@ WebRtcMediaManager.prototype.createPeerConnection = function () { var application = this; if (webrtcDetectedBrowser == "firefox") { pc_config = {"iceServers": [ - {"url": "stun:23.21.150.121"} + {"url": "stun:" + application.stunServer} ]}; } else { pc_config = {"iceServers": [ - {"url": "stun:stun.l.google.com:19302"} + {"url": "stun:" + application.stunServer} ]}; } this.peerConnection = new RTCPeerConnection(pc_config, {"optional": [ - {"DtlsSrtpKeyAgreement": true} + {"DtlsSrtpKeyAgreement": false} ]}); this.peerConnection.onaddstream = function (event) { @@ -149,20 +154,26 @@ WebRtcMediaManager.prototype.createOffer = function (createOfferCallback, hasVid var me = this; try { if (me.getConnectionState() != "established") { + console.debug("Connection state is not established. Initializing..."); me.init(); } function create() { + console.debug("Creating offer..."); if (me.peerConnection == null) { + console.debug("peerConnection is null"); me.createPeerConnection(); me.peerConnection.addStream(me.localAudioStream); } else { + console.debug("peerConnection is not null"); if (hasVideo) { + console.debug("hasVideo"); if (me.localAudioStream) { //me.peerConnection.removeStream(me.localAudioStream); } me.peerConnection.addStream(me.localAudioVideoStream); me.hasVideo = true; } else { + console.debug("do not have video"); if (me.localAudioVideoStream) { me.peerConnection.removeStream(me.localAudioVideoStream); } @@ -204,14 +215,15 @@ WebRtcMediaManager.prototype.createOffer = function (createOfferCallback, hasVid }; WebRtcMediaManager.prototype.createAnswer = function (createAnswerCallback) { - console.debug("WebRtcMediaManager:createAnswer()"); var me = this; + console.debug("WebRtcMediaManager:createAnswer() me.getConnectionState(): "+me.getConnectionState()+" me.hasVideo: "+me.hasVideo); var hasVideo = me.hasVideo; if (me.getConnectionState() != "established") { me.init(); } try { function create() { + console.debug("create() me.peerConnection: "+me.peerConnection); if (me.peerConnection == null) { me.createPeerConnection(); me.peerConnection.addStream(me.localAudioStream); @@ -268,6 +280,7 @@ WebRtcMediaManager.prototype.createAnswer = function (createAnswerCallback) { }; WebRtcMediaManager.prototype.onCreateOfferSuccessCallback = function (offer) { + console.debug("onCreateOfferSuccessCallback this.peerConnection: "+this.peerConnection+" this.peerConnectionState: "+this.peerConnectionState); if (this.peerConnection != null) { if (this.peerConnectionState == 'new' || this.peerConnectionState == 'established') { var application = this; @@ -288,13 +301,16 @@ WebRtcMediaManager.prototype.onCreateOfferSuccessCallback = function (offer) { }; WebRtcMediaManager.prototype.onSetLocalDescriptionSuccessCallback = function (sdp) { + console.debug("onSetLocalDescriptionSuccessCallback"); if (webrtcDetectedBrowser == "firefox") { console.debug("WebRtcMediaManager:onSetLocalDescriptionSuccessCallback: sdp=" + sdp); if (this.peerConnectionState == 'preparing-offer') { + console.debug("Current PeerConnectionState is 'preparing-offer' sending offer..."); this.peerConnectionState = 'offer-sent'; this.createOfferCallback(sdp); } else if (this.peerConnectionState == 'preparing-answer') { + console.debug("Current PeerConnectionState is 'preparing-answer' going to established..."); this.peerConnectionState = 'established'; this.createAnswerCallback(sdp); } @@ -308,7 +324,7 @@ WebRtcMediaManager.prototype.getConnectionState = function () { }; WebRtcMediaManager.prototype.setRemoteSDP = function (sdp, isInitiator) { - console.debug("WebRtcMediaManager:setRemoteSDP: sdp=" + sdp); + console.debug("WebRtcMediaManager:setRemoteSDP: isInitiator: "+isInitiator+" sdp=" + sdp); if (isInitiator) { var sdpAnswer = new RTCSessionDescription({ type: 'answer', @@ -327,11 +343,14 @@ WebRtcMediaManager.prototype.setRemoteSDP = function (sdp, isInitiator) { }; WebRtcMediaManager.prototype.onSetRemoteDescriptionSuccessCallback = function () { + console.debug("onSetRemoteDescriptionSuccessCallback"); if (this.peerConnection != null) { if (this.peerConnectionState == 'answer-received') { + console.debug("Current PeerConnectionState is 'answer-received' changing the PeerConnectionState to 'established'"); this.peerConnectionState = 'established'; } else if (this.peerConnectionState == 'offer-received') { + console.debug("Current PeerConnectionState is 'offer-received' creating appropriate answer..."); var application = this; this.peerConnection.createAnswer(function (answer) { application.onCreateAnswerSuccessCallback(answer); @@ -350,8 +369,10 @@ WebRtcMediaManager.prototype.onSetRemoteDescriptionSuccessCallback = function () WebRtcMediaManager.prototype.onCreateAnswerSuccessCallback = function (answer) { + console.debug("onCreateAnswerSuccessCallback "+this.peerConnection); if (this.peerConnection != null) { if (this.peerConnectionState == 'offer-received') { + console.debug("Current PeerConnectionState is 'offer-received', preparing answer..."); // Prepare answer. var application = this; this.peerConnectionState = 'preparing-answer'; @@ -370,6 +391,9 @@ WebRtcMediaManager.prototype.onCreateAnswerSuccessCallback = function (answer) { } }; +WebRtcMediaManager.prototype.setStunServer = function (server) { + this.stunServer = server; +} WebRtcMediaManager.prototype.onCreateAnswerErrorCallback = function (error) { console.error("WebRtcMediaManager:onCreateAnswerErrorCallback(): error: " + error); diff --git a/client/client/src/js/ws/Messenger.js b/client/client/src/js/ws/Messenger.js index 8d7809a9..10eab6c0 100644 --- a/client/client/src/js/ws/Messenger.js +++ b/client/client/src/js/ws/Messenger.js @@ -28,7 +28,17 @@ Messenger.prototype = { } else if (message.state == "IMDN_FAILED" || message.state == "IMDN_FORBIDDEN" || message.state == "IMDN_ERROR") { this.notifyDeliveryFailed(message.notificationResult); } else if (message.state == "RECEIVED") { - this.notifyReceived(message, notificationResult); + //here we will choose what to display on multiple contacts in "from". + if (message.from.indexOf(",") != -1) { + var fromList = message.from.split(","); + message.from = fromList[0]; + } + //Don't show service message to user + if (message.contentType.toLowerCase() == "message/fsservice+xml") { + console.log("Received service message"); + } else { + this.notifyReceived(message, notificationResult); + } } }, diff --git a/client/client/src/js/ws/WebSocketManager.js b/client/client/src/js/ws/WebSocketManager.js index 956f8815..d8e5daaf 100644 --- a/client/client/src/js/ws/WebSocketManager.js +++ b/client/client/src/js/ws/WebSocketManager.js @@ -5,6 +5,7 @@ var WebSocketManager = function (localVideoPreview, remoteVideo) { me.configLoaded = false; me.webRtcMediaManager = new WebRtcMediaManager(localVideoPreview, remoteVideo); me.soundControl = new SoundControl(); + me.stripCodecs = new Array(); var rtcManager = this.webRtcMediaManager; var proccessCall = function (call) { for (var i in me.calls) { @@ -48,7 +49,7 @@ var WebSocketManager = function (localVideoPreview, remoteVideo) { }, registered: function (sipHeader) { - notifyRegistered(); + notifyRegistered(sipHeader); }, ring: function (call, sipHeader) { @@ -62,7 +63,7 @@ var WebSocketManager = function (localVideoPreview, remoteVideo) { setRemoteSDP: function (call, sdp, isInitiator, sipHeader) { proccessCall(call); - //this.stopSound("RING"); + this.stopSound("RING"); rtcManager.setRemoteSDP(sdp, isInitiator); if (!isInitiator && rtcManager.getConnectionState() == "established") { me.answer(call.id); @@ -162,6 +163,11 @@ WebSocketManager.prototype = { openInfoView("Configuring WebRTC connection...", 0); this.webRtcMediaManager.createOffer(function (sdp) { closeInfoView(); + //here we will strip codecs from SDP if requested + if (me.stripCodecs.length) { + sdp = me.stripCodecsSDP(sdp); + console.log("New SDP: " + sdp); + } callRequest.sdp = sdp; me.webSocket.send("call", callRequest); }, false); @@ -187,6 +193,12 @@ WebSocketManager.prototype = { openInfoView("Configuring WebRTC connection...", 0, 60); this.webRtcMediaManager.createOffer(function (sdp) { closeInfoView(); + //here we will strip codecs from SDP if requested + if (me.stripCodecs.length) { + sdp = me.stripCodecsSDP(sdp); + console.log("New SDP: " + sdp); + } + sdp = me.removeCandidatesFromSDP(sdp); callRequest.sdp = sdp; me.webSocket.send("call", callRequest); }, false); @@ -210,11 +222,30 @@ WebSocketManager.prototype = { answer: function (callId) { var me = this; openInfoView("Configuring WebRTC connection...", 0, 60); - this.webRtcMediaManager.createAnswer(function (sdp) { + /** + * If we receive INVITE without SDP, we should send answer with SDP based on webRtcMediaManager.createOffer because we do not have remoteSdp here + */ + if (this.webRtcMediaManager.lastReceivedSdp !== null && this.webRtcMediaManager.lastReceivedSdp.length==0){ + this.webRtcMediaManager.createOffer(function (sdp) { closeInfoView(); + //here we will strip codecs from SDP if requested + if (me.stripCodecs.length) { + sdp = me.stripCodecsSDP(sdp); + console.log("New SDP: " + sdp); + } + sdp = me.removeCandidatesFromSDP(sdp); me.webSocket.send("answer", {callId: callId, sdp: sdp}); - } - ); + }, false); + } else{ + /** + * If we receive a normal INVITE with SDP we should create answering SDP using normal createAnswer method because we already have remoteSdp here. + */ + this.webRtcMediaManager.createAnswer(function (sdp) { + closeInfoView(); + me.webSocket.send("answer", {callId: callId, sdp: sdp}); + } + ); + } }, hangup: function (callId) { @@ -312,6 +343,91 @@ WebSocketManager.prototype = { notificationResult: function (result) { this.webSocket.send("notificationResult",result); + }, + + setStripCodecs: function (array) { + this.stripCodecs = array; + }, + + removeCandidatesFromSDP: function (sdp) { + var sdpArray = sdp.split("\n"); + + for (i = 0; i < sdpArray.length; i++) { + if (sdpArray[i].search("a=candidate:") != -1) { + sdpArray[i] = ""; + } + } + + //normalize sdp after modifications + var result = ""; + for (i = 0; i < sdpArray.length; i++) { + if (sdpArray[i] != "") { + result += sdpArray[i] + "\n"; + } + } + + return result; + }, + + stripCodecsSDP: function (sdp) { + var sdpArray = sdp.split("\n"); + console.dir(this.stripCodecs); + + //search and delete codecs line + var pt = []; + for (p = 0; p < this.stripCodecs.length; p++) { + console.log("Searching for codec " + this.stripCodecs[p]); + for (i = 0; i < sdpArray.length; i++) { + if (sdpArray[i].search(this.stripCodecs[p]) != -1 && sdpArray[i].indexOf("a=rtpmap") == 0) { + console.log(this.stripCodecs[p] + " detected"); + pt.push(sdpArray[i].match(/[0-9]+/)[0]); + sdpArray[i] = ""; + } + } + } + + if (pt.length) { + //searching for fmtp + for (p = 0; p < pt.length; p++) { + for (i = 0; i < sdpArray.length; i++){ + if (sdpArray[i].search("a=fmtp:"+pt[p]) != -1) { + console.log("PT "+pt[p]+" detected"); + sdpArray[i] = ""; + } + } + } + + //delete entries from m= line + for (i = 0; i < sdpArray.length; i++) { + if (sdpArray[i].search("m=audio") != -1) { + console.log("m line detected " + sdpArray[i]); + var mLineSplitted = sdpArray[i].split(" "); + var newMLine = ""; + for (m = 0; m < mLineSplitted.length; m++) { + if (pt.indexOf(mLineSplitted[m]) == -1 || m <= 2){ + newMLine += " " + mLineSplitted[m]; + } + } + sdpArray[i] = newMLine.substr(1); + console.log("Resulting m= line is: " + sdpArray[i]); + break; + } + } + } + + //normalize sdp after modifications + var result = ""; + for (i = 0; i < sdpArray.length; i++) { + if (sdpArray[i] != "") { + result += sdpArray[i] + "\n"; + } + } + + return result; + }, + + setStunServer: function(server) { + this.webRtcMediaManager.setStunServer(server); } }; diff --git a/client/client/src/styles/style_html.css b/client/client/src/styles/style_html.css index 2306d584..b95d25f7 100644 --- a/client/client/src/styles/style_html.css +++ b/client/client/src/styles/style_html.css @@ -168,10 +168,10 @@ input::-webkit-input-placeholder { opacity: 1; } .transferButton, .holdButton { - top: 97px; - width: 14px; - height: 14px; -} + top: 97px; + width: 14px; + height: 14px; + } .closeButton { background: url(../assets/close.png); right: 3px; @@ -185,6 +185,7 @@ input::-webkit-input-placeholder { right: 27px; visibility:hidden; } + .holdImage { background: url(../assets/hold_big.png); right: 55px; @@ -407,6 +408,22 @@ input::-webkit-input-placeholder { margin: 10px 0 0 15px; width: 80px; } + +#testButton { + position: absolute; + top: 330px; + left: 250px; + height: 20px; + width: 80px; + float: left; + background-color: green; + font: 14px Courier; + color: #ffffff; + padding: 1px; + visibility: hidden; + text-align: center; +} + /* ------------ Video + Security settings view -------------- */ #video_requestUnmuteDiv { top: 32px;