Reference12r1:Concept myPBX Toolbox: Difference between revisions

From innovaphone wiki
Jump to navigation Jump to search
(Replacing page with 'myPBX WebRTC Softwarephone Version 12r1 is not compatible with WebRTC any more due to the missing RTCP-Mux feature that is only included in 12r2 and up.')
 
Line 1: Line 1:
[[Category:Concept|myPBX WebRTC Softwarephone]]
[[Category:Concept|myPBX WebRTC Softwarephone]]
The myPBX toolbox is a set of Java Script files that can be used for integrating functionality of the innovaphone PBX into arbitrary web sites.


= Supported features =
Version 12r1 is not compatible with WebRTC any more due to the missing RTCP-Mux feature that is only included in 12r2 and up.
* Presence monitoring
* Outgoing WebRTC calls with audio, video and application sharing
 
= Requirements =
; Download
*  The required files can be obtained from [http://download.innovaphone.com/ the download website] (V12r1 and up) in the ''toolbox'' package)
;Web site:
* Any web server with https support should be compatible with the toolbox. The JavaScript library files have to be included in the web site
;PBX:
* User object with password for login and a WebRTC device
* Call filters for that user object
* Valid STUN/TURN configuration
 
The TURN credentials are transferred in clear text to the browser. This is by design of the WebRTC clients.
To avoid disclosure of credentials matching for other services define additional username/password combination on the TURN server that is used by WebRTC clients only.
;Licenses:
* myPBX license
* Video license (for video telephony)
* Application Sharing license (for viewing shared applications)
* WebRTC channel license (per WebRTC call)
:Note: The WebRTC channel license must be activated on each indiviual PBX by configuring a number of "Max WebRTC calls".
;Network:
* The PBX service (<code>https://</code>''pbx.sample.domain''<code>/PBX0/WEBSOCKET</code>) must be accessible from the public internet (NAT port forwarding or reverse proxy).
;Certificates:
* The caller (more precisely, the web browser used by the user accessing your web site) will create a websocket TLS connection to your PBX, so it needs to trust the PBX's (<code>https://</code>''pbx.sample.domain'') certificate. As the web site probably shall be usable by any client out there on the internet, you can not control the calling browsers certificate trust list. You therefore must install a valid certificate created by a well-known certificate authority on your PBX.
 
= Authentication =
For connecting the credentials of a PBX user account is needed. The credentials can be hard coded in the Java Script code (not recommended) or they can be stored on the web server that hosts the application (recommended).
 
A typical flow for the second method would be like follows.
# The PBX chooses the server parameters (realm, sessionId, serverNonce) and sends it to the Application
# The callback onAuthenticate(realm, sessionId, serverNonce) is called.
# The Application sends the parameters to the web server.
# The Webserver chooses the client parameters (username, clientNonce) and calculated the login digest and sends it back to the application.
# The application calls the setAuthentication(username, clientNonce, digest) function.
# The PBX accepts the login.
 
= Reference =
== innovaphone.pbxwebsocket.Connection ==
Used to create a connection to the PBX for presence subscription.
 
=== Includes ===
The following files have to be included in the html file in order to use the WebRTC endpoint.
* <code>innovaphone.common.crypto.js</code>
* <code>innovaphone.pbxwebsocket.Connection.js</code>
 
=== Constructor ===
;new Connection(url, username, password):
:;url: The URL of the PBX websocket service (e.g. <code>wss://xxx/PBX0/WEBSOCKET/websocket</code>)
:;username: The username of the PBX user object.
:;password: The password of the PBX user object.
: you can set the following members to define callback functions provided by your web page:
::;          connection.onauthenticate : called when the login authentication needs to be computed with the parameters (realm, sessionId, serverNonce). Applications should calculate the login hash using these parameters and the username and password and call setAuthentication.
::;          connection.onconnected : called when the call is connected
::;          connection.onerror : called on error
::;          connection.onclosed : called when the call is closed
::;          connection.onendpointpresence : called when a subscribed users' presence information changes
 
=== Methods ===
;function close(): Disconnects from the PBX.
;function setAuthentication(username, clientNonce, digest): Sets the client parameters of the authentication and the calculated digest.
:;username: The H323 id of the user object
:;clientNonce: A random number
:;digest: sha256("innovaphonePbxWebsocket:ClientAuth:" + realm + ":" + sessionId + ":" + username + ":" + password + ":" + clientNonce + ":" + serverNonce)
;function sendSubscribeEndpoint(name, number): Starts presence monitoring for a given endpoint specifyed by name ''or'' number.
:;name: The URI or H.323 id
:;number: The phone number
;function sendUnsubscribeEndpoint(name, number): Stops presence monitoring for a given endpoint specifyed by name ''or'' number.
:;name: The URI or H.323 id
:;number: The phone number
 
The methods for WebRTC calls are not described here. Use innovaphone.pbxwebsocket.WebRtcEndpoint.js instead.
 
=== Callbacks ===
;function onauthenticate(realm, sessionId, serverNonce): This callback is used for authenting users when the web page shall not be aware of the user password. Set <code>connection.onauthenticate</code> to get a callback during login process. The login hash can then be calculated on the webserver and be given back using function setAuthentication.
 
;function onconnected(userInfo): Called when the connection has been successfully established.
:;userInfo: An object containing informmation about the connected user.
 
;function onerror(error): Called when the connection could not be established or an other error occurred.
:;error: A string containing an error message.
 
; function onclosed(): Called if the connection was closed.
 
;function onendpointpresence(name, number, phoneStatus, imStatus, activity, note): Called if the presence of a subscribed endpoint has changed.
:;name: The URI or H.323 id
:;number: The phone number
:;phoneStatus: Tells if a phone is registered, <code>open</code> or <code>closed</code>.
:;imStatus: Tells if a chat client is registered, <code>open</code> or <code>closed</code>.
:;activity: Presence activity <code>away</code>, <code>busy</code>, <code>lunch</code>, <code>vacation</code>, <code>busy</code>, <code>dnd</code>, <code>on-the-phone</code>
:;note: Presence note
 
The callbacks for WebRTC calls are not described here. Use innovaphone.pbxwebsocket.WebRtcEndpoint.js instead.
 
== innovaphone.pbxwebsocket.WebRtcEndpoint ==
This file contains a WebRTC endpoint implementation that can be used for adding WebRTC calls to a web page. For that the credentials of a user object on the PBX is needed. Visitors of the web page will use that user object for making phone calls.
 
'''In some browsers like Chrome WebRTC only works on HTTPS pages. So it is mandatory to use HTTPS and WSS on your page.'''
 
The ''WebRtcEndpoint'' is derived from the ''Connection'' described above.  As such, all interfaces defined for those are also available, plus some more.
 
=== Includes ===
The following files have to be included in the html file in order to use the WebRTC endpoint.
* <code>innovaphone.common.crypto.js</code>
* <code>innovaphone.pbxwebsocket.Connection.js</code>
* <code>innovaphone.pbxwebsocket.ToneGenerator.js</code>
* <code>innovaphone.pbxwebsocket.WebRtcEndpoint.js</code>
 
If you want to use application sharing the following files must also be included:
* <code>innovaphone.applicationSharing.jpeg.js</code>
* <code>innovaphone.applicationSharing.zlib.js</code>
* <code>innovaphone.applicationSharing.png.js</code>
* <code>innovaphone.applicationSharing.main.js</code>
 
Additionally the MP3 files containing the ring tones and ring back tones are needed on the web server in the same directory as the javascript files.
 
=== Compatibility check ===
 
You can test if the browser supports WebRTC by checking the bool <code>innovaphone.pbxwebsocket.WebRtc.supported</code>.
 
=== Constructor ===
;new WebRtcEndpoint(url, username, password, device, physicalLocation, regContext, onLog, onCall, onAuthenticate):
:;url: The URL of the PBX websocket service (e.g. <code>ws://10.0.0.1/PBX0/WEBSOCKET/websocket</code>)
:;username: The username of the PBX user object.
:;password: The password of the PBX user object.
:;device: The device ID that shall be used for making calls.
:;physicalLocation: The physical location of the user (optional).
:;regContext: must be set to <code>null</code>.
:;logFunction: A callback function that is called for logging debug info (optional).
:;onCall: A callback function that is called when calls are added, updated or removed. Applications that want to use call control have to specify this callback function. Applications that don't should give <code>null</code>. (optional)
:;onAuthenticate: An optional callback function. If set it is called with the parameters (realm, sessionId, serverNonce). Applications should calculate the login hash using these parameters and the username and password and call setAuthentication.
 
=== Methods ===
;function close(): Closes the WebRTC endpoint and disconnects from the PBX.
;function initCall(name, number, video, sharing): Starts a phone call. This is only possible if the application supplied an <code>onCall</code> callback to the constructor.
:;name: The URI (e.g. ''Name'' in the PBX ''User'' object) to be called (optional, supply name or number).
:;number: The phone number to be called (optional, supply name or number).
:;video: Set to <code>true</code> for starting a video call (optional).
:;sharing: Set to <code>true</code> for starting an application sharing call (optional).
;function connectCall(id): Connects a call.
:;id: The ID of the call.
;function dtmfCall(id, digits): Sends dtmf digits to a connected call.
:;id: The ID of the call.
:;digits: A string containing DTMF digits (0-9,*,#,A,B,C,D).
;function clearCall(id): Terminates a call.
:;id: The ID of the call (optional). If no ID is supplied, all calls are terminated.
;function attachVideo(local, remote): Attaches HTML video elements to the WebRTC endpoints. The endpoint will use them to playback video. Applications can both attach before or during a video call. Also attaching multiple video elements for a single call is possible. The application should mute the video elements (<code>muted="muted"</code>) in order to avoid playback of audio.
:;local: The HTML video element for playback of the local webcam image (optional, may be <code>null</code>).
:;remote: The HTML video element for playback of the remote video image (optional, may be <code>null</code>).
;function detachVideo(local, remote): Detaches HTML video elements that have previously attached. This will stop the playback on the supplied elements.
:;local: The HTML video element to be detached (optional, may be <code>null</code>).
:;remote: The HTML video element to be detached (optional, may be <code>null</code>).
;function attachSharing(sharingDiv, createAppCallback, removeAppCallback, resizeCalback): Attaches a DIV element to the WebRTC endpoints. The endpoint will allocate inside this element a canvas object where the application sharing data will be displayed. This DIV element must have as style '''position:relative''', otherwise the mouse will not be displayed correctly. Since more than one application could be shared, the javascript application has to provide a callback to be informed that a new application arrived and another callback to be informed that an application is not anymore shared. Both callbacks should received an '''id''' as argument. Another callback will be provided in case the shared application changes its resolution. It is also possible that the JS application receives data from different participants, for instance during a conference. That is why this sender_id is also needed.
:;createAppCallback: createAppCallback(sender_id, id, name). This callback also receives the name of the new application.
:;removeAppCallback: removeAppCallback(sender_id, id).
:;resizeCallback: resizeCallback().
;function detachSharing(): Detaches elements that were previously attached.
;function sharingEvent(type, data): The endpoint provides an interface to send events to the application sharing class.
:;changeDisplaySender: This event is used to switch between the users which are sharing something. Useful in a conference. type is equal to '''changeDisplaySender''' and data should be the '''id''' of the sender. This '''id''' is the one provided in the create application callback.
:;changeDisplayApp: This event is used to switch inside the canvas object between applications being shared. type is equal to '''changeDisplayApp''' and data should be the '''id''' of the application to be displayed. This '''id''' is the one provided in the create application callback.
:;fitToElement: This event adjusts the application being shared to the size of the DIV element provided with the '''attachSharing''' function. type is equal to '''fitToElement''' and data should be '''true''' if the application should be adjusted to the canvas element or false otherwise (original size).
:;requestControl: This event allows the client to request control (mouse, keyboard) over the shared applications. The sharing party still must accept this request before the client gets the control. type is equal to '''requestControl''' and data should be '''null'''.
;function setAuthentication(username, clientNonce, digest): Sets the client parameters of the authentication and the calculated digest.
:;username: The H323 id of the user object
:;clientNonce: A random number
:;digest: sha256("innovaphonePbxWebsocket:ClientAuth:" + realm + ":" + sessionId + ":" + username + ":" + password + ":" + clientNonce + ":" + serverNonce)
 
=== Callbacks ===
;function onLog(text): Should write the supplied text to the log of the application.
;function onCall(event, call): Is called when the state of calls of this WebRTC endpoint changes.
:;event: A string that can be <code>added</code>, <code>updated</code> and <code>removed</code>.
:;call: The call object containing the current info about the call.
::;id: The numeric id of the call.
::;dir: The direction of the call (<code>in</code> or <code>out</code>).
::;state:The state of the call (<code>idle</code>, <code>calling</code>, <code>incomplete</code>, <code>complete</code>, <code>alerting</code>, <code>connected</code>, <code>disconnecting</code>, <code>disconnected</code>, <code>parked</code>).
::;hold: <code>true</code>, if the call is on hold (optional).
::;name: The URI of the remote party (optional).
::;number: The phone number of the remote party (optional).
::;video: <code>true</code>, if video is active (optional).
::;sharing: <code>true</code>, if application sharing is active (optional).
::;cause: The cause code (optional).
 
= Example =
== Usage ==
This sample code uses the [[Howto:Use the innovaphone Demo PBX | innovaphone demo PBX ]], which is publicly available, so you don't need to setup your own PBX to test the code.
 
It will create a WebRTC endpoint and register it as user ''extern-web'' at the demo PBX.  The following options are provided:
 
; Log Toolbox : turns on internal toolbox logging if checked
; Log Web Site : turns on web-site logging if checked
; Actions : call control actions:
:; START : initialize the web application
:; OFF_HOOK : start a WebRTC call to the extension given in the input field ''Target number''
:; ON_HOOK : drop an active WebRTC call
:; END : dispose all objects used in the web application
; Target number : defines the extension to be called on ''OFF_HOOK''. Also, a presence subscription will be established to this extension on ''START''. The current presence status is displayed next to this field.
: Despite ist name, you also can enter a SIP URI (i.e. ''Name'') to be called
; Local Video : calls are done with video support if checked
; Sharing : calls are done with application sharing support if checked
 
=== Using a different PBX ===
You can change some configuration parameters by modifying <code>var config</code> in the sample code.
 
=== Using different STUN/TURN Servers ===
The toolbox code will retrieve and use the STUN and TURN server settings from the PBX (in ''IP4/General/STUN'').  You can not override that using the toolbox code. When you use a different PBX, make sure those settings are well configured there.
 
=== Debugging STUN/TURN ===
Issues with your STUN and/or TURN server are best debugged using the browser's specific diagnostic tools:
; Chrome : chrome://webrtc-internals/
; Firefox : about:webrtc
 
=== Sending additional Data to the called Destination ===
When calling a name, you can add additional information to it using the ? syntax.  I.e. to send the the information <code>user=Müller</code> when calling user <code>user-1</code>, you can call <code>user-1?user=M%C3%BCller</code>.  This information will be passed to the called endpoint.  This information can also be retrieved when monitoring the calling WebRTC endpoint with SOAP (you will see it in the ''h323'' property of the <code>peer</code>-''No'' entry in the ''CallInfo'' record received with ''Poll())''.
 
To see where you are connected to, you can look at the ''name'' and ''number'' properties of the <code>onCall</code> callback with ''event'' = <code>updated</code> and ''state'' = <code>connected</code>.
 
== Known Issues ==
; untrusted PBX certificate : The code will open a secure web socket connection to the PBX.  This connection will thus use TLS in any case and some browsers will refuse to open the connection if the certificate of the target server (in this case, the innovaphone Demo PBX) is not trusted.  You will likely see a message such as <code>web: onPresenceError(WEBSOCKET_ERROR)</code>.  Try to create a security exception in your browser for the PBX (for example, connect to https://demo.innovaphone.com, confirm access and save this permanently).
: In Chrome and Firefox, you can permanently save the security exception directly from within the browser.
: In Edge, you must install the certificate in to the windows system:
:* open http://demo.innovaphone.com/
:* navigate to ''General/Certificates''
:* right-click on the ''DER'' link next to the ''IP811-41-06-f1'' certificate in the ''Trust list''
:* save this file to your hard disk
:* in file explorer, right click on the ''IP811-41-06-f1.cer'' file
:* select ''Install certificate''
:* in the wizard, select ''current user'', ''Store certificate in this storage'', ''trusted Certificate Authorities'' (exact wording depends on windows version and language)
: Internet Explorer and Safari do not support WebRTC anyway (October 2017)
; WebRTC not supported : older browsers do not support WebRTC.  You will see the message <code>sorry, WebRTC not supported by your browser</code>. Use another browser
 
== Sample Code ==
=== Toolkit Library ===
The required files can be obtained from [http://download.innovaphone.com/ the download website] (V12r1 and up) in the ''toolbox'' package. Unzip ''innovaphone-jslib.zip'' in to an empty directory on your web server. For testing, you can also copy the files to your local hard disk.
 
=== Sample Application ===
Save the following HTML code as <code>index.html</code> in to the directory you saved the toolbox library to and open it with your browser from there.  Some browser require HTTPS to use WebRTC, so be sure to open it with <code>https://</code>.
 
The sample code and the toolbox library files are also installed on the demo PBX.  You can open https://demo.innovaphone.com/ and navigate to ''General/SSD/Browse content (HTML)/toolbox/index.html''.
 
<code xml>
<html>
    <head>
        <title>innovaphone WebRTC Demo</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <link rel="shortcut icon" href="logo.gif">
        <script type="text/javascript" src="innovaphone.common.crypto.js"></script>
        <script type="text/javascript" src="innovaphone.pbxwebsocket.Connection.js"></script>
        <script type="text/javascript" src="innovaphone.pbxwebsocket.ToneGenerator.js"></script>
        <script type="text/javascript" src="innovaphone.applicationSharing.jpeg.js"></script>
        <script type="text/javascript" src="innovaphone.applicationSharing.zlib.js"></script>
        <script type="text/javascript" src="innovaphone.applicationSharing.png.js"></script>
        <script type="text/javascript" src="innovaphone.applicationSharing.main.js"></script>
        <script type="text/javascript" src="innovaphone.pbxwebsocket.WebRtcEndpoint.js"></script>
        <script type="text/javascript">
 
            var config = {
                url: "wss://demo.innovaphone.com/PBX0/WEBSOCKET/websocket",
                username: "extern-web",
                password: "demo",
                device: "extern-web",
                physicalLocation: null,
                regContext: null
            };
            var WebRtcEndpoint = innovaphone.pbxwebsocket.WebRtc.Endpoint;
            var Connection = innovaphone.pbxwebsocket.Connection;
            var endpoint = null;
            var presence = null;
            var mycall = null;
            var t0 = new Date().getTime();
            var sha256 = innovaphone.common.crypto.sha256;
            function getTimeSinceInit() {
                var ts = new Date().getTime() - t0;
                return ((ts / 1000) | 0) + "." + ts % 1000;
            }
            function toolkitLog(text) {
                _log("toolkit: " + text, "toolboxdebug");
            }
            function log(text) {
                _log("web: " + text, "webdebug");
            }
            function _log(text, type) {
                text = getTimeSinceInit() + ": " + text;
                console.log(text);
                var flag = document.getElementById(type);
                if (flag && flag.checked) {
                    var logdiv = document.getElementById("logdiv");
                    if (logdiv) {
                        logdiv.innerHTML = "<div style='font-family: monospace'>" + text + "</div>" + logdiv.innerHTML;
                    }
                }
            }
 
            // receive call related events
            function onCall(event, call) {
                if (event == "added")
                    mycall = call.id;
                else if (event == "removed") {
                    mycall = null;
                } else if (call.state == "disconnecting") {
                    endpoint.clearCall(call.id);
                }
                if (call.dir == "in") {
                    if (endpoint) {
                        log("rejecting incoming call");
                        endpoint.clearCall(call.id);
                    }
                }
                log("Call " + event + ": " + JSON.stringify(call));
            }
 
            function onAuthenticatePresence(realm, sessionId, serverNonce) {
                log("onAuthenticatePresence(" + realm + ", " + sessionId + ", " + serverNonce + ")");
                doLogin(presence, realm, sessionId, serverNonce);
            }
 
            function onAuthenticateCall(realm, sessionId, serverNonce) {
                log("onAuthenticateCall(" + realm + ", " + sessionId + ", " + serverNonce + ")");
                doLogin(endpoint, realm, sessionId, serverNonce);
            }
 
            /*
            * compute authentication
            * you can calculate your digest inline (as we do here, same as not specifiying the onAuthenticate at all)
            * or you can have a server-side script calculate it for slightly better security
            * (see WebRtcEndpoint.html for a method to do that)
            */
            function doLogin(connection, realm, sessionId, serverNonce) {
                var clientNonce = 0;
                while (clientNonce == 0) {
                    clientNonce = Math.ceil(Math.random() * 0xffffffff);
                }
                connection.setAuthentication(
                        config.username,
                        clientNonce,
                        sha256("innovaphonePbxWebsocket:ClientAuth:" + realm + ":" + sessionId + ":" + config.username + ":" + config.password + ":" + clientNonce + ":" + serverNonce)
                        );
            }
 
 
            // receive application sharing canvas resizing
            function resizeCanvas() {
                log("resizing canvas(ignored)");
            }
 
            // receive remote endpoint presence
            function onEndpointPresence(name, number, phoneStatus, imStatus, activity, note) {
                log("presence: name=" + name + " number=" + number + " phoneStatus=" + phoneStatus + " imStatus=" + imStatus + " activity=" + activity + " note=" + note);
                var status = document.getElementById("presence");
                if (status)
                    status.innerHTML = " phoneStatus=" + phoneStatus + " imStatus=" + imStatus + " activity=" + activity + " note=" + note;
            }
 
            // create new application sharing session
            function createNewApplication(sender_id, id, name) {
                var iid = "appSharing_" + sender_id + "_" + id;
                log("createNewApplication(" + sender_id + ", " + id + ", " + name + "), id=" + iid);
                if (document.getElementById(iid))
                    return;
                var new_app = document.createElement("input");
                new_app.setAttribute("id", iid);
                new_app.setAttribute("value", name + "(" + iid + ")");
                new_app.setAttribute("type", "button");
                new_app.onclick = function () {
                    changeDisplayApp(iid);
                };
                var sl = document.getElementById("sharing-local");
                if (sl) {
                    sl.appendChild(new_app);
                    changeDisplayApp(iid);
                } else
                    log("no 'sharing-local' element");
            }
 
            // switch to given application sharing session
            function changeDisplayApp(id) {
                log("changeDisplayApp(" + id + ")");
                var input_b = document.getElementById(id);
                if (input_b) {
                    var sl = document.getElementById("sharing-local");
                    if (sl) {
                        var ci;
                        for (ci = 0; ci < sl.children.length; ci++) {
                            if (sl.children[ci].id.substring(0, 11) == "appSharing_") {
                                sl.children[ci].style.backgroundColor = null;
                                sl.children[ci].disabled = false;
                            }
                        }
                    }
                    input_b.style.backgroundColor = "LightGreen";
                    input_b.disabled = true;
                    var s_app_id = String(id); // id == appSharing_x_y where x is the sender and y the application, both are numbers, 0, 1, 2, ..
                    var a_app_id = s_app_id.split("_"); // a_app_id is an array with three elements.
                    endpoint.sharingEvent('changeDisplaySender', a_app_id[1]);
                    endpoint.sharingEvent('changeDisplayApp', a_app_id[2]);
                }
            }
 
            // remove application sharing session
            function removeApplication(sender_id, id) {
                var iid = "appSharing_" + sender_id + "_" + id;
                log("removeApplication(" + sender_id + ", " + id + "), id=" + iid);
                var input_b = document.getElementById(iid);
                if (input_b) {
                    var sl = document.getElementById("sharing-local");
                    if (sl)
                        sl.removeChild(input_b);
                    else
                        log("no 'sharing-local' element");
                } else
                    log("no '" + "appSharing_" + sender_id + "_" + id + "' element");
            }
 
            // create a presence subscription
            function startPresence() {
                if (presence)
                    endPresence();
                var number = document.getElementById("number");
                if (number && number.value && number.value.trim() != "") {
                    presence = new Connection(config.url, null, null);
                    presence.onauthenticate = onAuthenticatePresence;
                    presence.onconnected = onPresenceConnected;
                    presence.onerror = onPresenceError;
                    presence.onclosed = onPresenceClosed;
                    presence.onendpointpresence = onEndpointPresence;
                }
            }
 
            function onPresenceConnected(userInfo) {
                log("onPresenceConnected(" + JSON.stringify(userInfo) + ")");
                var number = document.getElementById("number");
                if (number && number.value && number.value.trim() != "") {
                    var n = number.value.trim();
                    var e164 = (!isNaN(parseFloat(n)) && isFinite(n)) ? number.value.trim() : null;
                    var sipuri = (!isNaN(parseFloat(n)) && isFinite(n)) ? null : number.value.trim();
                    log("starting presence subscription for " + sipuri + "/" + e164);
                    presence.sendSubscribeEndpoint(sipuri, e164);
                }
            }
 
            function onPresenceError(error) {
                log("onPresenceError(" + error + ")");
            }
 
            function onPresenceClosed() {
                log("onPresenceClosed");
                presence = null;
            }
 
            // end presence subscription
            function endPresence() {
                if (presence) {
                    log("ending presence");
                    presence.close();
                    var status = document.getElementById("presence");
                    if (status)
                        status.innerHTML = "no presence available";
                }
            }
 
            // initialize page code
            function start() {
                // create a WebRTC endpoint
                if (!endpoint) {
                    if (!innovaphone.pbxwebsocket.WebRtc.supported) {
                        log("sorry, WebRTC not supported by your browser");
                    } else {
                        t0 = new Date().getTime();
                        log("start()");
                        configdiv = document.getElementById("config");
                        if (configdiv) {
                            var ui = config.url.replace(/^wss?:\/\/([^/]*).*/i, "https://$1");
                            configdiv.innerHTML = "Config: user=" + config.username + ", device=" + config.device + ", WS=" + config.url + ", PBX-UI=<a href='" + ui + "'>" + ui + "</a>";
                        }
                        endpoint = new WebRtcEndpoint(config.url, config.username, config.password, config.device, config.physicalLocation, config.regContext, toolkitLog, onCall, onAuthenticateCall);
                        endpoint.attachVideo(document.getElementById("video-local"), document.getElementById("video-remote"));
                        endpoint.attachSharing(document.getElementById("sharing-local"), createNewApplication, removeApplication, resizeCanvas);
                    }
                } else {
                    log("WebRTC endpoint already started");
                }
                // create presence subscription
                if (!presence)
                    startPresence();
                else {
                    log("presence subscription already started");
                }
            }
 
            // finalize page code
            function end() {
                if (endpoint) {
                    if (mycall)
                        onhook();
                    log("closing endpoint");
                    endpoint.detachVideo(document.getElementById("video-local"), document.getElementById("video-remote"));
                    endpoint.detachSharing();
                    endpoint.close();
                    endpoint = null;
                } else {
                    log("no endpoint to end");
                }
                if (presence) {
                    log("closing subscription");
                    presence.close();
                } else {
                    log("no subscription to end");
                }
            }
 
            // initiate call
            function offhook() {
                if (endpoint) {
                    if (mycall != null) {
                        log("already off hook");
                    } else {
                        var number = document.getElementById("number");
                        if (number && number.value && number.value.trim() != "") {
                            var n = number.value.trim();
                            var e164 = (!isNaN(parseFloat(n)) && isFinite(n)) ? number.value.trim() : null;
                            var sipuri = (!isNaN(parseFloat(n)) && isFinite(n)) ? null : number.value.trim();
                            var usevideo = document.getElementById("usevideo");
                            usevideo = (usevideo && usevideo.checked);
                            var usesharing = document.getElementById("usesharing");
                            usesharing = (usesharing && usesharing.checked);
                            log("calling " + sipuri + "/" + e164 + (usevideo ? " with " : " no ") + " video, " + (usesharing ? " with " : " no ") + " sharing ...");
                            endpoint.initCall(sipuri, e164, usevideo, usesharing); // we always call nmubers, no names in this demo
                        } else {
                            log("Please specify <i>Target number</i>");
                        }
                    }
                } else {
                    log("not started");
                }
            }
 
            // terminate call
            function onhook() {
                if (mycall) {
                    log("closing call " + mycall);
                    if (endpoint)
                        endpoint.clearCall(mycall);
                } else {
                    log("not off hook");
                }
            }
 
        </script>
    </head>
 
    <!-- the web page -->
    <body onunload="end();">
        Log Toolbox: <input type="checkbox" id="toolboxdebug"/>
        Log Web Site: <input type="checkbox" id="webdebug" checked="checked"/>
        <div id="config"></div>
        <hr>
        <div>Actions:
            <a onclick="start();
                    return false;" href="#">START</a>
            <a onclick="offhook();
                    return false;" href="#">OFF HOOK</a>
            <a onclick="onhook();
                    return false;" href="#">ON HOOK</a>
            <a onclick="end();
                    return false;" href="#">END</a>
        </div>
        <hr><div>Target number: <input type="text" id="number" value="11" onchange="startPresence();"/> <span id="presence"></span></div>
        <hr><div>Local Video<input type="checkbox" id="usevideo" checked="checked"/><video id="video-local" muted="muted">video</video>Remote Video<video id="video-remote" muted="muted">video</video></div>
        <div id="sharing-local" style="position: relative;">Sharing: <input type="checkbox" id="usesharing" checked="checked"/></div>
        <hr><div id="logdiv" style="overflow-y: scroll; height: 50vh; font-family: monospace;"></div>
    </body>
</html>
</code>

Latest revision as of 19:35, 28 October 2019


Version 12r1 is not compatible with WebRTC any more due to the missing RTCP-Mux feature that is only included in 12r2 and up.