C2D: How do you integrate with SIP.js 0.8+

Problem

You want to use WebRTC to integrate into the Teleforge PBX, but since SIP.js 0.8 the API has become a lot harder to use, how do we handle it?

Solution

basic_webrtc.html
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <script src="sip-0.11.2.js"></script>
    <script src="basic_webrtc.js"></script>
</style></head>
    <body cz-shortcut-listen="true">
        <audio autoplay="autoplay" id="audio"></audio>
        <div>
            <button id="makeCall">Make call</button>
            <button id="endCall">End call</button>
        </div>
    </body>
</html>
  • This config uses Secure WebSockets (wss) by default.
  • From v4.0.0 X-Platforma headers have been deprecated in favour of X-Teleforge

Using SIP.Web.Simple will not work because it does not allow custom headers to be sent.

Variables
  • teleforgePbxAddressPBX IP/Domain Address
  • agentExtensionAgent extension/number
  • agentExtensionPass: Agents Password for the Extension
  • <callee>: number to dial
  • <outbound_idnumber to use as caller ID
  • <custom_call_ref>  your custom call reference
basic_webrtc.js (SIP.UA)
// site config
const remoteAudio = document.getElementById("audio");
let session; // the active session needs to be stored somewhere to be accessible by the handlers.
// global config, same for all agents for this client
const teleforgePbxAddress = "<????>.forge-cloud.com";
// per agent config, each agent has their own credentials
const agentExtension = "9000";
const agentExtensionPass = "supersecret";
const agentDisplayName = "0120001234"; // either a CLID or a name, it's not used but does add some additional info.

// Creates the user agent
const userAgent = new SIP.UA({
    uri: agentExtension + "@" + teleforgePbxAddress,
    authorizationUser: agentExtension,
    password: agentExtensionPass,
    displayName: agentDisplayName,
    transportOptions: {
        wsServers: ["wss://" + teleforgePbxAddress + ":8089/ws"]
    },
    //hackIpInContact: true, // may or may not be needed, try without first.
    register: true,
    sessionDescriptionHandlerFactoryOptions: {
        constraints: {
            audio: true,
            video: false
        }
    },
});

userAgent.on("registrationFailed", function (response, cause) {
    // Normally either bad user credentials or PBX issues, if the latter then mail support@teleforge.co.za
    console.error("registrationFailed cause", cause);
    console.error("registrationFailed response", response);
});

userAgent.on("registered", function (response, cause) {
    // credentials are good
    console.info("registered");
});

userAgent.on("unregistered", function (response, cause) {
    // not normally a problem but could indicate one.
    console.warn("unregistered cause", cause);
    console.warn("unregistered response", response);
});

function onFailedCall(request) {
    //http://sipjs.com/api/0.11.0/causes/
    console.error("Call Failed");
    //We send back special error messages for validation errors.
    const validationErrors = request.getHeaders("X-Teleforge-Error"); // from v4.0.0 (X-Platforma-Error has been deprecated)
  
    if (0 < validationErrors.length) { 
        validationErrors.forEach(function(error) {
            console.error("Validation Error: ", error);
        });
    }
}

function onIncomingCall(session) {
    const confirmation = confirm("Incoming call from " + session.remoteIdentity.displayName);
    if (confirmation) {
        session.accept();
    } else {
        session.reject();
    }
}

function handleMedia(session) {
    const pc = session.sessionDescriptionHandler.peerConnection;
    const remoteStream = new MediaStream();
    pc.getReceivers().forEach(function(receiver) {
        remoteStream.addTrack(receiver.track);
    });
    remoteAudio.srcObject = remoteStream;
    remoteAudio.play();
}
 
function makeOutboundCall() {
    // Outbound API Call options
    const outboundOptions = {
        extraHeaders : [
            "X-Teleforge-Outbound-ID: <outbound_id>",    // from v4.0.0 (X-Platforma-Outbound-ID has been deprecated)
            "X-Teleforge-Contact-Ref: <custom_call_ref>" // from v4.0.0 (X-Platforma-Contact-Ref has been deprecated)
        ]
    };

    session = userAgent.invite("sip:" + <callee> + "@" + teleforgePbxAddress, outboundOptions);

    // Error while placing a call
    session.on("failed", onFailedCall);
    session.on("trackAdded", function() {
        handleMedia(session);
    });

    console.info("Call started");
    console.debug("session:" + session);
}
 
function hangupCall() {
    session.bye();
    session = null;
    remoteAudio.srcObject = null;
    console.info("Hangup Call...");
}

// Listen for the invite: inbound calls
userAgent.on("invite", onIncomingCall);

// UI
const makeButton = document.getElementById("makeCall");
const endButton = document.getElementById("endCall");

makeButton.addEventListener("click", makeOutboundCall);
endButton.addEventListener("click", hangupCall, false);
  1. use the latest 0.11.x SIP.js (newer should be supported)
  2. ensure you create the UserAgent with the defined parameters
  3. ensure you handle the media as shown.