<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Metered Video</title>
    <description>The latest articles on Forem by Metered Video (@metered-video).</description>
    <link>https://forem.com/metered-video</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F5444%2F62c58ba0-46aa-433f-a1a9-fdf43d392111.png</url>
      <title>Forem: Metered Video</title>
      <link>https://forem.com/metered-video</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/metered-video"/>
    <language>en</language>
    <item>
      <title>What is a TURN server? (Traversal Using Relays around NAT)</title>
      <dc:creator>alakkadshaw</dc:creator>
      <pubDate>Tue, 08 Jul 2025 18:22:50 +0000</pubDate>
      <link>https://forem.com/metered-video/what-is-a-turn-server-traversal-using-relays-around-nat-28fg</link>
      <guid>https://forem.com/metered-video/what-is-a-turn-server-traversal-using-relays-around-nat-28fg</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published on the Metered Blog: &lt;a href="https://www.metered.ca/blog/what-is-a-turn-server-3/" rel="noopener noreferrer"&gt;What is a TURN server?&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is a TURN server?&lt;/strong&gt; A TURN server relays WebRTC traffic around NATs and firewalls, keeping voice, video and data flowing without drops.&lt;/p&gt;

&lt;h2&gt;
  
  
  The NAT &amp;amp; Firewall problem
&lt;/h2&gt;

&lt;p&gt;Network address translation (NAT) lets many devices which are behind it and have private IP addresses share a single public IP address, but&lt;/p&gt;

&lt;p&gt;NAT also &lt;strong&gt;changes source ports and blocks the unsolicited inbound traffic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When two devices are each behind their own firewall and NAT, they have knowledge of only their &lt;strong&gt;own private IP address&lt;/strong&gt; and any inbound packets from any other outside peer are dropped.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Analogy: imagine there are two offices each at the end of a busy hallway. Alice can walk out to send mail and access the hallway (internet) and Bob can do the same. But each of them cannot open other's office door from the outside. A relay receptionist (TURN servers) in the hallway is the only way to pass envelopes back and forth&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8rfybwo7on7p9xrdhe01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8rfybwo7on7p9xrdhe01.png" alt="Failed to connect peers through NAT" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How WebRTC and ICE try to connect
&lt;/h2&gt;

