Introduction to Asterisk WebRTC Technology
What is Asterisk WebRTC?
Asterisk WebRTC is a powerful integration that leverages the capabilities of Asterisk, a popular open-source PBX (Private Branch Exchange) system, with WebRTC (Web Real-Time Communication), a technology that enables real-time audio, video, and data sharing through web browsers. Asterisk, developed by Digium, allows developers to build customized communication solutions that handle traditional telephony, VoIP (Voice over IP), and more. WebRTC, on the other hand, provides the necessary protocols to facilitate peer-to-peer connections, offering a seamless communication experience directly from web applications without the need for additional plugins or software.
The combination of Asterisk and WebRTC opens up a myriad of possibilities for modern telecommunication systems. This integration enables businesses to create flexible, scalable, and cost-effective communication solutions that can handle both traditional and web-based communication channels. Asterisk WebRTC is particularly beneficial for companies looking to unify their communication systems, allowing users to make and receive calls directly from their web browsers, enhancing accessibility and user convenience.
Importance of WebRTC in Modern Telecommunication
WebRTC has revolutionized the way we communicate by providing a standard for real-time communications that work across all major browsers. It eliminates the barriers of proprietary protocols and software, enabling developers to integrate real-time communication features into web applications easily. The technology supports various use cases, including video conferencing, online customer support, telehealth, and remote collaboration tools.
With the rise of remote work and the increasing demand for flexible communication solutions, WebRTC has become a cornerstone of modern telecommunication. Its ability to deliver high-quality audio and video streams, coupled with robust security measures, makes it an ideal choice for businesses seeking to enhance their communication infrastructure.
Asterisk as a Leading Open-Source PBX System
Asterisk stands out as one of the most versatile and widely used open-source PBX systems available today. It provides a comprehensive suite of features that support traditional telephony, VoIP, and now WebRTC. Its flexibility allows developers to create tailored communication solutions that meet specific business needs, from small office setups to large enterprise systems.
The open-source nature of Asterisk means that it is continually evolving, with a vast community of contributors adding new features, fixing bugs, and improving performance. This community-driven approach ensures that Asterisk remains at the forefront of communication technology, offering robust and innovative solutions that keep pace with the ever-changing landscape of telecommunications.
By integrating WebRTC with Asterisk, developers can leverage the strengths of both technologies to create powerful, real-time communication applications that are accessible from anywhere with an internet connection. This integration not only enhances the functionality of Asterisk but also expands its reach, making it an invaluable tool for modern communication needs.
Create a New Asterisk WebRTC App
Integrating Asterisk with WebRTC involves several steps, from installing Asterisk to configuring it for WebRTC. This part will guide you through the initial setup and structure of your project.
Install Asterisk
To begin, you need to install Asterisk on your server. Follow these steps to get started:
[a] Update Your System
sh
1 sudo apt-get update
2 sudo apt-get upgrade
[b] Install Necessary Dependencies
sh
1 sudo apt-get install build-essential libxml2-dev libncurses5-dev uuid-dev libsqlite3-dev
[c] Download Asterisk
sh
1 cd /usr/src
2 sudo wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-18-current.tar.gz
3 sudo tar -zxvf asterisk-18-current.tar.gz
4 cd asterisk-18.*
[d] Install Asterisk
sh
1 sudo ./configure
2 sudo make
3 sudo make install
4 sudo make samples
5 sudo make config
6 sudo ldconfig
Structure of the Project
Once Asterisk is installed, it's essential to understand the project structure. A typical Asterisk project includes several key directories and configuration files:
- /etc/asterisk/: This directory contains all the primary configuration files.
- /var/lib/asterisk/: Stores sound files and other data.
- /var/log/asterisk/: Contains log files for debugging and monitoring.
- /var/spool/asterisk/: Used for storing call recordings, voicemail, etc.
Key configuration files include:
- sip.conf: Configures SIP (Session Initiation Protocol) endpoints.
- extensions.conf: Defines the dial plan and call routing logic.
- http.conf: Configures the HTTP server settings for WebRTC.
- rtp.conf: Manages RTP (Real-Time Protocol) settings for media transport.
App Architecture
Understanding the architecture of an Asterisk-based WebRTC application is crucial. The architecture typically involves:
- Endpoints: These are the devices (browsers in the case of WebRTC) that connect to Asterisk.
- Dial Plan: Defined in
extensions.conf
, it dictates how calls are handled and routed. - Media Transport: Managed through RTP, ensuring that audio and video streams are efficiently handled.
- Web Interface: This is the frontend, usually built with HTML, CSS, and JavaScript, that interacts with Asterisk through SIP over WebSockets.
In a typical Asterisk WebRTC setup, users access a web page that uses JavaScript to handle WebRTC APIs. This web page connects to Asterisk via WebSockets, enabling real-time communication. Asterisk then manages the signaling and media exchange between participants.
Here's a high-level overview of the architecture:
1User 1 (Browser) <---> WebRTC (via WebSockets) <---> Asterisk Server <---> WebRTC (via WebSockets) <---> User 2 (Browser)
By following these steps and understanding the project structure and architecture, you lay the groundwork for building a robust Asterisk WebRTC application. In the next sections, we'll dive deeper into the configuration and implementation details.
Step 1: Initial Setup
Configure Asterisk for WebRTC
Setting up Asterisk for WebRTC involves configuring several key files to enable WebRTC support and ensure seamless communication between browsers. This section will guide you through the essential configuration steps.
[a] Configure SIP for WebRTC*
First, you need to configure Asterisk to handle WebRTC clients. This is done by editing the
sip.conf
file to define SIP settings and enable WebSockets.Edit
sip.conf
Open the sip.conf
file, usually located in /etc/asterisk/
, and add the following configurations:ini
1 [general]
2 transport=ws,wss,udp
3 nat=force_rport,comedia
4 avpf=yes
5 icesupport=yes
6 rtcp_mux=yes
7 directmedia=no
8 srvlookup=yes
9 encryption=yes
10
11 [webrtc_client](!)
12 type=friend
13 host=dynamic
14 context=from-internal
15 encryption=yes
16 avpf=yes
17 force_avp=yes
18 icesupport=yes
19 dtlsenable=yes
20 dtlsverify=fingerprint
21 dtlscertfile=/etc/asterisk/keys/asterisk.pem
22 dtlsprivatekey=/etc/asterisk/keys/asterisk.key
23 dtlssetup=actpass
24 rtcp_mux=yes
25 transport=ws,wss,udp
26
27 [webrtc_user](webrtc_client)
28 username=webrtc_user
29 secret=webrtc_password
transport
: Specifies the transport protocols (WebSocket (ws), secure WebSocket (wss), and UDP).nat
: Configuration to handle NAT traversal.avpf
,icesupport
,rtcp_mux
: Settings required for WebRTC.encryption
: Ensures media is encrypted.dtlsenable
and related DTLS settings: Necessary for secure media transport with WebRTC.
[b] Configure HTTP for WebSockets
Next, configure the HTTP server in Asterisk to support WebSockets, which are essential for WebRTC signaling.
Edit
http.conf
Open the http.conf
file and ensure the following settings are enabled:ini
1 [general]
2 enabled=yes
3 bindaddr=0.0.0.0
4 bindport=8088
Edit
rtp.conf
RTP settings are crucial for handling media streams in WebRTC.ini
1 [general]
2 rtpstart=10000
3 rtpend=20000
[c] Configure the Dial Plan
The dial plan in
extensions.conf
defines how calls are routed and handled. Add a simple dial plan to handle WebRTC calls.Edit
extensions.conf
Open extensions.conf
and add the following context:ini
1 [from-internal]
2 exten => 1000,1,Answer()
3 exten => 1000,n,Playback(hello-world)
4 exten => 1000,n,Hangup()
This dial plan answers the call, plays a "hello world" message, and then hangs up. This is a basic example to test the setup.
[d] Generate SSL Certificates
WebRTC requires secure communication. Generate SSL certificates for DTLS.
Generate Certificates
Use OpenSSL to generate the required certificates:
sh
1 mkdir /etc/asterisk/keys
2 cd /etc/asterisk/keys
3 openssl genpkey -algorithm RSA -out asterisk.key
4 openssl req -new -key asterisk.key -out asterisk.csr
5 openssl x509 -req -days 365 -in asterisk.csr -signkey asterisk.key -out asterisk.pem
[e] Move Certificates
Ensure the generated certificates are placed in the correct directory:
sh
1 mv asterisk.key /etc/asterisk/keys/
2 mv asterisk.pem /etc/asterisk/keys/
By following these steps, you will have configured Asterisk to support WebRTC clients, enabling them to connect and communicate via web browsers. The next section will cover designing the user interface and wiring up the components to create a functional WebRTC application.
Step 2: Wireframe All the Components
Designing the user interface (UI) for your Asterisk WebRTC application is a crucial step that lays the foundation for a seamless user experience. This section will guide you through the process of creating a basic wireframe and setting up the necessary components for your WebRTC application.
Designing the Application
Planning the User Interface
Before diving into the code, it's essential to outline the structure and components of your WebRTC application. The key components typically include:
- Login/Join Screen: Where users enter their credentials or join the conference.
- Controls Panel: For call controls such as mute, unmute, and hang up.
- Participant View: Displaying video streams of all participants.
Necessary Components for a WebRTC Application
- HTML for the Structure: Basic HTML to structure the application.
- CSS for Styling: CSS to style the components and ensure a responsive design.
- JavaScript for Functionality: JavaScript to handle WebRTC APIs and user interactions.
Creating the Join Screen
Let's start by creating the HTML and CSS for the Join Screen.
HTML - index.html
HTML
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Asterisk WebRTC App</title>
7 <link rel="stylesheet" href="styles.css">
8</head>
9<body>
10 <div class="join-screen" id="joinScreen">
11 <h2>Join Conference</h2>
12 <input type="text" id="username" placeholder="Enter Username">
13 <button id="joinButton">Join</button>
14 </div>
15
16 <div class="controls-panel" id="controlsPanel" style="display:none;">
17 <button id="muteButton">Mute</button>
18 <button id="unmuteButton">Unmute</button>
19 <button id="hangUpButton">Hang Up</button>
20 </div>
21
22 <div class="participant-view" id="participantView" style="display:none;">
23 <!-- Video streams will be dynamically added here -->
24 </div>
25
26 <script src="app.js"></script>
27</body>
28</html>
CSS - styles.css
CSS
1body {
2 font-family: Arial, sans-serif;
3 display: flex;
4 flex-direction: column;
5 align-items: center;
6 justify-content: center;
7 height: 100vh;
8 margin: 0;
9 background-color: #f0f0f0;
10}
11
12.join-screen, .controls-panel, .participant-view {
13 margin: 20px;
14 padding: 20px;
15 border: 1px solid #ccc;
16 border-radius: 8px;
17 background-color: #fff;
18}
19
20.join-screen {
21 text-align: center;
22}
23
24#username {
25 padding: 10px;
26 margin: 10px 0;
27 width: 80%;
28}
29
30button {
31 padding: 10px 20px;
32 margin: 10px;
33 cursor: pointer;
34}
35
36button:hover {
37 background-color: #007bff;
38 color: #fff;
39}
40
41.participant-view {
42 display: flex;
43 flex-wrap: wrap;
44 justify-content: center;
45}
46
47.participant-view video {
48 width: 45%;
49 margin: 10px;
50 border: 1px solid #ccc;
51 border-radius: 8px;
52 background-color: #000;
53}
JavaScript for Handling User Input and Connection Setup
Next, we'll add JavaScript to handle the user interactions and setup the WebRTC connection.
JavaScript - app.js
JavaScript
1document.getElementById('joinButton').addEventListener('click', function() {
2 const username = document.getElementById('username').value;
3 if (username) {
4 joinConference(username);
5 } else {
6 alert('Please enter a username');
7 }
8});
9
10function joinConference(username) {
11 // Hide join screen and show controls panel and participant view
12 document.getElementById('joinScreen').style.display = 'none';
13 document.getElementById('controlsPanel').style.display = 'block';
14 document.getElementById('participantView').style.display = 'block';
15
16 // Initialize WebRTC connection and add local stream to the participant view
17 initializeWebRTC(username);
18}
19
20function initializeWebRTC(username) {
21 // Placeholder for WebRTC initialization logic
22 console.log(`Joining conference as ${username}`);
23 // The actual WebRTC setup will be implemented in the next steps
24}
By following these steps, you've created a basic wireframe and set up the initial components for your Asterisk WebRTC application. The next section will focus on implementing the join screen functionality in detail, ensuring users can join the conference seamlessly.
Step 3: Implement Join Screen
Now that we have the basic structure and components in place, let's focus on implementing the join screen functionality. This step will ensure users can join the WebRTC conference seamlessly and set up their connections properly.
Creating the Join Screen
[a] Enhancing the HTML
We already have a basic HTML structure for the join screen. We'll now ensure that it includes necessary elements for a WebRTC connection, such as input fields for user credentials and buttons for interaction.
Updated HTML -
index.html
HTML
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Asterisk WebRTC App</title>
7 <link rel="stylesheet" href="styles.css">
8</head>
9<body>
10 <div class="join-screen" id="joinScreen">
11 <h2>Join Conference</h2>
12 <input type="text" id="username" placeholder="Enter Username">
13 <button id="joinButton">Join</button>
14 </div>
15
16 <div class="controls-panel" id="controlsPanel" style="display:none;">
17 <button id="muteButton">Mute</button>
18 <button id="unmuteButton">Unmute</button>
19 <button id="hangUpButton">Hang Up</button>
20 </div>
21
22 <div class="participant-view" id="participantView" style="display:none;">
23 <!-- Video streams will be dynamically added here -->
24 </div>
25
26 <script src="app.js"></script>
27</body>
28</html>
[b] Adding WebRTC Connection Logic
We'll now add JavaScript to handle the WebRTC connection setup. This includes capturing media devices, establishing a connection with the Asterisk server, and handling signaling.
JavaScript -
app.js
JavaScript
1document.getElementById('joinButton').addEventListener('click', function() {
2 const username = document.getElementById('username').value;
3 if (username) {
4 joinConference(username);
5 } else {
6 alert('Please enter a username');
7 }
8});
9
10async function joinConference(username) {
11 // Hide join screen and show controls panel and participant view
12 document.getElementById('joinScreen').style.display = 'none';
13 document.getElementById('controlsPanel').style.display = 'block';
14 document.getElementById('participantView').style.display = 'block';
15
16 // Initialize WebRTC connection and add local stream to the participant view
17 try {
18 await initializeWebRTC(username);
19 } catch (error) {
20 console.error('Error joining conference:', error);
21 }
22}
23
24async function initializeWebRTC(username) {
25 console.log(`Joining conference as ${username}`);
26
27 // Get local media stream
28 const localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
29 const localVideo = document.createElement('video');
30 localVideo.srcObject = localStream;
31 localVideo.autoplay = true;
32 localVideo.muted = true;
33 document.getElementById('participantView').appendChild(localVideo);
34
35 // Placeholder for signaling server connection (e.g., using WebSocket)
36 const signalingServerUrl = 'wss://your-signaling-server-url';
37 const signalingSocket = new WebSocket(signalingServerUrl);
38
39 signalingSocket.onopen = () => {
40 console.log('Connected to the signaling server');
41 signalingSocket.send(JSON.stringify({ type: 'join', username }));
42 };
43
44 signalingSocket.onmessage = async (message) => {
45 const data = JSON.parse(message.data);
46 // Handle incoming messages from the signaling server
47 console.log('Received message:', data);
48 };
49
50 signalingSocket.onerror = (error) => {
51 console.error('Signaling server error:', error);
52 };
53
54 signalingSocket.onclose = () => {
55 console.log('Disconnected from the signaling server');
56 };
57
58 // Placeholder for creating and handling RTCPeerConnection
59 // You will need to implement the full WebRTC signaling and peer connection logic here
60}
[c] Handling User Media and WebRTC Connections
The above script initializes a WebRTC connection by capturing the user's media devices and connecting to a signaling server. Here's a breakdown of the key steps:
- Capture Local Media Stream: Use the
navigator.mediaDevices.getUserMedia
API to capture the user's video and audio. - Display Local Stream: Create a video element and display the local stream in the participant view.
- Connect to Signaling Server: Establish a WebSocket connection to the signaling server for exchanging signaling messages.
- Handle Signaling Messages: Listen for messages from the signaling server to manage the WebRTC connection.
[d] Setting Up the Signaling Server
For a fully functional WebRTC application, you need a signaling server to coordinate connections between peers. This example uses WebSockets to communicate with the signaling server.
Example Node.js Signaling Server -
signaling-server.js
JavaScript
1const WebSocket = require('ws');
2const server = new WebSocket.Server({ port: 8080 });
3
4let clients = [];
5
6server.on('connection', (socket) => {
7 console.log('New client connected');
8 clients.push(socket);
9
10 socket.on('message', (message) => {
11 const data = JSON.parse(message);
12 console.log('Received message:', data);
13
14 // Broadcast message to all other clients
15 clients.forEach(client => {
16 if (client !== socket && client.readyState === WebSocket.OPEN) {
17 client.send(message);
18 }
19 });
20 });
21
22 socket.on('close', () => {
23 console.log('Client disconnected');
24 clients = clients.filter(client => client !== socket);
25 });
26
27 socket.on('error', (error) => {
28 console.error('WebSocket error:', error);
29 });
30});
31
32console.log('Signaling server is running on ws://localhost:8080');
By setting up this signaling server, you enable the exchange of signaling messages required for WebRTC peer connections. This completes the implementation of the join screen functionality, allowing users to join the conference and set up their WebRTC connections.
In the next section, we will implement the control functionalities, such as muting, unmuting, and hanging up the call.
Step 4: Implement Controls
In this section, we will add functionalities to the control panel that allow users to mute, unmute, and hang up the call. These controls are crucial for managing the WebRTC session effectively.
Adding User Controls
[a] Enhancing the HTML
We already have the control buttons in our HTML structure. We will now add event listeners to these buttons to handle user interactions.
HTML -
index.html
(No changes needed)HTML
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1.0">
6 <title>Asterisk WebRTC App</title>
7 <link rel="stylesheet" href="styles.css">
8</head>
9<body>
10 <div class="join-screen" id="joinScreen">
11 <h2>Join Conference</h2>
12 <input type="text" id="username" placeholder="Enter Username">
13 <button id="joinButton">Join</button>
14 </div>
15
16 <div class="controls-panel" id="controlsPanel" style="display:none;">
17 <button id="muteButton">Mute</button>
18 <button id="unmuteButton">Unmute</button>
19 <button id="hangUpButton">Hang Up</button>
20 </div>
21
22 <div class="participant-view" id="participantView" style="display:none;">
23 <!-- Video streams will be dynamically added here -->
24 </div>
25
26 <script src="app.js"></script>
27</body>
28</html>
[b] Implementing JavaScript for Controls
We will now implement the JavaScript functionality for the control buttons. This includes muting and unmuting the local audio stream, as well as hanging up the call.
JavaScript -
app.js
JavaScript
1let localStream;
2let peerConnection;
3const signalingServerUrl = 'wss://your-signaling-server-url';
4let signalingSocket;
5
6document.getElementById('joinButton').addEventListener('click', function() {
7 const username = document.getElementById('username').value;
8 if (username) {
9 joinConference(username);
10 } else {
11 alert('Please enter a username');
12 }
13});
14
15document.getElementById('muteButton').addEventListener('click', function() {
16 toggleMute(true);
17});
18
19document.getElementById('unmuteButton').addEventListener('click', function() {
20 toggleMute(false);
21});
22
23document.getElementById('hangUpButton').addEventListener('click', function() {
24 hangUpCall();
25});
26
27async function joinConference(username) {
28 document.getElementById('joinScreen').style.display = 'none';
29 document.getElementById('controlsPanel').style.display = 'block';
30 document.getElementById('participantView').style.display = 'block';
31
32 try {
33 await initializeWebRTC(username);
34 } catch (error) {
35 console.error('Error joining conference:', error);
36 }
37}
38
39async function initializeWebRTC(username) {
40 console.log(`Joining conference as ${username}`);
41
42 localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
43 const localVideo = document.createElement('video');
44 localVideo.srcObject = localStream;
45 localVideo.autoplay = true;
46 localVideo.muted = true;
47 document.getElementById('participantView').appendChild(localVideo);
48
49 signalingSocket = new WebSocket(signalingServerUrl);
50
51 signalingSocket.onopen = () => {
52 console.log('Connected to the signaling server');
53 signalingSocket.send(JSON.stringify({ type: 'join', username }));
54 };
55
56 signalingSocket.onmessage = async (message) => {
57 const data = JSON.parse(message.data);
58 // Handle incoming messages from the signaling server
59 console.log('Received message:', data);
60 };
61
62 signalingSocket.onerror = (error) => {
63 console.error('Signaling server error:', error);
64 };
65
66 signalingSocket.onclose = () => {
67 console.log('Disconnected from the signaling server');
68 };
69
70 // Placeholder for creating and handling RTCPeerConnection
71 // You will need to implement the full WebRTC signaling and peer connection logic here
72}
73
74function toggleMute(mute) {
75 localStream.getAudioTracks().forEach(track => {
76 track.enabled = !mute;
77 });
78 console.log(`Audio ${mute ? 'muted' : 'unmuted'}`);
79}
80
81function hangUpCall() {
82 if (peerConnection) {
83 peerConnection.close();
84 peerConnection = null;
85 }
86 if (signalingSocket) {
87 signalingSocket.close();
88 signalingSocket = null;
89 }
90 document.getElementById('controlsPanel').style.display = 'none';
91 document.getElementById('participantView').innerHTML = '';
92 document.getElementById('joinScreen').style.display = 'block';
93 console.log('Call ended');
94}
[c] Functionality Breakdown
- Toggle Mute: This function enables or disables the audio tracks in the local media stream.
- Hang Up Call: This function closes the WebRTC peer connection and the signaling socket, then resets the UI to the initial join screen.
[d] Testing the Controls
Ensure that the controls work as expected by joining a conference and using the mute, unmute, and hang-up buttons. Adjust the UI or code if any issues arise.
By implementing these control functionalities, you provide users with essential tools to manage their WebRTC calls effectively. The next section will focus on implementing the participant view, which displays video streams of all connected participants.
Step 5: Implement Participant View
In this step, we will implement the participant view to display video streams of all connected participants. This includes handling the addition and removal of video elements dynamically as participants join and leave the conference.
Displaying Participants
[a] Setting Up Peer Connections
To manage multiple participants, we will use RTCPeerConnection objects to handle the media streams. Each participant will have their own RTCPeerConnection.
JavaScript -
app.js
JavaScript
1let localStream;
2let peerConnections = {}; // To store peer connections
3const signalingServerUrl = 'wss://your-signaling-server-url';
4let signalingSocket;
5
6document.getElementById('joinButton').addEventListener('click', function() {
7 const username = document.getElementById('username').value;
8 if (username) {
9 joinConference(username);
10 } else {
11 alert('Please enter a username');
12 }
13});
14
15document.getElementById('muteButton').addEventListener('click', function() {
16 toggleMute(true);
17});
18
19document.getElementById('unmuteButton').addEventListener('click', function() {
20 toggleMute(false);
21});
22
23document.getElementById('hangUpButton').addEventListener('click', function() {
24 hangUpCall();
25});
26
27async function joinConference(username) {
28 document.getElementById('joinScreen').style.display = 'none';
29 document.getElementById('controlsPanel').style.display = 'block';
30 document.getElementById('participantView').style.display = 'block';
31
32 try {
33 await initializeWebRTC(username);
34 } catch (error) {
35 console.error('Error joining conference:', error);
36 }
37}
38
39async function initializeWebRTC(username) {
40 console.log(`Joining conference as ${username}`);
41
42 localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
43 const localVideo = document.createElement('video');
44 localVideo.srcObject = localStream;
45 localVideo.autoplay = true;
46 localVideo.muted = true;
47 document.getElementById('participantView').appendChild(localVideo);
48
49 signalingSocket = new WebSocket(signalingServerUrl);
50
51 signalingSocket.onopen = () => {
52 console.log('Connected to the signaling server');
53 signalingSocket.send(JSON.stringify({ type: 'join', username }));
54 };
55
56 signalingSocket.onmessage = async (message) => {
57 const data = JSON.parse(message.data);
58 console.log('Received message:', data);
59
60 switch (data.type) {
61 case 'offer':
62 handleOffer(data.offer, data.sender);
63 break;
64 case 'answer':
65 handleAnswer(data.answer, data.sender);
66 break;
67 case 'candidate':
68 handleCandidate(data.candidate, data.sender);
69 break;
70 case 'leave':
71 handleLeave(data.sender);
72 break;
73 }
74 };
75
76 signalingSocket.onerror = (error) => {
77 console.error('Signaling server error:', error);
78 };
79
80 signalingSocket.onclose = () => {
81 console.log('Disconnected from the signaling server');
82 };
83}
84
85async function handleOffer(offer, sender) {
86 const peerConnection = createPeerConnection(sender);
87 await peerConnection.setRemoteDescription(new RTCSessionDescription(offer));
88 const answer = await peerConnection.createAnswer();
89 await peerConnection.setLocalDescription(answer);
90 signalingSocket.send(JSON.stringify({ type: 'answer', answer, recipient: sender }));
91}
92
93async function handleAnswer(answer, sender) {
94 const peerConnection = peerConnections[sender];
95 await peerConnection.setRemoteDescription(new RTCSessionDescription(answer));
96}
97
98function handleCandidate(candidate, sender) {
99 const peerConnection = peerConnections[sender];
100 peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
101}
102
103function handleLeave(sender) {
104 const videoElement = document.getElementById(sender);
105 if (videoElement) {
106 videoElement.remove();
107 }
108 const peerConnection = peerConnections[sender];
109 if (peerConnection) {
110 peerConnection.close();
111 delete peerConnections[sender];
112 }
113}
114
115function createPeerConnection(participantId) {
116 const peerConnection = new RTCPeerConnection();
117 peerConnections[participantId] = peerConnection;
118
119 peerConnection.onicecandidate = (event) => {
120 if (event.candidate) {
121 signalingSocket.send(JSON.stringify({
122 type: 'candidate',
123 candidate: event.candidate,
124 recipient: participantId
125 }));
126 }
127 };
128
129 peerConnection.ontrack = (event) => {
130 const remoteVideo = document.createElement('video');
131 remoteVideo.id = participantId;
132 remoteVideo.srcObject = event.streams[0];
133 remoteVideo.autoplay = true;
134 document.getElementById('participantView').appendChild(remoteVideo);
135 };
136
137 localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
138
139 return peerConnection;
140}
141
142function toggleMute(mute) {
143 localStream.getAudioTracks().forEach(track => {
144 track.enabled = !mute;
145 });
146 console.log(`Audio ${mute ? 'muted' : 'unmuted'}`);
147}
148
149function hangUpCall() {
150 for (let participantId in peerConnections) {
151 peerConnections[participantId].close();
152 delete peerConnections[participantId];
153 document.getElementById(participantId).remove();
154 }
155 if (signalingSocket) {
156 signalingSocket.close();
157 signalingSocket = null;
158 }
159 document.getElementById('controlsPanel').style.display = 'none';
160 document.getElementById('participantView').innerHTML = '';
161 document.getElementById('joinScreen').style.display = 'block';
162 console.log('Call ended');
163}
[b] Handling Remote Streams
ontrack Event: The
ontrack
event handler adds a video element for each remote stream. When a remote stream is received, a new video element is created and added to the participant view.[c] Handling ICE Candidates
onicecandidate Event: This event handler sends ICE candidates to the signaling server. These candidates are necessary for establishing a peer-to-peer connection.
[d] Handling Signaling Messages
- Offer: When an offer is received, the client sets the remote description and creates an answer.
- Answer: When an answer is received, the client sets the remote description.
- Candidate: When a candidate is received, it is added to the peer connection.
- Leave: When a participant leaves, their video element and peer connection are removed.
By implementing these functionalities, you enable your WebRTC application to handle multiple participants dynamically, ensuring that all video streams are displayed correctly in the participant view. The next section will cover running your code and testing the complete setup to ensure everything works as expected.
Step 6: Run Your Code Now
In this final step, we'll focus on running your complete WebRTC application and ensuring everything is working as expected. This includes testing the signaling server, WebRTC connections, and user interface.
Testing the Application
[a] Start the Signaling Server
Ensure your signaling server is running. If you're using the Node.js signaling server example provided earlier, start it by running the following command in your terminal:
sh
1node signaling-server.js
This should output a message indicating that the signaling server is running and listening for connections.
[b] Open the Application in Your Browser
Open your web browser and navigate to the URL where your
index.html
file is hosted. If you're running a local server, the URL might look something like http://localhost:8080
.[c] Join the Conference
- Enter a username in the input field and click the "Join" button.
- Allow the browser to access your camera and microphone when prompted.
[d] Verify Video and Audio
- Ensure that your local video stream is displayed in the participant view.
- Use another device or open another browser window/tab to join the conference with a different username and repeat the steps.
[e] Test the Controls
- Mute/Unmute: Click the "Mute" and "Unmute" buttons to toggle your audio stream. Verify that the audio track is enabled/disabled accordingly.
- Hang Up: Click the "Hang Up" button to end the call. Ensure that the WebRTC connection is closed and the UI returns to the join screen.
[f] Debugging Common Issues
- No Video/Audio: Ensure that your browser has permission to access the camera and microphone. Check the console for any errors related to media devices.
- Connection Issues: Verify that your signaling server is running and accessible. Check for any WebSocket connection errors in the console.
- ICE Candidate Errors: Ensure that your network allows peer-to-peer connections. Check for any ICE candidate errors and ensure that candidates are being exchanged properly.
Conclusion
By following this guide, you have successfully created a WebRTC application integrated with Asterisk, complete with functionalities to join a conference, manage user controls, and display participant video streams dynamically. This setup provides a solid foundation for building more advanced communication features.
Want to level-up your learning? Subscribe now
Subscribe to our newsletter for more tech based insights
FAQ