How to Build SimpleRealtime Server (SRS) WebRTC App with JavaScript?

Learn how to set up and use SimpleRealtime Server (SRS) for WebRTC. This comprehensive guide covers configuration, implementation, and optimization of SRS for real-time video communication.

Introduction to SimpleRealtime Server WebRTC

WebRTC (Web Real-Time Communication) is a powerful technology that enables real-time communication directly within web browsers, making it ideal for video conferencing, live streaming, and peer-to-peer data sharing applications. One of the robust solutions leveraging WebRTC is the SimpleRealtime Server (SRS), an open-source real-time video server.

What is SimpleRealtime Server WebRTC?

The SimpleRealtime Server, commonly referred to as SRS, is a high-performance, feature-rich real-time video server developed using C/C++. It supports various streaming protocols, including HTTP Live Streaming (HLS), Real-Time Messaging Protocol (RTMP), and WebRTC. This versatility makes SRS an excellent choice for developers looking to build scalable and efficient real-time communication applications.
SRS employs a publish (push) and subscribe (play) server model, allowing seamless integration with a wide range of broadcasting and playback scenarios. Whether you're looking to set up a live streaming service, build a video conferencing application, or create an interactive multimedia experience, SRS provides the necessary tools and functionalities.

Key features of SRS include:

  • High Performance: Built with C/C++ for optimized performance and low latency.
  • Protocol Support: Extensive support for HLS, RTMP, and WebRTC, catering to various streaming needs.
  • Scalability: Designed to handle large-scale deployments with ease.
  • Flexibility: Easily configurable and extensible to suit specific requirements.
With these capabilities, SRS stands out as a reliable and efficient solution for real-time video communication, empowering developers to create cutting-edge applications with minimal hassle. In the following sections, we'll delve deeper into setting up and configuring SRS, implementing key functionalities, and running your WebRTC application seamlessly.

Getting Started with the Code!

In this section, we will guide you through the initial steps to set up and start working with SimpleRealtime Server (SRS) for WebRTC applications. We'll cover everything from creating a new SRS project to understanding the structure and architecture of your application.

[a] Create a New SimpleRealtime Server WebRTC App

To get started with SRS, you'll first need to create a new project. Here's a step-by-step guide:

[b] Clone the SRS Repository

Begin by cloning the SRS repository from GitHub.

bash

1   git clone https://github.com/ossrs/srs.git
2   cd srs/trunk

[c] Install Dependencies

Make sure you have the necessary dependencies installed. SRS requires tools like Git, CMake, and GCC.

bash

1   sudo apt-get update
2   sudo apt-get install -y git cmake gcc g++

Install SRS

Now that you have the repository, the next step is to install SRS on your system.

[a] Build SRS

Navigate to the trunk directory and run the build script.

bash

1   ./configure
2   make
This command configures the build environment and compiles the SRS source code.

[b] Verify Installation

Once the build process is complete, you can verify the installation by checking the SRS version.

bash

1   ./objs/srs -v

Structure of the Project

Understanding the structure of your SRS project is crucial for effective development. Here's a breakdown of key directories and files:
  • trunk: This is the main directory containing all source code and configuration files.
  • conf: Contains configuration files for various SRS functionalities.
  • objs: Directory where compiled binaries and objects are stored.
  • src: Source code of the SRS server, including core functionalities and protocol handling.
  • research: Contains experimental features and additional tools for testing and development.

App Architecture

Sequence diagram illustrating how SRS WebRTC works
The architecture of an SRS application is designed to be modular and scalable. Key components include:

Core Server

The core server handles the main operations, including session management, data processing, and communication protocols.

Modules

SRS supports various modules for different streaming protocols like RTMP, HLS, and WebRTC. Each module is responsible for handling its specific protocol.

Configuration Files

These files define the settings and parameters for the server and its modules, allowing for customizable and flexible setups.
By following these steps, you will have a functional SRS installation ready for development. The next sections will dive deeper into configuring SRS, implementing essential features, and building a complete WebRTC application.
With the basics covered, you're now prepared to start configuring and coding your SRS WebRTC application. Let's move on to setting up the initial configuration and building out your application's wireframe in the next steps.

Step 1: Get Started with Configuration

