Web
I. Installation
Download SIP.js here: https://github.com/onsip/SIP.js/releases or if the project uses npm then use
npm install sip.js
Current latest version: 0.20.0
Document SIP.js: https://github.com/onsip/SIP.js/blob/master/docs/README.md
II. Extension login
Declare global variables and import the following classes
import {
Invitation,
InvitationAcceptOptions,
Inviter,
InviterInviteOptions,
Registerer,
Session,
SessionState,
UserAgent,
UserAgentOptions,
Web
} from "sip.js";
import {IncomingResponse} from "sip.js/lib/core";
userAgent: UserAgent;
// register to manage extension login and logout operations.
registerer: Registerer;
// incomingInvitation to manage events related to incoming calls
incomingInvitation: Invitation;
// outgoingInviter to manage events related to outgoing calls
outgoingInviter: Inviter;
// When any call takes place, a session is created, this variable is used to track the status of the call until the call ends.
session: Session;
Declare UserAgent and Registerer to log in to the extension:
// Explain:
// username: extension_number (Ex: 1234, 5678, ...)
// password: extension_password (Ex: abcDefg123, xzyqerTT11, ...)
// domain: tenant_domain (Ex: voip.example.com, ...)
async login(username: string, password: string, domain: string) {
const _uri = UserAgent.makeURI(`sip:${username}@${tenant_domain`);
if (!_uri) { return; }
const _userAgentOptions: UserAgentOptions = {
uri: _uri,
authorizationUsername: username,
authorizationPassword: password,
transportOptions: {
server: "wss://sip.etelecom.vn:10443",
},
logLevel: "debug",
displayName: username,
delegate: {
// contains events such as incoming calls, forwarded calls, ...
// for example, section V will talk about receiving incoming calls, that part will be put in here
}
};
this.userAgent = new UserAgent(_userAgentOptions);
this.registerer = new Registerer(this.userAgent, {});
await this.userAgent?.start();
await this.registerer?.register();
}
III. Log out of extension
async logout() {
await this.registerer?.unregister();
await this.userAgent?.stop();
}
IV. Making an outgoing call
To make a call:
async call(phoneNumber: string) {
const target = UserAgent.makeURI(`sip:${phone}@${tenant_domain}`);
if (!target) { return; }
const inviter = new Inviter(this.userAgent, target, {});
this.outgoingInviter = inviter;
this.session = inviter;
**// listen to the call's States: start, end, ... Will be discussed in section VII.**
this._sessionStateListener();
const _inviterInviteOptions: InviterInviteOptions = {
requestDelegate: {
// When the call starts, this onProgress event occurs.
onProgress: (response: IncomingResponse) => {
if (response.message.headers['X-Session-Id']?.length) {
// TODO: get X-Session-Id to serve for getting Call Logs (if needed).
}
},
// When the call is accepted by the receiver, this onAccept event occurs.
onAccept: (response: IncomingResponse) => {
this.session = inviter;
**// Handle calls, get remoteMediaStream (audio/video) of customers
// to be able to listen and talk to customers.
// Will be discussed in section VI.**
this._processCalls();
}
}
};
await this.outgoingInviter.invite(_inviterInviteOptions);
}
Video Call, continues the call()
above function:
// you can customize these parameters if needed.
const videoConstraints: MediaTrackConstraints = {
advanced: [
{
width: 480,
height: 640,
echoCancellation: true,
frameRate: 20,
}
]
};
async call(phoneNumber: string) {
[...]
**// listen to the call's States: start, end, ... Will be discussed in section VII.**
this._sessionStateListener();
const constraints: MediaStreamConstraints = {
video: videoConstraints,
audio: true
};
**// prepare webcam for video call.**
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
// find Local Video Element (Local is the user side, different from Remote which is the side of the person talking to the User)
const localVideo = document.getElementById("localVideo") as HTMLVideoElement;
// then assign MediaStream as the source of that Video Element
localVideo.srcObject = stream;
// call play() function to play video.
localVideo.play().catch(err => console.error("ERROR in Streaming Local Video", err));
});
const _inviterInviteOptions: InviterInviteOptions = {
requestDelegate: {
[...]
sessionDescriptionHandlerOptions: {constraints}
}
};
}
V. Receive call (incoming call)
Add this value to the delegate field of UserAgentOptions in section II, step 2.
**// khi có cuộc gọi đến, sự kiện onInvite này sẽ xảy ra**
onInvite: (invitation: Invitation) => {
this.incomingInvitation = invitation;
this.session = invitation;
**// listen to the call's States: start, end, ... Will be discussed in section VII.**
this._sessionStateListener();
if (this.incomingInvitation.request.headers['X-Session-Id']?.length) {
// TODO: get X-Session-Id to serve for getting Call Logs (if needed).
}
}
If it is a video call, check the variable
incomingInvitation
this.incomingInvitation.body.includes("label:video_label")
Accept call
async answer() {
await this.incomingInvitation.accept();
**// Handle calls, get remoteMediaStream (audio/video) of customers
// to be able to listen and talk to customers.
// Will be discussed in section VI.**
this._processCalls();
}
If it is a video call, with the same function
answer()
above, we can implement it as follows:
async answer() {
const constraints: MediaStreamConstraints = {
video: videoConstraints,
audio: true
};
**// prepare webcam for video call.**
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
// find Local Video Element (Local is the user side, different from Remote which is the side of the person talking to the User)
const localVideo = document.getElementById("localVideo") as HTMLVideoElement;
// then assign MediaStream as the source of that Video Element
localVideo.srcObject = stream;
// call play() function to play video.
localVideo.play().catch(err => console.error("ERROR in Streaming Local Video", err));
});
await this.incomingInvitation.accept({
sessionDescriptionHandlerOptions: {constraints}
});
this._processCalls();
}
Reject call
async reject() {
await this.incomingInvitation.reject();
}
VI. Call handling, getting remoteMediaStream
_processCalls() {
const sessionDescriptionHandler = this.session.sessionDescriptionHandler;
if (sessionDescriptionHandler && sessionDescriptionHandler instanceof Web.SessionDescriptionHandler) {
if (**cuộc gọi là video**) {
// find Remote Video Element on HTML DOM
const remoteVideo = document.getElementById("remoteVideo") as HTMLVideoElement;
// then assign MediaStream as the source of that Video Element
remoteVideo.srcObject = sessionDescriptionHandler.remoteMediaStream;
// call play() function to play video.
remoteVideo.play().catch(err => console.error("ERROR in Streaming Remote Video", err));
} else {
// find Audio Element on HTML DOM
const remoteAudio = document.getElementById("remote") as HTMLAudioElement;
// then assign MediaStream as the source of that Audio Element
remoteAudio.srcObject = sessionDescriptionHandler.remoteMediaStream;
// call play() function to play sound.
remoteAudio.play().catch(err => console.error("ERROR in Streaming Remote Audio", err));
}
}
}
Your interface must have tags
<audio>
and<video>
to display video and play audio when making calls. With local being the user side and remote being the side where the user is talking to .For example:
<video id="remoteVideo" autoplay>
</video>
<video id="localVideo" autoplay>
</video>
<audio style="display: none" id="remoteAudio"></audio>
<!--localAudio can be used to contain audio about ring back tone, incoming call ringtone...-->
<audio style="display: none" id="localAudio" loop></audio>
VII. Listening to the Call States
_sessionStateListener() {
this.session.stateChange.addListener((state: SessionState) => {
switch (state) {
case SessionState.Initial:
// The call has just been initiated.
case SessionState.Establishing:
// The call has just been accepted.
case SessionState.Established:
// The call starts.
case SessionState.Terminating:
// The call has just ended.
case SessionState.Terminated:
// The call is completely terminated
default:
}
});
}
VIII. Actively end the call
async hangup() {
if (**The call is an outgoing call and has just been initiated, not yet accepted.n**) {
await this.outgoingInviter.cancel();
} else {
await this.session?.bye();
}
}
IX. Hold the line
async hold(isHold: boolean) {
const _holdOptions: Web.SessionDescriptionHandlerOptions = {
hold: isHold
};
this.session.sessionDescriptionHandlerOptionsReInvite = _holdOptions;
this.session.invite().then();
}
Last updated