Calling a door phone from a web browser
In this tutorial, I will take you through the steps of building a VoIP network that will allow you to make calls from a web browser out to a door phone. This setup would be helpful for access control for commercial buildings, monitoring and security, calls for assistance in hospitals, and more.
We will use SIP.js as the client, Routr as the signaling server, and RTPEngine to proxy RTP traffic.
Requirements
Before you start this tutorial, you will need the following:
- Docker Engine installed on your computer or the cloud
- NodeJS 18+ (Use nvm if possible)
- Routr command-line tool (Install with
npm install -g @routr/ctl
) - A door phone such as Fanvil i20S
Running Routr and RTPEngine with Docker Compose
This tutorial will use Routr for signaling and RTPEngine to proxy RTP traffic.
The simplest way to run both services is using Docker Compose.
To run the services with Docker Compose, first, create a folder named voipnet and, in it, a file named compose.yaml with the following content:
Filename: voipnet/compose.yml
version: "3"
services:
routr:
image: fonoster/routr-one:latest
environment:
EXTERNAL_ADDRS: ${DOCKER_HOST_ADDRESS}
RTPENGINE_HOST: rtpengine
ports:
- 51908:51908
- 5062:5062
- 5060:5060
volumes:
# Ensures the data survives container restarts
- shared:/var/lib/postgresql/data
# RTPEngine requires network mode "host" to work properly. However, this option doesn't work on
# Windows and MacOs. For development, we are opening a few ports to the host machine.
# For production, you must use the network_mode: host which works on Linux.
rtpengine:
image: fonoster/rtpengine:latest
# Uncomment the following line for production
# network_mode: host
environment:
# Set DOCKER_HOST_ADDRESS to an IP address that is reachable to the SIP clients
PUBLIC_IP: ${DOCKER_HOST_ADDRESS}
PORT_MIN: 10000
PORT_MAX: 10100
LOG_LEVEL: 6
ports:
- 22222:22222/udp
- 10000-10100:10000-10100/udp
volumes:
shared:
Notice how we included the environment variables EXTERNAL_ADDRS and PUBLIC_IP in the previous file, whose value must be set to an IP that all parties can reach to avoid wrong signaling and audio issues.
Also noteworthy is that the port 51908 was opened for administration, 5060 for TCP signaling, and 5062 for signaling with SIP.js
Next, save the file and run the following command:
# NOTE: Be sure to update the IP address
DOCKER_HOST_ADDRESS=192.168.1.7 docker compose up
The previous command will pull Routr and RTPEngine from Docker Hub and start the containers. You could also add the -d
option to run the service in the background.
Configuring a Domain and a set of Agents
You will use Routr's command-line tool to issue commands to the server and build the VoIP network.
The network will have a Domain, sip.local
, an Agent named "Door Phone," and an Agent named "Admin."
To build the VoIP network, first create a new Domain with:
rctl domains create --insecure
Notice the --insecure
flag, which is required since we don't have a TLS certificate.
The output of your command will look similar to the output below:
Press ^C at any time to quit.
› Warning: Egress rules unavailable due to 0 configured numbers.
? Friendly Name Local Domain
? SIP URI sip.local
? IP Access Control List None
? Ready? Yes
Creating Domain Local Domain... 3b20410a-3c80-4f66-b7b3-58f65ff65352
Next, create two sets of credentials—one for the administrator and one for the door phone.
To create a set of credentials, issue the following command:
rctl credentials create --insecure
You must do this twice, one for the Door Phone and one for Admin. Please set the Door Phone username to doorphone1
and the Admin to admin
Your output will be similar to this:
This utility will help you create a new set of Credentials.
Press ^C at any time to quit.
? Friendly Name Door Phone #1 - Credentials
? Username doorphone1
? Password [hidden]
? Ready? Yes
Creating Credentials Door Phone - Credentials... 5fbc7367-a59d-4555-9fc4-a15ff29c24c8
Finally, create Agents to represent the Door Phone and the Admin using the following command:
rctl agents create --insecure
Follow the prompt and ensure that the username matches that of the credentials.
Your output will look similar to this:
This utility will help you create a new Agent.
Press ^C at any time to quit.
? Friendly Name Door Phone #1
? Select a Domain sip.local
? Username doorphone1
? Credentials Name Door Phone #1 - Credentials
? Max Contacts 1
? Privacy None
? Enabled? Yes
? Ready? Yes
Creating Agent Door Phone #1... 662a379d-66f1-4e6e-9df5-5126f1dcb930
Be sure to repeat the process for the Admin.
You might use the get
subcommand for any previously created resources to verify your settings. For example, to get a list of Agents, you might run this:
rctl agents get --insecure
Configuring the door phone
The configuration on your door phone will depend on the make and model. However, generally speaking, it should have the following parameters:
- Username
- Password
- Domain
- Proxy/Server
For a Fanvil iS20, the configuration will be like this:
Building a barebones tool for calling
To create the SIP Agent, we will use the SimpleAgent implementation SIP.js, which is perfect for testing.
For a bare-bone SIP Agent, copy the following code in a file named index.html:
Filename voipnet/index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Video Feed</title>
<style>
h1 { font-family: 'Courier New', Courier, monospace; }
</style>
</head>
<body>
<h1>Door Phone Video Feed</h1>
<div style="border: 1px solid black; width: 300px; height: 300px;">
<video id="remoteVideo" autoplay muted style="background-color: black; height: 100%"></video>
</div>
<br />
<button id="callButton">Call Door Phone</button>
<button id="hangupButton">Hangup</button>
<audio style="display: none" id="remoteAudio" controls>
<p>Your browser doesn't support HTML5 audio.</p>
</audio>
<script type="module">
import { Web } from "https://unpkg.com/sip.js@0.21.2/lib/index.js";
document.addEventListener('DOMContentLoaded', () => {
const remoteVideo = document.getElementById('remoteVideo');
const callButton = document.getElementById('callButton');
const hangupButton = document.getElementById('hangupButton');
const remoteAudio = document.getElementById('remoteAudio');
const config = {
server: "ws://localhost:5062",
aor: "sip:admin@sip.local",
doorPhoneAor: "sip:doorphone1@sip.local"
}
const options = {
aor: config.aor,
media: {
constraints: { audio: true, video: true },
remote: {
audio: remoteAudio,
video: remoteVideo
}
},
userAgentOptions: {
authorizationUsername: "admin",
authorizationPassword: "1234",
}
};
const simpleUser = new Web.SimpleUser(config.server, options);
callButton.addEventListener('click', async() => {
await simpleUser.connect();
simpleUser.call(config.doorPhoneAor);
});
hangupButton.addEventListener('click', () => {
simpleUser.hangup();
});
});
</script>
</body>
</html>
Then, lunch the SIP Agent with:
npx http-server .
Calling the door phone from the web browser
Now that created a SIP agent using SIP.js, we only need to register the door phone to Routr and press the "Call Door Phone" button in the web browser.
If you have issues calling the door phone, please inspect the browser's console for errors.
The SIP agent should connect to the door phone, and you should see a video feed.
Example of the output:
What's next?
Please comment if you find this tutorial helpful and check out the following relevant tutorials:
Top comments (2)
great job
Thanks for the support, @prawee. You rock!