Configuring SimpleRealtime Server (SRS) for WebRTC is a critical step to ensure your application runs smoothly. In this section, we'll walk you through the initial configuration process, including key configuration files and parameters necessary for setting up WebRTC with SRS.

[a] Configuring SRS for WebRTC

The first step in configuring SRS is to modify the main configuration file to support WebRTC. The primary configuration file for SRS is srs.conf, located in the conf directory. Here’s a step-by-step guide:

[b] Locate the Configuration File

Navigate to the conf directory within your SRS installation.

bash

1   cd /path/to/srs/trunk/conf

[c] Open the Configuration File

Use a text editor to open srs.conf.

bash

1   nano srs.conf

[d] Configure WebRTC

Add the following configuration to enable WebRTC in SRS:
1   listen              1935;
2   max_connections     1000;
3   daemon              off;
4   srs_log_tank        console;
5   srs_log_level       trace;
6   http_server {
7       enabled         on;
8       listen          8080;
9       dir             ./objs/nginx/html;
10   }
11   rtc_server {
12       enabled         on;
13       listen          8000;
14       candidate       $CANDIDATE;
15       auto_play       on;
16   }
17   vhost __defaultVhost__ {
18       rtc {
19           enabled     on;
20       }
21   }

[e] Save and Exit

After making these changes, save the file and exit the text editor.

Important Configuration Parameters

  • listen: The port number that SRS will listen on for incoming connections. The default is 1935, commonly used for RTMP.
  • max_connections: Sets the maximum number of simultaneous connections that SRS will handle.
  • http_server: Enables the HTTP server component, necessary for serving WebRTC and other HTTP-based services.
    • listen: The port number for the HTTP server.
    • dir: The directory from which static files will be served.
  • rtc_server: Enables the RTC (Real-Time Communication) server, which handles WebRTC connections.
    • listen: The port number for the RTC server.
    • candidate: The public IP address or domain name of your server, used for establishing WebRTC connections.
    • auto_play: Automatically plays the stream for the clients.

Additional Considerations

Firewall Configuration

Ensure that your server's firewall allows traffic on the ports you've configured (1935 for RTMP, 8000 for RTC, and 8080 for HTTP).

Public IP Address

If your server is behind a NAT or firewall, configure the candidate parameter with your public IP address or domain name to ensure WebRTC connections can be established from external clients.
By following these configuration steps, you’ve set up your SRS server to handle WebRTC connections. This foundational setup is essential for building and deploying a WebRTC application using SRS.
In the next part, we will move on to wireframing all the components of your application, providing a blueprint for the user interface and user experience. This will set the stage for implementing specific features and functionalities in subsequent steps.

Step 2: Wireframe All the Components

Wireframing your application is an essential step in the development process. It helps you visualize the structure and layout of your app, ensuring that all necessary components are included and correctly positioned. In this section, we’ll create a basic wireframe for our SimpleRealtime Server (SRS) WebRTC application and implement the initial HTML structure.

Creating a Basic Wireframe

The basic components of our SRS WebRTC application will include:
  1. Join Screen: A screen where users can enter a room ID and join a video conference.
  2. Control Panel: Controls for muting audio, disabling video, and leaving the call.
  3. Participant View: A view to display all participants in the video conference.

Here is the HTML structure to wireframe these components

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>SRS WebRTC App</title>
7    <style>
8        body {
9            font-family: Arial, sans-serif;
10            display: flex;
11            flex-direction: column;
12            align-items: center;
13            justify-content: center;
14            height: 100vh;
15            margin: 0;
16            background-color: #f4f4f4;
17        }
18        .container {
19            width: 80%;
20            max-width: 800px;
21            background: white;
22            padding: 20px;
23            border-radius: 10px;
24            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
25        }
26        .join-screen, .control-panel, .participant-view {
27            display: none;
28        }
29        .join-screen.active, .control-panel.active, .participant-view.active {
30            display: block;
31        }
32        .join-screen input {
33            width: calc(100% - 22px);
34            padding: 10px;
35            margin-bottom: 10px;
36            border: 1px solid #ccc;
37            border-radius: 5px;
38        }
39        .join-screen button {
40            padding: 10px 20px;
41            border: none;
42            background-color: #007bff;
43            color: white;
44            border-radius: 5px;
45            cursor: pointer;
46        }
47        .control-panel {
48            margin-top: 20px;
49        }
50        .control-panel button {
51            padding: 10px;
52            border: none;
53            background-color: #007bff;
54            color: white;
55            border-radius: 5px;
56            cursor: pointer;
57            margin-right: 10px;
58        }
59        .participant-view {
60            margin-top: 20px;
61            display: flex;
62            flex-wrap: wrap;
63        }
64        .participant-view video {
65            width: 100%;
66            max-width: 200px;
67            margin: 10px;
68            border-radius: 10px;
69            background-color: #000;
70        }
71    </style>
72</head>
73<body>
74    <div class="container">
75        <div class="join-screen active">
76            <h2>Join a Room</h2>
77            <input type="text" id="room-id" placeholder="Enter Room ID">
78            <button onclick="joinRoom()">Join</button>
79        </div>
80        <div class="control-panel">
81            <button onclick="toggleAudio()">Mute/Unmute Audio</button>
82            <button onclick="toggleVideo()">Disable/Enable Video</button>
83            <button onclick="leaveRoom()">Leave Room</button>
84        </div>
85        <div class="participant-view">
86            <!-- Participant videos will be added here dynamically -->
87        </div>
88    </div>
89
90    <script>
91        function joinRoom() {
92            document.querySelector('.join-screen').classList.remove('active');
93            document.querySelector('.control-panel').classList.add('active');
94            document.querySelector('.participant-view').classList.add('active');
95            // Initialize WebRTC connection and join the specified room
96        }
97
98        function toggleAudio() {
99            // Toggle audio stream
100        }
101
102        function toggleVideo() {
103            // Toggle video stream
104        }
105
106        function leaveRoom() {
107            document.querySelector('.join-screen').classList.add('active');
108            document.querySelector('.control-panel').classList.remove('active');
109            document.querySelector('.participant-view').classList.remove('active');
110            // Leave the WebRTC room and clean up streams
111        }
112    </script>
113</body>
114</html>

Explanation of the Wireframe Components

Join Screen

  • Contains an input field for the user to enter a room ID and a button to join the room.
  • This section is initially visible (.join-screen.active).

Control Panel

  • Contains buttons to mute/unmute audio, disable/enable video, and leave the room.
  • This section is initially hidden and becomes visible after joining a room (.control-panel.active).

Participant View

  • A flexible container to display video streams of participants in the room.
  • This section is initially hidden and becomes visible after joining a room (.participant-view.active).
This wireframe sets up the basic layout and user interface for your WebRTC application. In the next parts, we will implement the functionalities for each of these components, starting with the join screen and then moving on to adding controls and handling participant views. By the end of these steps, you will have a fully functional WebRTC application using SRS.

Step 3: Implement Join Screen

The join screen is the first interface users will interact with when they access your SimpleRealtime Server (SRS) WebRTC application. It allows users to enter a room ID and join a video conference. In this section, we will implement the JavaScript functionality needed to make the join screen operational.

Building the Join Screen

We already have the HTML structure for the join screen. Now, we need to add the JavaScript code to handle user input and establish a WebRTC connection using SRS.
Here’s the step-by-step implementation:

HTML Structure

Ensure your HTML join screen structure is in place as shown in the previous part.

JavaScript for Join Screen

Add the following JavaScript code within the <script> tags at the bottom of your HTML file.

HTML

