Question

In: Computer Science

Create a simple video conferencing web app (P2P or WebRTC). Needs to handle more than one-on-one...

Create a simple video conferencing web app (P2P or WebRTC).

Needs to handle more than one-on-one communication and should have option to screen share as well.

(prefer not to use sockets if possible. But if needed then that works too)

Solutions

Expert Solution

Dependencies

We’ll be using the following dependencies to build our project:

  • SimpleWebRTC — the WebRTC library
  • Semantic UI CSS — an elegant CSS framework
  • jQuery — used for selecting elements on the page and event handling.
  • Handlebars — a JavaScript templating library, which we’ll use to generate HTML for the messages
  • Express — NodeJS server.

Project Setup

Go to your workspace and create a folder simplewebrtc-messenger. Open the folder in VSCode or your favorite editor and create the following files and folder structure:

simplewebrtc-messenger
├── public
│   ├── images
│   │   └── image.png
│   ├── index.html
│   └── js
│       └── app.js
├── README.md
└── server.js

Or, if you prefer, do the same via the command line:

mkdir -p simplewebrtc-messenger/public/{images,js}
cd simplewebrtc-messenger
touch public/js/app.js public/index.html .gitignore README.md server.js

Open README.md and copy the following content:

# Simple WebRTC Messenger

A tutorial on building a WebRTC video chat app using SimpleWebRTC.

Add the line node_modules to the .gitignore file if you plan to use a git repository. Generate the package.json file using the following command:

npm init -y

You should get the following output:

{
  "name": "simplewebrtc-messenger",
  "version": "1.0.0",
  "description": "A tutorial on building a WebRTC video chat app using SimpleWebRTC.",
  "main": "server.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

Now let’s install our dependencies:

npm install express handlebars jquery semantic-ui-css simplewebrtc

As the installation proceeds, copy this code to server.js:

const express = require('express');

const app = express();
const port = 3000;

// Set public folder as root
app.use(express.static('public'));

// Provide access to node_modules folder from the client-side
app.use('/scripts', express.static(`${__dirname}/node_modules/`));

// Redirect all traffic to index.html
app.use((req, res) => res.sendFile(`${__dirname}/public/index.html`));

app.listen(port, () => {
  console.info('listening on %d', port);
});

The server code is pretty standard. Just read the comments to understand what’s going on.

Next, let’s set up our public/index.html file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="scripts/semantic-ui-css/semantic.min.css">
  <title>SimpleWebRTC Demo</title>
  <style>
    html { margin-top: 20px; }
    #chat-content { height: 180px;  overflow-y: scroll; }
  </style>
</head>
<body>
  <!-- Main Content -->
  <div class="ui container">
    <h1 class="ui header">Simple WebRTC Messenger</h1>
    <hr>
  </div>

  <!-- Scripts -->
  <script src="scripts/jquery/dist/jquery.min.js"></script>
  <script src="scripts/semantic-ui-css/semantic.min.js"></script>
  <script src="scripts/handlebars/dist/handlebars.min.js "></script>
  <script src="scripts/simplewebrtc/out/simplewebrtc-with-adapter.bundle.js"></script>
  <script src="js/app.js"></script>
</body>
</html>

Next, let’s set up our base client-side JavaScript code. Copy this code to public/js/app.js:

window.addEventListener('load', () => {
  // Put all client-side code here
});

Finally, download this image from our GitHub repository and save it inside the public/images folder.

Now we can run our app:

npm start

Open the URL localhost:3000 in your browser and you should see the following:

Markup

Let’s now work on public/index.html. For the sake of simplicity (especially if you’re already familiar with Handlebars) you can copy the entire markup code from our GitHub repository. Otherwise, let’s go through things step by step. First off, copy this code and place it after the <hr> tag within the ui container div:

<div class="ui two column stackable grid">

  <!-- Chat Section -->
  <div class="ui ten wide column">
    <div class="ui segment">
      <!-- Chat Room Form -->
      <div class="ui form">
        <div class="fields">
          <div class="field">
            <label>User Name</label>
            <input type="text" placeholder="Enter user name" id="username" name="username">
          </div>
          <div class="field">
            <label>Room</label>
            <input type="text" placeholder="Enter room name" id="roomName" name="roomName">
          </div>
        </div>
        <br>
        <div class="ui buttons">
          <div id="create-btn" class="ui submit orange button">Create Room</div>
          <div class="or"></div>
          <div id="join-btn" class="ui submit green button">Join Room</div>
        </div>
      </div>
      <!-- Chat Room Messages -->
      <div id="chat"></div>
    </div>
  </div>
  <!-- End of Chat Section -->

  <!-- Local Camera -->
  <div class="ui six wide column">
    <h4 class="ui center aligned header" style="margin:0;">
      Local Camera
    </h4>
    <img id="local-image" class="ui large image" src="images/image.png">
    <video id="local-video" class="ui large image hidden" autoplay></video>
  </div>

</div>

<!-- Remote Cameras -->
<h3 class="ui center aligned header">Remote Cameras</h3>
<div id="remote-videos" class="ui stackable grid">
  <div class="four wide column">
    <img class="ui centered medium image" src="images/image.png">
  </div>
  <div class="four wide column">
    <img class="ui centered medium image" src="images/image.png">
  </div>
  <div class="four wide column">
    <img class="ui centered medium image" src="images/image.png">
  </div>
  <div class="four wide column">
    <img class="ui centered medium image" src="images/image.png">
  </div>
</div>

Go through the markup code and read the comments to understand what each section is for. Also check out the Semantic UI documentation if you’re unfamiliar with the CSS library. Refresh your browser. You should have the following view:

We’re using a blank image as a placeholder to indicate where the camera location will stream to on the web page. Take note that this app will be able to support multiple remote connections, provided your internet bandwidth can handle it.

Templates

Now let’s add the three Handlebar templates that will make our web page interactive.

Place the following markup right after the ui container div (although the location doesn’t really matter). We’ll start off with the chat container, which simply is made up of:

  • Room ID
  • empty chat messages container (to be populated later via JavaScript)
  • input for posting messages.
<!-- Chat Template -->
<script id="chat-template" type="text/x-handlebars-template">
  <h3 class="ui orange header">Room ID -> <strong>{{ room }}</strong></h3>
  <hr>
  <div id="chat-content" class="ui feed"> </div>
  <hr>
  <div class="ui form">
    <div class="ui field">
      <label>Post Message</label>
      <textarea id="post-message" name="post-message" rows="1"></textarea>
    </div>
    <div id="post-btn" class="ui primary submit button">Send</div>
  </div>
</script>

Next, add the following template, which will be used to display user chat messages:

<!-- Chat Content Template -->
<script id="chat-content-template" type="text/x-handlebars-template">
  {{#each messages}}
    <div class="event">
      <div class="label">
        <i class="icon blue user"></i>
      </div>
      <div class="content">
        <div class="summary">
          <a href="#"> {{ username }}</a> posted on
          <div class="date">
            {{ postedOn }}
          </div>
        </div>
        <div class="extra text">
          {{ message }}
        </div>
      </div>
    </div>
  {{/each}}
</script>

Finally, add the following template, which will be used to display streams from a remote camera:

<!-- Remote Video Template -->
<script id="remote-video-template" type="text/x-handlebars-template">
  <div id="{{ id }}" class="four wide column"></div>
</script>

The markup code is hopefully pretty self-explanatory, so let’s move on to writing the client-side JavaScript code for our application.

Main App Script

Open the file public/js/app.js and add this code:

// Chat platform
const chatTemplate = Handlebars.compile($('#chat-template').html());
const chatContentTemplate = Handlebars.compile($('#chat-content-template').html());
const chatEl = $('#chat');
const formEl = $('.form');
const messages = [];
let username;

// Local Video
const localImageEl = $('#local-image');
const localVideoEl = $('#local-video');

// Remote Videos
const remoteVideoTemplate = Handlebars.compile($('#remote-video-template').html());
const remoteVideosEl = $('#remote-videos');
let remoteVideosCount = 0;

// Add validation rules to Create/Join Room Form
formEl.form({
  fields: {
    roomName: 'empty',
    username: 'empty',
  },
});

Here we’re initializing several elements that we plan to manipulate. We’ve also added validation rules to the form so that a user can’t leave either of the fields blank.

Next, let’s initialize our WebRTC code:

// create our WebRTC connection
const webrtc = new SimpleWebRTC({
  // the id/element dom element that will hold "our" video
  localVideoEl: 'local-video',
  // the id/element dom element that will hold remote videos
  remoteVideosEl: 'remote-videos',
  // immediately ask for camera access
  autoRequestMedia: true,
});

// We got access to local camera
webrtc.on('localStream', () => {
  localImageEl.hide();
  localVideoEl.show();
});

Now you know why it’s called SimpleWebRTC. That’s all we need to do to initialize our WebRTC code. Noticed we haven’t even specified any ICE servers or STUN servers. It just works. However, you can use other TURN services such as Xirsys. You’ll need to set up a local SignalMaster server for handling WebRTC signaling.

Let’s do a quick refresh of the web page to confirm the new code is working:

The page should request access to your camera and microphone. Just click Accept and you should get the above view.

Chat Room Script

Now let’s make the form functional. We need to write logic for creating and joining a room. In addition, we need to write additional logic for displaying the chat room. We’ll use the chat-room-template for this. Let’s start by attaching click handlers to the form’s buttons:

$('.submit').on('click', (event) => {
  if (!formEl.form('is valid')) {
    return false;
  }
  username = $('#username').val();
  const roomName = $('#roomName').val().toLowerCase();
  if (event.target.id === 'create-btn') {
    createRoom(roomName);
  } else {
    joinRoom(roomName);
  }
  return false;
});

Next, we need to declare the createRoom and joinRoom functions. Place the following code before the click handler code:

// Register new Chat Room
const createRoom = (roomName) => {
  console.info(`Creating new room: ${roomName}`);
  webrtc.createRoom(roomName, (err, name) => {
    showChatRoom(name);
    postMessage(`${username} created chatroom`);
  });
};

// Join existing Chat Room
const joinRoom = (roomName) => {
  console.log(`Joining Room: ${roomName}`);
  webrtc.joinRoom(roomName);
  showChatRoom(roomName);
  postMessage(`${username} joined chatroom`);
};

Creating or joining a room is a simple as that: just use SimpleWebRTC’s createRoom and joinRoom methods.

You may also have noticed that we have showChatroom and postMessage functions that we haven’t defined yet. Let’s do that now by inserting the following code before the calling code:

// Post Local Message
const postMessage = (message) => {
  const chatMessage = {
    username,
    message,
    postedOn: new Date().toLocaleString('en-GB'),
  };
  // Send to all peers
  webrtc.sendToAll('chat', chatMessage);
  // Update messages locally
  messages.push(chatMessage);
  $('#post-message').val('');
  updateChatMessages();
};

// Display Chat Interface
const showChatRoom = (room) => {
  // Hide form
  formEl.hide();
  const html = chatTemplate({ room });
  chatEl.html(html);
  const postForm = $('form');
  // Post Message Validation Rules
  postForm.form({
    message: 'empty',
  });
  $('#post-btn').on('click', () => {
    const message = $('#post-message').val();
    postMessage(message);
  });
  $('#post-message').on('keyup', (event) => {
    if (event.keyCode === 13) {
      const message = $('#post-message').val();
      postMessage(message);
    }
  });
};

Take some time to go through the code to understand the logic. You’ll soon come across another function we haven’t declared, updateChatMessages. Let’s add it now:

// Update Chat Messages
const updateChatMessages = () => {
  const html = chatContentTemplate({ messages });
  const chatContentEl = $('#chat-content');
  chatContentEl.html(html);
  // automatically scroll downwards
  const scrollHeight = chatContentEl.prop('scrollHeight');
  chatContentEl.animate({ scrollTop: scrollHeight }, 'slow');
};

The purpose of this function is simply to update the Chat UI with new messages. We need one more function that accepts messages from remote users. Add the following function to app.js:

// Receive message from remote user
webrtc.connection.on('message', (data) => {
  if (data.type === 'chat') {
    const message = data.payload;
    messages.push(message);
    updateChatMessages();
  }
});

That’s all the logic we need to have the chat room work. Refresh the page and log in:

Hit the Create Room button. You’ll be taken to this view. Post some messages to confirm the chat room is working.

Once you’ve confirmed it’s working, move on to the next task.

Remote Video Camera

As mentioned earlier, SimpleWebRTC supports multiple peers. Here’s the code for adding remote video streams when a new user joins a room:

// Remote video was added
webrtc.on('videoAdded', (video, peer) => {
  const id = webrtc.getDomId(peer);
  const html = remoteVideoTemplate({ id });
  if (remoteVideosCount === 0) {
    remoteVideosEl.html(html);
  } else {
    remoteVideosEl.append(html);
  }
  $(`#${id}`).html(video);
  $(`#${id} video`).addClass('ui image medium'); // Make video element responsive
  remoteVideosCount += 1;
});

That’s it.

Deployment

This method that we’re about to perform is one of the easiest ways to deploy a NodeJS app. All we have to do is first register an account with now.sh.

Simply choose the free plan. You’ll need to provide your email address. You’ll also need to verify your email address for your account to activate. Next, install now CLI tool on your system:

npm install -g now

After installation is complete, you can deploy the application. Simply execute the following command at the root of your project folder:

now --public

If this is the first time you’re running the command, you’ll be asked to enter your email address. You’ll then receive an email that you’ll need in order to verify your login. After verification has been done, you’ll need to execute the command now --public again. After a few seconds, your app will be up and running at a specified URL that will be printed out on the terminal.

If you’re using the VSCode integrated terminal, simply press ALT and click to open the URL in your browser.

You’ll need to allow the page to access your camera and microphone. Next create a room just like before. After you’ve signed in, you need to access another device — such as another laptop or smartphone with a front-facing camera. You could also ask a friend with an internet connection to help you with this. Simply access the same URL, and enter a new username and the same room name. The remote user will have to hit the Join Room button. Within a few seconds, both devices should be connected to the chat room. If a device doesn’t have a camera, that’s okay, as the chat functionality will still work.


Related Solutions

Create a simple video conferencing web app (P2P or WebRTC). Needs to handle more than one-on-one...
Create a simple video conferencing web app (P2P or WebRTC). Needs to handle more than one-on-one communication and should have option to screen share as well. (prefer not to use sockets if possible. But if needed then that works too)
Create a simple video conferencing web app (P2P or WebRTC). Needs to handle more than one-on-one...
Create a simple video conferencing web app (P2P or WebRTC). Needs to handle more than one-on-one communication and should have option to screen share as well. (prefer not to use sockets if possible. But if needed then that works too)
Create a simple video conferencing web app (P2P or WebRTC). Needs to handle more than one-on-one...
Create a simple video conferencing web app (P2P or WebRTC). Needs to handle more than one-on-one communication and should have option to screen share as well. (prefer not to use sockets if possible. But if needed then that works too)
Create a simple python app that allows the user to create a roster of students and...
Create a simple python app that allows the user to create a roster of students and their grade on CUS-1166. Moreover the app needs to calculate the average grade of students added to the roster. 1-To begin with, create a new file n the same working folder as part A (i.e. cus1166_lab1) and name it app.py. Moreover, create a subfolder and name it mymodules. 2-Within mymodules create the files __init__.py , models.py , math_utils.py . 3-In the models.py file define...
Summary Develop a Spring Boot Web App which allows user to create a list of the...
Summary Develop a Spring Boot Web App which allows user to create a list of the destinations they have visited during vacations. Your application should allow user to search for a destination based on year of visit. Form View The initial page should have links to either search for a destination , add a new destination, display a list of all destinations that they have visited. The search destination page will have a text field to search by year. The...
Suppose a video conferencing provider, Soom, is planning to pay its next dividend in one year....
Suppose a video conferencing provider, Soom, is planning to pay its next dividend in one year. The dividend will be $1 per share, and Soom expects dividends to grow at 10% per year over the next three years. After that, they expect dividends to grow at 2% per year indefinitely (that is, forever). Suppose that investors have the alternative of buying corporate bonds that will earn 5% per year. Assume the company will next pay a dividend of $1 per...
Find one video or cartoon example of one of these fallacies on the Web. The example...
Find one video or cartoon example of one of these fallacies on the Web. The example must take the form of a video/cartoon (check out YouTube, for instance, or search for the fallacies in cartoon banks). The Cartoon Network, Family Guy, Stephen Colbert/Jon Stewart, or even just plain old cartoon websites are good places to look. Once you've located the example, paste in a link to the file (embed it) in your Discussion Board thread. Finally, explain why you think...
Part 1; a simple slide show: Create a Web document called l5p1.html that displays one of...
Part 1; a simple slide show: Create a Web document called l5p1.html that displays one of the images from lab 2 in approximately the center of the page; centering need not be exact. Below the image, place three button elements labeled with the names of items from lab 2. (One of them must be the name of the item you chose to display.) Pressing a button should change the displayed image to that of the item named on the button....
Needs to be written in C# Console App! Create a base class BankAccount. Decide what characteristics...
Needs to be written in C# Console App! Create a base class BankAccount. Decide what characteristics are common for checking and saving accounts and include these characteristics in the base class. Define derived classes for checking and savings. In your design , do not allow the banking base account to be instantiated --only the checking and saving classes. Use a Program class to test your design.
There can be more than one correct answer! Thanks 7. How is glucose reabsorbed? A. simple...
There can be more than one correct answer! Thanks 7. How is glucose reabsorbed? A. simple diffusion B. co-transported with Na+ C. counter-transported with H+ D. actively transported E. none of the above 8. The mechanism for the reabsorption of Na+ is similar in the A. proximal and distal convoluted tubules B. loop of Henle and distal convoluted tubules C. proximal convoluted tubules and cortical collecting ducts D. cortical and medullary collecting ducts E. Loop of Henle and proximal convoluted...
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT