This document has been written by us (Doubango Telecom) to help developers to quickly create innovative multimedia applications
for the desktop and mobile platforms. If you are a developer and is looking for the best way to develop a plugin-free NGN (VoIP, Messaging, Video Conferencing, ...) applications for these platforms
then, your are at the right place.
If you want to get help or have some feedbacks then please visit the developer's website or subscribe to our developer mailing list.
We highly recommend checking other SIPML5 components: webrtc2sip, click-to-call, webrtc4all and SIP TelePresence (Video Group chat) client.
The complete API is available here
This section is a quick overview of the main features and may lack details. For more information on each function please click on it.
A fully featured demo is hosted at http://sipml5.org/call.htm.
Below, a very compact code showing how to initialize the engine, start the stack and make video call from bob to alice in less than 15 lines:
SIPml.init(
function(e){
var stack = new SIPml.Stack({realm: 'example.org', impi: 'bob', impu: 'sip:[email protected]', password: 'mysecret',
events_listener: { events: 'started', listener: function(e){
var callSession = stack.newSession('call-audiovideo', {
video_local: document.getElementById('video-local'),
video_remote: document.getElementById('video-remote'),
audio_remote: document.getElementById('audio-remote')
});
callSession.call('alice');
}
}
});
stack.start();
}
);
Below, the image is an overview of the main classes and functions:
Class diagram |
The API is a single javascript file which could be downloaded here. This file is generated as explained here.
var readyCallback = function(e){
createSipStack(); // see next section
};
var errorCallback = function(e){
console.error('Failed to initialize the engine: ' + e.message);
}
SIPml.init(readyCallback, errorCallback);
A SIP stack is a base object and must be created before any attempt to make/receive calls, send messages or manage presence. This section shows how to create a stack and start it. Starting a stack is an asynchronous function which mean you have to use an event listener to be notified for the state change.
var sipStack;
var eventsListener = function(e){
if(e.type == 'started'){
login();
}
else if(e.type == 'i_new_message'){ // incoming new SIP MESSAGE (SMS-like)
acceptMessage(e);
}
else if(e.type == 'i_new_call'){ // incoming audio/video call
acceptCall(e);
}
}
function createSipStack(){
sipStack = new SIPml.Stack({
realm: 'example.org', // mandatory: domain name
impi: 'bob', // mandatory: authorization name (IMS Private Identity)
impu: 'sip:[email protected]', // mandatory: valid SIP Uri (IMS Public Identity)
password: 'mysecret', // optional
display_name: 'Bob legend', // optional
websocket_proxy_url: 'wss://sipml5.org:10062', // optional
outbound_proxy_url: 'udp://example.org:5060', // optional
enable_rtcweb_breaker: false, // optional
events_listener: { events: '*', listener: eventsListener }, // optional: '*' means all events
sip_headers: [ // optional
{ name: 'User-Agent', value: 'IM-client/OMA1.0 sipML5-v1.0.0.0' },
{ name: 'Organization', value: 'Doubango Telecom' }
]
}
);
}
sipStack.start();
Registering to the server is not required in order to be able to make audio/video call or send messages.
var registerSession;
var eventsListener = function(e){
console.info('session event = ' + e.type);
if(e.type == 'connected' && e.session == registerSession){
makeCall();
sendMessage();
publishPresence();
subscribePresence('johndoe'); // watch johndoe's presence status change
}
}
var login = function(){
registerSession = sipStack.newSession('register', {
events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
});
registerSession.register();
}
It's not required to login in order to make audio/video calls but it is highly recommended if you're not using the stack in p2p mode.
var callSession;
var eventsListener = function(e){
console.info('session event = ' + e.type);
}
var makeCall = function(){
callSession = sipStack.newSession('call-audiovideo', {
video_local: document.getElementById('video-local'),
video_remote: document.getElementById('video-remote'),
audio_remote: document.getElementById('audio-remote'),
events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
});
callSession.call('johndoe');
}
To accept incoming audio/video call:
var acceptCall = function(e){
e.newSession.accept(); // e.newSession.reject() to reject the call
}
Sharing your screen or desktop with any SIP client is just like making a video call and the only difference is the session call type (call-screenshare instead of call-audiovideo).
A screen/desktop sharing session doesn't contain audio stream which means you'll need to make a second audio-only call to speak to the remote party. You don't need to open another page as multi-line is supported.
Please check here for more information about screen/desktop sharing.
It's not required to login in order to send SIP MESSAGEs (SMS-like) but it's highly recommended if you're not using the stack in p2p mode.
var messageSession;
var eventsListener = function(e){
console.info('session event = ' + e.type);
}
var sendMessage = function(){
messageSession = sipStack.newSession('message', {
events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
});
messageSession.send('johndoe', 'Pêche à la moule', 'text/plain;charset=utf-8');
}
To accept incoming SIP MESSAGE:
var acceptMessage = function(e){
e.newSession.accept(); // e.newSession.reject(); to reject the message
console.info('SMS-content = ' + e.getContentString() + ' and SMS-content-type = ' + e.getContentType());
}
It's not required to login in order to publish your presence status but it's highly recommended if you're not using the stack in p2p mode.
Presence publication is used to indicate your presence (e.g. online), mood (e.g. happy) or any personal information to your wtachers (most likely the contacts in your address book). In the coming versions the presence
feature will be combined with XCAP to allow developing fully-featured RCS-e applications.
var publishSession;
var eventsListener = function(e){
console.info('session event = ' + e.type);
}
var publishPresence = function(){
publishSession = sipStack.newSession('publish', {
events_listener: { events: '*', listener: eventsListener } // optional: '*' means all events
});
var contentType = 'application/pidf+xml';
var content = '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n' +
'<presence xmlns=\"urn:ietf:params:xml:ns:pidf\"\n' +
' xmlns:im=\"urn:ietf:params:xml:ns:pidf:im\"' +
' entity=\"sip:[email protected]\">\n' +
'<tuple id=\"s8794\">\n' +
'<status>\n'+
' <basic>open</basic>\n' +
' <im:im>away</im:im>\n' +
'</status>\n' +
'<contact priority=\"0.8\">tel:+33600000000</contact>\n' +
'<note xml:lang=\"fr\">Bonjour de Paris :)</note>\n' +
'</tuple>\n' +
'</presence>';
// send the PUBLISH request
publishSession.publish(content, contentType,{
expires: 200,
sip_caps: [
{ name: '+g.oma.sip-im' },
{ name: '+sip.ice' },
{ name: 'language', value: '\"en,fr\"' }
],
sip_headers: [
{ name: 'Event', value: 'presence' },
{ name: 'Organization', value: 'Doubango Telecom' }
]
});
}
Subscription is used to watch the status of a SIP entity. This SIP entity could be contact from your address book, an rls-service, a voice mail, etc.
When the entity status change, the subscriber will be notified using SIP NOTIFY request.
Below, the code shows how to subscribe for johndoe's presence status and parse the content of the NOTIFY request received from the server.
var subscribeSession;
var eventsListener = function(e){
console.info('session event = ' + e.type);
if(e.type == 'i_notify'){
console.info('NOTIFY content = ' + e.getContentString());
console.info('NOTIFY content-type = ' + e.getContentType());
if (e.getContentType() == 'application/pidf+xml') {
if (window.DOMParser) {
var parser = new DOMParser();
var xmlDoc = parser ? parser.parseFromString(e.getContentString(), "text/xml") : null;
var presenceNode = xmlDoc ? xmlDoc.getElementsByTagName ("presence")[0] : null;
if(presenceNode){
var entityUri = presenceNode.getAttribute ("entity");
var tupleNode = presenceNode.getElementsByTagName ("tuple")[0];
if(entityUri && tupleNode){
var statusNode = tupleNode.getElementsByTagName ("status")[0];
if(statusNode){
var basicNode = statusNode.getElementsByTagName ("basic")[0];
if(basicNode){
console.info('Presence notification: Uri = ' + entityUri + ' status = ' + basicNode.textContent);
}
}
}
}
}
}
}
}
var subscribePresence = function(to){
subscribeSession = sipStack.newSession('subscribe', {
expires: 200,
events_listener: { events: '*', listener: eventsListener },
sip_headers: [
{ name: 'Event', value: 'presence' }, // only notify for 'presence' events
{ name: 'Accept', value: 'application/pidf+xml' } // supported content types (COMMA-sparated)
],
sip_caps: [
{ name: '+g.oma.sip-im', value: null },
{ name: '+audio', value: null },
{ name: 'language', value: '\"en,fr\"' }
]
});
// start watching for entity's presence status (You may track event type 'connected' to be sure that the request has been accepted by the server)
subscribeSession.subscribe(to);
}
Please check our issue tracker or developer group if you have any problem.