1<script>
2    // Function to join a room
3    async function joinRoom() {
4        const roomId = document.getElementById('room-id').value.trim();
5        if (!roomId) {
6            alert('Please enter a room ID.');
7            return;
8        }
9
10        document.querySelector('.join-screen').classList.remove('active');
11        document.querySelector('.control-panel').classList.add('active');
12        document.querySelector('.participant-view').classList.add('active');
13
14        try {
15            // Initialize WebRTC connection
16            await initializeWebRTC(roomId);
17        } catch (error) {
18            console.error('Failed to join room:', error);
19            alert('Failed to join room. Please try again.');
20            document.querySelector('.join-screen').classList.add('active');
21            document.querySelector('.control-panel').classList.remove('active');
22            document.querySelector('.participant-view').classList.remove('active');
23        }
24    }
25
26    // Function to initialize WebRTC connection
27    async function initializeWebRTC(roomId) {
28        const configuration = {
29            iceServers: [
30                { urls: 'stun:stun.l.google.com:19302' }
31            ]
32        };
33        const peerConnection = new RTCPeerConnection(configuration);
34
35        // Handle ICE candidates
36        peerConnection.onicecandidate = event => {
37            if (event.candidate) {
38                sendMessage('candidate', roomId, event.candidate);
39            }
40        };
41
42        // Handle remote stream
43        peerConnection.ontrack = event => {
44            const remoteVideo = document.createElement('video');
45            remoteVideo.srcObject = event.streams[0];
46            remoteVideo.autoplay = true;
47            remoteVideo.playsInline = true;
48            document.querySelector('.participant-view').appendChild(remoteVideo);
49        };
50
51        // Get user media
52        const localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
53        localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
54
55        // Create offer
56        const offer = await peerConnection.createOffer();
57        await peerConnection.setLocalDescription(offer);
58
59        // Send offer to the server
60        sendMessage('offer', roomId, offer);
61
62        // Listen for answer from the server
63        socket.on('answer', async (message) => {
64            if (message.roomId === roomId) {
65                const remoteDesc = new RTCSessionDescription(message.sdp);
66                await peerConnection.setRemoteDescription(remoteDesc);
67            }
68        });
69
70        // Listen for candidate from the server
71        socket.on('candidate', async (message) => {
72            if (message.roomId === roomId) {
73                const candidate = new RTCIceCandidate(message.candidate);
74                await peerConnection.addIceCandidate(candidate);
75            }
76        });
77    }
78
79    // Function to send messages to the server
80    function sendMessage(type, roomId, data) {
81        const message = { type, roomId, data };
82        socket.emit(type, message);
83    }
84
85    // Initialize socket.io client
86    const socket = io.connect('http://localhost:8080');
87</script>

Explanation of the Code

Join Room Function

  • Retrieves the room ID entered by the user.
  • Shows and hides the appropriate screen sections.
  • Calls initializeWebRTC to set up the WebRTC connection.

Initialize WebRTC Function

  • Configures ICE servers for peer-to-peer connection.
  • Sets up event handlers for ICE candidates and remote streams.
  • Retrieves the user's media (audio and video) and adds it to the peer connection.
  • Creates an SDP offer and sends it to the server.
  • Listens for SDP answer and ICE candidates from the server.

Send Message Function

  • Sends signaling messages (offer, answer, candidate) to the server using Socket.IO.

Socket.IO Initialization

  • Establishes a connection to the server using Socket.IO for signaling.
By following these steps, you will have implemented the join screen functionality, enabling users to enter a room ID and join a video conference. In the next part, we will implement controls for managing the media streams and other interactive features in your application.

Step 4: Implement Controls

Implementing controls for your SimpleRealtime Server (SRS) WebRTC application is essential for providing users with the ability to manage their audio and video streams. In this section, we’ll add functionality to the control panel, allowing users to mute/unmute audio, disable/enable video, and leave the room.

Adding Controls to the Application

We already have the HTML buttons for the control panel in place. Now, we need to add the JavaScript functionality to handle these controls.

HTML Structure

Ensure your control panel buttons are in place as shown in the previous parts.

JavaScript for Controls

Add the following JavaScript code within the <script> tags at the bottom of your HTML file.

HTML

1<script>
2    let localStream;
3    let peerConnection;
4
5    // Function to join a room
6    async function joinRoom() {
7        const roomId = document.getElementById('room-id').value.trim();
8        if (!roomId) {
9            alert('Please enter a room ID.');
10            return;
11        }
12
13        document.querySelector('.join-screen').classList.remove('active');
14        document.querySelector('.control-panel').classList.add('active');
15        document.querySelector('.participant-view').classList.add('active');
16
17        try {
18            // Initialize WebRTC connection
19            await initializeWebRTC(roomId);
20        } catch (error) {
21            console.error('Failed to join room:', error);
22            alert('Failed to join room. Please try again.');
23            document.querySelector('.join-screen').classList.add('active');
24            document.querySelector('.control-panel').classList.remove('active');
25            document.querySelector('.participant-view').classList.remove('active');
26        }
27    }
28
29    // Function to initialize WebRTC connection
30    async function initializeWebRTC(roomId) {
31        const configuration = {
32            iceServers: [
33                { urls: 'stun:stun.l.google.com:19302' }
34            ]
35        };
36        peerConnection = new RTCPeerConnection(configuration);
37
38        // Handle ICE candidates
39        peerConnection.onicecandidate = event => {
40            if (event.candidate) {
41                sendMessage('candidate', roomId, event.candidate);
42            }
43        };
44
45        // Handle remote stream
46        peerConnection.ontrack = event => {
47            const remoteVideo = document.createElement('video');
48            remoteVideo.srcObject = event.streams[0];
49            remoteVideo.autoplay = true;
50            remoteVideo.playsInline = true;
51            document.querySelector('.participant-view').appendChild(remoteVideo);
52        };
53
54        // Get user media
55        localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
56        localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
57
58        // Create offer
59        const offer = await peerConnection.createOffer();
60        await peerConnection.setLocalDescription(offer);
61
62        // Send offer to the server
63        sendMessage('offer', roomId, offer);
64
65        // Listen for answer from the server
66        socket.on('answer', async (message) => {
67            if (message.roomId === roomId) {
68                const remoteDesc = new RTCSessionDescription(message.sdp);
69                await peerConnection.setRemoteDescription(remoteDesc);
70            }
71        });
72
73        // Listen for candidate from the server
74        socket.on('candidate', async (message) => {
75            if (message.roomId === roomId) {
76                const candidate = new RTCIceCandidate(message.candidate);
77                await peerConnection.addIceCandidate(candidate);
78            }
79        });
80    }
81
82    // Function to send messages to the server
83    function sendMessage(type, roomId, data) {
84        const message = { type, roomId, data };
85        socket.emit(type, message);
86    }
87
88    // Function to toggle audio
89    function toggleAudio() {
90        const audioTracks = localStream.getAudioTracks();
91        if (audioTracks.length === 0) {
92            console.log('No audio track found');
93            return;
94        }
95
96        for (const track of audioTracks) {
97            track.enabled = !track.enabled;
98        }
99    }
100
101    // Function to toggle video
102    function toggleVideo() {
103        const videoTracks = localStream.getVideoTracks();
104        if (videoTracks.length === 0) {
105            console.log('No video track found');
106            return;
107        }
108
109        for (const track of videoTracks) {
110            track.enabled = !track.enabled;
111        }
112    }
113
114    // Function to leave the room
115    function leaveRoom() {
116        document.querySelector('.join-screen').classList.add('active');
117        document.querySelector('.control-panel').classList.remove('active');
118        document.querySelector('.participant-view').classList.remove('active');
119
120        // Close the peer connection and stop local tracks
121        if (peerConnection) {
122            peerConnection.close();
123            peerConnection = null;
124        }
125
126        if (localStream) {
127            localStream.getTracks().forEach(track => track.stop());
128            localStream = null;
129        }
130
131        // Clean up participant view
132        document.querySelector('.participant-view').innerHTML = '';
133
134        // Notify server about leaving the room
135        sendMessage('leave', roomId, {});
136    }
137
138    // Initialize socket.io client
139    const socket = io.connect('http://localhost:8080');
140</script>

Explanation of the Code

Toggle Audio Function

  • Retrieves all audio tracks from the local media stream.
  • Toggles the enabled property of each audio track to mute/unmute the audio.

Toggle Video Function

  • Retrieves all video tracks from the local media stream.
  • Toggles the enabled property of each video track to disable/enable the video.

Leave Room Function

  • Hides the control panel and participant view, showing the join screen again.
  • Closes the peer connection and stops all local media tracks.
  • Clears the participant view and notifies the server that the user has left the room.

Socket.IO Initialization

  • Establishes a connection to the server using Socket.IO for signaling.
By implementing these controls, users can now manage their audio and video streams and leave the room when needed. In the next part, we will implement the participant view, allowing the display and management of multiple video streams in the application. This will complete the core functionality of your SRS WebRTC application.

