End of Life for Twilio Programmable Video - Upgrade to VideoSDKLearn More

How to Build Asterisk WebRTC App with C?

Learn how to integrate Asterisk with WebRTC for real-time audio, video, and data communication directly through web browsers. Follow this step-by-step guide to set up, configure, and test your Asterisk WebRTC application.

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

Asterisk-WebRTC
Understanding the architecture of an Asterisk-based WebRTC application is crucial. The architecture typically involves:
  1. Endpoints: These are the devices (browsers in the case of WebRTC) that connect to Asterisk.
  2. Dial Plan: Defined in extensions.conf, it dictates how calls are handled and routed.
  3. Media Transport: Managed through RTP, ensuring that audio and video streams are efficiently handled.
  4. 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.

Get Free 10,000 Minutes Every Months

No credit card required to start.

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