&lt;p&gt;Interactive Connectivity establishment or ICE is WebRTC's 3 step way to piercing those locked doors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Host Candidates- each peer first tests its own local (that is private) IPs. This way if both the devices are on a local network using LAN or VPN they can easily connect&lt;/li&gt;
&lt;li&gt;STUN Candidate- the peer asks a STUN server for its (device's own) public IP/port number and uses that to connect to another device by sharing this information with the other client. This fails on Symmetric NATs or firewall rules that block inbound UDP traffic&lt;/li&gt;
&lt;li&gt;TURN candidate- Lastly each peer allocates a relay address on a TURN server and all the media is sent through the TURN server, thus guaranteeing connectivity.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example of iceServers array (Metered.ca STUN + TURN)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const pc = new RTCPeerConnection({
  iceServers: [
    {
      urls: [
        "stun:relay.metered.ca:80",
        "stun:relay.metered.ca:443"
      ]
    },
    {
      urls: [
        "turn:relay.metered.ca:80",
        "turn:relay.metered.ca:443",
        "turn:relay.metered.ca:443?transport=tcp"
      ],
      username: "YOUR_TURN_USERNAME",
      credential: "YOUR_TURN_PASSWORD"
    }
  ]
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvy6qas68ygcbs3djz8dn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvy6qas68ygcbs3djz8dn.png" alt="How TURN servers work" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TURN vs STUN at a Glance
&lt;/h2&gt;

&lt;p&gt;The success figures here are used from the Chrome UMA metrics and other production grade studies that show roughly one in five WebRTC calls must fall back on TURN&lt;/p&gt;

&lt;p&gt;let us consider the differences between STUN and TURN using a table&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Criterion&lt;/th&gt;
&lt;th&gt;STUN Server&lt;/th&gt;
&lt;th&gt;TURN Server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Connectivity&lt;/td&gt;
&lt;td&gt;Works when NAT is less restrictive and allows UDP traversal&lt;/td&gt;
&lt;td&gt;Works 100% of the time through any firewall or NAT type.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bandwidth cost&lt;/td&gt;
&lt;td&gt;none just requires a one of handshake around 1KB (this is because no traffic travels through STUN servers all the traffic travels peer-to-peer)&lt;/td&gt;
&lt;td&gt;High because every data packet travelles through the turn servers. the data is encrypted end-to-end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Success rate&lt;/td&gt;
&lt;td&gt;fails 20-25% of the time. ICE first tries STUN and then as a fallback uses TURN&lt;/td&gt;
&lt;td&gt;Always works&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How TURN server works (Step by Step)
&lt;/h2&gt;

&lt;p&gt;Here is how the TURN server works step by step&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Allocation request:&lt;/strong&gt; Here each peer sends a ALLOCATE request to the TURN server over UDP/TCP/TLS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relay address issued:&lt;/strong&gt; The TURN server replies with relay transport address (public IP + port) that other peers can send data to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Packets flow A -&amp;gt; TURN -&amp;gt; B and back:&lt;/strong&gt; Both the peers send the data to the relay and the TURN server forwards the data in each other's way&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection is kept alive:&lt;/strong&gt; Peers periodically send the &lt;code&gt;REFRESH&lt;/code&gt; or ChannelBind messages so that the allocation does not expire.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlp0p26mg7sj7f6l40y6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvlp0p26mg7sj7f6l40y6.png" alt="How TURN Server relays traffic" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  When do you actually need TURN?
&lt;/h2&gt;

&lt;p&gt;Even through ICE first tries direct or STUN assisted paths, roughly 20 percent traffic goes through TURN servers&lt;/p&gt;

&lt;p&gt;Two large scale studies from reputed sources indicate that one in five webrtc calls require a TURN server to connect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why these failures happen
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;What breaks the direct/STUN path&lt;/th&gt;
&lt;th&gt;Real-world examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise Wi-Fi / corporate firewalls&lt;/td&gt;
&lt;td&gt;Strict firewall policies block UDP and port ranges only allows 443/80&lt;/td&gt;
&lt;td&gt;Office or corporate networks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Carrier Grade NAT (CG-NAT)&lt;/td&gt;
&lt;td&gt;The mobile ISP terminates thousands of devices behind a single public IP address and uses the Symmetric NAT rules that reject unsolicited inbound traffic.&lt;/td&gt;
&lt;td&gt;cellular 4G/5G networks some small fiber providers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public hotspots and hotels&lt;/td&gt;
&lt;td&gt;additional captive-portal proxies and other rate-limiters rewrite or throttle UDP and does not allow connections&lt;/td&gt;
&lt;td&gt;Airports or hotels even coffee shop wifi&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gov networks&lt;/td&gt;
&lt;td&gt;With Deep packet inspections blocks unknown UDP flows&lt;/td&gt;
&lt;td&gt;regions with strict internet controls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Free &amp;amp; Hosted Options for TURN server
&lt;/h2&gt;

&lt;p&gt;In this section we are looking at some of the free as well as paid options for getting a TURN server for you application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsrl5t2m0zyodpb5d7iwf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsrl5t2m0zyodpb5d7iwf.png" alt="Open Relay Project" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Open Relay Project
&lt;/h3&gt;

&lt;p&gt;OpenRelayProject is a free turn server for use. OpenRelay provides 20 GB of free monthly turn server usage. It is distributed all over the world, so you get low latency and high throughput&lt;/p&gt;

&lt;p&gt;And it is a good option if you need a free turn server&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdvugr3806yu5gv435ov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdvugr3806yu5gv435ov.png" alt="Metered TURN Servers" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Metered.ca TURN servers
&lt;/h2&gt;

&lt;p&gt;Metered is a Canadian corporation that offers TURN server service, that is distributed all over the world and offers high throughput, low latency turn servers&lt;/p&gt;

&lt;p&gt;Metered has features like 99.999% Uptime and 100 plus edge pops etc&lt;/p&gt;

&lt;p&gt;Metered also has excellent support features like 24X7 emergency support number, tech support by engineers etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  CoTURN
&lt;/h3&gt;

&lt;p&gt;CoTURN is a free and open source turn server, that you have can install in your VM in any cloud and get the TURN server for free.&lt;/p&gt;

&lt;p&gt;But remember there are costs associated with running you own turn server like VM costs and bandwidth costs.&lt;/p&gt;

&lt;p&gt;Also, costs associated with running and maintaining the turn server code including updating and security etc.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;What makes it a good choice&lt;/th&gt;
&lt;th&gt;Notable specs and perks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open Relay Project&lt;/td&gt;
&lt;td&gt;Truly free turn server service&lt;/td&gt;
&lt;td&gt;20 GB monthly Cap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metered TURN&lt;/td&gt;
&lt;td&gt;10X higher throughput, automatic geo-routing along with geo-fencing capabilities, powerful APIs&lt;/td&gt;
&lt;td&gt;UDP/TCP/TLS/DTLS support, usage stats, AI-friendly low latency network with awesome support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CoTURN&lt;/td&gt;
&lt;td&gt;Open Source TURN server software that you can run on any cloud provider AWS, AZURE or Google&lt;/td&gt;
&lt;td&gt;free but you need to pay for cloud compute and bandwidth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Self Hosting the TURN server with CoTURN
&lt;/h2&gt;

&lt;p&gt;In this section we are going to learn how to set up a functional TURN server using CoTURN in any VM running Ubuntu/Debian&lt;/p&gt;

&lt;p&gt;Here is an easy 7 step formula you can use to set it up&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 Spin a cloud VM
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a small VPS (1 vCPU/1GB RAM is good enough) with a public Ipv4 address, generally you get the IP address free with the VM&lt;/li&gt;
&lt;li&gt;In the provider that could be AWS, GCP or Azure go to the firewall or security-group and all&lt;/li&gt;
&lt;li&gt;3478/udp – the standard STUN/TURN port.&lt;/li&gt;
&lt;li&gt;3478/tcp – TURN over TCP fallback.&lt;/li&gt;
&lt;li&gt;5349/tcp – TURN over TLS (recommended for networks that allow only TLS-443-style traffic).&lt;/li&gt;
&lt;li&gt;49152-65535/udp – optional high-range UDP relay ports for maximum throughput.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2 Install the CoTURN package
&lt;/h2&gt;

&lt;p&gt;The CoTURN is free and open source so you can easily install it using apt like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo apt-get update
sudo apt-get install coturn -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This package also installs the &lt;code&gt;systemd&lt;/code&gt; service unit and helper tools such as &lt;code&gt;turnutiles_uclient&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 Create a minimal configuration
&lt;/h3&gt;

&lt;p&gt;open the configuration file like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/turnserver.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;paste this essential configuration then save and exit&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Ports
listening-port=3478
tls-listening-port=5349

# Your DNS domain (or anything you want to set here)
realm=myapp.example.com

# If your VM has both a private and public IP (EC2, GCP, Azure etc.)
external-ip=232.34.234.45

# Simple long-term credential for a quick test
user=lamicall:VeryStrongPassword
lt-cred-mech

# Helpful extras
fingerprint              # adds fingerprints your STUN messages
#cert=/etc/ssl/certs/fullchain.pem
#pkey=/etc/ssl/private/privkey.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Optional:&lt;/strong&gt;For greater security you can also replace the static &lt;code&gt;user=&lt;/code&gt; line with &lt;code&gt;static-auth-secret=&amp;lt;RANDOM_SECRET&amp;gt;&lt;/code&gt; so that you can generate short lived credentials instead of hardcoding your passwords&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: Enable and start the service
&lt;/h3&gt;

&lt;p&gt;Set the Ubuntu or Debian to start the service at boot and then start it right now&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "TURNSERVER_ENABLED=1" | sudo tee -a /etc/default/coturn
sudo systemctl enable --now coturn
sudo systemctl status coturn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;after this you will get something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Listening on:(UDP) 3478
Listening on:(TCP) 5349
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can also check manually if the turn server is running by&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ctrl-c to stop the logs
sudo journalctl -u coturn -f
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;look for lines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Listening on:(TLS) 5349
Total General servers: 1
SQLite DB connection success: /var/lib/turn/turndb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if you see &lt;code&gt;fatal1 or&lt;/code&gt;Cannot bind` then usually firewall or something else is blocking the ports 3478/5349&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 Smoke-test using the TURN server testing page
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;go to &lt;a href="https://www.metered.ca/turn-server-testing" rel="noopener noreferrer"&gt;https://www.metered.ca/turn-server-testing&lt;/a&gt; in your chrome or safari&lt;/li&gt;
&lt;li&gt;Enter&lt;/li&gt;
&lt;li&gt;TURN url: turn::3478 (or turns::5349 if you enabled TLS)&lt;/li&gt;
&lt;li&gt;Username: lamicall&lt;/li&gt;
&lt;li&gt;Password: VeryStrongPassword&lt;/li&gt;
&lt;li&gt;Click on the Add server then Launch server test&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TURN Server FAQs
&lt;/h3&gt;

&lt;p&gt;Here we look at some of the commonly asked questions with regards to the TURN server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do all WebRTC calls use TURN?
&lt;/h3&gt;

&lt;p&gt;No. Chrome UMA telemetry data and other production grade studies show that only about 20-25% WebRTC data sessions go through a TURN server&lt;/p&gt;

&lt;p&gt;For 75-80% of the WebRTC sessions a direct connection is successful using STUN so no media is sent through the TURN servers.&lt;/p&gt;

&lt;p&gt;Why is there a gap like this? This is because by default TURN server is only used when all other ways to connecting to the peer are unsuccessful, this is to save costs and is done by the ICE protocol automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is TURN the same as a signalling server?
&lt;/h3&gt;

&lt;p&gt;No, turn servers are different from signalling servers. here is a table that illustrates the differences&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;TURN Server&lt;/th&gt;
&lt;th&gt;Signalling Server&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Role&lt;/td&gt;
&lt;td&gt;Relays data when peer-to-peer connections fail&lt;/td&gt;
&lt;td&gt;Relays SDP/ICE messages so that the peer devices can negotiate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standardized?&lt;/td&gt;
&lt;td&gt;yes (IETF RFC 8656)&lt;/td&gt;
&lt;td&gt;NO- WebRTC deliberately leaves signalling server non-standardized. This means you can use anything like Websocket REST others for signalling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In the media path?&lt;/td&gt;
&lt;td&gt;yes the media is relayed through the TURN server&lt;/td&gt;
&lt;td&gt;NO Once ICE have negotiated, there is no need for signalling server. unless you want to re-negotiate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resource Load&lt;/td&gt;
&lt;td&gt;High CPU and Bandwidth requirements&lt;/td&gt;
&lt;td&gt;low mostly small JSON or text messages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;NAT and Firewall (The Need for TURN Servers)&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Most Consumer and enterprise networks are behind NAT and firewall that blocks inbound traffic&lt;/li&gt;
&lt;li&gt;These barriers does not allow peer-to-peer media flows unless TURN servers are used&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;ICE's Three layered Playbook&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Host candidates try a private LAN connection first&lt;/li&gt;
&lt;li&gt;Then ICE uses STUN to find out the private IP/port number of the devices in order to establish a direct connection with the peer. This works 80% of the time&lt;/li&gt;
&lt;li&gt;Lastly the ICE fallbacks to TURN which relays the data through its servers.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;STUN vs TURN in one sentence&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;STUN is a lightweight option that just tells the devices which are behind NAT what their public IP and port number is&lt;/li&gt;
&lt;li&gt;TURN relays the traffic through its servers to the devices across the internet&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Deployment choices&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Hosted TURN services such as Open Relay and Metered let you paste credentials and go&lt;/li&gt;
&lt;li&gt;You can also self host with CoTURN that gives you full control but you need to bear costs regarding VM and bandwidth plus do maintenance security and updates.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webrtc</category>
      <category>networking</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Jitsi TURN STUN Server Setup</title>
      <dc:creator>alakkadshaw</dc:creator>
      <pubDate>Mon, 19 Dec 2022 15:10:51 +0000</pubDate>
      <link>https://forem.com/metered-video/jitsi-turn-stun-server-setup-di3</link>
      <guid>https://forem.com/metered-video/jitsi-turn-stun-server-setup-di3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was original published at Metered Video Blog: &lt;a href="https://www.metered.ca/blog/jitsi-turn-server/" rel="noopener noreferrer"&gt;Jitsi TURN STUN Server Setup&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In this guide we will go through the process of setting up optimal TURN Server configuration in Jitsi meet.&lt;/p&gt;

&lt;p&gt;We will learn which config file to update, to add the TURN Server configuration and I will also show you from where you can obtain the TURN Server credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Jitsi Meet?
&lt;/h2&gt;

&lt;p&gt;If you are reading this Guide then you must be familiar with Jitsi meet. Jitsi Meet is an open-source video conferencing solution, that allows the participants to have a video call with each other.&lt;/p&gt;

&lt;p&gt;Jitsi meet uses WebRTC protocol for communication  between the participants, and Jitsi Meet first tries to establish first a peer to peer connect between the participants.&lt;/p&gt;

&lt;p&gt;Peer to Peer connection means, all the participants in the meeting will send the audio+video feed directly to each other.&lt;/p&gt;

&lt;p&gt;Typically web services work in a Client-Server manner, where the client sends all the traffic to the server, and the server forwards the traffic to other clients.&lt;/p&gt;

&lt;p&gt;But in a peer-to-peer connection the clients directly send the data to other clients without requiring a server in the middle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a TURN Server?
&lt;/h2&gt;

&lt;p&gt;So we have understood what is Jitsi Meet, why it first tries to establish a peer-to-peer connection.&lt;/p&gt;

&lt;p&gt;But often times the clients are not able to connect directly with each other to transmit the data, because the clients are behind a restrictive firewall.&lt;/p&gt;

&lt;p&gt;In such scenarios, a TURN Server is required. The TURN server is a large server that has a Public IP address.&lt;/p&gt;

&lt;p&gt;The Public IP address allows the TURN server to be reached from anywhere in the internet.&lt;/p&gt;

&lt;p&gt;The clients who fails to establish a direct connection with each other, establishes the connection with the TURN server.&lt;/p&gt;

&lt;p&gt;TURN Server then relays the traffic from one client to another.&lt;/p&gt;

&lt;p&gt;All the traffic that passes through the TURN server is end-to-end encrypted by the clients using DTLS Encryption and the TURN server has no way to view the data that is passing through it.&lt;/p&gt;

&lt;p&gt;TURN Server just relays the data from the client who are not able to establish a direct connection with each other.&lt;/p&gt;

&lt;p&gt;It is estimated that 40% of traffic is relayed through the TURN server, hence TURN server is necessary for any successful Jitsi Meet Deployment.&lt;/p&gt;

&lt;p&gt;Relaying all the traffic consumes a lot of CPU and Bandwidth and hence the TURN servers are large servers and are expensive to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a STUN Server?
&lt;/h2&gt;

&lt;p&gt;As we have learned that Jitsi first tries to establish a peer-to-peer connection.&lt;/p&gt;

&lt;p&gt;But most of the devices connected to the internet are behind a NAT.&lt;/p&gt;

&lt;p&gt;NAT allows muliple devices to share a single Public IP address.&lt;/p&gt;

&lt;p&gt;If you have a home internet connection, your internet service provider, gives you a router/modem, that device has one public IP address and all the devices that connect to the router/modem to connect to the internet shares the same public IP.&lt;/p&gt;

&lt;p&gt;To establish a peer-to-peer connection, with a device behind the NAT, STUN server uses variety of techniques and punches a hole through the NAT to directly connect to the device behind the NAT.&lt;/p&gt;

&lt;p&gt;If the device is unsuccessful in bypassing the NAT then it uses the TURN Server to relay the traffic.&lt;/p&gt;

&lt;p&gt;The STUN server do not relay the traffic, they are just used by the devices behind the NAT to determine their Public IP and Port.&lt;/p&gt;

&lt;p&gt;If the devices are able to punch the hole through the NAT they share this info with other peers in the meeting and establish a direct connection with each other.&lt;/p&gt;

&lt;p&gt;The STUN Server are not expensive to run and provided for free by Jitis, Google as well as other providers.&lt;/p&gt;

&lt;h2&gt;
  
  
  TURN &amp;amp; STUN Server Configuration in Jitsi Meet
&lt;/h2&gt;

&lt;p&gt;We have learned about the TURN and STUN servers and why it is important, now lets go through the steps of updating the Jisti Meet with TURN Server credentials.&lt;/p&gt;

&lt;p&gt;We will learn how you can add the credentials of a TURN Server you are selfhosting, and if don't have a TURN server, I will show from where you can obtain the TURN Server credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: SSH into your Jitsi Meet Server
&lt;/h2&gt;

&lt;p&gt;SSH into the server where you have installed Jitsi Meet, the SSH instructions depends upon your hosting provider. If you have installed Jitsi Meet then you must be familiar with how to SSH into the Server :)&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Edit the Jitsi Meet Config
&lt;/h2&gt;

&lt;p&gt;Now we will edit the jisti meet configuration file, the configuration file is located at &lt;code&gt;/etc/jitsi/meet/your-domain-name.com-config.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;your-domain-name.com&amp;gt;&lt;/code&gt; is the domain name that you had provided during the jitsi meeting installation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/jitsi/meet/your-domain-name.com-config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Update the TURN Server configuration
&lt;/h2&gt;

&lt;p&gt;Now scroll down to until you find the line &lt;code&gt;stunServers&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen8vpuh83qhcirt9047f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen8vpuh83qhcirt9047f.png" alt="Jitsi Meeting STUN and TURN Config" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now here we can add our TURN Server configuration. Here by default Jitsi STUN server is provided.  STUN Server is used to establish a peer-to-peer connection, but as we have learned, in many situations peer-to-peer connection cannot be established, hence we need a TURN Server.&lt;/p&gt;

&lt;p&gt;You can add the TURN server in the following manner&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;stunServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;turn:your-turn-server.com:443&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;your-turn-server.com&lt;/em&gt; is &lt;em&gt;NOT&lt;/em&gt; an actual TURN Server URL, and "your-username" and "your-password" are just placeholders.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the image below we have used the credentials for actual TURN Server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6hxggv8an0j2hay38ht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn6hxggv8an0j2hay38ht.png" alt="TURN Server Configuration" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Obtaining TURN Server Credentials
&lt;/h2&gt;

&lt;p&gt;TURN Server consume a lot of CPU and bandwidth hence they are expensive to run.&lt;/p&gt;

&lt;p&gt;We at Metered Video offer free TURN Server Plan which is enough for most users, you can &lt;a href="https://dashboard.metered.ca/signup?tool=turnserver" rel="noopener noreferrer"&gt;visit here to signup for our free plan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also selfhost your TURN server, for instructions regarding selfhosting your turn server, you can refer to our &lt;a href="https://www.metered.ca/blog/coturn/" rel="noopener noreferrer"&gt;coturn guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We will use the OpenRelay TURN Server from Metered Video, if you have not already &lt;a href="https://dashboard.metered.ca/signup?tool=turnserver" rel="noopener noreferrer"&gt;signup for a free account from here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The TURN Server from Metered Video has 99.999% uptime with enterprise grade reliability and is a cost effective solution.&lt;/p&gt;

&lt;p&gt;Once you have signup for a free account go to dashboard-&amp;gt;turn server page click on "Add New Credential" button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxep1hbqxnmwaqftutlsa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxep1hbqxnmwaqftutlsa.png" alt="OpenRelay TURN Server Dashboard" width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click on the "instructions" button&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp9wvapxb6kpblzlq40r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvp9wvapxb6kpblzlq40r.png" alt="TURN Server page showing credentials" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the instructions popup copy everything in between the  square brackets [ ]&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn36d8rt5sof40uvfga2z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn36d8rt5sof40uvfga2z.png" alt="TURN Server Credentials" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then delete everything under the &lt;code&gt;stunServers&lt;/code&gt; array and paste it inside the &lt;code&gt;stunServers&lt;/code&gt; array []&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favrhedm99yen5wzzoakf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Favrhedm99yen5wzzoakf.png" alt="Jitsi Meet TURN Credentials" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now save exit the editor by pressing Cltr+x and y&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 (optional): Force all the traffic through the TURN Server
&lt;/h2&gt;

&lt;p&gt;If you want force the clients to relay all the traffic through the TURN server then you can remove the line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    url: "stun:relay.metered.ca:80"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, why you would want to relay all the traffic through the TURN server?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLDR: To Speed up the time it takes to connect the call.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you relay all the traffic through the TURN server, then the clients will not try to establish a peer-to-peer connection with each other.&lt;/p&gt;

&lt;p&gt;To establish a peer-to-peer connection with each other the clients uses a STUN Server, and uses hole punching technique to gather ICE candidates and determines the open port and public IP address, to learn more about this in detail read &lt;a href="https://www.metered.ca/tools/openrelay/stun-servers-and-friends" rel="noopener noreferrer"&gt;STUN Server and NAT Traversal.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Gathering candidates and determining the open port and public IP takes time.&lt;/p&gt;

&lt;p&gt;Then it determines whether it is a able to establish a peer-to-peer connection, if establishing a peer-to-peer connection fails, it fallbacks to the TURN server.&lt;/p&gt;

&lt;p&gt;All this process increases the time it takes for the user to connect to the meeting.&lt;/p&gt;

&lt;p&gt;To speed up the time it takes for the user to connect to the meeting, we can eliminate this process altogether.&lt;/p&gt;

&lt;p&gt;If we remove the STUN Server URL and only provide the TURN Server URLs then all the connections will be relayed through the TURN server by default.&lt;/p&gt;

&lt;p&gt;The user will be connected to each other through the TURN server.&lt;/p&gt;

&lt;p&gt;The drawback of this is that as all the connection will be relayed through the TURN server, your TURN Server bandwidth consumption will increase.&lt;/p&gt;

&lt;p&gt;If you are selfhosting your TURN server then it might add some latency to the call if the TURN server is far away from the users in the meeting.&lt;/p&gt;

&lt;p&gt;For e.g If there are 2 users in a call and both of them are in London, and the TURN Server is in US then it would add some latency to the call as all the connection would have to be routed through US.&lt;/p&gt;

&lt;p&gt;If one user is in London and another is in US, and the TURN Server is in U.S, then it doesn't make much difference as the video stream would have to reach U.S to connect to the user in U.S.&lt;/p&gt;

&lt;p&gt;If you are using Metered TURN Server, then we automatically route the traffic through the TURN Server nearest to the user.&lt;/p&gt;

&lt;p&gt;Now lets get back to our configuration, now if we reopen the Jitsi meet configuration file, and scroll down the the &lt;code&gt;stunServers&lt;/code&gt; array and remove.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    url: "stun:relay.metered.ca:80"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our final configuration will look like the image below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv02lw6tjhib840slv89y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv02lw6tjhib840slv89y.png" alt="Stun Server Array" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the image as you can see there are only TURN URLs and there is no URL that begins with &lt;code&gt;stun:&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: That's it!
&lt;/h2&gt;

&lt;p&gt;We have learned how to setup TURN Server in Jitsi Meet and also gone through the process of obtaining the TURN Server credentials and an optional step to force all the traffic through the TURN server decrease the time it takes for the user to connect the call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose Metered TURN Server?
&lt;/h2&gt;

&lt;p&gt;Metered TURN Server offers 99.999% uptime and it is ultra-low latency, provides routing to the nearest server to the user.&lt;/p&gt;

&lt;p&gt;It also offers a generous free plan which would be sufficient for most users and saves cost running your own TURN Server.&lt;/p&gt;

&lt;p&gt;If you are a large organization that would use the TURN Server heavily then in that case as well, using Metered Video TURN Server would prove to be a cost effective solution, because you will pay more in bandwidth + EC2 instance cost in AWS.&lt;/p&gt;

&lt;p&gt;Also it is hard to achieve 99.999% uptime selfhosting the TURN server and adds more operational overhead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashboard.metered.ca/signup?tool=turnserver" rel="noopener noreferrer"&gt;Signup for Free TURN Server Account&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this guide we have learned what is a TURN Server, why it is essential in Jitsi Meet and we went through the process of setting up the TURN server configuration in Jitsi Meet and discussed selfhosting vs using Metered TURN Server.&lt;/p&gt;

&lt;p&gt;If you have any questions or comments you can contact us support[at]metered.ca or book a meeting with our specialist.&lt;/p&gt;

</description>
      <category>tutorial</category>
    </item>
    <item>
      <title>WebRTC with Python and React: Building a Video Chat Application</title>
      <dc:creator>alakkadshaw</dc:creator>
      <pubDate>Tue, 05 Jul 2022 17:11:37 +0000</pubDate>
      <link>https://forem.com/metered-video/webrtc-with-python-and-react-building-a-video-chat-application-mbp</link>
      <guid>https://forem.com/metered-video/webrtc-with-python-and-react-building-a-video-chat-application-mbp</guid>
      <description>&lt;p&gt;In this guide we will build a video chat application using python+flask in the back-end and React + WebRTC and &lt;a href="https://www.metered.ca" rel="noopener noreferrer"&gt;Metered Video SDK&lt;/a&gt; in front-end to build a video calling application.&lt;/p&gt;

&lt;p&gt;Our video chat application would allow users to have group video chat, with the ability to share their screen.&lt;/p&gt;

&lt;p&gt;The application would run on all modern browsers as well iOS Safari and in Android web browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;To build the application we would use Metered Video API and SDK, if you don't have an account, you can signup for account.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://www.metered.ca" rel="noopener noreferrer"&gt;https://www.metered.ca/&lt;/a&gt; and click &lt;strong&gt;"Signup and Start Building"&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;After  you have create the account, come back here for the next steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Application Structure - Backend
&lt;/h2&gt;

&lt;p&gt;Our application would have Python+Flask backend and React Front-End, the backend would provide API to our front-end React Application.&lt;/p&gt;

&lt;p&gt;The Application structure of our backend code is very simple, as shown in the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjepgtt0az3xn37m5285r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjepgtt0az3xn37m5285r.png" alt="WebRTC with Python Application Structure&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are creating a simple flask application, the project directory contains&lt;/p&gt;

&lt;p&gt;&lt;code&gt;flaskr/&lt;/code&gt; - This folder will contain the python code of our flask application&lt;/p&gt;

&lt;p&gt;&lt;code&gt;__init__.py&lt;/code&gt; - This file contains our Python+Flask Application Code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;venv&lt;/code&gt; - Virtual environment folder created using the venv command&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.env&lt;/code&gt; - This file contains our &lt;code&gt;METERED_SECRET_KEY&lt;/code&gt; AND &lt;code&gt;METERED_DOMAIN&lt;/code&gt; (I will share more info on how to obtain those below)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;requirements.txt&lt;/code&gt; - Contains a list of python dependencies required for our project&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the Backend
&lt;/h2&gt;

&lt;p&gt;We will first build out our Python+Flask backend and then move on to building our front-end using React.&lt;/p&gt;

&lt;p&gt;In the backend we will build the our API's that will be required by our front-end application. We will call the Metered REST API from the backend.&lt;/p&gt;

&lt;p&gt;We do not want to call the Metered REST API directly from our front-end application because we do not want to expose our &lt;code&gt;METERED_SECRET_KEY&lt;/code&gt; in the front-end.&lt;/p&gt;
&lt;h3&gt;
  
  
  Installing Dependencies
&lt;/h3&gt;

&lt;p&gt;We will use virtual environment to manage dependencies, we will create our project directory and initialize the virtual environment in the project directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir myapp
cd myapp
mkdir backend
cd backend
python3 -m venv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create file &lt;code&gt;requirements.txt&lt;/code&gt; and add the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flask
requests
python-dotenv
flask-cors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the command to install the dependencies&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating .env file
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; in the root of your project directory and add the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export FLASK_APP=./flaskr
export METERED_DOMAIN=yourappname.metered.live
export METERED_SECRET_KEY=hoHqpIkn8MqVIZvwHReHt8tm_6K0SRMgg6vHwPrBoKz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To obtain your &lt;code&gt;METERED_DOMAIN&lt;/code&gt; and &lt;code&gt;METERED_SECRET_KEY&lt;/code&gt; go to Metered Dashboard -&amp;gt; Developers&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz91wdk84q2qyypv1qh33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz91wdk84q2qyypv1qh33.png" alt="Metered Developer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Building the Backend REST API
&lt;/h3&gt;

&lt;p&gt;We will create a file named &lt;code&gt;__init__.py&lt;/code&gt; inside the &lt;code&gt;flaskr/&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;This file will contain our flask code with our REST API that would be needed by our front-end React Application.&lt;/p&gt;

&lt;p&gt;We need our backend service to provide primarily 2 services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Able to create a new meeting room&lt;/li&gt;
&lt;li&gt;Validate existing meeting room&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So we will be creating the following routes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;/api/create/room&lt;/code&gt; - This Endpoint will allow us to create a new meeting room and get the ID of the meeting room&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/validate-meeting&lt;/code&gt; - This Endpoint will accept the &lt;code&gt;roomId&lt;/code&gt; and will check if the room exists or not&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/api/metered-domain&lt;/code&gt; - We will use this endpoint to fetch our Metered Domain from the backed. This is as optional endpoint, you can directly add the Metered Domain in your front-end application, but we are creating an endpoint for flexibility.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is the boilerplate code for our backend server, we will go through each route and build it as we go along.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask_cors&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CORS&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Metered Secret Key
&lt;/span&gt;&lt;span class="n"&gt;METERED_SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METERED_SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Metered Domain
&lt;/span&gt;&lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# API Route to create a meeting room
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/create/room&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_room&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Create Meeting Room&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="c1"&gt;# API Route to validate meeting
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/validate-meeting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_meeting&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Validate Meeting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="c1"&gt;# API Route to fetch the Metered Domain
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/metered-domain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_metered_domain&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Backend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;__init__.py&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating API to Create a Meeting Room
&lt;/h3&gt;

&lt;p&gt;We will use the Metered Create Room API to create a meeting room. Which is &lt;code&gt;/api/v1/room&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# API Route to create a meeting room
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/create/room&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_room&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/v1/room&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?secretKey=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;METERED_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This endpoint returns the following response&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"__v"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"62a1218be0a28612ff36a9f5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"app"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"61002fccfa1937440e5d1134"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"archived"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"audioOnlyRoom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"autoJoin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"compositionLayout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"grid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"compositionOrientation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wide"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2022-06-08T22:24:11.259Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deleteOnExp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ejectAtRoomExp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableChat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableComposition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableLiveStreaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableRTMPOut"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableRecording"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableRequestToJoin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableScreenSharing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enableWatermark"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"joinAudioOn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"joinVideoOn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lang"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"newChatForMeetingSession"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ownerOnlyBroadcast"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"privacy"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"recordComposition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"recordRoom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"roomName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jfbkg78pca"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"showInviteBox"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"watermarkPosition"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bottom_right"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For us &lt;code&gt;roomName&lt;/code&gt; is the property of interest, each time we will call this API, and if we do not provide a &lt;code&gt;roomName&lt;/code&gt; it will create a new room with a unique room name.&lt;/p&gt;

&lt;p&gt;If we specify the &lt;code&gt;roomName&lt;/code&gt; then it will create a new room of the specified roomName.&lt;/p&gt;

&lt;p&gt;But for our use case, the unqiue auto-generated roomName is sufficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating an API to Validate a Meeting Room
&lt;/h3&gt;

&lt;p&gt;After we have created a meeting room, we need an  API to validate the Meeting Room.&lt;/p&gt;

&lt;p&gt;This endpoint will be used validate the room name entered by the user when they are trying to join a room.&lt;/p&gt;

&lt;p&gt;Using the API we will check if the room is valid, and if it is valid then we will allow the user to join the room.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# API Route to validate meeting
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/validate-meeting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_meeting&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;roomName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/v1/room/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                         &lt;span class="n"&gt;roomName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?secretKey=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;METERED_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomFound&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomFound&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please specify roomName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  API to Fetch Metered Domain
&lt;/h3&gt;

&lt;p&gt;The API to fetch Metered Domain is very straightforward, we will just send the &lt;code&gt;METERED_DOMAIN&lt;/code&gt; variable as response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# API Route to fetch the Metered Domain
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/metered-domain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_metered_domain&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;

&lt;p&gt;Here is our final backend service &lt;code&gt;__init__.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Metered Secret Key
&lt;/span&gt;&lt;span class="n"&gt;METERED_SECRET_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METERED_SECRET_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Metered Domain
&lt;/span&gt;&lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="c1"&gt;# API Route to create a meeting room
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/create/room&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_room&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/v1/room&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?secretKey=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;METERED_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;


&lt;span class="c1"&gt;# API Route to validate meeting
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/validate-meeting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;validate_meeting&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;roomName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/v1/room/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                         &lt;span class="n"&gt;roomName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?secretKey=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;METERED_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomFound&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;roomFound&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Please specify roomName&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="c1"&gt;# API Route to fetch the Metered Domain
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/metered-domain&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_metered_domain&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Backend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using Metered Pre-Built UI
&lt;/h2&gt;

&lt;p&gt;Instead of building the custom front-end in React we can use the Metered Pre-built UI to &lt;a href="https://www.metered.ca/embed-video-chat" rel="noopener noreferrer"&gt;Embed Video Chat&lt;/a&gt; into your web application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7j438qa066zzdtjpb7ry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7j438qa066zzdtjpb7ry.png" alt="Metered Embed Video Chat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your roomURL is simply &lt;code&gt;&amp;lt;your_metered_domain&amp;gt;.metered.live/&amp;lt;your_room_name&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Each Room you create in Metered Video can be used with the pre-built UI. Just open the roomURL in your browser and you will be presented with the pre-built UI.&lt;/p&gt;

&lt;p&gt;The Metered Pre-Built UI has built-in Chat, Video Calling, and Screen Sharing capabilities and the options can be enabled/disabled using dashboard or using the API.&lt;/p&gt;

&lt;p&gt;To Embed the Pre-Built UI into an existing application you can use the following Embed Code.&lt;/p&gt;

&lt;p&gt;Just replace the &lt;code&gt;roomURL&lt;/code&gt; with your own &lt;code&gt;roomURL&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the Custom Front-End in React
&lt;/h2&gt;

&lt;p&gt;If you choose to build you custom front-end in React then follow along.&lt;/p&gt;

&lt;p&gt;Our front-end application would allow 3 main area:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Join/Create Meeting: Here we will allow the user to join an existing meeting or create a new meeting&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Meeting Area: The main meeting interface&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Meeting Ended Screen: We will take the user to this area after the meeting has ended.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Installing the dependencies
&lt;/h3&gt;

&lt;p&gt;We will use &lt;code&gt;Create React App&lt;/code&gt; to scaffold our single page React application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd myapp
npx create-react-app react-frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Scaffolding the Application UI
&lt;/h3&gt;

&lt;p&gt;We will create 3 components one for each of the areas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sxb2xgbi63duwhatwlr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sxb2xgbi63duwhatwlr.png" alt="React Front-End Scaffolding"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;App.js&lt;/code&gt; - Will be the main container of the application&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Join.js&lt;/code&gt; -  UI to Join and Existing Meeting or Create a new Meeting&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Meeting.js&lt;/code&gt; - Will contain the main Meeting Screen&lt;/p&gt;

&lt;p&gt;&lt;code&gt;MeetingEnded.js&lt;/code&gt; -  Interface to show when the meeting ends&lt;/p&gt;

&lt;h3&gt;
  
  
  Including the Metered JavaScript SDK
&lt;/h3&gt;

&lt;p&gt;We will include the latest Metered JavaScript in our application.&lt;/p&gt;

&lt;p&gt;To add the Metered SDK open &lt;code&gt;public/index.html&lt;/code&gt; and paste the SDK before closing the of the head tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="//cdn.metered.ca/sdk/video/1.4.3/sdk.min.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"%PUBLIC_URL%/favicon.ico"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"theme-color"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"#000000"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt;
      &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;
      &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"Web site created using create-react-app"&lt;/span&gt;
    &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"apple-touch-icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"%PUBLIC_URL%/logo192.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--
      manifest.json provides metadata used when your web app is installed on a
      user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
    --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"manifest"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"%PUBLIC_URL%/manifest.json"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    --&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;React App&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

      &lt;span class="c"&gt;&amp;lt;!-- METERED VIDEO SDK --&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"//cdn.metered.ca/sdk/video/1.4.3/sdk.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;noscript&amp;gt;&lt;/span&gt;You need to enable JavaScript to run this app.&lt;span class="nt"&gt;&amp;lt;/noscript&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the &amp;lt;body&amp;gt; tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;index.html&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Initializing the SDK
&lt;/h3&gt;

&lt;p&gt;We will initialize the Metered SDK in &lt;code&gt;App.js&lt;/code&gt; and handle all the meeting events in &lt;code&gt;App.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Join&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Join&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Meeting&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Meeting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initializing the SDK&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Metered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Meeting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Will set it to true when the user joins the meeting&lt;/span&gt;
  &lt;span class="c1"&gt;// and update the UI.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;meetingJoined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMeetingJoined&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Storing onlineUsers, updating this when a user joins&lt;/span&gt;
  &lt;span class="c1"&gt;// or leaves the meeting&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;onlineUsers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setOnlineUsers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="c1"&gt;// This useEffect hooks will contain all&lt;/span&gt;
  &lt;span class="c1"&gt;// event handler, like participantJoined, participantLeft etc.&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="c1"&gt;// Will call the API to create a new&lt;/span&gt;
  &lt;span class="c1"&gt;// room and join the user.&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="c1"&gt;// Will call th API to validate the room&lt;/span&gt;
  &lt;span class="c1"&gt;// and join the user&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;meetingJoined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Meeting&lt;/span&gt; &lt;span class="na"&gt;onlineUsers&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onlineUsers&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Join&lt;/span&gt;
          &lt;span class="na"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Join Meeting Component
&lt;/h3&gt;

&lt;p&gt;Lets build the Join Meeting Component, the Join Meeting component is very simple, it will allow the user to join an existing meeting by entering the &lt;code&gt;roomName&lt;/code&gt; or creating a new meeting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleJoinMeeting&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRoomName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"joinView"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-full items-center justify-center flex"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-base-300 w-11/12 max-w-screen-md  rounded mt-48 p-10"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"label-text"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Name:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nf"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-full input input-primary input-bordered"&lt;/span&gt;
            &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Enter your name"&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"divider"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;AND&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"form-control"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"label-text"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Meeting ID&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"relative"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
              &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;setRoomName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingId"&lt;/span&gt;
              &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
              &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Meeting ID"&lt;/span&gt;
              &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-full pr-16 input input-primary input-bordered"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
              &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"joinExistingMeeting"&lt;/span&gt;
              &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"absolute top-0 right-0 rounded-l-none btn btn-primary text-xs"&lt;/span&gt;
            &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;
                &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                  &lt;span class="nf"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden sm:block"&lt;/span&gt;
              &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                Join Existing Meeting
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"sm:hidden"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Join&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"divider"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;OR&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex justify-center"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
            &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nf"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"createANewMeeting"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn btn-primary"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Create a new meeting
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Join.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oshih52h1g5t3daig4t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3oshih52h1g5t3daig4t.png" alt="Join.js Component"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Join Meeting Component we are just handling the events and calls the props which has methods from the App Component, and the logic to handle "Join Existing Meeting" and "Create a new Meeting" will be handled in the App Component&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Logic to Create and Join the Meeting
&lt;/h3&gt;

&lt;p&gt;In the &lt;code&gt;App.js&lt;/code&gt; we will add the logic to handle the events triggered by pressing the "Join Existing Meeting" and "Create a New Meeting" buttons in the Join Component.&lt;/p&gt;

&lt;p&gt;The logic to &lt;code&gt;handleCreateMeeting&lt;/code&gt; is very simple, we call our backend API &lt;code&gt;/api/create/room&lt;/code&gt; to create a room.&lt;/p&gt;

&lt;p&gt;Then we call &lt;code&gt;/api/metered-domain&lt;/code&gt; to fetch our Metered Domain.&lt;/p&gt;

&lt;p&gt;And finally we call the &lt;code&gt;join&lt;/code&gt; method of the Metered Javascript SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Will call the API to create a new&lt;/span&gt;
  &lt;span class="c1"&gt;// room and join the user.&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Calling API to create room&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/create/room&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Calling API to fetch Metered Domain&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/metered-domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Extracting Metered Domain and Room Name&lt;/span&gt;
    &lt;span class="c1"&gt;// From responses.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Calling the join() of Metered SDK&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joinResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;roomURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Updating the state meetingJoined to true&lt;/span&gt;
    &lt;span class="nf"&gt;setMeetingJoined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The logic for &lt;code&gt;handleJoinMeeting&lt;/code&gt; is also very straightforward, here we already have the &lt;code&gt;roomName&lt;/code&gt; which will be provided by the user, we need to validate the &lt;code&gt;roomName&lt;/code&gt; and if the &lt;code&gt;roomName&lt;/code&gt; is valid then we will call the join method of the Metered JavaScript SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;// Will call th API to validate the room&lt;/span&gt;
  &lt;span class="c1"&gt;// and join the user&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Calling API to validate the roomName&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/validate-meeting?roomName=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roomFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Calling API to fetch Metered Domain&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/metered-domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Extracting Metered Domain and Room Name&lt;/span&gt;
      &lt;span class="c1"&gt;// From responses.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Calling the join() of Metered SDK&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joinResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;roomURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nf"&gt;setMeetingJoined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid roomName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To validate the roomName we will call our backend API &lt;code&gt;/api/validate-meeting?roomName=&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The we will be checking if the &lt;code&gt;roomFound&lt;/code&gt; is True, if it is True then we will fetch our Metered Domain and call the &lt;code&gt;join()&lt;/code&gt; method and update the &lt;code&gt;meetingJoined&lt;/code&gt; state variable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Events
&lt;/h3&gt;

&lt;p&gt;We need to handle the following events in our application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;participantJoined&lt;/code&gt;: When a paritcipant joins the meeting this event is triggered, we will add the user to the onlineUsers array.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;participantLeft&lt;/code&gt;: When a participant leaves the meeting this event is triggered, we will remove the user from the onlineUsers array.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;remoteTrackStarted&lt;/code&gt;: When a remote participant shares their camera/microphone/screen this event is emitted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;remoteTrackStopped&lt;/code&gt;: When a remote participant stops sharing their camera/microphone/screen this event is emitted.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;onlineParticipants&lt;/code&gt;: This event is emitted multiple times during the lifecycle of the meeting. It contains that array of users currently in the meeting users currently in the meeting.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will create a &lt;code&gt;useEffect&lt;/code&gt; hook and in the hook to handle the events and return a function that will do the cleanup of the event listener.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStarted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantJoined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localTrackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantLeft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localTrackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onlineParticipants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onlineParticipants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStarted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantJoined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantLeft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onlineParticipants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will create two array as state variables, one array will store the list of onlineParticipants and another array will store the list of remote video and audio tracks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRemoteTracks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="c1"&gt;// This useEffect hooks will contain all&lt;/span&gt;
  &lt;span class="c1"&gt;// event handler, like participantJoined, participantLeft etc.&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStarted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setRemoteTracks&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;streamId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;setRemoteTracks&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantJoined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localTrackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantLeft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localTrackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onlineParticipants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onlineParticipants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setOnlineUsers&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;onlineParticipants&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStarted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantJoined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantLeft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onlineParticipants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can show a notification and play a sound when a participant enters or leaves the meeting in the &lt;code&gt;participantJoined&lt;/code&gt; and &lt;code&gt;participantLeft&lt;/code&gt; event handlers.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;onlineParticipants&lt;/code&gt; event handler is triggered each time a participant enters or leaves and meeting and returns the array of participants, so we can use just that event handler to load the list of online participants.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;remoteTrackStarted&lt;/code&gt; event handler we are just pushing the remoteTrack item to the remoteVideoTracks array and setting the state.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;remoteTrackStopped&lt;/code&gt; event handler, we are looping through the array to find the remoteTrackItem that was stoppped and and removing it from the array and setting the state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Displaying the Remote Streams
&lt;/h3&gt;

&lt;p&gt;We have handled the &lt;code&gt;remoteTrackStarted&lt;/code&gt; event and we are storing the remote tracks in the &lt;code&gt;remoteTracks&lt;/code&gt; state variable. The remote tracks can be played in a &lt;code&gt;videoTag&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;videoTag&lt;/code&gt; has an &lt;code&gt;srcObject&lt;/code&gt; attribute and we can pass the &lt;code&gt;MediaStream&lt;/code&gt; to the srcObject attribute the play the remote streams.&lt;/p&gt;

&lt;p&gt;We will create a custom &lt;code&gt;VideoTag&lt;/code&gt; component that will accept our mediaStream as prop and create a html &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag with srcObject attribute and will play the video when the stream is ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating Component to Display MediaStream
&lt;/h3&gt;

&lt;p&gt;The video and audio stream, can be added to a video tag, but they have to added to the &lt;code&gt;srcObject&lt;/code&gt; property, to handle this we will create our own &lt;code&gt;&amp;lt;VideoTag /&amp;gt;&lt;/code&gt; component where we can provide srcObject as prop and it handles the reset.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;classNames&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;classnames&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VideoTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;srcObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;static shadow-lg bg-slate-900 max-w-full max-h-full&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCanPlay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcObject&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;srcObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;srcObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onCanPlay&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCanPlay&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;playsInline&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;autoPlay&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;VideoTag&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;VideoTag.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This component is very simple, here we have created a &lt;code&gt;useEffect&lt;/code&gt; hook and in the hook we can see if srcObject prop has a value, if it has then we are assigning it to the video tag, and we are handling the &lt;code&gt;onCanPlay&lt;/code&gt; event emiited by the video tag, and when that event is emitted we are calling &lt;code&gt;play()&lt;/code&gt; method of the video tag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing the Meeting Area
&lt;/h3&gt;

&lt;p&gt;Now we have added the logic to handle the onlineParticipants and their remote tracks, now let's build the Meeting&lt;/p&gt;

&lt;p&gt;The Meeting Area is saved in the &lt;code&gt;Meeting.js&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;In the Meeting Area we will show the video/audio of the remote participants, add the ability to allow the user to share his/her microphone, camera and screen, and show the user their own video if they are sharing camera/screen.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;App.js&lt;/code&gt; component we will check if the user has joined the Meeting, if yes then we will show the Meeting component. If the user has not joined the meeting then we will show the Join Component.&lt;/p&gt;

&lt;p&gt;We will also pass the &lt;code&gt;onlineUsers&lt;/code&gt; and &lt;code&gt;remoteTracks&lt;/code&gt; as props to the &lt;code&gt;Meeting.js&lt;/code&gt; component, and also methods to handle the camera, screen, microphone button click events.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;meetingJoined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Meeting&lt;/span&gt;
          &lt;span class="na"&gt;handleMicBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleMicBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;localVideoStream&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localVideoStream&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onlineUsers&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onlineUsers&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;meetingInfo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;meetingInfo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Join&lt;/span&gt;
          &lt;span class="na"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;App.js render&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We have scaffold out the &lt;code&gt;Meeting.js&lt;/code&gt; Component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VideoTag&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./VideoTag&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Meeting&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nx"&gt;handleMicBtn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;localVideoStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;onlineUsers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;meetingInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;userStreamMap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;trackItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userStreamMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantSessionId&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;userStreamMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantSessionId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;userStreamMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantSessionId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;remoteParticipantTags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;onlineUsers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Skip if self&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;meetingInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantSessionId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;videoTags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userStreamMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;userStreamMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// User has remote tracks&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;trackItem&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;userStreamMap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MediaStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;videoTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VideoTag&lt;/span&gt; &lt;span class="na"&gt;srcObject&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;audio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;videoTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VideoTag&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;srcObject&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;remoteParticipantTags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"remoteVideos"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;videoTags&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingView"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-8 text-center bg-black"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;MeetingID: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1 grid grid-cols-2 grid-rows-2"&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"remoteParticipantContainer"&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;remoteParticipantTags&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col bg-base-300"&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;150px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localVideoStream&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VideoTag&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingAreaLocalVideo"&lt;/span&gt;
            &lt;span class="na"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;srcObject&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localVideoStream&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;150px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;""&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingAreaUsername"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-base-300 bg-black"&lt;/span&gt;
          &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;textAlign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;justifyContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;center&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;20px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"space-x-4"&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingViewMicrophone"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt;
          &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleMicBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-6 h-6"&lt;/span&gt;
            &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
            &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
            &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
            &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinecap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinejoin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingViewCamera"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt;
          &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-6 h-6"&lt;/span&gt;
            &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
            &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
            &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
            &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinecap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinejoin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
          &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingViewScreen"&lt;/span&gt;
          &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt;
          &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-6 h-6"&lt;/span&gt;
            &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
            &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
            &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
            &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinecap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinejoin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"meetingViewLeave"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"btn"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"w-6 h-6"&lt;/span&gt;
            &lt;span class="na"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"none"&lt;/span&gt;
            &lt;span class="na"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;
            &lt;span class="na"&gt;viewBox&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"0 0 24 24"&lt;/span&gt;
            &lt;span class="na"&gt;xmlns&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinecap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeLinejoin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"round"&lt;/span&gt;
              &lt;span class="na"&gt;strokeWidth&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;svg&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Meeting&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling the sharing of camera, microphone and screen
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;App.js&lt;/code&gt; we create the methods to handle the click events on Microphone, Camera, Screen and Leave Meeting Buttons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx19g34z3mhyv2a0g4ka.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx19g34z3mhyv2a0g4ka.png" alt="Microphone, Camera, Screen and Leave Meeting Buttons&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will call the methods from the &lt;a href="https://www.metered.ca/" rel="noopener noreferrer"&gt;Metered Video SDK&lt;/a&gt; to handle the click events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.metered.ca/docs/SDK-Reference/JavaScript/Methods/startAudio" rel="noopener noreferrer"&gt;startAudio()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.metered.ca/docs/SDK-Reference/JavaScript/Methods/startVideo" rel="noopener noreferrer"&gt;startVideo()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.metered.ca/docs/SDK-Reference/JavaScript/Methods/stopAudio" rel="noopener noreferrer"&gt;stopAudio()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.metered.ca/docs/SDK-Reference/JavaScript/Methods/stopVideo" rel="noopener noreferrer"&gt;stopVideo()&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.metered.ca/docs/SDK-Reference/JavaScript/Methods/startScreenShare" rel="noopener noreferrer"&gt;startScreenShare()&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.metered.ca/docs/SDK-Reference/JavaScript/Methods/getLocalVideoStream" rel="noopener noreferrer"&gt;getLocalVideoStream()&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMicBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;micShared&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopAudio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setMicShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAudio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setMicShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cameraShared&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setCameraShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setCameraShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;screenShared&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startScreenShare&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setScreenShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setCameraShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setScreenShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Building Meeting Ended/Leave Meeting Screen
&lt;/h3&gt;

&lt;p&gt;To build the Meeting Ended screen, we will create a state variable called meetingEnded and in the handleLeaveBtn() method we will set it to true, and call the &lt;a href="https://www.metered.ca/docs/SDK-Reference/JavaScript/Methods/leaveMeeting" rel="noopener noreferrer"&gt;leaveMeeting()&lt;/a&gt; method of Metered Video SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;leaveMeeting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setMeetingEnded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we will check if &lt;code&gt;meetingEnded&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; and if it is true then we will hide the Meeting component and show the &lt;code&gt;MeetingEnded.js&lt;/code&gt; component instead.&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;This is how our final &lt;code&gt;App.js&lt;/code&gt; file looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Join&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Join&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Meeting&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Meeting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;MeetingEnded&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./MeetingEnded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initializing the SDK&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Metered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Meeting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:5000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Will set it to true when the user joins the meeting&lt;/span&gt;
  &lt;span class="c1"&gt;// and update the UI.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;meetingJoined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMeetingJoined&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Storing onlineUsers, updating this when a user joins&lt;/span&gt;
  &lt;span class="c1"&gt;// or leaves the meeting&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;onlineUsers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setOnlineUsers&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRemoteTracks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;localVideoStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;micShared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMicShared&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cameraShared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCameraShared&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;screenShared&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setScreenShared&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;meetingEnded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMeetingEnded&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setRoomName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;meetingInfo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setMeetingInfo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
  &lt;span class="c1"&gt;// This useEffect hooks will contain all&lt;/span&gt;
  &lt;span class="c1"&gt;// event handler, like participantJoined, participantLeft etc.&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStarted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setRemoteTracks&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;streamId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;streamId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nf"&gt;setRemoteTracks&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantJoined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localTrackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantLeft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;localTrackItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onlineParticipants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onlineParticipants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setOnlineUsers&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;onlineParticipants&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localTrackUpdated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MediaStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStarted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remoteTrackStopped&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantJoined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participantLeft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onlineParticipants&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localTrackUpdated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Will call the API to create a new&lt;/span&gt;
  &lt;span class="c1"&gt;// room and join the user.&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Calling API to create room&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/create/room&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Calling API to fetch Metered Domain&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/metered-domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Extracting Metered Domain and Room Name&lt;/span&gt;
    &lt;span class="c1"&gt;// From responses.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Calling the join() of Metered SDK&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joinResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;roomURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setRoomName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setMeetingInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;joinResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setMeetingJoined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Will call th API to validate the room&lt;/span&gt;
  &lt;span class="c1"&gt;// and join the user&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Calling API to validate the roomName&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/validate-meeting?roomName=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roomFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Calling API to fetch Metered Domain&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;API_LOCATION&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/metered-domain&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// Extracting Metered Domain and Room Name&lt;/span&gt;
      &lt;span class="c1"&gt;// From responses.&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="c1"&gt;// Calling the join() of Metered SDK&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joinResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;roomURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;METERED_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nf"&gt;setUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setRoomName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setMeetingInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;joinResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="nf"&gt;setMeetingJoined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid roomName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleMicBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;micShared&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopAudio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setMicShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAudio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setMicShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cameraShared&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setCameraShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setLocalVideoStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setCameraShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;screenShared&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startScreenShare&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setScreenShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopVideo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setCameraShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setScreenShared&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;meteredMeeting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;leaveMeeting&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setMeetingEnded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"App"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;meetingJoined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;meetingEnded&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MeetingEnded&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Meeting&lt;/span&gt;
            &lt;span class="na"&gt;handleMicBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleMicBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCameraBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handelScreenBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLeaveBtn&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;localVideoStream&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;localVideoStream&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;onlineUsers&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onlineUsers&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;remoteTracks&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;remoteTracks&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;meetingInfo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;meetingInfo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Join&lt;/span&gt;
          &lt;span class="na"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCreateMeeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleJoinMeeting&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We have successfully built the group video calling application with Python Backend and React front-end.&lt;/p&gt;

&lt;p&gt;You can grab the complete source code from &lt;strong&gt;Github&lt;/strong&gt;:  &lt;a href="https://github.com/metered-ca/python-react-video-chat-app" rel="noopener noreferrer"&gt;https://github.com/metered-ca/python-react-video-chat-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The application is also avaliable as &lt;strong&gt;Docker Containers&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Backend: &lt;a href="https://hub.docker.com/r/metered/python-video-demo" rel="noopener noreferrer"&gt;https://hub.docker.com/r/metered/python-video-demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Frontend: &lt;a href="https://hub.docker.com/r/metered/react-video-demo" rel="noopener noreferrer"&gt;https://hub.docker.com/r/metered/react-video-demo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>webrc</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to embed video chat in WordPress in 7 easy steps</title>
      <dc:creator>alakkadshaw</dc:creator>
      <pubDate>Tue, 05 Jul 2022 13:22:16 +0000</pubDate>
      <link>https://forem.com/metered-video/how-to-embed-video-chat-in-wordpress-in-7easy-steps-m01</link>
      <guid>https://forem.com/metered-video/how-to-embed-video-chat-in-wordpress-in-7easy-steps-m01</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In this article I am going to explain how to add video chat in your WordPress website.&lt;/p&gt;

&lt;p&gt;WordPress is an awesome CMS and the most famous one in the world. According to WordPress.com 43% of the web runs on WordPress.&lt;/p&gt;

&lt;p&gt;With a whole host of plugins and customizations available for WordPress you cannot go wrong with it.&lt;/p&gt;

&lt;p&gt;Video chat is an awesome tool to have on your website. Whether to increase conversion or engagement, building relationships with your customers and audiences. There is simply no better tool than video.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;WordPress Admin Access&lt;/li&gt;
&lt;li&gt;A Video Chat Provider&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NuFDQorA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t2bncyp2v0v5dvoi16rn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NuFDQorA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t2bncyp2v0v5dvoi16rn.png" alt="WordPress Login" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Video Chat to WordPress
&lt;/h2&gt;

&lt;p&gt;You can add &lt;a href="https://metered.ca"&gt;Video Chat &lt;/a&gt; to WordPress by following these steps:&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Login to your WordPress account.
&lt;/h2&gt;

&lt;p&gt;Login to your WordPress website and go to the page where you want to add the video chat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--venHcam8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qp1xc7vqkefpq8e1whrj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--venHcam8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qp1xc7vqkefpq8e1whrj.png" alt="Add a Custom HTML Block" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Add a Custom HTML Block
&lt;/h2&gt;

&lt;p&gt;Once you are on the page go to the place where you want to add the Video Chat.&lt;/p&gt;

&lt;p&gt;click on the add block button and search for a Custom HTML Block&lt;/p&gt;

&lt;p&gt;In the Custom HTML block we will paste the code to add video chat to the WordPress website, more on this later&lt;/p&gt;

&lt;p&gt;First, we need a video chat provider. We recommend going with Metered Video Chat. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LHG91GFI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t89rmcmpkwwfmwfih1pw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LHG91GFI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t89rmcmpkwwfmwfih1pw.png" alt="Create a Metered Video Account" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create a Metered Video Account
&lt;/h2&gt;

&lt;p&gt;To create a &lt;a href="https://metered.ca"&gt;Metered Video&lt;/a&gt; Account go to the Metered Video page &lt;a href="https://metered.ca"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Click on the Get Started button to create a free account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4G8MwHBv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2lobg6kqbyjmckmly43.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4G8MwHBv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j2lobg6kqbyjmckmly43.png" alt="Create Room" width="800" height="763"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Create a room
&lt;/h2&gt;

&lt;p&gt;Once you have created an account on the Dashboard click on the create room to button to create a room.&lt;/p&gt;

&lt;p&gt;There are various settings that you can do here to customize the room to your liking&lt;/p&gt;

&lt;p&gt;some of them are&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a personalized URL&lt;/li&gt;
&lt;li&gt;Up to 10,000 participants video chat&lt;/li&gt;
&lt;li&gt;Live Chat with file and Image Sharing&lt;/li&gt;
&lt;li&gt;Cloud Recordings&lt;/li&gt;
&lt;li&gt;Moderation Features&lt;/li&gt;
&lt;li&gt;Customizations&lt;/li&gt;
&lt;li&gt;Privacy Settings&lt;/li&gt;
&lt;li&gt;Create Public Rooms&lt;/li&gt;
&lt;li&gt;Create Private Rooms&lt;/li&gt;
&lt;li&gt;Record Streams&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Moderation Video Chat Features
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Camera on Join&lt;/li&gt;
&lt;li&gt;Microphone on Join&lt;/li&gt;
&lt;li&gt;Screen Sharing&lt;/li&gt;
&lt;li&gt;Text Chat&lt;/li&gt;
&lt;li&gt;New Chat for Meeting Session&lt;/li&gt;
&lt;li&gt;Request to Join&lt;/li&gt;
&lt;li&gt;Show Invite Instructions&lt;/li&gt;
&lt;li&gt;Auto Join&lt;/li&gt;
&lt;li&gt;Admin Only Broadcast&lt;/li&gt;
&lt;li&gt;Add Watermark to your Video&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;and many more you can explore on the create room / edit room dashboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ic7laSA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubs1kbohbnl96enk5783.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ic7laSA2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ubs1kbohbnl96enk5783.png" alt="Embed Info Metered Video" width="800" height="850"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Copy the Code
&lt;/h2&gt;

&lt;p&gt;After you have created the video room you can see the embed code on the next page.&lt;/p&gt;

&lt;p&gt;Copy the code, we will need to paste this code in our WordPress site to add video chat to your website&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2MCs5IO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l47hjfbiybpymd41va04.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2MCs5IO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l47hjfbiybpymd41va04.png" alt="Paste the Code" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: Paste the code in the custom HTML block in WordPress
&lt;/h2&gt;

&lt;p&gt;On the WordPress website we created a custom HTML block where we needed the video chat to appear.&lt;/p&gt;

&lt;p&gt;In the custom HTML code block paste the embed code and save. Now you have created a Video Chat on your WordPress website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: You have added the Video Chat to your WordPress site.
&lt;/h2&gt;

&lt;p&gt;Now you have added the Video Chat in your WordPress site. You can customize the video experience including colors and font, functionality, recording as much as you like&lt;/p&gt;

&lt;p&gt;for more on this keep reading&lt;/p&gt;

&lt;h2&gt;
  
  
  Group Video Chat to WordPress
&lt;/h2&gt;

&lt;p&gt;You can adjust the settings to allow for group video calls. With Metered Video you can create group video calls with up to 10,0o0 participants&lt;/p&gt;

&lt;p&gt;When creating a video chat room simple leave blank the Maximum participants settings or set it to the max number of participants that you expect in your meeting.&lt;/p&gt;

&lt;p&gt;The video chat that you have added to your WordPress now becomes a group video chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  1-1 Video Chat on WordPress
&lt;/h2&gt;

&lt;p&gt;You can adjust the settings to make the Video Chat on your WordPress 1-1 only.&lt;/p&gt;

&lt;p&gt;When creating a room in Metered Video set the Maximum participant setting to 2.&lt;/p&gt;

&lt;p&gt;Now, there can be only 1-1 video meeting in that room. If in the future you want to set the 1-1 setting to a group video, you can easily leave blank the maximum participant setting or set a number for the participants of the group video&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--f-vxgrUl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tz3ot8fp92m5zk432c8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--f-vxgrUl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1tz3ot8fp92m5zk432c8.png" alt="Customizing the video chat in WordPress" width="800" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the &lt;a href="https://www.metered.ca/embed-video-chat"&gt;Video Chat&lt;/a&gt; in WordPress
&lt;/h2&gt;

&lt;p&gt;You can easily customize the video chat including the size of the video and how it looks in your website.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Size of the video Chat&lt;/li&gt;
&lt;li&gt;Setting Custom Room Name&lt;/li&gt;
&lt;li&gt;Setting the Video Chat Public or Private&lt;/li&gt;
&lt;li&gt;Set Camera join&lt;/li&gt;
&lt;li&gt;Microphone on join&lt;/li&gt;
&lt;li&gt;Screen Sharing&lt;/li&gt;
&lt;li&gt;Text Chat&lt;/li&gt;
&lt;li&gt;New chat for Meeting Session&lt;/li&gt;
&lt;li&gt;Request to join&lt;/li&gt;
&lt;li&gt;Show invite instructions&lt;/li&gt;
&lt;li&gt;Auto Join&lt;/li&gt;
&lt;li&gt;Admin only broadcast&lt;/li&gt;
&lt;li&gt;Watermark on Video&lt;/li&gt;
&lt;li&gt;Maximum Participants&lt;/li&gt;
&lt;li&gt;Meeting Join Webhook&lt;/li&gt;
&lt;li&gt;Meeting left Webhook&lt;/li&gt;
&lt;li&gt;Meeting ended Webhook&lt;/li&gt;
&lt;li&gt;end Meeting after no activity in seconds&lt;/li&gt;
&lt;li&gt;eject after&lt;/li&gt;
&lt;li&gt;not before unix sec (NOT before)&lt;/li&gt;
&lt;li&gt;expire Unix Sec (Expires)&lt;/li&gt;
&lt;li&gt;eject on expire unix sec&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Size of the chat
&lt;/h3&gt;

&lt;p&gt;To adjust the size of the video iframe simply adjust the size of the &lt;code&gt;div&lt;/code&gt; tag with the id metered-frame&lt;/p&gt;

&lt;p&gt;The size and shape of the div that determines the size of the video chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Setting the custom room name.
&lt;/h3&gt;

&lt;p&gt;Go to Dashboard -&amp;gt; Rooms -&amp;gt; edit room / create room&lt;/p&gt;

&lt;p&gt;There you can see the Room name. If the room name is left blank then it automatically generates the room name or you can specify a custom room name here&lt;/p&gt;

&lt;p&gt;When creating a custom room name remember to not place any spaces in the room name.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Setting the Video Chat Public or Private
&lt;/h3&gt;

&lt;p&gt;You can set the Video chat as Public: Anyone can join the room. When setting the room as public a good idea would be to set Request to join to be true so that people must request to join your video call.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Set Camera join
&lt;/h3&gt;

&lt;p&gt;You can set the setting such that the meeting participants join with their camera as on.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.Microphone on join
&lt;/h3&gt;

&lt;p&gt;You can set the setting such that the meeting participants join with their microphone as on.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Screen Sharing
&lt;/h3&gt;

&lt;p&gt;You can set to allow the participants to be able to share screen. You can also switch this setting to off so that the participants cannot share their screens&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Text Chat
&lt;/h3&gt;

&lt;p&gt;You can also add Text chat along with the video calls. Text chat also comes with the ability to send files and images in the chat.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. New Chat for Meeting Session
&lt;/h3&gt;

&lt;p&gt;You can set so that you have a new chat session for a new meeting. or you can have it so the chat history is carried over to the next meeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Request to join
&lt;/h3&gt;

&lt;p&gt;You can enable the request to join. When a new participants wants to join the meeting they will have to request to join and the admin must allow them to join the meeting&lt;/p&gt;

&lt;p&gt;Admin can deny any the participant as well.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. Show invite instructions
&lt;/h3&gt;

&lt;p&gt;You can show the invite instructions in the room and participants can invite other people in the video room if the video is public.&lt;/p&gt;

&lt;p&gt;Or you can choose to hide the invite instructions.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Auto join / Show Lobby
&lt;/h3&gt;

&lt;p&gt;You can choose to show the lobby or hide the lobby and directly add the participant in the meeting&lt;/p&gt;

&lt;h3&gt;
  
  
  12. Admin Only Broadcast
&lt;/h3&gt;

&lt;p&gt;When enabled only admin will be able to share camera, screen and microphone. All the other participants will not&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Watermark
&lt;/h3&gt;

&lt;p&gt;You can upload a watermark on your video call. If you are recording the video you can also see the watermark on your recorded file.&lt;/p&gt;

&lt;p&gt;You can set the height and width of the watermark as well as the position of the watermark on the video&lt;/p&gt;

&lt;p&gt;The position can be set as on the X axis and Y axis for a custom position&lt;/p&gt;

&lt;p&gt;default positions include TOP LEFT, TOP RIGHT, BOTTOM LEFT, BOTTOM RIGHT&lt;/p&gt;

&lt;h3&gt;
  
  
  14. Maximum participants
&lt;/h3&gt;

&lt;p&gt;You can set the maximum participants allowed in the meeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  15. Webhooks
&lt;/h3&gt;

&lt;p&gt;You can set webhooks for when some one joins the meeting, leaves the meeting, when the meeting starts, when the meeting ends.&lt;/p&gt;

&lt;p&gt;You can even set different end point for each of these webhooks.&lt;/p&gt;

&lt;h3&gt;
  
  
  16. end Meeting after no activity.
&lt;/h3&gt;

&lt;p&gt;You can set to end meeting after no activity. You can set the time in seconds after which the meeting will end automatically&lt;/p&gt;

&lt;p&gt;For example you can set the meeting to end after no activity for 7200 seconds. If there is no activity for 7200 seconds than the meeting will end automatically.&lt;/p&gt;

&lt;h3&gt;
  
  
  17. Eject After:
&lt;/h3&gt;

&lt;p&gt;Automatically eject all the participants after a certain amount of time. You can specify the amount of time after which the participants would be ejected.&lt;/p&gt;

&lt;h3&gt;
  
  
  18. Not before Unix Sec (Not before):
&lt;/h3&gt;

&lt;p&gt;You can set the time and date before which the participants cannot join the meeting.&lt;/p&gt;

&lt;h3&gt;
  
  
  19. Expire Unix Sec (expires)
&lt;/h3&gt;

&lt;p&gt;You can set the time and date after which the participants cannot join the meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording Video Chat in WordPress (Optional)
&lt;/h2&gt;

&lt;p&gt;You can record the meeting in the cloud as well and download them when needed. To set the recording go to Dashboard -&amp;gt; Create Room / Edit Room&lt;/p&gt;

&lt;p&gt;and then enable Record Room (individual streams for audio and video will be recorded separately)&lt;/p&gt;

&lt;p&gt;You can also record a composite stream where  you get a single stream with audio and video combined.&lt;/p&gt;

&lt;p&gt;If you are recording a composite video you can choose from a set of options like&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Grid&lt;/li&gt;
&lt;li&gt;Active Speaker and&lt;/li&gt;
&lt;li&gt;Broadcaster&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can also stream the meeting using Metered Live Stream or by using RTMP out to stream using third party providers like YouTube and Facebook Live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downloading the recorded videos
&lt;/h2&gt;

&lt;p&gt;You can easily download the recorded video using&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dashboard&lt;/li&gt;
&lt;li&gt;Through &lt;a href="https://www.metered.ca/docs/"&gt;API&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;1. Dashboard&lt;/strong&gt;&lt;br&gt;
Go to &lt;strong&gt;Dashboard-&amp;gt; Cloud Recordings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;here you can see a list of recordings and you can download the recordings also delete the recording if you want to&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Using &lt;a href="https://www.metered.ca/docs/"&gt;API&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can use the API to download the recording. Here is the &lt;a href="https://www.metered.ca/docs/"&gt;API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article I explained how we can add video chat and video meeting to your WordPress site.&lt;/p&gt;

&lt;p&gt;Adding video chat with Metered Video is a as easy as adding an YouTube Video. Just add the iframe code to your WordPress site to add video chat to your WordPress site.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>wordpress</category>
      <category>html</category>
    </item>
    <item>
      <title>WebRTC with NodeJS: Building a Video Chat APP.</title>
      <dc:creator>alakkadshaw</dc:creator>
      <pubDate>Mon, 21 Mar 2022 15:19:08 +0000</pubDate>
      <link>https://forem.com/metered-video/webrtc-with-nodejs-building-a-video-chat-app-1boc</link>
      <guid>https://forem.com/metered-video/webrtc-with-nodejs-building-a-video-chat-app-1boc</guid>
      <description>&lt;h2&gt;
  
  
  Using WebRTC with NodeJS we will be creating a Video Chat App.
&lt;/h2&gt;

&lt;p&gt;In the guide we will go through building a Group Video Chat application, the application would allow the users to have a video conference and it would have features like active speaker detecting, waiting room and the ability to do screen sharing.&lt;/p&gt;

&lt;p&gt;We will build the application using HTML+JavaScript with Node.JS + Express in the backend, the backend logic would be very simple, it will call the Metered REST API to create meeting rooms and to validate the meeting ids.&lt;/p&gt;

&lt;p&gt;Our final application would run on all modern browsers on Windows/Mac/Linux as well as on mobile devices, like iOS and Android and would look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6s4VOoPb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqsdoj9e515pdai3rinh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6s4VOoPb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqsdoj9e515pdai3rinh.png" alt="video_calling_application" width="800" height="634"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Metered video Calling Application&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can download the complete source code from Github:&lt;br&gt;
&lt;a href="https://github.com/metered-ca/video-javascript-quickstart"&gt;https://github.com/metered-ca/video-javascript-quickstart&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Prerequisite
&lt;/h2&gt;

&lt;p&gt;To build the video calling application to follow this guide you need to have basic knowledge of HTML and JavaScript and some familiarity with Node.JS.&lt;/p&gt;

&lt;p&gt;We will use the Metered API and JavaScript SDK, for that you will need to have a Metered account, if you don't have it then you can easily create a free account by visiting &lt;a href="https://dashboard.metered.ca/signup"&gt;https://dashboard.metered.ca/signup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you have created an account, come back here for the next steps.&lt;/p&gt;
&lt;h2&gt;
  
  
  Application Structure
&lt;/h2&gt;

&lt;p&gt;Our application would have a Node.JS + Express backend and HTML+JavaScript font-end, the backend would provide APIs to the front-end to create a meeting room and generate a meeting id and also to validate an existing meeting id.&lt;/p&gt;

&lt;p&gt;Our folder structure would look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Xu1UjXZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qf6gpvlc8zy1rk3swcht.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Xu1UjXZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qf6gpvlc8zy1rk3swcht.png" alt="Metered sample app folder structure" width="212" height="289"&gt;&lt;/a&gt;&lt;br&gt;
 &lt;em&gt;Metered Group Video Calling Application Folder Structure&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;.env&lt;/strong&gt;&lt;br&gt;
 The .env file contains the environment variables here we will specify the &lt;code&gt;METERED_DOMAIN&lt;/code&gt; and &lt;code&gt;METERED_SECRET_KEY&lt;/code&gt; more on this later in the document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;src&lt;/strong&gt;&lt;br&gt;
The src folder contains all the source code for the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;src/server.js&lt;/strong&gt;&lt;br&gt;
The server.js file contains the backend code and API routes and also contains the code to serve the front-end files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;src/config.js&lt;/strong&gt;&lt;br&gt;
The config.js contains the config variables for the project and also loads the values from the .env file or from the environment variables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;src/public/index.html&lt;/strong&gt;&lt;br&gt;
The index.html file contains all the front-end user interface built with HTML&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;src/public/script.js&lt;/strong&gt;&lt;br&gt;
The script.js file contains all the front-end login related to our video calling application, it will use the Metered JavaScript SDK and is the brains of our application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building the Backend
&lt;/h2&gt;

&lt;p&gt;Let's start with building the backend of our application first.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Initializing our project
&lt;/h3&gt;

&lt;p&gt;We will initialize our project and create a &lt;code&gt;package.json&lt;/code&gt;, run the command below in your project root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm init -y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we will install a few dependencies that would be needed in build our backend service, we would require the following dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dotenv: To load the environment variables from the .env file.&lt;/li&gt;
&lt;li&gt;axios: To call the Metered REST APIs to create and validate Meeting IDs.&lt;/li&gt;
&lt;li&gt;express: To create REST routes for our server.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install dotenv --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install axios --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install express --save
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Creating config.js and .env file
&lt;/h3&gt;

&lt;p&gt;We will create a config.js file and here we will add the variables that we would need in our application, like the port the application will run on and the Metered Domain and Metered Secret Key&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript config.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;require('dotenv').config();
module.exports = {
    METERED_DOMAIN: process.env.METERED_DOMAIN || "",
    METERED_SECRET_KEY: process.env.METERED_SECRET_KEY || "",
    port: process.env.PORT || 4000
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To obtain the Metered Domain and Secret Key, open your Metered Dashboard by going to &lt;a href="https://dashboard.metered.ca"&gt;https://dashboard.metered.ca&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The name of your app + metered.live is your Metered Domain, for e.g name of your app is simpleapp then your Metered domain would be simpleapp.metered.live&lt;/p&gt;

&lt;p&gt;Then go to Developers tab there you will find the secret key:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XKRVEKzr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/me8udij6if791d091w4l.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XKRVEKzr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/me8udij6if791d091w4l.jpg" alt="Metered Secret Key" width="800" height="503"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Metered Secret Key&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now create a .env file in the project's root with the following contents, and replace &lt;code&gt;&amp;lt;METERED_DOMAIN&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;METERED_SECRET&amp;gt;&lt;/code&gt; key with the actual domain and secret key that we have obtained from the previous step.&lt;br&gt;
(Be sure to paste the key &lt;strong&gt;without&lt;/strong&gt; the &amp;lt; &amp;gt; angle brackets)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript .env&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;METERED_DOMAIN = "&amp;lt;METERED_DOMAIN&amp;gt;"
METERED_SECRET_KEY="&amp;lt;METERED_SECRET_KEY&amp;gt;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Writing the code for backend service in server.js
&lt;/h3&gt;

&lt;p&gt;Our server.js file would contain the APIs that would be used by our front-end application, and in the server.js file, we will call the Metered REST APIs.&lt;/p&gt;

&lt;p&gt;There are 3 tasks we need to accomplish:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Serving the front-end application&lt;/li&gt;
&lt;li&gt;API to create a meeting room and obtain a meeting id&lt;/li&gt;
&lt;li&gt;API to validate an existing meeting Id&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To accomplish that we will create 3 endpoints:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;/&lt;/code&gt; - Going to this route will serve our index.html&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/validate-meeting&lt;/code&gt; - This route will validate the meeting ID, we will call the Metered REST API to validate the Meeting ID here.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/create-meeting-room&lt;/code&gt; - This route will create a new meeting room, thus generating a new meeting ID, we will call the Metered REST API to create a room here and send the Room ID as the response.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/metered-domain&lt;/code&gt; - This is a very simple route we have created, it will send the metered domain that we have specified in our .env / config.js to the front-end&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Create server.js boilerplate code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will require the dependencies and create the route handlers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript server.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
//Requiring dependencies
const path = require("path");
const express = require('express');
var axios = require("axios").default;

// Creating express app
const app = express()
// Requiring the config
const config = require("./config");
const port = config.port;
// Printing the config for debugging
console.log(config);

// Checking if METERED_DOMAIN is specified, otherwise throwing an error.
if (!config.METERED_DOMAIN) {
    throw new Error("Please specify the METERED_DOMAIN.\nAdd as an environment variable or in the .env file or directly specify in the src/config.js\nIf you are unsure where to get METERED_DOMAIN please read the Advanced SDK Guide here: https://metered.ca/docs/Video%20Calls/JavaScript/Building%20a%20Group%20Video%20Calling%20Application");
}

// Check if METERED_SECRET_KEY is specified, otherwise throwing an error.
if (!config.METERED_SECRET_KEY) {
    throw new Error("Please specify the METERED_SECRET_KEY.\nAdd as an environment variable or in the .env file or directly specify in the src/config.js\nIf you are unsure where to get METERED_SECRET_KEY please read the Advanced SDK Guide here: https://metered.ca/docs/Video%20Calls/JavaScript/Building%20a%20Group%20Video%20Calling%20Application");
}

// Serving static files in the public folder
app.use("/", express.static(path.join(__dirname, '/public')))

app.get("/validate-meeting", function (req, res) {

});

app.post("/create-meeting-room", function(req, res) {

});

app.get("/metered-domain", function(req, res) {

});


app.listen(port, () =&amp;gt; {
    console.log(`app listening at http://localhost:${port}`)
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Serving Static Files&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To serve the static files in the public folder, that contains our front-end code, like &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;script.js&lt;/code&gt; we are using the express static middleware.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.use("/", express.static(path.join(__dirname, '/public')))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating /validate-meeting route&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;/validate-meeting&lt;/code&gt; route we are going to call the Metered REST API, we will call the &lt;a href="https://www.metered.ca/docs/"&gt;Get Room API&lt;/a&gt; and pass it is &lt;code&gt;Meeting ID&lt;/code&gt; sent to us by the client to validate if a such a room exists, if it does then we will send a success response and if it doesn't then we will return an error.&lt;/p&gt;

&lt;p&gt;We will use axios to make the HTTP request to the Metered Server.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
app.get("/validate-meeting", function (req, res) {
    /**
     * Using the Metered Get Room API to check if the 
     * Specified Meeting ID is valid.
     * https://metered.ca/api#tag/room-api/paths/~1room~1{roomName}/get
     */
    var options = {
        method: 'GET',
        url: "https://" + config.METERED_DOMAIN + '/api/v1/room/' + req.query.meetingId,
        params: {
            secretKey: config.METERED_SECRET_KEY
        },
        headers: {
            Accept: 'application/json'
        }
    };

    axios.request(options).then(function (response) {
        console.log(response.data);
        res.send({
            success: true
        })
    }).catch(function (error) {
        console.error(error);
        res.send({
            success: false
        })
    });
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Creating /create-meeting-room route&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the Create Meeting Room route we will again call the Metered REST API, and this time we will call the &lt;a href="https://www.metered.ca/docs/"&gt;Create Room API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
app.post("/create-meeting-room", function(req, res) {
    /**
     * Using the Metered Create Room API to create a new
     * Meeting Room.
     * https://metered.ca/api#tag/room-api/paths/~1room/post
     */
    var options = {
        method: 'POST',
        url: "https://" + config.METERED_DOMAIN + '/api/v1/room/',
        params: {
            secretKey: config.METERED_SECRET_KEY
        },
        headers: {
            Accept: 'application/json'
        }
    };

    axios.request(options).then(function (response) {
        console.log(response.data);
        res.send({
            success: true,
            ...response.data
        })
    }).catch(function (error) {
        console.error(error);
        res.send({
            success: false
        })
    });
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create /metered-domain route&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Metered Domain route is very simple, here we will just return the Metered Domain value that we have specified in the .env / config.js file.&lt;/p&gt;

&lt;p&gt;We are creating this route so that we can fetch the metered domain in our front-end application to initialize the Metered SDK and to keep the config centralized.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
app.get("/metered-domain", function(req, res) {
    res.send({
        domain: config.METERED_DOMAIN
    });
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Putting it all together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is our final server.js code with all the code put together:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
//Requiring dependencies
const path = require("path");
const express = require('express');
var axios = require("axios").default;

// Creating express app
const app = express()
// Requiring the config
const config = require("./config");
const port = config.port;
// Priting the config for debugging
console.log(config);

// Checking if METERED_DOMAIN is specified, otherwise throwing an error.
if (!config.METERED_DOMAIN) {
    throw new Error("Please specify the METERED_DOMAIN.\nAdd as an environment variable or in the .env file or directly specify in the src/config.js\nIf you are unsure where to get METERED_DOMAIN please read the Advanced SDK Guide here: https://metered.ca/docs/Video%20Calls/JavaScript/Building%20a%20Group%20Video%20Calling%20Application");
}

// Check if METERED_SECRET_KEY is specified, otherwise throwing an error.
if (!config.METERED_SECRET_KEY) {
    throw new Error("Please specify the METERED_SECRET_KEY.\nAdd as an environment variable or in the .env file or directly specify in the src/config.js\nIf you are unsure where to get METERED_SECRET_KEY please read the Advanced SDK Guide here: https://metered.ca/docs/Video%20Calls/JavaScript/Building%20a%20Group%20Video%20Calling%20Application");
}

// Serving static files in the public folder
app.use("/", express.static(path.join(__dirname, '/public')))

app.get("/validate-meeting", function (req, res) {
    /**
     * Using the Metered Get Room API to check if the 
     * Specified Meeting ID is valid.
     * https://metered.ca/api#tag/room-api/paths/~1room~1{roomName}/get
     */
    var options = {
        method: 'GET',
        url: "https://" + config.METERED_DOMAIN + '/api/v1/room/' + req.query.meetingId,
        params: {
            secretKey: config.METERED_SECRET_KEY
        },
        headers: {
            Accept: 'application/json'
        }
    };

    axios.request(options).then(function (response) {
        console.log(response.data);
        res.send({
            success: true
        })
    }).catch(function (error) {
        console.error(error);
        res.send({
            success: false
        })
    });
});

app.post("/create-meeting-room", function(req, res) {
    /**
     * Using the Metered Create Room API to create a new
     * Meeting Room.
     * https://metered.ca/api#tag/room-api/paths/~1room/post
     */
    var options = {
        method: 'POST',
        url: "https://" + config.METERED_DOMAIN + '/api/v1/room/',
        params: {
            secretKey: config.METERED_SECRET_KEY
        },
        headers: {
            Accept: 'application/json'
        }
    };

    axios.request(options).then(function (response) {
        console.log(response.data);
        res.send({
            success: true,
            ...response.data
        })
    }).catch(function (error) {
        console.error(error);
        res.send({
            success: false
        })
    });
});

app.get("/metered-domain", function(req, res) {
    res.send({
        domain: config.METERED_DOMAIN
    });
});


app.listen(port, () =&amp;gt; {
    console.log(`app listening at http://localhost:${port}`)
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Front End
&lt;/h2&gt;

&lt;p&gt;Let's start building the front-end of our application, we will first create our &lt;code&gt;index.html&lt;/code&gt; file and &lt;code&gt;script.js&lt;/code&gt; files and add some boilerplate code.&lt;/p&gt;

&lt;p&gt;In the front-end we have to build 4 main areas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Join Meeting Area - Allow the user to enter an existing meeting id or create a new meeting&lt;/li&gt;
&lt;li&gt;Waiting Area - Allow user to set a username, and select camera and microphone, see the preview of the camera and join the meeting&lt;/li&gt;
&lt;li&gt;Meeting Area - Main meeting interface&lt;/li&gt;
&lt;li&gt;Meeting Ended Area - A screen to show when the meeting end or the user decides to leave the meeting.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Boilerplate code
&lt;/h3&gt;

&lt;p&gt;We will create &lt;code&gt;index.html&lt;/code&gt; file and include the front-end dependencies like:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Metered Javascript SDK&lt;/li&gt;
&lt;li&gt;Daily UI a CSS Component library and Tailwind CSS for Styling&lt;/li&gt;
&lt;li&gt;jQuery&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will also create 4 main containers to hold our 4 views, the Join Meeting Area, Waiting Area, Meeting Area, and Meeting Ended Area and we will show/hide them as the user moves from one view to another.&lt;/p&gt;

&lt;p&gt;Initially the Join Meeting Area would be visible and rest of the views will be hidden:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en" class="bg-white"&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1" /&amp;gt;
    &amp;lt;title&amp;gt;Demo App&amp;lt;/title&amp;gt;



    &amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"
        integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="https://code.jquery.com/jquery-3.6.0.min.js"
        integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the webpage's stylesheet --&amp;gt;
    &amp;lt;link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.1/dist/tailwind.min.css" rel="stylesheet" type="text/css" /&amp;gt;
    &amp;lt;link href="https://cdn.jsdelivr.net/npm/daisyui@1.11.1/dist/full.css" rel="stylesheet" type="text/css" /&amp;gt;
    &amp;lt;script src="//cdn.metered.ca/sdk/video/1.1.6/sdk.min.js"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

   &amp;lt;!-- Header Nav Bar --&amp;gt;
    &amp;lt;div class="navbar mb-2 shadow-lg bg-neutral text-neutral-content"&amp;gt;
        &amp;lt;div class="flex-none px-2 mx-2"&amp;gt;
            &amp;lt;span class="text-lg font-bold"&amp;gt;
                Metered
            &amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;div class="flex-1 px-2 mx-2"&amp;gt;
            &amp;lt;div class="items-stretch hidden lg:flex"&amp;gt;
                &amp;lt;a href="https://metered.ca/docs/Video%20Calls/JavaScript/Building%20a%20Group%20Video%20Calling%20Application" target="_blank"
                    class="btn btn-ghost btn-sm rounded-btn"&amp;gt;
                    Advanced SDK Guide
                &amp;lt;/a&amp;gt;
                &amp;lt;a href="https://metered.ca/docs/Video%20Calls/JavaScript/Tips%20and%20Best%20Practices" target="_blank"
                    class="btn btn-ghost btn-sm rounded-btn"&amp;gt;
                    Tips and Best practices
                &amp;lt;/a&amp;gt;
                &amp;lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/Methods%20Introduction" target="_blank" class="btn btn-ghost btn-sm rounded-btn"&amp;gt;
                    SDK Reference
                &amp;lt;/a&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;!-- Header Nav Bar End --&amp;gt;



    &amp;lt;div id="meetingIdContainer" class="w-full bg-base-300 hidden font-bold text-center py-2"&amp;gt;
        Meeting ID: &amp;lt;span id="displayMeetingId"&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Join view --&amp;gt;
    &amp;lt;div id="joinView" class="w-full items-center justify-center flex"&amp;gt;

    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Waiting area --&amp;gt;
    &amp;lt;div id="waitingArea" class="w-full items-center justify-center flex hidden"&amp;gt;

    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Meeting view --&amp;gt;
    &amp;lt;div id="meetingView" class="hidden "&amp;gt;

    &amp;lt;/div&amp;gt;

  &amp;lt;!-- Leave View --&amp;gt;
    &amp;lt;div id="leaveView" class="flex items-center justify-center hidden"&amp;gt;

    &amp;lt;/div&amp;gt;
    &amp;lt;!-- Import the webpage's javascript file --&amp;gt;
    &amp;lt;script src="/script.js" defer&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Building the Join Meeting Area
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QM81po7m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tzhgtvpgusha856e6myq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QM81po7m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tzhgtvpgusha856e6myq.png" alt="join_meeting_area" width="800" height="238"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Metered Group Video Calling Application Join Area.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;#joinView&lt;/code&gt; div we will create the Join Meeting Area, the Join Meeting Area would contain an input to enter the Meeting ID and buttons to join the existing meeting or create a new meeting.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;html&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;div id="joinView" class="w-full items-center justify-center flex"&amp;gt;
        &amp;lt;div class="bg-base-300 w-11/12 max-w-screen-md  rounded mt-48 p-10"&amp;gt;
            &amp;lt;div class="form-control"&amp;gt;
                &amp;lt;label class="label"&amp;gt;
                    &amp;lt;span class="label-text"&amp;gt;Meeting ID&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;div class="relative"&amp;gt;
                    &amp;lt;input id="meetingId" type="text" placeholder="Meeting ID"
                        class="w-full pr-16 input input-primary input-bordered" /&amp;gt;
                    &amp;lt;button id="joinExistingMeeting" class="absolute top-0 right-0 rounded-l-none btn btn-primary text-xs"&amp;gt;
                        &amp;lt;span class="hidden sm:block"&amp;gt;Join Existing Meeting&amp;lt;/span&amp;gt;
                        &amp;lt;span class="sm:hidden"&amp;gt;Join&amp;lt;/span&amp;gt;
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="divider"&amp;gt;OR&amp;lt;/div&amp;gt;
            &amp;lt;div class="flex justify-center"&amp;gt;
                &amp;lt;button id="createANewMeeting" class="btn btn-primary"&amp;gt;Create a new meeting&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;#meetingId&lt;/code&gt; - Input will hold the value for the an existing meeting id that the user wish to join.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#joinExistingMeeting&lt;/code&gt; - Button will call our &lt;code&gt;/validate-meeting&lt;/code&gt; endpoint which will in turn call our Metered REST API to validate the meeting id, if the meeting id is valid then we will call the Metered SDK method to join the meeting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#createANewMeeting&lt;/code&gt; - Button will call our &lt;code&gt;/create-meeting-room&lt;/code&gt; endpoint to create a new room, and then will cal the Metered SDK method to join the newly created room.&lt;/p&gt;

&lt;p&gt;Here is our script.js code to handle the click events on the buttons        &lt;code&gt;#joinExistingMeeting&lt;/code&gt; and &lt;code&gt;#createANewMeeting&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// Creating instance of Metered Javascript SDK
const meeting = new Metered.Meeting();
// Creating a Global variable to store the Meeting ID
let meetingId = "";

$("#joinExistingMeeting").on("click", async function (e) {
    if (e) e.preventDefault();


    meetingId = $("#meetingId").val();
    if (!meetingId) {
        return alert("Please enter meeting id");
    }

    // Sending request to validate meeting id
    try {
        const response = await axios.get("/validate-meeting?meetingId=" + meetingId);
        if (response.data.success) {
            // Meeting id is valid, taking the user to the waiting area.
            $("#joinView").addClass("hidden")
            $("#waitingArea").removeClass("hidden");
            $("#displayMeetingId").text(meetingId);
            $("#meetingIdContainer").removeClass("hidden");
            initializeWaitingArea();
        } else {
            alert("meeting id is invalid");
        }
    } catch (ex) {
        alert("meeting Id is invalid");
    }

});

$("#createANewMeeting").on("click", async function (e) {
    if (e) e.preventDefault();

    // Sending request to create a new meeting room
    try {
        const response = await axios.post("/create-meeting-room");
        if (response.data.success) {
            $("#joinView").addClass("hidden")
            $("#waitingArea").removeClass("hidden");
            $("#displayMeetingId").text(response.data.roomName);
            $("#meetingIdContainer").removeClass("hidden");
            meetingId = response.data.roomName;
            initializeWaitingArea();
        }
    } catch (ex) {
        alert("Error occurred when creating a new meeting");
    }
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here if the existing meeting id is valid or after creating a new meeting id, we are calling the &lt;code&gt;initializeWaitingArea()&lt;/code&gt; method which we will discuss in the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Building the Waiting Area
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hyKM3CtI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74b3tj477x9vj6m127sx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hyKM3CtI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/74b3tj477x9vj6m127sx.jpg" alt="metered_group_video_calling_waiting_area" width="800" height="832"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Metered Group Video Calling Application Waiting Area.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;#waitingArea&lt;/code&gt; &lt;code&gt;div&lt;/code&gt; we will build the waiting area of the application, in the waiting area, we would want to perform the following operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allow the user to select the camera by listing the available cameras on the device&lt;/li&gt;
&lt;li&gt;Allow the user to select the microphone by listing the available microphones on the device.
&lt;/li&gt;
&lt;li&gt;Allow the user to select the speaker by listing the available audio output devices.&lt;/li&gt;
&lt;li&gt;Allow the user to join the meeting with microphone muted/unmuted&lt;/li&gt;
&lt;li&gt;Allow the user to join the meeting with camera muted/unmuted&lt;/li&gt;
&lt;li&gt;Show the preview of the selected camera&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Metered SDK provides us with various helper methods that would allow to easily accomplish these tasks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;html&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;div id="waitingArea" class="w-full items-center justify-center flex hidden"&amp;gt;
        &amp;lt;div class="bg-base-300 w-11/12 rounded mt-48 p-10"&amp;gt;
            &amp;lt;video id="waitingAreaVideoTag" class="w-full" muted autoplay playsinline&amp;gt;&amp;lt;/video&amp;gt;


            &amp;lt;div class="flex items-center justify-center mt-4 space-x-4"&amp;gt;
                &amp;lt;button id="waitingAreaMicrophoneButton" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"&amp;gt;
                        &amp;lt;path fill-rule="evenodd"
                            d="M7 4a3 3 0 016 0v4a3 3 0 11-6 0V4zm4 10.93A7.001 7.001 0 0017 8a1 1 0 10-2 0A5 5 0 015 8a1 1 0 00-2 0 7.001 7.001 0 006 6.93V17H6a1 1 0 100 2h8a1 1 0 100-2h-3v-2.07z"
                            clip-rule="evenodd"&amp;gt;&amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;
                &amp;lt;button id="waitingAreaCameraButton" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"&amp;gt;
                        &amp;lt;path
                            d="M2 6a2 2 0 012-2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zM14.553 7.106A1 1 0 0014 8v4a1 1 0 00.553.894l2 1A1 1 0 0018 13V7a1 1 0 00-1.447-.894l-2 1z"&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div class="divider"&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;div class="grid grid-cols-3 space-x-4"&amp;gt;
                &amp;lt;div class="form-control"&amp;gt;
                    &amp;lt;label class="label"&amp;gt;
                        &amp;lt;span class="label-text"&amp;gt;Camera&amp;lt;/span&amp;gt;
                    &amp;lt;/label&amp;gt;
                    &amp;lt;select id="cameras" class="select select-bordered w-full"&amp;gt; &amp;lt;/select&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;div class="form-control"&amp;gt;
                    &amp;lt;label class="label"&amp;gt;
                        &amp;lt;span class="label-text"&amp;gt;Microphone&amp;lt;/span&amp;gt;
                    &amp;lt;/label&amp;gt;
                    &amp;lt;select id="microphones" class="select select-bordered w-full"&amp;gt; &amp;lt;/select&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;div class="form-control"&amp;gt;
                    &amp;lt;label class="label"&amp;gt;
                        &amp;lt;span class="label-text"&amp;gt;Speaker&amp;lt;/span&amp;gt;
                    &amp;lt;/label&amp;gt;
                    &amp;lt;select id="speakers" class="select select-bordered w-full"&amp;gt; &amp;lt;/select&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div class="form-control"&amp;gt;
                &amp;lt;label class="label"&amp;gt;
                    &amp;lt;span class="label-text"&amp;gt;Enter a username&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;div class="relative"&amp;gt;
                    &amp;lt;input id="username" type="text" placeholder="username" class="w-full pr-16 input input-primary input-bordered" /&amp;gt;
                    &amp;lt;button id="joinMeetingButton" class="absolute top-0 right-0 rounded-l-none btn btn-primary"&amp;gt;
                        &amp;lt;span class="hidden sm:block"&amp;gt;Join Existing Meeting&amp;lt;/span&amp;gt;
                        &amp;lt;span class="sm:hidden"&amp;gt;Join&amp;lt;/span&amp;gt;
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;#waitingAreaVideoTag&lt;/code&gt; - &lt;em&gt;Video Tag:&lt;/em&gt; Used to show the preview of the camera.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#waitingAreaMicrophoneButton&lt;/code&gt; - &lt;em&gt;Button:&lt;/em&gt; Used to mute/unmute the microphone when the user joins the meeting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#waitingAreaCameraButton&lt;/code&gt; - &lt;em&gt;Button:&lt;/em&gt; Used to enable/disable the camera when the user joins the meeting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#cameras&lt;/code&gt; - &lt;em&gt;Select Input:&lt;/em&gt; Show the list of available cameras on the system.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#microphones&lt;/code&gt; - &lt;em&gt;Select Input:&lt;/em&gt; Show the list of available microphones on the system.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#speakers&lt;/code&gt; - &lt;em&gt;Select Input:&lt;/em&gt; Show the list of available audio outputs on the device.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#username&lt;/code&gt; - &lt;em&gt;Text Input:&lt;/em&gt; Allow the user to enter a username to join the meeting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#joinMeetingButton&lt;/code&gt; - &lt;em&gt;Button:&lt;/em&gt; When pressed the user will join the meeting, we will hide the waiting area and show the meeting area.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
/**
 * Method to initialize the waiting area:
 * This methods calls the SDK methods to request the 
 * user for microphone and camera permissions.
 */
var videoUnavailable = true;
var audioUnavailable = true;
async function initializeWaitingArea() {
    let audioOutputDevices = [];
    try {
        audioOutputDevices = await meeting.listAudioOutputDevices()
    } catch (ex) {
        console.log("option not available - it is unsupported in firefox", ex);
    }

    let audioInputDevices = [];
    try {
        audioInputDevices = await meeting.listAudioInputDevices();
    } catch (ex) {
        console.log("camera not available or have disabled camera access", ex);
        audioUnavailable = true;
        // Disabling the camera button
        $("#waitingAreaMicrophoneButton").attr("disabled", true)
    }

    let videoInputDevices = [];
    try {
        videoInputDevices = await meeting.listVideoInputDevices()
    } catch (ex) {
        console.log("camera not available or have disabled camera access", ex);
        videoUnavailable = true;
        // Disabling the camera button
        $("#waitingAreaCameraButton").attr("disabled", true)
    }



    let cameraOptions = [];
    for (let device of videoInputDevices) {
        cameraOptions.push(
            `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
        )
    }
    let microphoneOptions = [];
    for (let device of audioInputDevices) {
        microphoneOptions.push(
            `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
        )
    }
    let speakerOptions = [];
    for (let device of audioOutputDevices) {
        speakerOptions.push(
            `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
        )
    }

    $("#cameras").html(cameraOptions.join(""));
    $("#microphones").html(microphoneOptions.join(""));
    $("#speakers").html(speakerOptions.join(""));

    // Selecting different camera
    $("#cameras").on("change", async function (value) {
        const deviceId = $("#cameras").val();
        console.log(deviceId);
        await meeting.chooseVideoInputDevice(deviceId);
    });

    // Setting different microphone
    $("#microphones").on("change", async function (value) {
        const deviceId = $("#microphones").val();
        await meeting.chooseAudioInputDevice(deviceId);
    });

    // Setting different speaker
    $("#speakers").on("change", async function (value) {
        const deviceId = $("#speakers").val();
        await meeting.chooseAudioOutputDevice(deviceId);
    });
}

/**
 * Adding click events to buttons in waiting area
 */
let microphoneOn = false;
$("#waitingAreaMicrophoneButton").on("click", function () {
    if (microphoneOn) {
        $("#waitingAreaMicrophoneButton").removeClass("bg-accent");
        microphoneOn = false;
    } else {
        microphoneOn = true;
        $("#waitingAreaMicrophoneButton").addClass("bg-accent");
    }
});

let cameraOn = false;
let localVideoStream = null;
$("#waitingAreaCameraButton").on("click", async function () {
    if (cameraOn) {
        cameraOn = false;
        $("#waitingAreaCameraButton").removeClass("bg-accent");
        const tracks = localVideoStream.getTracks();
        tracks.forEach(function (track) {
            track.stop();
        });
        localVideoStream = null;
        $("#waitingAreaVideoTag")[0].srcObject = null;
    } else {
        try {
            $("#waitingAreaCameraButton").addClass("bg-accent");
            localVideoStream = await meeting.getLocalVideoStream();
            $("#waitingAreaVideoTag")[0].srcObject = localVideoStream;
            cameraOn = true;

        } catch (ex) {
            $("#waitingAreaCameraButton").removeClass("bg-accent");
            console.log("Error occurred when trying to acquire video stream", ex);
            $("#waitingAreaCameraButton").attr("disabled", true)
        }
    }
});

let meetingInfo = {};
$("#joinMeetingButton").on("click", async function () {
    var username = $("#username").val();
    if (!username) {
        return alert("Please enter a username");
    }

    try {
        console.log(meetingId)

        const {
            data
        } = await axios.get("/metered-domain");
        console.log(data.domain)

        meetingInfo = await meeting.join({
            roomURL: `${data.domain}/${meetingId}`,
            name: username
        });
        console.log("Meeting joined", meetingInfo);
        $("#waitingArea").addClass("hidden");
        $("#meetingView").removeClass("hidden");
        $("#meetingAreaUsername").text(username);
        if (cameraOn) {
            $("#meetingViewCamera").addClass("bg-accent");
            if (localVideoStream) {
                const tracks = localVideoStream.getTracks();
                tracks.forEach(function (track) {
                    track.stop();
                });
                localVideoStream = null;
            }
            await meeting.startVideo();
        }

        if (microphoneOn) {
            $("#meetingViewMicrophone").addClass("bg-accent");
            await meeting.startAudio();
        }
    } catch (ex) {
        console.log("Error occurred when joining the meeting", ex);
    }
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's see how we have accomplished 6 of our above tasks with Metered SDK:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loading the available cameras in the Select Box&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Metered SDK Provides a method called &lt;code&gt;listVideoInputDevices&lt;/code&gt; that returns a list of cameras connected to the device, in case of a mobile device it will list the front and back cameras and for a computer is multiple cameras are connected it would list all of them, allowing the user to select which camera they wish to share.&lt;/p&gt;

&lt;p&gt;You can read more about the method here &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/listVideoInputDevices"&gt;listVideoInputDevices()&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
let videoInputDevices = [];
    try {
        videoInputDevices = await meeting.listVideoInputDevices()
    } catch (ex) {
        console.log("camera not available or have disabled camera access", ex);
        videoUnavailable = true;
        // Disabling the camera button
        $("#waitingAreaCameraButton").attr("disabled", true)
    }

    let cameraOptions = [];
    for (let device of videoInputDevices) {
        cameraOptions.push(
            `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
        )
    }
 $("#cameras").html(cameraOptions.join(""));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet we are loading the list of cameras, and then populating the select box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Camera Selection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Metered SDK provides a method called &lt;code&gt;chooseVideoInputDevice()&lt;/code&gt; which accepts the a &lt;code&gt;deviceId&lt;/code&gt; which is returned by the &lt;code&gt;listVideoInputDevices()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;You can read more about the &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/chooseVideoInputDevice_deviceId"&gt;chooseVideoInputDevice() method here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// Selecting different camera
$("#cameras").on("change", async function (value) {
    const deviceId = $("#cameras").val();
    console.log(deviceId);
    await meeting.chooseVideoInputDevice(deviceId);
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we had attached an &lt;code&gt;onchange&lt;/code&gt; listener on the select box and then calling the &lt;code&gt;chooseVideoInputDevice()&lt;/code&gt; method of the Metered SDK and passing the &lt;code&gt;deviceId&lt;/code&gt; of the selected camera.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loading List of Available Microphones in the Select Box&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Metered SDK Provides a method called &lt;code&gt;listAudioInputDevices()&lt;/code&gt; that returns a list of microphones connected to the device.&lt;/p&gt;

&lt;p&gt;You can read more about the method &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/listAudioInputDevices"&gt; here listAudioInputDevices().&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
let audioInputDevices = [];
try {
    audioInputDevices = await meeting.listAudioInputDevices();
} catch (ex) {
    console.log("camera not available or have disabled camera access", ex);
    audioUnavailable = true;
    // Disabling the camera button
    $("#waitingAreaMicrophoneButton").attr("disabled", true)
}

let microphoneOptions = [];
for (let device of audioInputDevices) {
    microphoneOptions.push(
        `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
    )
}
$("#microphones").html(microphoneOptions.join(""));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet we are fetching the list of microphones and then adding them to a select box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Microphone Selection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Metered SDK provides a method called &lt;code&gt;chooseAudioInputDevice()&lt;/code&gt; which accepts the a &lt;code&gt;deviceId&lt;/code&gt; which is returned by the &lt;code&gt;listAudioInputDevices()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;You can read more about the &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/chooseAudioInputDevice_deviceId"&gt;chooseAudioInputDevice() method here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// Setting different microphone
$("#microphones").on("change", async function (value) {
    const deviceId = $("#microphones").val();
    await meeting.chooseAudioInputDevice(deviceId);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we had attached an &lt;code&gt;onchange&lt;/code&gt; listener on the select box and then calling the &lt;code&gt;chooseAudioInputDevice()&lt;/code&gt; method of the Metered SDK and passing the &lt;code&gt;deviceId&lt;/code&gt; of the selected camera.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loading List of Available Audio Outputs (Speakers) in the Select Box&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Metered SDK Provides a method called &lt;code&gt;listAudioOutputDevices()&lt;/code&gt; that returns a list of audio output devices, like speakers or headphones connected to the device.&lt;/p&gt;

&lt;p&gt;You can read more about the method &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/listAudioOutputDevices"&gt;here listAudioOutputDevices().&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works in Google Chrome, but not all browsers currently support this method.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
let audioOutputDevices = [];
try {
    audioOutputDevices = await meeting.listAudioOutputDevices()
} catch (ex) {
    console.log("option not available - it is unsupported in firefox", ex);
}
let speakerOptions = [];
for (let device of audioOutputDevices) {
    speakerOptions.push(
        `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
    )
}
$("#speakers").html(speakerOptions.join(""));

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet we are calling &lt;code&gt;listAudioOutputDevices&lt;/code&gt; method of the Metered SDK and then populating a select box with the returned values.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Speaker Selection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To select the speaker, there is method called a &lt;code&gt;chooseAudioOutputDevice()&lt;/code&gt; which accepts the &lt;code&gt;deviceId&lt;/code&gt; of the audio output device returned by the &lt;code&gt;listAudioOutputDevices()&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;You can read more about &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/chooseAudioOutputDevice_deviceId"&gt;chooseAudioOutputDevice() method here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
// Setting different speaker
$("#speakers").on("change", async function (value) {
    const deviceId = $("#speakers").val();
    await meeting.chooseAudioOutputDevice(deviceId);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet, we are attaching an &lt;code&gt;onchange&lt;/code&gt; listener to the select box where we have populated the audio output devices, and then when an option is selected we are passing the selected &lt;code&gt;deviceId&lt;/code&gt; to the &lt;code&gt;chooseAudioOutputDevice&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allow the user to join the meeting with microphone muted/unmuted&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will create a variable called &lt;code&gt;microphoneOn&lt;/code&gt; and add a click listener to the microphone button in the waiting area microphone button and then toggle the value of this variable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let microphoneOn = false;
$("#waitingAreaMicrophoneButton").on("click", function () {
    if (microphoneOn) {
        $("#waitingAreaMicrophoneButton").removeClass("bg-accent");
        microphoneOn = false;
    } else {
        microphoneOn = true;
        $("#waitingAreaMicrophoneButton").addClass("bg-accent");
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when the user presses the join meeting button, and after joining the meeting we will check the value of the &lt;code&gt;microphoneOn&lt;/code&gt; variable, if it is set to true then we will call the &lt;code&gt;startAudio()&lt;/code&gt; method of the Metered SDK, we will describe in the implementation of this later in the article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Allow the user to join the meeting with camera muted/unmuted and showing the preview of the camera in the waiting area&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Similar to &lt;code&gt;microphoneOn&lt;/code&gt; we will create a variable called &lt;code&gt;cameraOn&lt;/code&gt; and attach a click listener to the camera button in the waiting area, and toggle the value of the &lt;code&gt;cameraOn&lt;/code&gt; variable, and when the user presses the join meeting button we will call the &lt;code&gt;startVideo()&lt;/code&gt; method of the Metered SDK.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let cameraOn = false;
let localVideoStream = null;
$("#waitingAreaCameraButton").on("click", async function () {
    if (cameraOn) {
        cameraOn = false;
        $("#waitingAreaCameraButton").removeClass("bg-accent");
        const tracks = localVideoStream.getTracks();
        tracks.forEach(function (track) {
            track.stop();
        });
        localVideoStream = null;
        $("#waitingAreaVideoTag")[0].srcObject = null;
    } else {
        try {
            $("#waitingAreaCameraButton").addClass("bg-accent");
            localVideoStream = await meeting.getLocalVideoStream();
            $("#waitingAreaVideoTag")[0].srcObject = localVideoStream;
            cameraOn = true;

        } catch (ex) {
            $("#waitingAreaCameraButton").removeClass("bg-accent");
            console.log("Error occurred when trying to acquire video stream", ex);
            $("#waitingAreaCameraButton").attr("disabled", true)
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code snippet, there is an &lt;code&gt;if condition&lt;/code&gt;, which is checking whether the &lt;code&gt;cameraOn&lt;/code&gt; variable is set to true or not.&lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;cameraOn&lt;/code&gt; is set to true then we are turning off the camera, and if it is false then we are turning on the camera, let start with the "else" part first.&lt;/p&gt;

&lt;p&gt;In the else block we are calling a Metered SDK method &lt;code&gt;getLocalVideoStream()&lt;/code&gt; this method returns the video steam of the device video device or of the device selected using the &lt;code&gt;chooseVideoInputDevice()&lt;/code&gt; method, you read more about the &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/getLocalVideoStream"&gt;&lt;code&gt;getLocalVideoStream()&lt;/code&gt; method here.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;localVideoStream = await meeting.getLocalVideoStream();
$("#waitingAreaVideoTag")[0].srcObject = localVideoStream;
cameraOn = true;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we have created a video tag in our HTML file to show the local video, so we will be setting the &lt;code&gt;srcObject&lt;/code&gt; attribute of the video tag to our &lt;code&gt;localVideoStream&lt;/code&gt;, this will show the local video stream in the video tag and we will set the &lt;code&gt;cameraOn&lt;/code&gt; variable to true.&lt;/p&gt;

&lt;p&gt;Now if the user presses the camera button again, our method will be executed, and this time the &lt;code&gt;cameraOn&lt;/code&gt; variable will be set to true.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const tracks = localVideoStream.getTracks();
tracks.forEach(function (track) {
    track.stop();
});
localVideoStream = null;
$("#waitingAreaVideoTag")[0].srcObject = null;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we will stop the &lt;code&gt;localVideoStream&lt;/code&gt;, so that the camera light turns off, to do that we would need to fetch the tracks of the &lt;code&gt;localVideoStream&lt;/code&gt; and stop all the tracks, this will turn off the camera light, and we will set the &lt;code&gt;cameraOn&lt;/code&gt; variable to false.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Joining the Meeting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will attach an &lt;code&gt;onclick&lt;/code&gt; listener to the &lt;code&gt;#joinMeetingButton&lt;/code&gt; and in the event handler, we will call the &lt;code&gt;join()&lt;/code&gt; method of the Metered SDK.&lt;/p&gt;

&lt;p&gt;After the user successfully joins the meeting, we will check if the value of &lt;code&gt;cameraOn&lt;/code&gt; is set to true, if yes then we will stop the &lt;code&gt;localVideoStream&lt;/code&gt; which was used to show the preview of the camera in the waiting area and call the &lt;code&gt;startVideo()&lt;/code&gt; method to share the camera with the meeting participants.&lt;/p&gt;

&lt;p&gt;We will check if &lt;code&gt;microphoneOn&lt;/code&gt; variable is set to true, if yes then we will call the &lt;code&gt;startAudio()&lt;/code&gt; method to share the microphone with the meeting participants.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let meetingInfo = {};
$("#joinMeetingButton").on("click", async function () {
    var username = $("#username").val();
    if (!username) {
        return alert("Please enter a username");
    }

    try {
        console.log(meetingId)

       // Fetching our Metered Domain e.g: videoapp.metered.live 
       // that we have added in the .env/config.js file in backend

        const {
            data
        } = await axios.get("/metered-domain");
        console.log(data.domain)
                // Calling the Join Method of the Metered SDK
        meetingInfo = await meeting.join({
            roomURL: `${data.domain}/${meetingId}`,
            name: username
        });
        console.log("Meeting joined", meetingInfo);
        $("#waitingArea").addClass("hidden");
        $("#meetingView").removeClass("hidden");
        $("#meetingAreaUsername").text(username);
        if (cameraOn) {
            $("#meetingViewCamera").addClass("bg-accent");
            if (localVideoStream) {
                const tracks = localVideoStream.getTracks();
                tracks.forEach(function (track) {
                    track.stop();
                });
                localVideoStream = null;
            }
            await meeting.startVideo();
        }

        if (microphoneOn) {
            $("#meetingViewMicrophone").addClass("bg-accent");
            await meeting.startAudio();
        }
    } catch (ex) {
        console.log("Error occurred when joining the meeting", ex);
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Building the Meeting Area
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0oON88rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4cxv4hrpy4a46o2hk7t9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0oON88rY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4cxv4hrpy4a46o2hk7t9.jpg" alt="Metered meeting area" width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the meeting area the actual meeting takes place, here we have to implement the following functionality:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If the user has shared their camera/screen show the preview of the camera or screen&lt;/li&gt;
&lt;li&gt;When a remote user joins the meeting show the user in the online users list&lt;/li&gt;
&lt;li&gt;When a remote user leaves the meeting remove the user&lt;/li&gt;
&lt;li&gt;When remote user share their camera / screen show the video stream&lt;/li&gt;
&lt;li&gt;When remote user share microphone handle the audio stream&lt;/li&gt;
&lt;li&gt;Allow the user to share microphone&lt;/li&gt;
&lt;li&gt;Allow user to share camera&lt;/li&gt;
&lt;li&gt;Allow user to share screen&lt;/li&gt;
&lt;li&gt;Enable active speaker detection and show the user who is speaking in the center.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let start with building the UI for the meeting area:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- Meeting view --&amp;gt;
&amp;lt;div id="meetingView"&amp;gt;
    &amp;lt;!-- remote video containers --&amp;gt;
    &amp;lt;div id="remoteParticipantContainer" style="display: flex;"&amp;gt;

    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Active Speaker --&amp;gt;
    &amp;lt;div class="mt-4"&amp;gt;
        &amp;lt;div style=" border-radius: 5px;" class="bg-base-300"&amp;gt;
            &amp;lt;video id="activeSpeakerVideo" muted autoplay playsinline
                style="padding: 0; margin: 0; width: 100%; height: 400px;"&amp;gt;&amp;lt;/video&amp;gt;
            &amp;lt;div id="activeSpeakerUsername" class="bg-base-300  " style=" text-align: center;"&amp;gt;

            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;div class="flex flex-col bg-base-300" style="width: 150px"&amp;gt;
        &amp;lt;video id="meetingAreaLocalVideo" muted autoplay playsinline
            style="padding: 0; margin: 0; width: 150px; height: 100px;"&amp;gt;&amp;lt;/video&amp;gt;
        &amp;lt;div id="meetingAreaUsername" class="bg-base-300    " style=" text-align: center;"&amp;gt;

        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;!-- Controls --&amp;gt;
    &amp;lt;div style="display: flex; justify-content: center; margin-top: 20px;" class="space-x-4"&amp;gt;
        &amp;lt;button id="meetingViewMicrophone" class="btn"&amp;gt;
            &amp;lt;svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
                xmlns="http://www.w3.org/2000/svg"&amp;gt;
                &amp;lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"&amp;gt;
                &amp;lt;/path&amp;gt;
            &amp;lt;/svg&amp;gt;
        &amp;lt;/button&amp;gt;

        &amp;lt;button id="meetingViewCamera" class="btn"&amp;gt;
            &amp;lt;svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
                xmlns="http://www.w3.org/2000/svg"&amp;gt;
                &amp;lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"&amp;gt;
                &amp;lt;/path&amp;gt;
            &amp;lt;/svg&amp;gt;
        &amp;lt;/button&amp;gt;

        &amp;lt;button id="meetingViewScreen" class="btn"&amp;gt;
            &amp;lt;svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
                xmlns="http://www.w3.org/2000/svg"&amp;gt;
                &amp;lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                    d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"&amp;gt;
                &amp;lt;/path&amp;gt;
            &amp;lt;/svg&amp;gt;
        &amp;lt;/button&amp;gt;

        &amp;lt;button id="meetingViewLeave" class="btn"&amp;gt;
            &amp;lt;svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"&amp;gt;&amp;lt;path fill-rule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z" clip-rule="evenodd"&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;
        &amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;#remoteParticipantContainer&lt;/code&gt; div - Here we will add the remote participants as they join the meeting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#activeSpeakerVideo&lt;/code&gt; video tag - In this video tag we will show the video stream of the active speaker. this video tag is at the center of the page.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#activeSpeakerUsername&lt;/code&gt; div - Here we will show the username of the active speaker&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#meetingAreaLocalVideo&lt;/code&gt; video tag - The video preview of the local camera stream of the user if the user has shared his/her camera or screen.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#meetingAreaUsername&lt;/code&gt; div - This will contain the show the username of the current user.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#meetingViewMicrophone&lt;/code&gt; button - This button when pressed will share the microphone with other participants in the meeting.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#meetingViewCamera&lt;/code&gt; button - This button will share the camera with other participants in the meeting&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#meetingViewScreen&lt;/code&gt; button - This button will share the screen with other participants in the meeting&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#meetingViewLeave&lt;/code&gt; button - This exits the user from the meeting.&lt;/p&gt;

&lt;p&gt;Let's see how we can achieve the goals that we have listed above:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Showing/Hiding the preview of screen or camera shared by the user&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have created a video tag with id &lt;code&gt;#meetingAreaLocalVideo&lt;/code&gt;, in this video tag we will show the preview of the local camera or screen shared by our current user&lt;/p&gt;

&lt;p&gt;To achieve this, Metered SDK emits some events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;localTrackStarted - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/getLocalVideoStream"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;localTrackUpdated&lt;/li&gt;
&lt;li&gt;localTrackStopped - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Events/localTrackStopped"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whenever the local media is shared, wether audio or video this event is emitted, we will not do anything when audio is emitted (because if we add the audio tag and add the stream then user will hear his/her own voice through the speakers), but when a video stream is shared we will add it to our #meetingAreaLocalVideo video tag.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;meeting.on("localTrackStarted", function (trackItem) {
    if (trackItem.type === "video") {
        let track = trackItem.track;
        let mediaStream = new MediaStream([track]);
        $("#meetingAreaLocalVideo")[0].srcObject = mediaStream;
        $("#meetingAreaLocalVideo")[0].play();
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user selects a different camera or switches from camera sharing to screen sharing, the &lt;code&gt;localTrackUpdated&lt;/code&gt; event is emitted, when this event is emitted we have to update our video tag so that it shows the currently shared video stream. (If we do not handle this event and the user selects a different camera or select screen sharing, then the video tag will show blank video).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;meeting.on("localTrackUpdated", function (trackItem) {
    if (trackItem.type === "video") {
        let track = trackItem.track;
        let mediaStream = new MediaStream([track]);
        $("#meetingAreaLocalVideo")[0].srcObject = mediaStream;
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, when the user wants to stop sharing his/her camera or screen, we have to remove the video from the video tag.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
meeting.on("localTrackStopped", function (localTrackItem) {
    if (localTrackItem.type === "video") {
        $("#meetingAreaLocalVideo")[0].srcObject = null;
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Handling remote participants&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We haven't talked about how we are going to show the video or handle the microphone shared by the remote participants in the meeting, so here we will go through how that is handled.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling participant left and participant joined&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(1) When a remote participant joins the meeting we want to indicate that someone has joined the meeting, and we will show their username somewhere and create the video and audio tags to show the video if they share their camera or screen and listen to their audio if they share their microphone.&lt;/p&gt;

&lt;p&gt;(2) Similarly, when the participant leaves the meeting we want to remove the block where the username, audio, and video tag for the participant is present.&lt;/p&gt;

&lt;p&gt;(3) Also, when the user joins a meeting where there are already participants present, we need to handle get the list of all the existing users in the meeting and display their username and create audio and video tags to show the audio or video shared by the existing participants.&lt;/p&gt;

&lt;p&gt;To handle the above 3 scenarios we have events provided by the Metered JavaScript SDK,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;participantJoined - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Events/participantJoined"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;participantLeft - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Events/participantLeft"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;onlineParticipants. - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Events/onlineParticipants"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a new participant joins the meeting the &lt;code&gt;participantJoined&lt;/code&gt; event is emitted, when a participants leaves the meeting &lt;code&gt;participantLeft&lt;/code&gt; event is emitted and when the user joins a meeting where there are existing participants then &lt;code&gt;onlineParticipants&lt;/code&gt; event is emitted with a list of existing participants.&lt;/p&gt;

&lt;p&gt;Let write the code to handle the &lt;code&gt;participantJoined&lt;/code&gt; event:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;meeting.on("participantJoined", function (participantInfo) {

    // This event is emitted for all the users, even for the current user,
    // so we want ignore if it is the current user.
    if (participantInfo._id === meeting.participantSessionId) return;

    // Creating a div with video, audio and a div tag to show username
    // Giving the div tag id of the participant so that it is easy for us to remove the tag
    // when the participant leaves the meeting.
    var participant =
        `&amp;lt;div id="participant-${participantInfo._id}" class="bg-base-300"&amp;gt;
        &amp;lt;video id="participant-${participantInfo._id}-video" muted autoplay playsinline
            style="padding: 0; margin: 0; width: 150px; height: 100px;"&amp;gt;&amp;lt;/video&amp;gt;
        &amp;lt;audio id="participant-${participantInfo._id}-audio" autoplay playsinline
            style="padding: 0; margin: 0;"&amp;gt;&amp;lt;/audio&amp;gt;
        &amp;lt;div id="participant-${participantInfo._id}-username" class="bg-base-300    " style=" text-align: center;"&amp;gt;
            ${participantInfo.name}
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;`
    // Adding the HTML to our remoteParticipantContainer
    $("#remoteParticipantContainer").append(participant)
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code to handle the &lt;code&gt;participantLeft&lt;/code&gt; event: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;meeting.on("participantLeft", function (participantInfo) {
    console.log("participant has left the room", participantInfo);
    $(`#participant-${participantInfo._id}`).remove();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above code we are removing the div for the participant, that contains the participant's username, video and audio tags.&lt;/p&gt;

&lt;p&gt;Code to handle &lt;code&gt;onlineParticipants&lt;/code&gt; event:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;meeting.on("onlineParticipants", function (onlineParticipants) {

    $("#remoteParticipantContainer").html("");
    for (let participantInfo of onlineParticipants) {
        if (participantInfo._id !== meeting.participantSessionId) {
            var participant =
                `&amp;lt;div id="participant-${participantInfo._id}" class="bg-base-300"&amp;gt;
                &amp;lt;video id="participant-${participantInfo._id}-video" muted autoplay playsinline
                    style="padding: 0; margin: 0; width: 150px; height: 100px;"&amp;gt;&amp;lt;/video&amp;gt;
                &amp;lt;audio id="participant-${participantInfo._id}-audio" autoplay playsinline
                    style="padding: 0; margin: 0;"&amp;gt;&amp;lt;/audio&amp;gt;
                &amp;lt;div id="participant-${participantInfo._id}-username" class="bg-base-300    " style=" text-align: center;"&amp;gt;
                    ${participantInfo.name}
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;`
            $("#remoteParticipantContainer").append(participant)
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The online participant's code is very similar to &lt;code&gt;participantJoined&lt;/code&gt; event code, the only difference here is that we get an array of participants instead of one single participant and we loop through the array add them to the UI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling when remote participants share their camera, screen or microphone&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the previous step, we have created the audio and video tag for the remote participants, now we need to add the video stream or audio stream to the audio or video tag and remove the audio and video stream when they share their video (screen or camera) and audio respectively. &lt;/p&gt;

&lt;p&gt;For each remote participant we have created an audio tag with id &lt;code&gt;participant-${participantInfo._id}-audio&lt;/code&gt; and video tag with id &lt;code&gt;participant-${participantInfo._id}-video&lt;/code&gt; where ${participantInfo._id} will replace with the id of the participant, by creating id's like this it becomes easier for us to find the appropriate video/audio tag for the participant to attach the video or audio stream.    &lt;/p&gt;

&lt;p&gt;When the remote participant share's their video or microphone &lt;code&gt;remoteTrackStarted&lt;/code&gt; event is emitted to all the participants in the meeting, and when the remote participant stop's sharing the camera or microphone, &lt;code&gt;remoteTrackStopped&lt;/code&gt; event is emitted to all the participants.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remoteTrackStarted - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Events/remoteTrackStarted"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;remoteTrackStopped - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Events/remoteTrackStopped"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;meeting.on("remoteTrackStarted", function (trackItem) {

    if (trackItem.participantSessionId === meeting.participantSessionId) return;
    var track = trackItem.track;
    var mediaStream = new MediaStream([track]);
    $(`#participant-${trackItem.participantSessionId}-${trackItem.type}`)[0].srcObject = mediaStream;
    $(`#participant-${trackItem.participantSessionId}-${trackItem.type}`)[0].play();
});

meeting.on("remoteTrackStopped", function (trackItem) {
    if (trackItem.participantSessionId === meeting.participantSessionId) return;
    $(`#participant-${trackItem.participantSessionId}-${trackItem.type}`)[0].srcObject = null;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Handling active speaker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We have created a large video &lt;code&gt;#activeSpeakerVideo&lt;/code&gt; in the center of the page, and here we will show the user who is currently speaking, to implement this Metered SDK provides and event called as &lt;code&gt;activeSpeaker&lt;/code&gt;, this event contains the info of the user who is actively speaking.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;activeSpeaker - &lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Events/activeSpeaker"&gt;Read more about it here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var currentActiveSpeaker = "";
meeting.on("activeSpeaker", function (activeSpeaker) {

    if (currentActiveSpeaker === activeSpeaker.participantSessionId) return;

    $("#activeSpeakerUsername").text(activeSpeaker.name);
    currentActiveSpeaker = activeSpeaker.participantSessionId;
    if ($(`#participant-${activeSpeaker.participantSessionId}-video`)[0]) {
        let stream = $(`#participant-${activeSpeaker.participantSessionId}-video`)[0].srcObject;
        $("#activeSpeakerVideo")[0].srcObject = stream.clone();

    }

    if (activeSpeaker.participantSessionId === meeting.participantSessionId) {
        let stream = $(`#meetingAreaLocalVideo`)[0].srcObject;
        if (stream) {
            $("#activeSpeakerVideo")[0].srcObject = stream.clone();
        }

    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we will clone the video stream of the active speaking user from its video tag and show it in the &lt;code&gt;#activeSpeakerVideo&lt;/code&gt; video tag, and also show the username of user in the &lt;code&gt;#activeSpeakerUsername&lt;/code&gt; div tag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Leave Meeting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the participant closes the window, the participant automatically leaves the meeting, we can also call the &lt;code&gt;leaveMeeting()&lt;/code&gt;, if we want to leave the meeting.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;javascript&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
$("#meetingViewLeave").on("click", async function () {
    await meeting.leaveMeeting();
    $("#meetingView").addClass("hidden");
    $("#leaveView").removeClass("hidden");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Complete Front-End Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is our complete front-end code:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;script.js&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const meeting = new Metered.Meeting();
let meetingId = "";

$("#joinExistingMeeting").on("click", async function (e) {
    if (e) e.preventDefault();


    meetingId = $("#meetingId").val();
    if (!meetingId) {
        return alert("Please enter meeting id");
    }

    // Sending request to validate meeting id
    try {
        const response = await axios.get("/validate-meeting?meetingId=" + meetingId);
        if (response.data.success) {
            // Meeting id is valid, taking the user to the waiting area.
            $("#joinView").addClass("hidden")
            $("#waitingArea").removeClass("hidden");
            $("#displayMeetingId").text(meetingId);
            $("#meetingIdContainer").removeClass("hidden");
            initializeWaitingArea();
        } else {
            alert("meeting id is invalid");
        }
    } catch (ex) {
        alert("meeting Id is invalid");
    }

});

$("#createANewMeeting").on("click", async function (e) {
    if (e) e.preventDefault();

    // Sending request to create a new meeting room
    try {
        const response = await axios.post("/create-meeting-room");
        if (response.data.success) {
            $("#joinView").addClass("hidden")
            $("#waitingArea").removeClass("hidden");
            $("#displayMeetingId").text(response.data.roomName);
            $("#meetingIdContainer").removeClass("hidden");
            meetingId = response.data.roomName;
            initializeWaitingArea();
        }
    } catch (ex) {
        alert("Error occurred when creating a new meeting");
    }
});


/**
 * Method to initialize the waiting area:
 * This methods calls the SDK methods to request the 
 * user for microphone and camera permissions.
 */
var videoUnavailable = true;
var audioUnavailable = true;
async function initializeWaitingArea() {
    let audioOutputDevices = [];
    try {
        audioOutputDevices = await meeting.listAudioOutputDevices()
    } catch (ex) {
        console.log("option not available - it is unsupported in firefox", ex);
    }

    let audioInputDevices = [];
    try {
        audioInputDevices = await meeting.listAudioInputDevices();
    } catch (ex) {
        console.log("camera not available or have disabled camera access", ex);
        audioUnavailable = true;
        // Disabling the camera button
        $("#waitingAreaMicrophoneButton").attr("disabled", true)
    }

    let videoInputDevices = [];
    try {
        videoInputDevices = await meeting.listVideoInputDevices()
    } catch (ex) {
        console.log("camera not available or have disabled camera access", ex);
        videoUnavailable = true;
        // Disabling the camera button
        $("#waitingAreaCameraButton").attr("disabled", true)
    }



    let cameraOptions = [];
    for (let device of videoInputDevices) {
        cameraOptions.push(
            `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
        )
    }
    let microphoneOptions = [];
    for (let device of audioInputDevices) {
        microphoneOptions.push(
            `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
        )
    }
    let speakerOptions = [];
    for (let device of audioOutputDevices) {
        speakerOptions.push(
            `&amp;lt;option value="${device.deviceId}"&amp;gt;${device.label}&amp;lt;/option&amp;gt;`
        )
    }

    $("#cameras").html(cameraOptions.join(""));
    $("#microphones").html(microphoneOptions.join(""));
    $("#speakers").html(speakerOptions.join(""));

    // Selecting different camera
    $("#cameras").on("change", async function (value) {
        const deviceId = $("#cameras").val();
        console.log(deviceId);
        await meeting.chooseVideoInputDevice(deviceId);
    });

    // Setting different microphone
    $("#microphones").on("change", async function (value) {
        const deviceId = $("#microphones").val();
        await meeting.chooseAudioInputDevice(deviceId);
    });

    // Setting different speaker
    $("#speakers").on("change", async function (value) {
        const deviceId = $("#speakers").val();
        await meeting.chooseAudioOutputDevice(deviceId);
    });

}


/**
 * Adding click events to buttons in waiting area
 */
let microphoneOn = false;
$("#waitingAreaMicrophoneButton").on("click", function () {
    if (microphoneOn) {
        $("#waitingAreaMicrophoneButton").removeClass("bg-accent");
        microphoneOn = false;
    } else {
        microphoneOn = true;
        $("#waitingAreaMicrophoneButton").addClass("bg-accent");
    }
});

let cameraOn = false;
let localVideoStream = null;
$("#waitingAreaCameraButton").on("click", async function () {
    if (cameraOn) {
        cameraOn = false;
        $("#waitingAreaCameraButton").removeClass("bg-accent");
        const tracks = localVideoStream.getTracks();
        tracks.forEach(function (track) {
            track.stop();
        });
        localVideoStream = null;
        $("#waitingAreaVideoTag")[0].srcObject = null;
    } else {
        try {
            $("#waitingAreaCameraButton").addClass("bg-accent");
            localVideoStream = await meeting.getLocalVideoStream();
            $("#waitingAreaVideoTag")[0].srcObject = localVideoStream;
            cameraOn = true;

        } catch (ex) {
            $("#waitingAreaCameraButton").removeClass("bg-accent");
            console.log("Error occurred when trying to acquire video stream", ex);
            $("#waitingAreaCameraButton").attr("disabled", true)
        }
    }

});


let meetingInfo = {};
$("#joinMeetingButton").on("click", async function () {
    var username = $("#username").val();
    if (!username) {
        return alert("Please enter a username");
    }

    try {
        console.log(meetingId)

        const {
            data
        } = await axios.get("/metered-domain");
        console.log(data.domain)

        meetingInfo = await meeting.join({
            roomURL: `${data.domain}/${meetingId}`,
            name: username
        });
        console.log("Meeting joined", meetingInfo);
        $("#waitingArea").addClass("hidden");
        $("#meetingView").removeClass("hidden");
        $("#meetingAreaUsername").text(username);
        if (cameraOn) {
            $("#meetingViewCamera").addClass("bg-accent");
            if (localVideoStream) {
                const tracks = localVideoStream.getTracks();
                tracks.forEach(function (track) {
                    track.stop();
                });
                localVideoStream = null;
            }
            await meeting.startVideo();
        }

        if (microphoneOn) {
            $("#meetingViewMicrophone").addClass("bg-accent");
            await meeting.startAudio();
        }
    } catch (ex) {
        console.log("Error occurred when joining the meeting", ex);
    }
});

/**
 * Adding click events to buttons in Meeting Area
 */
$("#meetingViewMicrophone").on("click", async function () {
    if (microphoneOn) {
        microphoneOn = false;
        $("#meetingViewMicrophone").removeClass("bg-accent");
        await meeting.stopAudio();
    } else {
        microphoneOn = true;
        $("#meetingViewMicrophone").addClass("bg-accent");
        await meeting.startAudio();
    }
});

$("#meetingViewCamera").on("click", async function () {
    if (cameraOn) {
        cameraOn = false;
        $("#meetingViewCamera").removeClass("bg-accent");
        await meeting.stopVideo();
    } else {
        cameraOn = true;
        $("#meetingViewCamera").addClass("bg-accent");
        await meeting.startVideo();
    }
});

let screenSharing = false;
$("#meetingViewScreen").on("click", async function () {
    if (screenSharing) {
        $("#meetingViewScreen").removeClass("bg-accent");
        await meeting.stopVideo();
        return;
    } else {
        try {
            await meeting.startScreenShare();
            screenSharing = true;
            cameraOn = false;
            $("#meetingViewCamera").removeClass("bg-accent");
            $("#meetingViewScreen").addClass("bg-accent");
        } catch (ex) {
            console.log("Error occurred when trying to share screen", ex);
        }
    }
});


/**
 * Listening to events
 */

meeting.on("localTrackStarted", function (trackItem) {
    if (trackItem.type === "video") {
        let track = trackItem.track;
        let mediaStream = new MediaStream([track]);
        $("#meetingAreaLocalVideo")[0].srcObject = mediaStream;
        $("#meetingAreaLocalVideo")[0].play();
    }
});
meeting.on("localTrackUpdated", function (trackItem) {
    if (trackItem.type === "video") {
        let track = trackItem.track;
        let mediaStream = new MediaStream([track]);
        $("#meetingAreaLocalVideo")[0].srcObject = mediaStream;
    }
});

meeting.on("localTrackStopped", function (localTrackItem) {
    if (localTrackItem.type === "video") {
        $("#meetingAreaLocalVideo")[0].srcObject = null;
    }
});


meeting.on("remoteTrackStarted", function (trackItem) {

    if (trackItem.participantSessionId === meeting.participantSessionId) return;
    var track = trackItem.track;
    var mediaStream = new MediaStream([track]);
    $(`#participant-${trackItem.participantSessionId}-${trackItem.type}`)[0].srcObject = mediaStream;
    $(`#participant-${trackItem.participantSessionId}-${trackItem.type}`)[0].play();
});

meeting.on("remoteTrackStopped", function (trackItem) {
    if (trackItem.participantSessionId === meeting.participantSessionId) return;
    $(`#participant-${trackItem.participantSessionId}-${trackItem.type}`)[0].srcObject = null;
});

meeting.on("participantJoined", function (participantInfo) {

    if (participantInfo._id === meeting.participantSessionId) return;
    var participant =
        `&amp;lt;div id="participant-${participantInfo._id}" class="bg-base-300"&amp;gt;
        &amp;lt;video id="participant-${participantInfo._id}-video" muted autoplay playsinline
            style="padding: 0; margin: 0; width: 150px; height: 100px;"&amp;gt;&amp;lt;/video&amp;gt;
        &amp;lt;audio id="participant-${participantInfo._id}-audio" autoplay playsinline
            style="padding: 0; margin: 0;"&amp;gt;&amp;lt;/audio&amp;gt;
        &amp;lt;div id="participant-${participantInfo._id}-username" class="bg-base-300    " style=" text-align: center;"&amp;gt;
            ${participantInfo.name}
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;`
    $("#remoteParticipantContainer").append(participant)
});

meeting.on("participantLeft", function (participantInfo) {
    console.log("participant has left the room", participantInfo);
    $(`#participant-${participantInfo._id}`).remove();
});

meeting.on("onlineParticipants", function (onlineParticipants) {

    $("#remoteParticipantContainer").html("");
    for (let participantInfo of onlineParticipants) {
        if (participantInfo._id !== meeting.participantSessionId) {
            var participant =
                `&amp;lt;div id="participant-${participantInfo._id}" class="bg-base-300"&amp;gt;
                &amp;lt;video id="participant-${participantInfo._id}-video" muted autoplay playsinline
                    style="padding: 0; margin: 0; width: 150px; height: 100px;"&amp;gt;&amp;lt;/video&amp;gt;
                &amp;lt;audio id="participant-${participantInfo._id}-audio" autoplay playsinline
                    style="padding: 0; margin: 0;"&amp;gt;&amp;lt;/audio&amp;gt;
                &amp;lt;div id="participant-${participantInfo._id}-username" class="bg-base-300    " style=" text-align: center;"&amp;gt;
                    ${participantInfo.name}
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;`
            $("#remoteParticipantContainer").append(participant)
        }
    }
});

var currentActiveSpeaker = "";
meeting.on("activeSpeaker", function (activeSpeaker) {

    if (currentActiveSpeaker === activeSpeaker.participantSessionId) return;

    $("#activeSpeakerUsername").text(activeSpeaker.name);
    currentActiveSpeaker = activeSpeaker.participantSessionId;
    if ($(`#participant-${activeSpeaker.participantSessionId}-video`)[0]) {
        let stream = $(`#participant-${activeSpeaker.participantSessionId}-video`)[0].srcObject;
        $("#activeSpeakerVideo")[0].srcObject = stream.clone();

    }

    if (activeSpeaker.participantSessionId === meeting.participantSessionId) {
        let stream = $(`#meetingAreaLocalVideo`)[0].srcObject;
        if (stream) {
            $("#activeSpeakerVideo")[0].srcObject = stream.clone();
        }

    }
});


$("#meetingViewLeave").on("click", async function () {
    await meeting.leaveMeeting();
    $("#meetingView").addClass("hidden");
    $("#leaveView").removeClass("hidden");
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;HTML code:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;index.html&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en" class="bg-white"&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1" /&amp;gt;
    &amp;lt;title&amp;gt;Demo App&amp;lt;/title&amp;gt;



    &amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js" integrity="sha512-bZS47S7sPOxkjU/4Bt0zrhEtWx0y0CRkhEp8IckzK+ltifIIE9EMIMTuT/mEzoIMewUINruDBIR/jJnbguonqQ==" crossorigin="anonymous" referrerpolicy="no-referrer"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;!-- Import the webpage's stylesheet --&amp;gt;
    &amp;lt;link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.1/dist/tailwind.min.css" rel="stylesheet" type="text/css" /&amp;gt;
    &amp;lt;link href="https://cdn.jsdelivr.net/npm/daisyui@1.11.1/dist/full.css" rel="stylesheet" type="text/css" /&amp;gt;
    &amp;lt;script src="//cdn.metered.ca/sdk/video/1.1.6/sdk.min.js"&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;


&amp;lt;div class="navbar mb-2 shadow-lg bg-neutral text-neutral-content"&amp;gt;
  &amp;lt;div class="flex-none px-2 mx-2"&amp;gt;
    &amp;lt;span class="text-lg font-bold"&amp;gt;
        Metered
    &amp;lt;/span&amp;gt;
  &amp;lt;/div&amp;gt; 
  &amp;lt;div class="flex-1 px-2 mx-2"&amp;gt;
    &amp;lt;div class="items-stretch hidden lg:flex"&amp;gt;
      &amp;lt;a href="https://metered.ca/docs/Video-Calls/JavaScript/Advanced-SDK-Guide" target="_blank" class="btn btn-ghost btn-sm rounded-btn"&amp;gt;
              Advanced SDK Guide
            &amp;lt;/a&amp;gt; 
      &amp;lt;a href="https://metered.ca/docs/Video-Calls/JavaScript/Tips-and-Best-Practices" target="_blank" class="btn btn-ghost btn-sm rounded-btn"&amp;gt;
        Tips and Best practices
            &amp;lt;/a&amp;gt; 
      &amp;lt;a href="https://metered.ca/docs/SDK-Reference/JavaScript/Methods/Methods%20Introduction" target="_blank" class="btn btn-ghost btn-sm rounded-btn"&amp;gt;
              SDK Reference
            &amp;lt;/a&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt; 
&amp;lt;/div&amp;gt;



    &amp;lt;div id="meetingIdContainer" class="w-full bg-base-300 hidden font-bold text-center py-2"&amp;gt;
        Meeting ID: &amp;lt;span id="displayMeetingId"&amp;gt;&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;!-- Join view --&amp;gt;
    &amp;lt;div id="joinView" class="w-full items-center justify-center flex"&amp;gt;
        &amp;lt;div class="bg-base-300 w-11/12 max-w-screen-md  rounded mt-48 p-10"&amp;gt;
            &amp;lt;div class="form-control"&amp;gt;
                &amp;lt;label class="label"&amp;gt;
                    &amp;lt;span class="label-text"&amp;gt;Meeting ID&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;div class="relative"&amp;gt;
                    &amp;lt;input id="meetingId" type="text" placeholder="Meeting ID"
                        class="w-full pr-16 input input-primary input-bordered" /&amp;gt;
                    &amp;lt;button id="joinExistingMeeting" class="absolute top-0 right-0 rounded-l-none btn btn-primary text-xs"&amp;gt;
                        &amp;lt;span class="hidden sm:block"&amp;gt;Join Existing Meeting&amp;lt;/span&amp;gt;
                        &amp;lt;span class="sm:hidden"&amp;gt;Join&amp;lt;/span&amp;gt;
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;div class="divider"&amp;gt;OR&amp;lt;/div&amp;gt;
            &amp;lt;div class="flex justify-center"&amp;gt;
                &amp;lt;button id="createANewMeeting" class="btn btn-primary"&amp;gt;Create a new meeting&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

    &amp;lt;!-- Waiting area --&amp;gt;
    &amp;lt;div id="waitingArea" class="w-full items-center justify-center flex hidden"&amp;gt;
        &amp;lt;div class="bg-base-300 w-11/12 rounded mt-48 p-10"&amp;gt;
            &amp;lt;video id="waitingAreaVideoTag" class="w-full" muted autoplay playsinline&amp;gt;&amp;lt;/video&amp;gt;


            &amp;lt;div class="flex items-center justify-center mt-4 space-x-4"&amp;gt;
                &amp;lt;button id="waitingAreaMicrophoneButton" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"&amp;gt;
                        &amp;lt;path fill-rule="evenodd"
                            d="M7 4a3 3 0 016 0v4a3 3 0 11-6 0V4zm4 10.93A7.001 7.001 0 0017 8a1 1 0 10-2 0A5 5 0 015 8a1 1 0 00-2 0 7.001 7.001 0 006 6.93V17H6a1 1 0 100 2h8a1 1 0 100-2h-3v-2.07z"
                            clip-rule="evenodd"&amp;gt;&amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;
                &amp;lt;button id="waitingAreaCameraButton" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"&amp;gt;
                        &amp;lt;path
                            d="M2 6a2 2 0 012-2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zM14.553 7.106A1 1 0 0014 8v4a1 1 0 00.553.894l2 1A1 1 0 0018 13V7a1 1 0 00-1.447-.894l-2 1z"&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div class="divider"&amp;gt;&amp;lt;/div&amp;gt;
            &amp;lt;div class="grid grid-cols-3 space-x-4"&amp;gt;
                &amp;lt;div class="form-control"&amp;gt;
                    &amp;lt;label class="label"&amp;gt;
                        &amp;lt;span class="label-text"&amp;gt;Camera&amp;lt;/span&amp;gt;
                    &amp;lt;/label&amp;gt;
                    &amp;lt;select id="cameras" class="select select-bordered w-full"&amp;gt; &amp;lt;/select&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;div class="form-control"&amp;gt;
                    &amp;lt;label class="label"&amp;gt;
                        &amp;lt;span class="label-text"&amp;gt;Microphone&amp;lt;/span&amp;gt;
                    &amp;lt;/label&amp;gt;
                    &amp;lt;select id="microphones" class="select select-bordered w-full"&amp;gt; &amp;lt;/select&amp;gt;
                &amp;lt;/div&amp;gt;

                &amp;lt;div class="form-control"&amp;gt;
                    &amp;lt;label class="label"&amp;gt;
                        &amp;lt;span class="label-text"&amp;gt;Speaker&amp;lt;/span&amp;gt;
                    &amp;lt;/label&amp;gt;
                    &amp;lt;select id="speakers" class="select select-bordered w-full"&amp;gt; &amp;lt;/select&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div class="form-control"&amp;gt;
                &amp;lt;label class="label"&amp;gt;
                    &amp;lt;span class="label-text"&amp;gt;Enter a username&amp;lt;/span&amp;gt;
                &amp;lt;/label&amp;gt;
                &amp;lt;div class="relative"&amp;gt;
                    &amp;lt;input id="username" type="text" placeholder="username" class="w-full pr-16 input input-primary input-bordered" /&amp;gt;
                    &amp;lt;button id="joinMeetingButton" class="absolute top-0 right-0 rounded-l-none btn btn-primary"&amp;gt;
                        &amp;lt;span class="hidden sm:block"&amp;gt;Join Existing Meeting&amp;lt;/span&amp;gt;
                        &amp;lt;span class="sm:hidden"&amp;gt;Join&amp;lt;/span&amp;gt;
                    &amp;lt;/button&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;

        &amp;lt;!-- Meeting view --&amp;gt;
        &amp;lt;div id="meetingView" class="hidden "&amp;gt;
            &amp;lt;!-- remote video containers --&amp;gt;
            &amp;lt;div id="remoteParticipantContainer" style="display: flex;"&amp;gt;

            &amp;lt;/div&amp;gt;

            &amp;lt;!-- Active Speaker --&amp;gt;
            &amp;lt;div class="mt-4"&amp;gt;
                &amp;lt;div style=" border-radius: 5px;" class="bg-base-300"&amp;gt;
                    &amp;lt;video id="activeSpeakerVideo" muted autoplay playsinline
                        style="padding: 0; margin: 0; width: 100%; height: 400px;"&amp;gt;&amp;lt;/video&amp;gt;
                    &amp;lt;div id="activeSpeakerUsername" class="bg-base-300  " style=" text-align: center;"&amp;gt;

                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div class="flex flex-col bg-base-300" style="width: 150px"&amp;gt;
                &amp;lt;video id="meetingAreaLocalVideo" muted autoplay playsinline
                    style="padding: 0; margin: 0; width: 150px; height: 100px;"&amp;gt;&amp;lt;/video&amp;gt;
                &amp;lt;div id="meetingAreaUsername" class="bg-base-300    " style=" text-align: center;"&amp;gt;

                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
            &amp;lt;!-- Controls --&amp;gt;
            &amp;lt;div style="display: flex; justify-content: center; margin-top: 20px;" class="space-x-4"&amp;gt;
                &amp;lt;button id="meetingViewMicrophone" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
                        xmlns="http://www.w3.org/2000/svg"&amp;gt;
                        &amp;lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                            d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z"&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;

                &amp;lt;button id="meetingViewCamera" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
                        xmlns="http://www.w3.org/2000/svg"&amp;gt;
                        &amp;lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                            d="M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;

                &amp;lt;button id="meetingViewScreen" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"
                        xmlns="http://www.w3.org/2000/svg"&amp;gt;
                        &amp;lt;path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                            d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"&amp;gt;
                        &amp;lt;/path&amp;gt;
                    &amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;

                &amp;lt;button id="meetingViewLeave" class="btn"&amp;gt;
                    &amp;lt;svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"&amp;gt;&amp;lt;path fill-rule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 102 0V4a1 1 0 00-1-1zm10.293 9.293a1 1 0 001.414 1.414l3-3a1 1 0 000-1.414l-3-3a1 1 0 10-1.414 1.414L14.586 9H7a1 1 0 100 2h7.586l-1.293 1.293z" clip-rule="evenodd"&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;
                &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;

        &amp;lt;div id="leaveView" class="flex items-center justify-center hidden"&amp;gt;
            &amp;lt;div class="bg-base-300 w-11/12 rounded-lg mt-20 p-4"&amp;gt;
                &amp;lt;h1 class="text-2xl font-bold"&amp;gt;You have Left the Meeting&amp;lt;/h1&amp;gt;
                &amp;lt;div class="divider"&amp;gt;&amp;lt;/div&amp;gt;
                &amp;lt;p&amp;gt;
                    &amp;lt;button class="btn btn-primary" onclick="window.location.reload()"&amp;gt;Join Another Meeting&amp;lt;/button&amp;gt;
                &amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;!-- Import the webpage's javascript file --&amp;gt;
    &amp;lt;script src="/script.js" defer&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the Application
&lt;/h2&gt;

&lt;p&gt;To run the application, will run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node src/server.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start the application on &lt;code&gt;localhost:4000&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing on multiple devices
&lt;/h2&gt;

&lt;p&gt;To test the application on multiple devices you can use ngrok, you can download and install it from here: &lt;a href="https://ngrok.com/download"&gt;https://ngrok.com/download&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After installing run the application then run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ngrok http 4000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ngrok will give you a URL that you can open on multiple devices to test out video conferencing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Github
&lt;/h2&gt;

&lt;p&gt;You can download the complete application from Github: &lt;a href="https://github.com/metered-ca/video-javascript-quickstart"&gt;https://github.com/metered-ca/video-javascript-quickstart&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>node</category>
      <category>webrtc</category>
    </item>
  </channel>
</rss>