Get Free 10,000 Minutes Every Months

No credit card required to start.

Step 5: Implement Participant View

The participant view is a crucial component of any video conferencing application. It displays the video streams of all participants in the room. In this section, we will implement the JavaScript code to manage and display multiple video streams in the participant view.

Developing the Participant View

We have already set up the HTML structure for the participant view in previous parts. Now, we'll enhance our JavaScript to handle multiple participants effectively.

HTML Structure

Ensure your participant view container is in place as shown in the previous parts.

JavaScript for Managing Participants

Add the following JavaScript code within the <script> tags at the bottom of your HTML file.
1<script>
2    let localStream;
3    let peerConnection;
4    const participants = {};  // Object to store participants
5
6    // Function to join a room
7    async function joinRoom() {
8        const roomId = document.getElementById('room-id').value.trim();
9        if (!roomId) {
10            alert('Please enter a room ID.');
11            return;
12        }
13
14        document.querySelector('.join-screen').classList.remove('active');
15        document.querySelector('.control-panel').classList.add('active');
16        document.querySelector('.participant-view').classList.add('active');
17
18        try {
19            // Initialize WebRTC connection
20            await initializeWebRTC(roomId);
21        } catch (error) {
22            console.error('Failed to join room:', error);
23            alert('Failed to join room. Please try again.');
24            document.querySelector('.join-screen').classList.add('active');
25            document.querySelector('.control-panel').classList.remove('active');
26            document.querySelector('.participant-view').classList.remove('active');
27        }
28    }
29
30    // Function to initialize WebRTC connection
31    async function initializeWebRTC(roomId) {
32        const configuration = {
33            iceServers: [
34                { urls: 'stun:stun.l.google.com:19302' }
35            ]
36        };
37        peerConnection = new RTCPeerConnection(configuration);
38
39        // Handle ICE candidates
40        peerConnection.onicecandidate = event => {
41            if (event.candidate) {
42                sendMessage('candidate', roomId, event.candidate);
43            }
44        };
45
46        // Handle remote stream
47        peerConnection.ontrack = event => {
48            const remoteStream = event.streams[0];
49            addParticipantStream(event.streams[0], event.streams[0].id);
50        };
51
52        // Get user media
53        localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
54        localStream.getTracks().forEach(track => peerConnection.addTrack(track, localStream));
55        addParticipantStream(localStream, 'local');
56
57        // Create offer
58        const offer = await peerConnection.createOffer();
59        await peerConnection.setLocalDescription(offer);
60
61        // Send offer to the server
62        sendMessage('offer', roomId, offer);
63
64        // Listen for answer from the server
65        socket.on('answer', async (message) => {
66            if (message.roomId === roomId) {
67                const remoteDesc = new RTCSessionDescription(message.sdp);
68                await peerConnection.setRemoteDescription(remoteDesc);
69            }
70        });
71
72        // Listen for candidate from the server
73        socket.on('candidate', async (message) => {
74            if (message.roomId === roomId) {
75                const candidate = new RTCIceCandidate(message.candidate);
76                await peerConnection.addIceCandidate(candidate);
77            }
78        });
79
80        // Listen for new participants
81        socket.on('new-participant', (message) => {
82            if (message.roomId === roomId) {
83                // Handle new participant joining
84                // This could involve setting up a new peer connection
85            }
86        });
87
88        // Listen for participant leaving
89        socket.on('participant-left', (message) => {
90            if (message.roomId === roomId && participants[message.participantId]) {
91                removeParticipantStream(message.participantId);
92            }
93        });
94    }
95
96    // Function to send messages to the server
97    function sendMessage(type, roomId, data) {
98        const message = { type, roomId, data };
99        socket.emit(type, message);
100    }
101
102    // Function to add participant's stream to the view
103    function addParticipantStream(stream, participantId) {
104        const participantVideo = document.createElement('video');
105        participantVideo.srcObject = stream;
106        participantVideo.autoplay = true;
107        participantVideo.playsInline = true;
108        participantVideo.id = participantId;
109        participantVideo.classList.add('participant-video');
110        document.querySelector('.participant-view').appendChild(participantVideo);
111        participants[participantId] = participantVideo;
112    }
113
114    // Function to remove participant's stream from the view
115    function removeParticipantStream(participantId) {
116        const participantVideo = document.getElementById(participantId);
117        if (participantVideo) {
118            participantVideo.srcObject.getTracks().forEach(track => track.stop());
119            participantVideo.remove();
120            delete participants[participantId];
121        }
122    }
123
124    // Function to toggle audio
125    function toggleAudio() {
126        const audioTracks = localStream.getAudioTracks();
127        if (audioTracks.length === 0) {
128            console.log('No audio track found');
129            return;
130        }
131
132        for (const track of audioTracks) {
133            track.enabled = !track.enabled;
134        }
135    }
136
137    // Function to toggle video
138    function toggleVideo() {
139        const videoTracks = localStream.getVideoTracks();
140        if (videoTracks.length === 0) {
141            console.log('No video track found');
142            return;
143        }
144
145        for (const track of videoTracks) {
146            track.enabled = !track.enabled;
147        }
148    }
149
150    // Function to leave the room
151    function leaveRoom() {
152        document.querySelector('.join-screen').classList.add('active');
153        document.querySelector('.control-panel').classList.remove('active');
154        document.querySelector('.participant-view').classList.remove('active');
155
156        // Close the peer connection and stop local tracks
157        if (peerConnection) {
158            peerConnection.close();
159            peerConnection = null;
160        }
161
162        if (localStream) {
163            localStream.getTracks().forEach(track => track.stop());
164            localStream = null;
165        }
166
167        // Clean up participant view
168        Object.keys(participants).forEach(participantId => removeParticipantStream(participantId));
169
170        // Notify server about leaving the room
171        sendMessage('leave', roomId, {});
172    }
173
174    // Initialize socket.io client
175    const socket = io.connect('http://localhost:8080');
176</script>

Explanation of the Code

Participants Object

  • An object to store the video elements of participants, keyed by participant ID.

Add Participant Stream Function

  • Creates a video element for the participant's stream.
  • Sets the video element's srcObject to the participant's media stream.
  • Adds the video element to the participant view and stores it in the participants object.

Remove Participant Stream Function

  • Retrieves the video element of the participant using their ID.
  • Stops all tracks of the participant's media stream and removes the video element from the DOM.
  • Deletes the participant's entry from the participants object.

Socket Event Listeners

  • new-participant: Handles new participants joining the room. You may need to set up new peer connections for each participant.
  • participant-left: Removes the video element of a participant who has left the room.
By implementing these features, your SRS WebRTC application will now be able to display and manage multiple participant video streams. This completes the core functionality needed for a basic video conferencing application. In the final part, we will cover running your code and provide a conclusion along with a comprehensive FAQ section.

Step 6: Run Your Code Now

With all the core functionalities implemented, it's time to run your SimpleRealtime Server (SRS) WebRTC application. This final part will guide you through the steps to test your application and ensure everything is working as expected.

Running the Application

[a] Start the SRS Server

Navigate to your SRS directory and start the server.
1   cd /path/to/srs/trunk
2   ./objs/srs -c conf/srs.conf

[b] Start Your Web Server

If you're serving your HTML file through a local web server, ensure it is running. You can use a simple server like http-server or serve through a more advanced setup.
1   http-server -p 8080
If you're using a different method to serve your HTML files, ensure it's correctly configured.

[c] Open Your Application in a Browser

  • Open your web browser and navigate to the local address where your HTML file is being served, such as http://localhost:8080.

[d] Test the Application

  • Enter a room ID in the join screen and click "Join".
  • Test the audio and video controls by muting/unmuting and enabling/disabling video.
  • Join the same room from another browser or device to ensure participant views are correctly displayed and managed.
  • Use the "Leave Room" button to verify that leaving the room works as expected and cleans up resources.

Conclusion

In this tutorial, we've walked through the process of setting up a SimpleRealtime Server (SRS) WebRTC application from scratch. We've covered configuring the server, building the HTML structure, implementing the join screen, adding controls, and managing participant views. By following these steps, you've created a basic yet functional video conferencing application.

Want to level-up your learning? Subscribe now

Subscribe to our newsletter for more tech based insights

FAQ