<?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>DEV Community: Arpit Mishra</title>
    <description>The latest articles on DEV Community by Arpit Mishra (@scarneck_arpit).</description>
    <link>https://dev.to/scarneck_arpit</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%2Fuser%2Fprofile_image%2F192929%2Fa033c824-3fa1-492a-baf8-57a781cd58dc.jpg</url>
      <title>DEV Community: Arpit Mishra</title>
      <link>https://dev.to/scarneck_arpit</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scarneck_arpit"/>
    <language>en</language>
    <item>
      <title>Map the Force Within You: Fastest Route and ETA for Rebels!</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Wed, 04 May 2022 11:48:50 +0000</pubDate>
      <link>https://dev.to/scarneck_arpit/map-the-force-within-you-fastest-route-and-eta-for-rebels-25n3</link>
      <guid>https://dev.to/scarneck_arpit/map-the-force-within-you-fastest-route-and-eta-for-rebels-25n3</guid>
      <description>&lt;p&gt;&lt;strong&gt;&lt;u&gt;Support us on Product Hunt - &lt;a href="https://www.producthunt.com/posts/map-the-force"&gt;https://www.producthunt.com/posts/map-the-force&lt;/a&gt;&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Calling all rebel pilots to New York! We’re under attack by the forces of the Empire. I repeat, we’re under attack and we need all hands on deck! NOW!&lt;br&gt;
Join up with your allies of the resistance at your nearest base, select your aircraft type and report to the New York base as fast as you can.&lt;br&gt;
Before you jump into your aircraft, load up your NextBillion.ai routing tools, enter your vehicle type and just follow the fastest route shown on your cockpit console display.&lt;br&gt;
The resistance needs you here … NOW!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The mission&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The distress message was clear to all rebel pilots –- reinforcements need to reach the rebel base in New York as quickly as possible. A fraction of a second’s delay could be enough for the opposition to gain an advantage and shift the momentum of the war.&lt;/p&gt;

&lt;p&gt;But even in the darkest hour, there’s always a faint glimmer of hope that shines through. That glimmer of hope rests on finding the fastest and safest route to the New York base, while also avoiding detection by the Imperial Forces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=DHtQf_LMeqk"&gt;https://www.youtube.com/watch?v=DHtQf_LMeqk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Rebel pilots cannot take to the open skies, as the Imperial Forces have their radar systems scanning all possible air routes to New York. Instead, they need to fly as low as possible. Therefore, it is highly imperative to find the most optimized route that supports their aircraft's stealth movement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose your route wisely&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K-coVFKQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w5um6gcfqd8l8pv1hoi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K-coVFKQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w5um6gcfqd8l8pv1hoi6.png" alt="Image description" width="880" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The force is strong with the Resistance, young pilot! It’s essential for you to cover ground and reach the New York base as fast as you can. For that, you’ll need to rely on your trusty NextBillion.ai routing tool. With accurate and optimized paths for any type of aircraft, this handy routing tool will fast track your journey and highlight the path of least risk and danger.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rebel fleet&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On reporting to your nearest base station, you can choose from the following aircraft types. Please bear in mind that smaller aircraft may not have advanced or heavy weapons systems in place, but they can move faster than larger aircraft. Hence, optimized routes for a lighter aircraft may not be the same as that of a larger aircraft type.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;TIE Fighter&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TJCd0zCx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tj1iytrvrgmlbg98pwf9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TJCd0zCx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tj1iytrvrgmlbg98pwf9.jpeg" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The classic TIE Fighter is the standard Imperial starfighter that’s seen in huge numbers throughout the galactic civil war. With a slim frame build, the TIE Fighter is nimble enough to navigate through narrow roads and shortcuts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;X-wing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v1WK4xnW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6siz6iz8d13xqqzxii5w.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v1WK4xnW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6siz6iz8d13xqqzxii5w.jpeg" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The X-Wing is renowned for its ‘almost perfect’ balance of speed, maneuverability and defensive shielding, making it the ship of choice for the rebel alliance. While it’s not as fast and nimble as the TIE fighter, it has more room for cargo and firepower.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Millenium Falcon&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iYzMS-bc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eya3a3v7fp3rubulhkfr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iYzMS-bc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eya3a3v7fp3rubulhkfr.jpeg" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fastest and the sleekest starship used by smugglers across the galaxy. Its aged appearance belies numerous advanced modifications to boost the ship’s speed, weaponry and defense mechanisms.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Slave-1&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gYUWzzqv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zvonqevxtt8hszcjffr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gYUWzzqv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zvonqevxtt8hszcjffr.jpeg" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Slave 1 is a well-armed starship. Its weaponry includes two heavy twin blaster cannons, as well as two rapid-firing laser cannons. Rounding out the ship’s armaments are two projectile launchers, each holding a magazine of three homing missiles, and a naval minelayer equipped with seismic charges.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Delta 7 Starfighter&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0yhLfknt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9yehfiyo7aygeohgqilo.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0yhLfknt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9yehfiyo7aygeohgqilo.jpeg" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Delta 7 was designed with a focus on agility. An elite transport, it can accommodate a single pilot and an integrated astromech droid. Unlike most ships, the Delta 7 doesn’t feature a built-in hyperdrive but relies on external hyperspace transport rings that attach to the starship.&lt;/p&gt;

&lt;p&gt;Ready to deploy?&lt;br&gt;
Now that you’ve been briefed, it’s time to get you on your way to the New York base! Let’s get you started by clicking on the link below:&lt;/p&gt;

&lt;p&gt;The interactive ‘Map the Force’ experience is here.&lt;/p&gt;

&lt;p&gt;May the fourth be with you!&lt;/p&gt;

&lt;p&gt;**&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out - &lt;a href="https://nextbillion.ai/"&gt;https://nextbillion.ai/&lt;/a&gt; to create more such amazing products!&lt;br&gt;
**&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>sdk</category>
      <category>sideprojects</category>
      <category>map</category>
      <category>api</category>
    </item>
    <item>
      <title>AMA &amp; Talk: Scaling your Android build with Jetpack &amp; Dagger</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Fri, 18 Feb 2022 06:19:37 +0000</pubDate>
      <link>https://dev.to/100mslive/ama-talk-scaling-your-android-build-with-jetpack-dagger-2e2c</link>
      <guid>https://dev.to/100mslive/ama-talk-scaling-your-android-build-with-jetpack-dagger-2e2c</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cc_17saA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj0x7z3towqrj7tiahri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cc_17saA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj0x7z3towqrj7tiahri.png" alt="Image description" width="880" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Guess who just joined the club. 👋 &lt;/p&gt;

&lt;p&gt;100ms is conducting its first 🤖 Android Developer event, and we’re here to talk about all things development, coding, and scaling. &lt;/p&gt;

&lt;p&gt;Join us for an exclusive Talk &amp;amp; AMA session with Rivu Chakraborty,  Aniket Kadam, and Honey Sonwani on 🗓 26th of February! We will be unlocking elements to scale the Android system by deep-diving into &lt;strong&gt;Dagger and Jetpack compose.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;💡 Going live on &lt;strong&gt;26th February at 11:00 am IST&lt;/strong&gt;. Don’t miss this!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.100ms.live/events/android-jetpack-dagger"&gt;https://www.100ms.live/events/android-jetpack-dagger&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  GDG #askmeanything #Android #jetpackcompose
&lt;/h1&gt;

</description>
      <category>android</category>
      <category>ama</category>
      <category>programming</category>
      <category>jetpack</category>
    </item>
    <item>
      <title>Building a Twitter Spaces clone in React Native</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Fri, 10 Dec 2021 09:20:34 +0000</pubDate>
      <link>https://dev.to/100mslive/building-a-twitter-spaces-clone-in-react-native-30a0</link>
      <guid>https://dev.to/100mslive/building-a-twitter-spaces-clone-in-react-native-30a0</guid>
      <description>&lt;p&gt;In the mood to learn something new over the weekend?🧑‍💻 &lt;/p&gt;

&lt;p&gt;100ms 🤝 GeekyAnts&lt;/p&gt;

&lt;p&gt;Join speaker 🎙  Vipul Bhardwaj, Senior Engineer at #NativeBase, for a webinar 🎥  with #100ms on how to build a Twitter Spaces clone with #React Native.&lt;/p&gt;

&lt;p&gt;What would you learn?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is NativeBase and how to build consistent UI with it&lt;/li&gt;
&lt;li&gt;What is Twitter Spaces and how does it work&lt;/li&gt;
&lt;li&gt;How to build Twitter Spaces with 100ms React Native SDK&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds exciting? then register now 👇&lt;br&gt;
Tune in on 🗓 18th December&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.100ms.live/events/twitter-spaces-clone"&gt;https://www.100ms.live/events/twitter-spaces-clone&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactnative</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>[ 🎥 Workshop] Building a Zoom clone in Flutter </title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Wed, 24 Nov 2021 07:12:40 +0000</pubDate>
      <link>https://dev.to/100mslive/-workshop-building-a-zoom-clone-in-flutter-o87</link>
      <guid>https://dev.to/100mslive/-workshop-building-a-zoom-clone-in-flutter-o87</guid>
      <description>&lt;p&gt;&lt;em&gt;Read detailed blog on the workshop&lt;/em&gt; - &lt;a href="https://www.100ms.live/blog/zoom-clone-in-flutter?utm_source=d2&amp;amp;utm_medium=flutter"&gt;Building Zoom clone in Flutter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can't keep calm #100ms is back with another exciting workshop.🤩&lt;/p&gt;

&lt;p&gt;Build a Zoom clone in Flutter with Vivek Yadav (Google Developer Expert in Flutter, Building Mobile at ZestMoney) on 2nd December at 10:00 AM ET | 8:30 PM IST. &lt;/p&gt;

&lt;p&gt;In this #workshop,&lt;br&gt;
☝️ Understand how Zoom works&lt;br&gt;
🤘 Integrate the 100ms Flutter SDK&lt;br&gt;
🖖 Build features like mute/unmute, leave/join room &amp;amp; messaging.&lt;/p&gt;

&lt;p&gt;Excited? Register here: 👇&lt;/p&gt;

&lt;p&gt;&lt;a href="https://community.100ms.live/zoom-clone-flutter"&gt;https://community.100ms.live/zoom-clone-flutter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>android</category>
      <category>kotlin</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build a Discord stage channel clone with 100ms and Next.js</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Tue, 16 Nov 2021 07:26:30 +0000</pubDate>
      <link>https://dev.to/100mslive/build-a-discord-stage-channel-clone-with-100ms-and-nextjs-5heb</link>
      <guid>https://dev.to/100mslive/build-a-discord-stage-channel-clone-with-100ms-and-nextjs-5heb</guid>
      <description>&lt;p&gt;Ever since the Clubhouse app rose to fame, the popularity of drop-in audio-only rooms has been increasing sharply and adopted by several platforms such as Slack, Twitter, and Discord. These rooms are great for hosting Q&amp;amp;A sessions, panel discussions, and a lot more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This content was originally published - &lt;a href="https://www.100ms.live/blog/build-discord-stage-channel-clone-hms" rel="noopener noreferrer"&gt;HERE&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Earlier this year, Discord introduced Stages, an audio-only channel to engage with your Discord community with separation between speakers and audience. In this blog, we’ll learn how to build a similar platform painlessly with 100ms.&lt;/p&gt;

&lt;h4&gt;
  
  
  What We’ll Be Building
&lt;/h4&gt;

&lt;p&gt;Using the 100ms React SDK, we’ll build our custom audio room application that will mimic these features from Discord &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stages:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Allow the user to join as a speaker, listener, or a moderator.&lt;br&gt;
Speakers and moderators will have the permission to mute or unmute themselves.&lt;/p&gt;

&lt;p&gt;Listeners will only be able to listen to the conversation, raise their hand to become a speaker, or leave the room.&lt;br&gt;
Moderators will be allowed to mute anyone and change the role of a person to speaker or listener.&lt;/p&gt;

&lt;p&gt;By the end of this blog, you can expect to build an application like this with Next.js (a React framework) and 100ms SDK:&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%2Ffsusqlmrfdblvkcqswih.gif" 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%2Ffsusqlmrfdblvkcqswih.gif" alt="How this would look"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only prerequisites for building this project is a fundamental understanding of Next.js and React hooks. The Next.js documentation is a great place to start reading about how Next.js works, but you can still follow along if you’ve only used React in the past.&lt;/p&gt;

&lt;p&gt;Familiarity with Flux-based architecture is a bonus but not a necessity, and no prior knowledge of WebRTC is required. How wonderful is that!&lt;/p&gt;
&lt;h4&gt;
  
  
  Setting up the Project
&lt;/h4&gt;

&lt;p&gt;Before diving right into the code, create a 100ms account from the 100ms Dashboard to get your &lt;code&gt;token_endpoint&lt;/code&gt; and &lt;code&gt;room_id&lt;/code&gt;. We’ll be needing these credentials in the later stages of building the application.&lt;/p&gt;

&lt;p&gt;Once you’ve created an account, follow the steps given below to create your application and set it up on the 100ms dashboard:&lt;/p&gt;

&lt;p&gt;Defining Roles&lt;/p&gt;

&lt;p&gt;We’ll be having four roles in our application: &lt;code&gt;listener&lt;/code&gt;, &lt;code&gt;speaker&lt;/code&gt;, &lt;code&gt;handraise&lt;/code&gt;, and &lt;code&gt;moderator&lt;/code&gt;. Let’s set up permissions for each of these roles, starting with the listener role.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;listener&lt;/code&gt; role, we can turn off all the publish strategies as we don’t want listeners to share their audio, video, or screen. Listeners will still be able to listen to others’ audio.&lt;/p&gt;

&lt;p&gt;Inside the permissions section, uncheck all the options except for &lt;code&gt;Can change any participant's role permission&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%2F4uw4ads5vonfkmlnqlpq.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%2F4uw4ads5vonfkmlnqlpq.png" alt="Second image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;handraise&lt;/code&gt; role, we can again turn off all the publish strategies and just keep the &lt;code&gt;Can change any participant's role&lt;/code&gt; permission turned on. This permission will allow us to switch the user from &lt;code&gt;listener&lt;/code&gt; role to handraise role, and vice-versa, and help us to to implement the hand-raise functionality.&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%2Fotruy82btlufdml1hlbd.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%2Fotruy82btlufdml1hlbd.png" alt="Third image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When a &lt;code&gt;listener&lt;/code&gt; wants to become a &lt;code&gt;speaker&lt;/code&gt;, they can click on the hand-raise button that will change their role to &lt;code&gt;handraise&lt;/code&gt;. When the user's role is &lt;code&gt;handraise&lt;/code&gt;, we'll display a small badge next to their avatar to notify the moderator.&lt;/p&gt;

&lt;p&gt;Now for the &lt;code&gt;speaker&lt;/code&gt; role, since we’re building an audio-only room, we can just check the &lt;code&gt;Can share audio&lt;/code&gt; publish strategy and leave the rest of them unchecked. We can leave all the permissions turned off for the &lt;code&gt;speaker&lt;/code&gt; role.&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%2F1gmispxcr8aqfsrjousl.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%2F1gmispxcr8aqfsrjousl.png" alt="Fourth Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, for the &lt;code&gt;moderator&lt;/code&gt; role, we can check the Can share audio publish strategy and move on towards the permissions. In the permissions section, turn on the &lt;code&gt;Can change any participant's role&lt;/code&gt; permission and the &lt;code&gt;Can mute any participant&lt;/code&gt; permission.&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%2Fhn32dt1rz0thbwwzjf9k.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%2Fhn32dt1rz0thbwwzjf9k.png" alt="Fifth Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For all the roles, set the subscribe strategies to &lt;code&gt;speaker&lt;/code&gt; and &lt;code&gt;moderator&lt;/code&gt;. And with that, we’re ready to move on and get the required credentials from the 100ms Dashboard.&lt;/p&gt;
&lt;h5&gt;
  
  
  Getting the token_enpoint
&lt;/h5&gt;

&lt;p&gt;Once you’re done creating your custom application and setting up the roles, head on over to the &lt;a href="https://dashboard.100ms.live/developer" rel="noopener noreferrer"&gt;Developers tab&lt;/a&gt; to get your token endpoint URL. Keep this URL handy. We’ll store this URL inside an environment variable shortly in the upcoming sections.&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%2Fozec8p0as5z6cst03nvu.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%2Fozec8p0as5z6cst03nvu.png" alt="Sixth Image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Getting the &lt;code&gt;room_id&lt;/code&gt;
&lt;/h5&gt;

&lt;p&gt;To obtain the room ID, head on over the &lt;a href="https://dashboard.100ms.live/rooms" rel="noopener noreferrer"&gt;Rooms tab&lt;/a&gt; on 100ms Dashboard. If you don’t have an existing room, you can go ahead and create one to get its ID. Otherwise, copy the room ID of an existing room and paste it somewhere for now.&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%2Fqonnhlp9ny6vvqqqenlg.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%2Fqonnhlp9ny6vvqqqenlg.png" alt="Seventh Image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  Understanding the Terminologies
&lt;/h5&gt;

&lt;p&gt;I know you’re excited to start coding, but let’s take a moment to understand the key terminologies associated with the 100ms SDK so that we’re on the same page.&lt;/p&gt;

&lt;p&gt;Room — A room is the basic object that 100ms SDKs return on successful connection. This contains references to peers, tracks and everything you need to render a live audio/video app.&lt;/p&gt;

&lt;p&gt;Peer — A peer is the object returned by 100ms SDKs that contains all information about a user — name, role, video track etc.&lt;/p&gt;

&lt;p&gt;Track — A track represents either the audio or video that a peer is publishing.&lt;/p&gt;

&lt;p&gt;Role — A role defines who a peer can see/hear, the quality at which they publish their video, whether they have permissions to publish video/screenshare, mute someone, change someone’s role.&lt;/p&gt;
&lt;h5&gt;
  
  
  An Overview of the Starter Code
&lt;/h5&gt;

&lt;p&gt;To ease the development process, you can grab the starter code with prebuilt components and styling by cloning the &lt;code&gt;template&lt;/code&gt; branch of this repo with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b template https://github.com/itsnitinr/discord-stages-clone-100ms.git 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starter code is built using the Create Next App CLI tool with the Tailwind CSS template. All the dependencies required for this building this project, such as the &lt;code&gt;@100mslive/hms-video&lt;/code&gt; and &lt;code&gt;@100mslive/hms-video-react&lt;/code&gt; SDK have already been added to the &lt;code&gt;package.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Therefore, make sure to run &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;yarn install&lt;/code&gt; to install these dependencies locally before moving forward.&lt;/p&gt;

&lt;p&gt;Remember the token endpoint URL and room ID we had saved earlier? It’s time to transfer them to an environment variable file. The starter code comes with an &lt;code&gt;.env.local.example&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cp .env.local.example .env.local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this command to copy this example env file and create an actual one:&lt;br&gt;
Now, add the token endpoint URL and room ID to this &lt;code&gt;.env.local&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// .env.local
TOKEN_ENDPOINT = &amp;lt;YOUR-TOKEN-ENDPOINT-URL&amp;gt;
ROOM_ID = &amp;lt;YOUR-ROOM-ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To start the Next.js development server, run the &lt;code&gt;dev&lt;/code&gt; script in this manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
#or
yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; on your browser and you’ll be greeted with this screen if everything goes well:&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%2F6m40ttx6d10b74uu0cuy.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%2F6m40ttx6d10b74uu0cuy.png" alt="Eigth Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fantastic! Let’s start implementing the features one by one in the upcoming sections.&lt;/p&gt;

&lt;h5&gt;
  
  
  Building the project
&lt;/h5&gt;

&lt;p&gt;Before we can start using the hooks, selectors, or store from the 100ms React SDK, we will need to wrap our entire application with &lt;code&gt;&amp;lt;HMSRoomProvider /&amp;gt;&lt;/code&gt; component from the &lt;code&gt;@100mslive/hms-video-react package&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s how your code should look like once you’ve completed this step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// pages/index.js

import { HMSRoomProvider } from '@100mslive/hms-video-react';
import Head from 'next/head';

import Join from '../components/Join';
import Room from '../components/Room';

const StagesApp = () =&amp;gt; {
  const isConnected = false;
  return isConnected ? &amp;lt;Room /&amp;gt; : &amp;lt;Join /&amp;gt;;
};

const App = () =&amp;gt; {
  return (
    &amp;lt;HMSRoomProvider&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Discord Stages Clone&amp;lt;/title&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;StagesApp /&amp;gt;
    &amp;lt;/HMSRoomProvider&amp;gt;
  );
};

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Joining a Room
&lt;/h5&gt;

&lt;p&gt;Right now, we’re conditionally rendering either the &lt;code&gt;&amp;lt;Room /&amp;gt;&lt;/code&gt; component or the &lt;code&gt;&amp;lt;Join /&amp;gt;&lt;/code&gt; component based on the isConnected variable. However, its value has been hardcoded to be false for now.&lt;/p&gt;

&lt;p&gt;To check if the user is connected to a room or not, we can use the &lt;code&gt;selectIsConnectedToRoom&lt;/code&gt; selector and &lt;code&gt;useHMSStore&lt;/code&gt; hook 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;// pages/index.js

import { HMSRoomProvider,
  useHMSStore,
  selectIsConnectedToRoom,
} from '@100mslive/hms-video-react';
import Head from 'next/head';

import Join from '../components/Join';
import Room from '../components/Room';

const StagesApp = () =&amp;gt; {
  const isConnected = useHMSStore(selectIsConnectedToRoom);
  return isConnected ? &amp;lt;Room /&amp;gt; : &amp;lt;Join /&amp;gt;;
};

const App = () =&amp;gt; {
  return (
    &amp;lt;HMSRoomProvider&amp;gt;
      &amp;lt;Head&amp;gt;
        &amp;lt;title&amp;gt;Discord Stages Clone&amp;lt;/title&amp;gt;
      &amp;lt;/Head&amp;gt;
      &amp;lt;StagesApp /&amp;gt;
    &amp;lt;/HMSRoomProvider&amp;gt;
  );
};

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the user will not be connected to any room, and hence, the &lt;code&gt;&amp;lt;Join /&amp;gt;&lt;/code&gt; component will be rendered. Let’s implement the functionality to join a room inside the &lt;code&gt;components/Join.jsx file&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To join a room, we can use the join() method on the &lt;code&gt;hmsActions&lt;/code&gt; object returned by the &lt;code&gt;useHMSActions() hook&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This &lt;code&gt;join(&lt;/code&gt;) method takes an object containing the userName, authToken and an optional settings object as the parameter.&lt;/p&gt;

&lt;p&gt;We can get the &lt;code&gt;userName&lt;/code&gt; from the local &lt;code&gt;name&lt;/code&gt; state variable created using the &lt;code&gt;useState()&lt;/code&gt; hook from React. However, to obtain the &lt;code&gt;authToken&lt;/code&gt;, we will need to make a network request to our custom Next.js API route along with the role we want to join the room with.&lt;/p&gt;

&lt;p&gt;We’re also tracking the role the user has selected using the local role state variable, similar to &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can find the API route inside the &lt;code&gt;pages/api/token.js&lt;/code&gt; file. Here’s how it will look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// pages/api/token.js

import { v4 } from 'uuid';

export default async function getAuthToken(req, res) {
  try {
    const { role } = JSON.parse(req.body);

    const response = await fetch(`${process.env.TOKEN_ENDPOINT}api/token`, {
      method: 'POST',
      body: JSON.stringify({
        user_id: v4(),
        room_id: process.env.ROOM_ID,
        role,
      }),
    });

    const { token } = await response.json();
    res.status(200).json({ token });
  } catch (error) {
    console.log('error', error);
    res.status(500).json({ error });
  }
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially, this API route makes a &lt;code&gt;POST&lt;/code&gt; request to our 100ms &lt;code&gt;token endpoint URL&lt;/code&gt;, which is stored inside the environment variables, along with a unique &lt;code&gt;user_id&lt;/code&gt;, &lt;code&gt;role&lt;/code&gt;, and the &lt;code&gt;room_id&lt;/code&gt;, which is also stored inside the environment variables.&lt;/p&gt;

&lt;p&gt;If successful, our Next.js API route will return the &lt;code&gt;authToken&lt;/code&gt;. Using this &lt;code&gt;authToken&lt;/code&gt;, we can join the &lt;code&gt;room&lt;/code&gt;. Since we don’t want the user to join with their mic turned on, we can set &lt;code&gt;isAudioMuted&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; inside the optional settings object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/Join.jsx

import Image from 'next/image';
import { useState } from 'react';
import Avatar from 'boring-avatars';
import { useHMSActions } from '@100mslive/hms-video-react';

import NameInput from './Join/NameInput';
import RoleSelect from './Join/RoleSelect';
import JoinButton from './Join/JoinButton';

const Join = () =&amp;gt; {
  const hmsActions = useHMSActions();

  const [name, setName] = useState('');
  const [role, setRole] = useState('listener');

  const joinRoom = async () =&amp;gt; {
    try {
      const response = await fetch('/api/token', {
        method: 'POST',
        body: JSON.stringify({ role }),
      });
      const { token } = await response.json();
      hmsActions.join({
        userName: name || 'Anonymous',
        authToken: token,
        settings: {
          isAudioMuted: true,
        },
      });
    } catch (error) {
      console.error(error);
    }
  };

  return (
    &amp;lt;&amp;gt;
      &amp;lt;Image
        src="https://imgur.com/27iLD4R.png"
        alt="Login background"
        className="w-screen h-screen object-cover relative"
        layout="fill"
      /&amp;gt;
      &amp;lt;div className="bg-gray-800 rounded-lg w-11/12 md:w-1/2 lg:w-1/3 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 p-8 text-white shadow-lg space-y-4 flex flex-col items-center max-w-md"&amp;gt;
        &amp;lt;Avatar name={name} variant="marble" size="72" /&amp;gt;
        &amp;lt;NameInput name={name} setName={setName} /&amp;gt;
        &amp;lt;RoleSelect role={role} setRole={setRole} /&amp;gt;
        &amp;lt;JoinButton joinRoom={joinRoom} /&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
};

export default Join;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ffucux02tmf02t55p9gcb.gif" 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%2Ffucux02tmf02t55p9gcb.gif" alt="Ninth image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And with just a few lines of code, we have implemented the functionality to join a room and render the &lt;code&gt;&amp;lt;Room /&amp;gt;&lt;/code&gt; component. Now, let’s move forward and render the peers connected to our room.&lt;/p&gt;

&lt;h5&gt;
  
  
  Rendering The Peers
&lt;/h5&gt;

&lt;p&gt;Right now, if you view the &lt;code&gt;&amp;lt;Room /&amp;gt;&lt;/code&gt; component inside the &lt;code&gt;components/Room.jsx&lt;/code&gt; file, you can see that we have hardcoded the value of peers to an empty array.Let’s make this dynamic.&lt;/p&gt;

&lt;p&gt;To do that, we can use the &lt;code&gt;selectPeers selector&lt;/code&gt; combined with the &lt;code&gt;useHMSStore()&lt;/code&gt; hook to get an array of all the peers connected to the room in the form of objects. Each of these peer objects will contain information such as their &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;roleName&lt;/code&gt; that we can use to render their tiles accordingly.&lt;/p&gt;

&lt;p&gt;Once we get an array of all the peers, we can use the &lt;code&gt;filter()&lt;/code&gt; JavaScript array method to separate them into &lt;code&gt;listenersAndHandraised&lt;/code&gt; and &lt;code&gt;speakersAndModerators&lt;/code&gt; using the &lt;code&gt;roleName&lt;/code&gt; property on each peer object. This will help us render the appropriate tile based on the user’s role.&lt;/p&gt;

&lt;p&gt;If the role of the user is a &lt;code&gt;listener&lt;/code&gt; or &lt;code&gt;handraise&lt;/code&gt;, we will render the &lt;code&gt;&amp;lt;ListenerTile /&amp;gt;&lt;/code&gt; component. Else, we will render the &lt;code&gt;&amp;lt;SpeakerTile /&amp;gt;&lt;/code&gt; component. While rendering these tiles, pass the &lt;code&gt;peer&lt;/code&gt; object as a prop in order to display the peer’s information inside the tiles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/Room.jsx

import { selectPeers, useHMSStore } from '@100mslive/hms-video-react';

import RoomInfo from './Room/RoomInfo';
import Controls from './Room/Controls';
import ListenerTile from './User/ListenerTile';
import SpeakerTile from './User/SpeakerTile';

const Room = () =&amp;gt; {
  const peers = useHMSStore(selectPeers);

  const speakersAndModerators = peers.filter(
    (peer) =&amp;gt; peer.roleName === 'speaker' || peer.roleName === 'moderator'
  );
  const listenersAndHandraised = peers.filter(
    (peer) =&amp;gt; peer.roleName === 'listener' || peer.roleName === 'handraise'
  );

  return (
    &amp;lt;div className="flex flex-col bg-main text-white min-h-screen p-6"&amp;gt;
      &amp;lt;RoomInfo count={peers.length} /&amp;gt;
      &amp;lt;div className="flex-1 py-8"&amp;gt;
        &amp;lt;h5 className="uppercase text-sm text-gray-300 font-bold mb-8"&amp;gt;
          Speakers - {speakersAndModerators.length}
        &amp;lt;/h5&amp;gt;
        &amp;lt;div className="flex space-x-6 flex-wrap"&amp;gt;
          {speakersAndModerators.map((speaker) =&amp;gt; (
            &amp;lt;SpeakerTile key={speaker.id} peer={speaker} /&amp;gt;
          ))}
        &amp;lt;/div&amp;gt;
        &amp;lt;h5 className="uppercase text-sm text-gray-300 font-bold my-8"&amp;gt;
          Listeners - {listenersAndHandraised.length}
        &amp;lt;/h5&amp;gt;
        &amp;lt;div className="flex space-x-8 flex-wrap"&amp;gt;
          {listenersAndHandraised.map((listener) =&amp;gt; (
            &amp;lt;ListenerTile key={listener.id} peer={listener} /&amp;gt;
          ))}
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;Controls /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Room;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fclv1bfvvhnd87j3m60o7.gif" 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%2Fclv1bfvvhnd87j3m60o7.gif" alt="Tenth Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;RoomInfo /&amp;gt;&lt;/code&gt; component takes a count prop with the total number of peers connected to the rooms as its value. For the speakers and listeners headings, we can access the length property of &lt;code&gt;speakersAndModerators&lt;/code&gt; and &lt;code&gt;listenersAndHandraised&lt;/code&gt; array, respectively, to get their count.&lt;/p&gt;

&lt;h5&gt;
  
  
  Adding Functionalities To The Controls
&lt;/h5&gt;

&lt;p&gt;Let’s go to the &lt;code&gt;&amp;lt;Controls /&amp;gt;&lt;/code&gt; component inside &lt;code&gt;components/Room/Controls.jsx&lt;/code&gt;. Essentially, we will be having three controls: one to toggle our mic on or off, one to toggle hand-raise, and lastly to leave the room. We'll cover the hand-raise functionality in the latter part of this blog post.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;MicButton /&amp;gt;&lt;/code&gt; component responsible for the toggle mic functionality will only be displayed to the speakers and moderators whereas, the &lt;code&gt;&amp;lt;ExitButton /&amp;gt;&lt;/code&gt; component will be displayed to all roles.&lt;/p&gt;

&lt;p&gt;We need to check if our role and if our mic is turned on or not to render the buttons accordingly. To do this, use the &lt;code&gt;selectIsLocalAudioEnabled&lt;/code&gt; selector to get the status of our mic, and the &lt;code&gt;selectLocalPeer&lt;/code&gt; selector to get our local peer object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/Room/Controls.jsx

import {
  useHMSStore,
  selectIsLocalAudioEnabled,
  selectLocalPeer,
} from '@100mslive/hms-video-react';

import MicButton from './MicButton';
import ExitButton from './ExitButton';
import HandRaiseButton from './HandRaiseButton';

const Controls = () =&amp;gt; {
  const isMicOn = useHMSStore(selectIsLocalAudioEnabled);
  const peer = useHMSStore(selectLocalPeer);

  const isListenerOrHandraised =
    peer.roleName === 'listener' || peer.roleName === 'handraise';

  return (
    &amp;lt;div className="flex justify-center space-x-4"&amp;gt;
      {!isListenerOrHandraised &amp;amp;&amp;amp; (
        &amp;lt;MicButton isMicOn={isMicOn} toggleMic={() =&amp;gt; {}} /&amp;gt;
      )}
      {isListenerOrHandraised &amp;amp;&amp;amp; (
        &amp;lt;HandRaiseButton
          isHandRaised={peer.roleName === 'handraise'}
          toggleHandRaise={() =&amp;gt; {}}
        /&amp;gt;
      )}
      &amp;lt;ExitButton exitRoom={() =&amp;gt; {}} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Controls;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, to add the functionalities, start by creating a new instance of the &lt;code&gt;useHMSActions()&lt;/code&gt; hook and store it inside &lt;code&gt;hmsActions&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Call the &lt;code&gt;setLocalAudioEnabled()&lt;/code&gt; method on the &lt;code&gt;hmsActions&lt;/code&gt; object inside the &lt;code&gt;toggleMic&lt;/code&gt; prop of the &lt;code&gt;&amp;lt;MicButton /&amp;gt;&lt;/code&gt; component. &lt;/p&gt;

&lt;p&gt;This method takes a boolean value: &lt;code&gt;true&lt;/code&gt; for turning on the mic and &lt;code&gt;false&lt;/code&gt; for turning it off. Since we want to toggle, we can pass the opposite of current status using the ! operator.&lt;/p&gt;

&lt;p&gt;To exit the room, we can simply call the &lt;code&gt;leave()&lt;/code&gt; method on the &lt;code&gt;hmsActions&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/Room/Controls.jsx

import {
  useHMSStore,
  useHMSActions,
  selectIsLocalAudioEnabled,
  selectLocalPeer,
} from '@100mslive/hms-video-react';

import MicButton from './MicButton';
import ExitButton from './ExitButton';
import HandRaiseButton from './HandRaiseButton';

const Controls = () =&amp;gt; {
  const hmsActions = useHMSActions();
  const isMicOn = useHMSStore(selectIsLocalAudioEnabled);
  const peer = useHMSStore(selectLocalPeer);

  const isListenerOrHandraised =
    peer.roleName === 'listener' || peer.roleName === 'handraise';

  return (
    &amp;lt;div className="flex justify-center space-x-4"&amp;gt;
      {!isListenerOrHandraised &amp;amp;&amp;amp; (
        &amp;lt;MicButton
          isMicOn={isMicOn}
          toggleMic={() =&amp;gt; hmsActions.setLocalAudioEnabled(!isMicOn)}
        /&amp;gt;
      )}
      {isListenerOrHandraised &amp;amp;&amp;amp; (
        &amp;lt;HandRaiseButton
          isHandRaised={peer.roleName === 'handraise'}
          toggleHandRaise={() =&amp;gt; {}}
        /&amp;gt;
      )}
      &amp;lt;ExitButton exitRoom={() =&amp;gt; hmsActions.leave()} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Controls;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fjv3q4hd66px6tcjq63pu.gif" 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%2Fjv3q4hd66px6tcjq63pu.gif" alt="Eleventh Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Displaying Audio Level and Mic Status
&lt;/h5&gt;

&lt;p&gt;When a user is speaking, we want to display a green ring just outside the user’s avatar to indicate the same. This will require us to know the audio level of the speaker, but how can we find that out? With 100ms React SDK, it is as simple as using the &lt;code&gt;selectPeerAudioByID&lt;/code&gt; selector.&lt;/p&gt;

&lt;p&gt;This selector function takes the peer’s ID as the parameter and returns an integer to represent the audio level. We can assign it to a variable and check if it is greater than 0 to check if the user is speaking.&lt;/p&gt;

&lt;p&gt;Similarly, to check if a user’s mic is turned on or not, we can use the &lt;code&gt;selectIsPeerAudioEnabled&lt;/code&gt; selector, which also takes the peer’s ID as the parameter and returns a boolean value to indicate the mic status.&lt;/p&gt;

&lt;p&gt;Using these two selectors, we can render the UI accordingly by adding a ring using Tailwind CSS classes and displaying the appropriate icon. Go to the &lt;code&gt;&amp;lt;SpeakerTile /&amp;gt;&lt;/code&gt; component inside &lt;code&gt;components/User/SpeakerTile.jsx&lt;/code&gt; and make the following changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/User/SpeakerTile.jsx

import Avatar from 'boring-avatars';
import { FiMic, FiMicOff } from 'react-icons/fi';
import {
  useHMSStore,
  selectPeerAudioByID,
  selectIsPeerAudioEnabled,
} from '@100mslive/hms-video-react';

import PermissionsMenu from './PermissionsMenu';

const SpeakerTile = ({ peer }) =&amp;gt; {
  const isSpeaking = useHMSStore(selectPeerAudioByID(peer.id)) &amp;gt; 0;
  const isMicOn = useHMSStore(selectIsPeerAudioEnabled(peer.id));

  return (
    &amp;lt;div className="relative bg-secondary px-12 py-6 rounded-lg border border-purple-500"&amp;gt;
      &amp;lt;PermissionsMenu id={peer.id} audioTrack={peer.audioTrack} /&amp;gt;
      &amp;lt;div className="flex flex-col gap-y-4 justify-center items-center"&amp;gt;
        &amp;lt;div
          className={
            isSpeaking
              ? 'ring rounded-full transition ring-3 ring-green-600 p-0.5'
              : 'p-0.5'
          }
        &amp;gt;
          &amp;lt;Avatar name={peer.name} size="60" /&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;p className="flex items-center gap-x-2"&amp;gt;
          {peer.name}
          {isMicOn ? (
            &amp;lt;FiMic className="h-3 w-3" /&amp;gt;
          ) : (
            &amp;lt;FiMicOff className="h-3 w-3" /&amp;gt;
          )}
        &amp;lt;/p&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default SpeakerTile;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F9b9ajee54gk3j511twhy.gif" 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%2F9b9ajee54gk3j511twhy.gif" alt="Twelth Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  The Permissions Menu
&lt;/h5&gt;

&lt;p&gt;Time to add some functionality to the &lt;code&gt;&amp;lt;PermissionsMenu /&amp;gt;&lt;/code&gt; component inside the &lt;code&gt;components/User/PermissionsMenu.jsx&lt;/code&gt; file. We want to display this menu only if we have the &lt;code&gt;moderator&lt;/code&gt; role.&lt;/p&gt;

&lt;p&gt;To get our &lt;code&gt;localPeer&lt;/code&gt; object, we can use the &lt;code&gt;selectLocalPeer&lt;/code&gt; selector function. This will return an object with the &lt;code&gt;roleName&lt;/code&gt; property that we can check to get our role. &lt;/p&gt;

&lt;p&gt;Alternatively, you can also choose to use the &lt;code&gt;selectLocalPeerRole&lt;/code&gt; selector and access the &lt;code&gt;name&lt;/code&gt; property of the returned object.&lt;/p&gt;

&lt;p&gt;To check if we are a &lt;code&gt;moderator&lt;/code&gt;, use the &lt;code&gt;===&lt;/code&gt; equality operator to check if our &lt;code&gt;roleName&lt;/code&gt; equates to &lt;code&gt;moderator&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Accordingly, we can either render this component, or null if we’re not a &lt;code&gt;moderator&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The permissions menu has three options: &lt;code&gt;Mute Peer&lt;/code&gt;, &lt;code&gt;Make Listener&lt;/code&gt;, and &lt;code&gt;Make Speaker&lt;/code&gt;. To achieve these functionalities, create a new instance of the &lt;code&gt;useHMSActions()&lt;/code&gt; hook to get access to all the required methods.&lt;/p&gt;

&lt;p&gt;For muting a peer, call the &lt;code&gt;setRemoteTrackEnabled()&lt;/code&gt; method on &lt;code&gt;hmsActions&lt;/code&gt; with the peer’s audio track (that we’re getting from the props) and &lt;code&gt;false&lt;/code&gt; as parameters.&lt;/p&gt;

&lt;p&gt;To change the role of a peer, call the &lt;code&gt;changeRole()&lt;/code&gt; method on &lt;code&gt;hmsActions&lt;/code&gt; along with the peer’s ID, new role, and a force boolean value to change their role without asking them or give them a chance to accept/reject.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/User/PermissionsMenu.jsx

import { useState } from 'react';
import { AiOutlineMenu } from 'react-icons/ai';
import {
  useHMSStore,
  useHMSActions,
  selectLocalPeer,
} from '@100mslive/hms-video-react';

const PermissionsMenu = ({ audioTrack, id }) =&amp;gt; {
  const hmsActions = useHMSActions();

  const mutePeer = () =&amp;gt; {
    hmsActions.setRemoteTrackEnabled(audioTrack, false);
  };

  const changeRole = (role) =&amp;gt; {
    hmsActions.changeRole(id, role, true);
  };

  const localPeer = useHMSStore(selectLocalPeer);

  const [showMenu, setShowMenu] = useState(false);

  const btnClass = 'w-full text-sm font-semibold hover:text-purple-800 p-1.5';

  const isModerator = localPeer.roleName === 'moderator';

  if (isModerator) {
    return (
      &amp;lt;div className="absolute right-1 top-1 z-50"&amp;gt;
        &amp;lt;AiOutlineMenu
          className="ml-auto"
          onClick={() =&amp;gt; setShowMenu(!showMenu)}
        /&amp;gt;
        {showMenu &amp;amp;&amp;amp; (
          &amp;lt;div className="mt-2 bg-white text-black py-2 rounded-md"&amp;gt;
            &amp;lt;button className={btnClass} onClick={() =&amp;gt; mutePeer()}&amp;gt;
              Mute
            &amp;lt;/button&amp;gt;
            &amp;lt;button className={btnClass} onClick={() =&amp;gt; changeRole('listener')}&amp;gt;
              Make Listener
            &amp;lt;/button&amp;gt;
            &amp;lt;button className={btnClass} onClick={() =&amp;gt; changeRole('speaker')}&amp;gt;
              Make Speaker
            &amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    );
  } else {
    return null;
  }
};

export default PermissionsMenu;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Filxguxobu8boukwi8nsr.gif" 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%2Filxguxobu8boukwi8nsr.gif" alt="Thirteenth Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Adding Hand-Raise Functionality
&lt;/h5&gt;

&lt;p&gt;Finally, let’s add the last bit of functionality to our application: &lt;code&gt;hand-raise&lt;/code&gt;. As a &lt;code&gt;listener&lt;/code&gt;, you might also want a chance to become a speaker at times. To notify the &lt;code&gt;moderator&lt;/code&gt;, we can build a simple hand-raise button that will display a badge next to your avatar to show that you're interested to speak.&lt;/p&gt;

&lt;p&gt;Therefore, start by building the functionality to change our role from &lt;code&gt;listener&lt;/code&gt; to &lt;code&gt;handraise&lt;/code&gt; on clicking the &lt;code&gt;&amp;lt;HandRaiseButton /&amp;gt;&lt;/code&gt; component. &lt;/p&gt;

&lt;p&gt;To do this, go back to the &lt;code&gt;&amp;lt;Controls /&amp;gt;&lt;/code&gt; component inside &lt;code&gt;components/Room/Controls.jsx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Over here, you’ll notice the &lt;code&gt;&amp;lt;HandRaiseButton /&amp;gt;&lt;/code&gt; component with 2 props: a &lt;code&gt;isHandRaised&lt;/code&gt; boolean prop to check if you've raised hand currently and a &lt;code&gt;toggleHandRaise&lt;/code&gt; function to toggle it. Also, we'll display this button only if we are a &lt;code&gt;listener&lt;/code&gt; or have &lt;code&gt;handraise&lt;/code&gt; role.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;isHandRaised&lt;/code&gt; prop, we simply need to get our role by accessing the &lt;code&gt;roleName&lt;/code&gt; property of our local peer and check if it equates to the &lt;code&gt;handraise&lt;/code&gt; role.&lt;/p&gt;

&lt;p&gt;For the toggle functionality, we can use the &lt;code&gt;changeRole()&lt;/code&gt; method available on the &lt;code&gt;hmsActions&lt;/code&gt; object like we did for the &lt;code&gt;&amp;lt;PermissionsMenu /&amp;gt;&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;changeRole()&lt;/code&gt; method takes our local peer's ID, the new role to set, and a force boolean prop. For the new role, if we are a &lt;code&gt;listener&lt;/code&gt; currently, we need to pass &lt;code&gt;handraise&lt;/code&gt; as the parameter. If we already have the role of &lt;code&gt;handraise&lt;/code&gt;, we need to set it back to listener.&lt;/p&gt;

&lt;p&gt;Here’s how your code should look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// components/Room/Controls.jsx

import {
  useHMSStore,
  useHMSActions,
  selectIsLocalAudioEnabled,
  selectLocalPeer,
} from '@100mslive/hms-video-react';

import MicButton from './MicButton';
import ExitButton from './ExitButton';
import HandRaiseButton from './HandRaiseButton';

const Controls = () =&amp;gt; {
  const hmsActions = useHMSActions();
  const isMicOn = useHMSStore(selectIsLocalAudioEnabled);
  const peer = useHMSStore(selectLocalPeer);

  const isListenerOrHandraised =
    peer.roleName === 'listener' || peer.roleName === 'handraise';

  return (
    &amp;lt;div className="flex justify-center space-x-4"&amp;gt;
      {!isListenerOrHandraised &amp;amp;&amp;amp; (
        &amp;lt;MicButton
          isMicOn={isMicOn}
          toggleMic={() =&amp;gt; hmsActions.setLocalAudoEnabled(!isMicOn)}
        /&amp;gt;
      )}
      {isListenerOrHandraised &amp;amp;&amp;amp; (
        &amp;lt;HandRaiseButton
          isHandRaised={peer.roleName === 'handraise'}
          toggleHandRaise={() =&amp;gt;
            hmsActions.changeRole(
              peer.id,
              peer.roleName === 'listener' ? 'handraise' : 'listener',
              true
            )
          }
        /&amp;gt;
      )}
      &amp;lt;ExitButton exitRoom={() =&amp;gt; hmsActions.leave()} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Controls;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fk12tcza7dynn49vpyujp.gif" 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%2Fk12tcza7dynn49vpyujp.gif" alt="Fourteenth Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The starter code already contains the code to display a hand-raise badge in the &lt;code&gt;&amp;lt;ListenerTile /&amp;gt;&lt;/code&gt; component. Inside this component, we just need to check if the peer's role is set to &lt;code&gt;handraise&lt;/code&gt; and then conditionally render the &lt;code&gt;&amp;lt;HandRaiseBadge /&amp;gt;&lt;/code&gt; accordingly.&lt;/p&gt;

&lt;h5&gt;
  
  
  And That’s a Wrap!
&lt;/h5&gt;

&lt;p&gt;Building real-time audio application with 100ms SDK is as simple as that. I hope you enjoyed building this app, and make sure to drop by the 100ms Discord Server in case of any queries.&lt;/p&gt;

&lt;p&gt;We can’t wait to see all the marvelous projects you build with 100ms. Till then, happy coding!&lt;/p&gt;

&lt;p&gt;Check 100ms Now -&amp;gt; &lt;a href="https://www.100ms.live/blog/build-discord-stage-channel-clone-hms" rel="noopener noreferrer"&gt;https://www.100ms.live/blog/build-discord-stage-channel-clone-hms&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
      <category>beginner</category>
    </item>
    <item>
      <title>Android SurfaceViewRenderer explained</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Tue, 02 Nov 2021 10:25:45 +0000</pubDate>
      <link>https://dev.to/100mslive/android-surfaceviewrenderer-explained-42bh</link>
      <guid>https://dev.to/100mslive/android-surfaceviewrenderer-explained-42bh</guid>
      <description>&lt;p&gt;The mighty SurfaceViewRenderer! It's the very end of a long WebRTC chain that gets video from other people to your screen.&lt;/p&gt;

&lt;p&gt;It can also be a bit difficult to use if it's your first time around. Especially in a dynamic android app with a lots of views flicking in and out.&lt;/p&gt;

&lt;p&gt;Let's a take a tour through the many ways it can go wrong and what you should know to keep your video conference calls running smoothly.&lt;/p&gt;

&lt;p&gt;Here's the lifecycle of a SurfaceViewRenderer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ec-gu2v5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/59ggipeyg8904i22uctt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ec-gu2v5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/59ggipeyg8904i22uctt.png" alt="Image description" width="880" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which means:&lt;/p&gt;

&lt;p&gt;*Once you create the SurfaceViewRenderer, you need to initialise it before it can be used at all. This kicks off a render thread.&lt;/p&gt;

&lt;p&gt;*addSink should be called after this. Where is addSink you ask? Well it's not on the SurfaceViewRenderer, it's a method on the VideoTrack you want to add to it.&lt;/p&gt;

&lt;p&gt;*removeSink should only be called when you're done showing the video and now it's time to either destroy the view (or recycle it), this stops downloading data from the peer's video too. Another function that's only on the VideoTrack, not the SurfaceViewRenderer though it takes the SurfaceViewRenderer as a parameter.&lt;/p&gt;

&lt;p&gt;*Finally release so the render thread can stop.&lt;/p&gt;

&lt;p&gt;Once released the SurfaceViewRenderer can be inited again and the cycle renews. Now let's look at what happens when any one of these is missed or not called with the right parameters.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Initing without a shared EglContext
&lt;/h3&gt;

&lt;p&gt;This one is actually mentioned in the &lt;a href="https://chromium.googlesource.com/external/webrtc/trunk/talk/+/cdd35e557b600d1f7dbe9c99531a42adf194f973/app/webrtc/java/android/org/webrtc/SurfaceViewRenderer.java#131"&gt;source code&lt;/a&gt; of SurfaceViewRenderer, when you call SurfaceViewRenderer.init(context) it must only be with a shared EGL context.&lt;/p&gt;

&lt;p&gt;What does that mean? That you call org.webrtc.EglBase.create() once in something like a singleton and then always initialize all the SurfaceViewRenderers with that saved context's eglBaseContext. &lt;/p&gt;

&lt;p&gt;If you call create() for each SurfaceView, you'd end up with no errors and no video playing at all. It's easy to get stuck here and wonder why SurfaceViewRenderer is not showing any video when seemingly nothing went wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Not releasing before initing
&lt;/h3&gt;

&lt;p&gt;If you don't release after you've removed a video sink, you're going to get the same result as if you initialised twice.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;java.lang.IllegalStateException: videoSurfaceViewAlready initialized&lt;br&gt;
    at org.webrtc.EglRenderer.init(EglRenderer.java:212)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So you've just got to release at the right time.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What if you never init at all?
&lt;/h3&gt;

&lt;p&gt;The view just never shows up ¯_(ツ)_/¯&lt;br&gt;
Behind the scenes a render thread was never created and the loop to draw wasn't kicked off so, of course, nothing would show up.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Double the addSinks, double vision
&lt;/h3&gt;

&lt;p&gt;Calling addSink on the same SurfaceViewRenderer is a pretty fun bug. You won't realise anything's wrong if you just added all the the videos for people twice, until the order of the videos is changed.&lt;/p&gt;

&lt;p&gt;Then you'll see flicker as two different video sources try to play onto the same surface. Both video sources will take race condition turns rendering.&lt;/p&gt;

&lt;p&gt;Oh, and you'll keep downloading the video from the source peer if you only called removeSink once when disposing of the view.&lt;/p&gt;

&lt;p&gt;Here's an image of what the two video streams look like separately and what happens when addSink for both was called on the same SurfaceViewRenderer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JaaZpDN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yhsm4h1ru942wvu87ge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JaaZpDN1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4yhsm4h1ru942wvu87ge.png" alt="Image description" width="320" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. How many SurfaceViewRenderers can you create? What happens beyond?
&lt;/h3&gt;

&lt;p&gt;Without initialising them, you can create a heck of a lot. But there's a limit to how many different initialised SurfaceViewRenderers you can prepare at a time.&lt;/p&gt;

&lt;p&gt;The limit varies from device to device, when I'd tried on one modestly powered Nokia 3.4 phone it got up to 30 contexts initialised until it failed with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.example.myvideocallapp, PID: 532java.lang.RuntimeException: java.lang.RuntimeException: Failed to create EGL context: 0x3003at org.webrtc.EglBase14Impl.createEglContext(EglBase14Impl.java:282)at org.webrtc.EglBase14Impl.(EglBase14Impl.java:78)at org.webrtc.EglBase.createEgl14(EglBase.java:215)at org.webrtc.EglBase.create(EglBase.java:158)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On others, like an Oppo F19 it gets beyond 150 and keep going. Considering that that's the number of videos you're displaying simultaneously, you may not need to get anywhere near that much.&lt;/p&gt;

&lt;p&gt;Now let's look at something even tricker than managing a single or a handful of a static number of SurfaceViewRenderers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing the lifecycle within a RecyclerView
&lt;/h3&gt;

&lt;p&gt;As a refresher, the RecyclerView is an essential View on Android that lets you avoid creating expensive view objects when scrolling through a large list.&lt;/p&gt;

&lt;p&gt;What's even more expensive than a typical view object? The SurfaceViewRenderer of course.&lt;/p&gt;

&lt;p&gt;Let's think about what a RecyclerView does in android.&lt;/p&gt;

&lt;p&gt;*It creates a view when a new one is required.&lt;/p&gt;

&lt;p&gt;*It keeps a cache of older views that aren't being used right now.&lt;/p&gt;

&lt;p&gt;*It may need to render a view in a different place because its order in the layout changed. (In case of a high score board, there may be a rapid change in positions)&lt;/p&gt;

&lt;h3&gt;
  
  
  How does that compare to the lifecycle of a SurfaceView?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gEo-I35M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96d8d4z21dfh4n53tth9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gEo-I35M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/96d8d4z21dfh4n53tth9.png" alt="Image description" width="880" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This poses a natural problem because there are multiple things that could cause the view to be changed.&lt;/p&gt;

&lt;p&gt;*The position of the view changes. A teacher who's supposed to be shown first, may join after the students.&lt;/p&gt;

&lt;p&gt;*The peer representing this video leaves, so has to be removed.&lt;/p&gt;

&lt;p&gt;*New people keep joining so new ones have to be made.&lt;/p&gt;

&lt;p&gt;*Someone scrolls the list, so no new views are made but they are constantly recycled.&lt;/p&gt;

&lt;p&gt;To further complicate matters, someone's video may not have been on at first, so you may just show the person's initials, but later they do turn it on, so now the SurfaceViewRender has to be initialized.&lt;/p&gt;

&lt;p&gt;If you're to Android, rebinding views is straightforward, you'd just do that in the AdapterView's bind method but a slightly unconventional approach is required here because anyone's video can go on or off at any time.&lt;/p&gt;

&lt;p&gt;So the big question becomes, when do you initialize the video?&lt;br&gt;
You'd quickly see a 'bind' method is insufficient.&lt;/p&gt;

&lt;p&gt;What we need here are some lesser known adapter methods:&lt;br&gt;
&lt;a href="https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.Adapter#onViewAttachedToWindow(VH)"&gt;onViewAttachedToWindow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Called when a new view is added to the window or a cached one is restored.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.Adapter#onViewAttachedToWindow(VH)"&gt;onViewDetachedFromWindow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Called when a view is removed, we don't know if it's going into cache or out permanently.&lt;/p&gt;

&lt;p&gt;Take a look at the PeerViewHolder and the PeerAdapter to see how all the disparate cases are handled. This is the simplest example of how you could manage the tricky SurfaceViewRenderer in an Android RecyclerView.&lt;br&gt;
This is part of the "hello world" demonstration of how the 100ms SDK can be used.&lt;/p&gt;

&lt;p&gt;To look into more advanced use cases, take a look at our &lt;a href="https://github.com/100mslive/100ms-android/"&gt;fully featured sample app&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>swift</category>
      <category>beginners</category>
      <category>webrtc</category>
    </item>
    <item>
      <title>How to build an audio app like Clubhouse for iOS</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Mon, 25 Oct 2021 18:10:31 +0000</pubDate>
      <link>https://dev.to/100mslive/how-to-build-an-audio-app-like-clubhouse-for-ios-2egp</link>
      <guid>https://dev.to/100mslive/how-to-build-an-audio-app-like-clubhouse-for-ios-2egp</guid>
      <description>&lt;p&gt;The face of video/audio communication has changed dramatically due to the global pandemic. As corporations consider video/audio conferencing as an ultimate solution to connect with remote workers/customers, the affinity to provide features has gone up.&lt;/p&gt;

&lt;p&gt;Building a feature-rich Video/audio conferencing application isn't that easy, and this is where 100ms comes to the rescue. In this guide, we will be building a Clubhouse clone using 100ms iOS SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Our Clubhouse clone will have the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audio Call&lt;/li&gt;
&lt;li&gt;Raise hand&lt;/li&gt;
&lt;li&gt;Move people who raised a hand to speakers and back to the audience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To focus on SDK integration rather than room list management, the app will use a single predefined room.&lt;/p&gt;

&lt;p&gt;*Join our upcoming workshop ‘ Build 👋 Clubhouse like app for iOS in 45 minutes’ — &lt;a href="https://community.100ms.live/developer-community-meetup-october"&gt;https://community.100ms.live/developer-community-meetup-october&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Terminology&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
Room - A room is a primary object that 100ms SDKs return on successful connection. This contains references to peers, tracks, and everything you need to render a live a/v app.&lt;/p&gt;

&lt;p&gt;Peer - A peer is an object returned by 100ms SDKs that contains all information about a user - name, role, video track, etc.&lt;/p&gt;

&lt;p&gt;Track - A track represents either the audio or video that a peer is publishing&lt;/p&gt;

&lt;p&gt;Role - A role defines who a peer can see/hear, the quality they publish their video, whether they have permission to publish video/screen share, mute someone, or change someone's role.&lt;/p&gt;
&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;To follow this tutorial, you must have a basic understanding of the elementary principles of iOS Development, Xcode, Swift.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting up Project
&lt;/h3&gt;

&lt;p&gt;To simplify things and help us focus on adding the core functionality, I already created a template project with the main UI for the audio room app ready for the SDK integration.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git clone &amp;lt;https://github.com/100mslive/clubhouse-clone-ios-swift&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Now that you have cloned the repo, you will find two folders inside:&lt;/p&gt;

&lt;p&gt;*AudioRoomTemplate - some boilerplate project if you want to go along with this guide step by step&lt;/p&gt;

&lt;p&gt;*AudioRoomComplete - if you'd instead explore the finished project.&lt;/p&gt;

&lt;p&gt;*&lt;strong&gt;&lt;em&gt;Dependencies&lt;/em&gt;&lt;/strong&gt;*&lt;/p&gt;

&lt;p&gt;Open AudioRoom.xcodeproj from the AudioRoomTemplate folder. Our first step would be adding 100ms SDK to the project. &lt;/p&gt;

&lt;p&gt;For that we will be using Swift package manager. Select "Add Package" from Xcode File menu and use &lt;a href="https://github.com/100mslive/100ms-ios-sdk.git"&gt;https://github.com/100mslive/100ms-ios-sdk.git&lt;/a&gt; as the package url.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Access Credentials&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The primary means of authentication for 100ms SDK is a jwt auth token. It is never a good idea to hardcode tokens in your app, so your backend should provide this token. &lt;/p&gt;

&lt;p&gt;You might not have a backend set up during prototyping, so 100ms provides a temporary token generation backend for you to try, which should not be used in production. To get a token from the 100ms backend, we need a token endpoint URL and a room id.&lt;/p&gt;

&lt;p&gt;To get these credentials, you first need to create an account at 100ms Dashboard. After your account is set up, head over to the Developer Section, and you can find your Token endpoint URL there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Creating Roles&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before we create a room, we will create a custom app; you can find it here. Click on "Add a new App." When you are asked to choose a template, select "Create your Own".&lt;/p&gt;

&lt;p&gt;Now click on “Create Roles” button this will open a modal were we can create our custom roles.&lt;/p&gt;

&lt;p&gt;We will be having 4 roles in our application.&lt;/p&gt;

&lt;p&gt;*audience - only able to listen to others, can change role to speakerwannabe&lt;/p&gt;

&lt;p&gt;*speakerwannabe - same as speaker, will be used to segregate people who raised their hand&lt;/p&gt;

&lt;p&gt;*speaker - able to listen and speak&lt;/p&gt;

&lt;p&gt;*host - everything that speaker can plus the ability to change other’s role&lt;/p&gt;

&lt;p&gt;We will create &lt;code&gt;host&lt;/code&gt; role first. Input &lt;code&gt;host&lt;/code&gt; into the role name field, leave only "Can share audio" enabled under "Publish Strategies". Under permissions enable "Can change any participant's role" and then click "save".&lt;/p&gt;

&lt;p&gt;Proceed to create &lt;code&gt;speaker&lt;/code&gt; role which should be identical to host. Now lets create &lt;code&gt;audience&lt;/code&gt; and &lt;code&gt;speakerwannabe&lt;/code&gt; roles.  Disable everything under "Publish Strategies". &lt;/p&gt;

&lt;p&gt;Under permissions enable "Can change any participant's role" and then click "save".&lt;/p&gt;

&lt;p&gt;Now that our roles are setup we will move on to our next step by clicking ‘Set up App’. You should see your custom app being created.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating a Room
&lt;/h3&gt;

&lt;p&gt;In the last step we created our custom app and setup custom roles , in this step we will create a room id.&lt;/p&gt;

&lt;p&gt;To create a room head over to &lt;a href="https://dashboard.100ms.live/create-room"&gt;Link&lt;/a&gt; page in 100ms Dashboard click on “Create Room” make sure to select the Custom App that you created in the last step. Fill out Room Name and click “Create Room”. After creating you will be routed to Room Details were you can find your room id.&lt;/p&gt;

&lt;p&gt;Awesome! Now that we have token endpoint and room id we will add it in our app. Open &lt;code&gt;TokenProvider.swift&lt;/code&gt; file and insert the values in the &lt;code&gt;Constants&lt;/code&gt; struct. It should look like this:&lt;/p&gt;

&lt;p&gt;Now we can start building our project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Joining The Room
&lt;/h3&gt;

&lt;p&gt;The initial screen of the app is the login screen controlled by the The initial screen of the app is the login screen controlled by the MainViewController.&lt;/p&gt;

&lt;p&gt;When the user taps the "JOIN ROOM" button, it uses TokenProvider class to fetch the token from the 100ms token backend and then passes it to RoomViewController, which it then presents modally. &lt;/p&gt;

&lt;p&gt;Having a token, we can proceed with joining the room. Open RoomViewController.swift file to start. First, you need to add an import for the SDK:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import HMSSDK&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The next thing we need to do is to have an instance of the SDK. It serves as an entry point to everything we will be using.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```class RoomViewController: UIViewController {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private let hms: HMSSDK = HMSSDK.build()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;...```&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Now we are ready to join a room. In the join function stub add following code:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;func join() {&lt;br&gt;
        let config = HMSConfig(userName: name, authToken: token)&lt;br&gt;
        hms.join(config: config, delegate: self)&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The above code creates a configuration for the join call from the user name and token that we have obtained earlier. The join function will be called fro the viewDidLoad override. With the join call we also provide a delegate that will receive the important updates from the SDK. So lets add conformance to the &lt;code&gt;HMSUpdateListener&lt;/code&gt; protocol:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```extension RoomViewController: HMSUpdateListener {&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    /// This will be called on a successful JOIN of the room by the user
    /// This is the point where applications can stop showing its loading state
func on(join room: HMSRoom) {

}

    /// This is called when there is a change in any property of the Room
func on(room: HMSRoom, update: HMSRoomUpdate) {

}

/// This will be called whenever there is an update on an existing peer
/// or a new peer got added/existing peer is removed.
/// This callback can be used to keep a track of all the peers in the room
func on(peer: HMSPeer, update: HMSPeerUpdate) {

}

    /// This is called when there are updates on an existing track
/// or a new track got added/existing track is removed
/// This callback can be used to render the video on screen whenever a track gets added
func on(track: HMSTrack, update: HMSTrackUpdate, for peer: HMSPeer) {

}

    /// This will be called when there is an error in the system
/// and SDK could not recover
func on(error: HMSError) {
            // Display an error alert to the user and bail to main screen
    showError(error.localizedDescription) { [weak self] in
        self?.dismiss(animated: true, completion: nil)
    }
}

    /// This is called when there is a new message from any other peer in the room
/// This can be used to implement chat is the room
func on(message: HMSMessage) {

}

    /// This is called every 1 second with list of active speakers
func on(updated speakers: [HMSSpeaker]) {

}

    /// This is called when SDK detects a network issue and is trying to recover
func onReconnecting() {

}

    /// This is called when SDK successfully recovered from a network issue
func onReconnected() {

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

&lt;/div&gt;

&lt;p&gt;}```&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The only thing we are going to handle at this stage is on(error:) which will let us see if something went wrong. Rest can be left as is.&lt;/p&gt;

&lt;p&gt;Now when user decides to leave the room we should call leave function. This will let other people know we left. The appropriate place to do that in case of a modally presented controller should be &lt;code&gt;endAppearanceTransition&lt;/code&gt; override:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```override func endAppearanceTransition() {&lt;br&gt;
        super.endAppearanceTransition()&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    if isBeingDismissed {
        UIApplication.shared.isIdleTimerDisabled = false

        hms.leave() // &amp;lt;--- cleanup
    }
 }```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With this in place quite a lot is achieved. You can join the room as host/audience/speaker. Audience will not be able to speak but host/speaker will. &lt;/p&gt;

&lt;p&gt;This is done by the SDK automatically publishing/subscribing to tracks based on the roles configuration we made in the beginning.&lt;/p&gt;
&lt;h3&gt;
  
  
  Showing Peers In The Room
&lt;/h3&gt;

&lt;p&gt;Now that we can join in the room, let's display the peers who have joined the room. To get all peers, we will use hms. room?.peers property. This will return us an array of all peers in the room.&lt;/p&gt;

&lt;p&gt;Each peer object stores the details of individual participants in the room. You can refer to the interface of HMSPeer in our API-reference docs.&lt;br&gt;
We will show these peers in a UICollectionView separated into two sections: audience and speakers. The host will also have the ability see peers who have raised a hand in the "Raised hand" section. We need to create a data model for the sections to use in the collection view data source to achieve that. The template project already has a Section class that can hold a list of peers and a type of section and logic to tell which role goes into which section. So let us create our data model in the RoomViewController class:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```class RoomViewController: UIViewController {&lt;br&gt;
...&lt;br&gt;
        // Recreates the model from the current peer list provided by the SDK&lt;br&gt;
    func reloadModel() {&lt;br&gt;
        // Get a list of peers in the room&lt;br&gt;
        let peers = hms.room?.peers ?? []&lt;br&gt;
        // Create a section of each type to add peers to&lt;br&gt;
        let sectionsModel = SectionType.allCases.map { Section(type: $0) }&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    for peer in peers {
        // Get section type for this peer based on its role
        let type = Section.sectionType(for: peer.mappedRole, showRaisedHand: isHost)
        // Find the index of this section in the resulting array
        let index = type.rawValue
        // Add the peer to the respective section
        sectionsModel[index].peers.append(peer)
    }

    // Remove empty sections and store the new model
    sections = sectionsModel.filter { !$0.peers.isEmpty }
}

    // Wether current peer is a host
var isHost: Bool {
    localRole == .host
}

    // Map the role of the local peer to one of our known roles. see Role.swift
var localRole: RoleType {
    return hms.localPeer?.mappedRole ?? .unknown
}

// Holds the current model, reloads the collection view on set
private var sections = [Section]() {
    didSet {
        participantsView.reloadData()
    }
}```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now that we have added logic to build the model we need to wire it up to the SDK updates so that it gets updated when any of these updates happen:&lt;/p&gt;

&lt;p&gt;*Room is joined and we get the initial list of peers in the room&lt;br&gt;
*Any peer joins or leaves&lt;br&gt;
*Peer changes its role&lt;br&gt;
*Peer mutes/unmutes  (in case he is a speaker/host)&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```     func on(join room: HMSRoom) {&lt;br&gt;
        reloadModel()&lt;br&gt;
    }&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func on(peer: HMSPeer, update: HMSPeerUpdate) {
    reloadModel()
}

func on(track: HMSTrack, update: HMSTrackUpdate, for peer: HMSPeer) {
    reloadModel()
}```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With all this in place we can now connect our model to the collection view. The template logic already has a &lt;code&gt;ParticipantCollectionViewCell&lt;/code&gt; class that we will use to show our participants.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```extension RoomViewController: UICollectionViewDataSource {&lt;br&gt;
    func numberOfSections(in collectionView: UICollectionView) -&amp;gt; Int {&lt;br&gt;
        return sections.count&lt;br&gt;
    }&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -&amp;gt; Int {
    return sections[safe: section]?.peers.count ?? 0
}

func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -&amp;gt; UICollectionReusableView {
    guard let section = sections[safe: indexPath.section], kind == UICollectionView.elementKindSectionHeader else {
        return UICollectionReusableView()
    }

    let sectionHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "header", for: indexPath) as! SectionHeader
    sectionHeader.nameLabel.text = section.sectionDisplayName()
    return sectionHeader
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&amp;gt; UICollectionViewCell {
    guard let peer = sections[safe: indexPath.section]?.peers[safe: indexPath.item],
          let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ParticipantCell",
                                              for: indexPath) as? ParticipantCollectionViewCell else {
        return UICollectionViewCell()
    }

    cell.name = peer.name
    cell.isMute = (peer.audioTrack?.isMute() ?? false)

    return cell
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;extension RoomViewController: UICollectionViewDelegateFlowLayout {&lt;br&gt;
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -&amp;gt; CGSize {&lt;br&gt;
        guard let section = sections[safe: section], section.type != .speakers else {&lt;br&gt;
            return .zero&lt;br&gt;
        }&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    return CGSize(width: collectionView.frame.width, height: 30)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}'&lt;/p&gt;

&lt;p&gt;And now we are able to see who is in the room, wether they belong to audience or speakers and their mic mute status.&lt;/p&gt;
&lt;h3&gt;
  
  
  Showing Active Speakers
&lt;/h3&gt;

&lt;p&gt;Another must have feature of an audio room app is showing who is currently speaking. This is quite easy to add. First create a storage for the active speaker ids:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;private var speakers = Set&amp;lt;String&amp;gt;() {&lt;br&gt;
        didSet {&lt;br&gt;
            participantsView.reloadData()&lt;br&gt;
        }&lt;br&gt;
    }&lt;/code&gt;``&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Then populate it from &lt;code&gt;on(updated:)&lt;/code&gt; callback&lt;br&gt;
&lt;code&gt;func on(updated speakers: [HMSSpeaker]) {&lt;br&gt;
                self.speakers = Set(speakers.map { $0.peer.peerID })&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now in the cell configuration add this:&lt;br&gt;
&lt;code&gt;func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&amp;gt; UICollectionViewCell {&lt;br&gt;
...&lt;br&gt;
            cell.isSpeaking = speakers.contains(peer.peerID)&lt;br&gt;
...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And we are done! The speaker will now be marked by a blue glow.&lt;/p&gt;
&lt;h3&gt;
  
  
  Mute/Unmute
&lt;/h3&gt;

&lt;p&gt;Now let's add a mic mute button. First we need a helper function to tell us if we can speak:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;private var canSpeak: Bool {&lt;br&gt;
        switch localRole {&lt;br&gt;
        case .host, .speaker:&lt;br&gt;
            return true&lt;br&gt;
        default:&lt;br&gt;
            return false&lt;br&gt;
        }&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then we will add a function to setup buttons visibility according to role:&lt;br&gt;
`private func setupButtonStates() {&lt;br&gt;
        muteButton.isHidden = !canSpeak&lt;br&gt;
}'&lt;/p&gt;

&lt;p&gt;This should be called in on(join:) because at that time we will know what role we joined as:&lt;br&gt;
`func on(join room: HMSRoom) {&lt;br&gt;
        reloadModel()&lt;br&gt;
        setupButtonStates()&lt;br&gt;
}'&lt;/p&gt;

&lt;p&gt;Finally add a mute button tap handler:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@IBAction func muteTapped(_ sender: Any) {&lt;br&gt;
        muteButton.isSelected = !muteButton.isSelected&lt;br&gt;
        hms.localPeer?.localAudioTrack()?.setMute(muteButton.isSelected)&lt;br&gt;
                reloadModel()&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The code above queries local peer for its audio track and then sets its mute status. Then reloads model so that our cell can reflect the change. Now joining as a host you will see the mute button. While joining as audience you will see none.&lt;/p&gt;
&lt;h3&gt;
  
  
  Raise Hand
&lt;/h3&gt;

&lt;p&gt;What makes audio rooms fun is that anyone can become a speaker at some point. To show interest in becoming a speaker we will add a raise hand button. The UI is already in place we just need to unhide it according to role just like with mute button.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;private func setupButtonStates() {&lt;br&gt;
        ...&lt;br&gt;
        raiseHandButton.isHidden = canSpeak&lt;br&gt;
}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Now in the button tap handler we will use changeRole api to change our own role to &lt;code&gt;speakerwannabe&lt;/code&gt; so that the host can see us:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;```@IBAction func raiseHandTapped(_ sender: UIButton) {&lt;br&gt;
        guard let peer = hms.localPeer else {&lt;br&gt;
            return&lt;br&gt;
        }&lt;br&gt;
        sender.isSelected = !sender.isSelected&lt;br&gt;
        sender.tintColor = sender.isSelected ? .red : .white&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            // If hand is already raised move ourselves back to audience
    let role = sender.isSelected ? Role.audience : Role.speakerwannabe

    change(peer: peer, to: role)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;func change(peer: HMSPeer, to role: Role) {&lt;br&gt;
        // Get a reference to HMSRole instance for required role&lt;br&gt;
        guard let newRole = hms.roles.first(where: { $0.name == role.rawValue }) else {&lt;br&gt;
            return&lt;br&gt;
        }&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;            // The force flag is used by the backend to decide wether peer 
    // should be changed immediately or promted to change instead.
    hms.changeRole(for: peer, to: newRole, force: true)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;}```&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Now as a host we should be able to move people who raised hand to speakers, as well as move speakers back to audience. Lets do that by adding showing an action sheet when cell has been tapped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let peer = sections[safe: indexPath.section]?.peers[safe: indexPath.item], isHost else {
            return}
        let action: UIAlertAction
        switch peer.mappedRole {
        case .speakerwannabe:
            action = changeRoleAction(peer: peer, role: .speaker, title: "Move to speakers")
        case .speaker:
            action = changeRoleAction(peer: peer, role: .audience, title: "Move to audience")
        default:
            return
        }
        let alertController = UIAlertController(title: "",
                                                message: "Select action",
                                                preferredStyle: .actionSheet)
        alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel))
        alertController.addAction(action)
        present(alertController, animated: true)}
    func changeRoleAction(peer: HMSPeer, role: Role, title: String) -&amp;gt; UIAlertAction {
        UIAlertAction(title: title, style: .default) { [weak self] _ in
           self?.change(peer: peer, to: role)}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we are done. Launch the app and try for yourself!&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>project</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>⚔️ Open Source v/s cPaaS ⚔️ - The architecture choices for building a live video applications.</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Mon, 18 Oct 2021 18:24:41 +0000</pubDate>
      <link>https://dev.to/100mslive/open-source-vs-cpaas-the-architecture-choices-for-building-a-live-video-applications-cfn</link>
      <guid>https://dev.to/100mslive/open-source-vs-cpaas-the-architecture-choices-for-building-a-live-video-applications-cfn</guid>
      <description>&lt;p&gt;Join Kshitij Gupta, Co-founder &amp;amp; CEO at #100ms along with Sean DuBois, founder of #Pion, Developer at Twitch talk about #WebRTC at upcoming #CTOTalk, October session! 🚀&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agenda&lt;/strong&gt; -&amp;gt; Open Source v/s cPaaS - The architecture choices for building a live video applications.&lt;/p&gt;

&lt;p&gt;Watch as the top minds in the space of live video systems answer queries about -&lt;/p&gt;

&lt;p&gt;👉 How live video applications work and why are they hard to build&lt;br&gt;
👉 Architecture choices for building live video applications&lt;br&gt;
👉 How to scale live video systems in production and war stories&lt;br&gt;
👉 How to grow an open source project&lt;/p&gt;

&lt;p&gt;Block your seats now ⬇️&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Date&lt;/strong&gt; : 9 AM - 11 AM IST Saturday, 23rd October &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Register here&lt;/strong&gt; : &lt;a href="https://bit.ly/ctotalk-opensource"&gt;https://bit.ly/ctotalk-opensource&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webrtc</category>
      <category>javascript</category>
      <category>reactnative</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Building highly scalable real-time video applications with WebRTC
</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Mon, 18 Oct 2021 11:04:36 +0000</pubDate>
      <link>https://dev.to/scarneck_arpit/building-highly-scalable-real-time-video-applications-with-webrtc-5gp9</link>
      <guid>https://dev.to/scarneck_arpit/building-highly-scalable-real-time-video-applications-with-webrtc-5gp9</guid>
      <description>&lt;p&gt;Join &lt;strong&gt;Kshitij Gupta&lt;/strong&gt; - CEO and Co-Founder at 100ms and &lt;strong&gt;Sean DuBois&lt;/strong&gt; - Developer Twitch, Creator Pion at CTOtalks session -&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt; - Building highly scalable production-ready real-time video applications with WebRTC&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When&lt;/strong&gt;: 9 AM - 11 AM IST Saturday, 23rd October  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Join Here&lt;/strong&gt; - &lt;a href="https://bit.ly/ctotalk-opensource"&gt;https://bit.ly/ctotalk-opensource&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The session will delve into the details of architecture and open-source choices for live video systems and how to run them in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agenda&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;➡️ How do live video applications work, and why is it hard to build&lt;br&gt;
➡️ Overview of WebRTC (Deep dive on one interesting WebRTC concept)&lt;br&gt;
➡️ Making life simpler for developers&lt;br&gt;
➡️ How to run WebRTC systems in production&lt;/p&gt;

&lt;p&gt;See you on 23rd with a power-packed session!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Register for FREE now - &lt;a href="https://bit.ly/ctotalk-opensource"&gt;https://bit.ly/ctotalk-opensource&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webrtc</category>
      <category>reactnative</category>
      <category>flutter</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Build Clubhouse 👋  like app for iOS</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Mon, 11 Oct 2021 12:38:38 +0000</pubDate>
      <link>https://dev.to/100mslive/build-clubhouse-like-app-for-ios-33p7</link>
      <guid>https://dev.to/100mslive/build-clubhouse-like-app-for-ios-33p7</guid>
      <description>&lt;p&gt;Join 100ms October developer meet-up where we cover - Building a Audio app like Clubhouse for iOS using 100ms SDK on October 13th, 2021 | 9:00 PM IST&lt;/p&gt;

&lt;p&gt;Join Now - &lt;a href="https://community.100ms.live/developer-community-meetup-october"&gt;https://community.100ms.live/developer-community-meetup-october&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Workshop Host - Dmitry Fedoseyev &amp;amp; Yogesh Singh, iOS developers at 100ms&lt;/p&gt;

&lt;p&gt;Agenda -&lt;br&gt;
-&amp;gt; The killer idea behind Clubhouse and its working&lt;br&gt;
-&amp;gt; Setting up &amp;amp; creating roles for the app.&lt;br&gt;
-&amp;gt; Understanding terminology - HMS SDK, rooms, etc.&lt;br&gt;
-&amp;gt; Integrating the HMS SDK to create an audio room from scratch&lt;br&gt;
-&amp;gt; QnA with 100ms team&lt;/p&gt;

</description>
      <category>ios</category>
      <category>swift</category>
      <category>tutorial</category>
      <category>android</category>
    </item>
    <item>
      <title>[Free Workshop] Build Clubhouse like app for iOS </title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Sat, 09 Oct 2021 05:04:50 +0000</pubDate>
      <link>https://dev.to/scarneck_arpit/free-workshop-build-clubhouse-like-app-for-ios-37mi</link>
      <guid>https://dev.to/scarneck_arpit/free-workshop-build-clubhouse-like-app-for-ios-37mi</guid>
      <description>&lt;p&gt;Join 100ms October developer meet-up where we cover - Building a Audio app like Clubhouse for iOS using 100ms SDK on October 13th, 2021 | 9:00 PM IST &lt;/p&gt;

&lt;p&gt;Join Now - &lt;a href="https://community.100ms.live/developer-community-meetup-october"&gt;https://community.100ms.live/developer-community-meetup-october&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Workshop Host - Dmitry Fedoseyev &amp;amp; Yogesh Singh, iOS developers at 100ms&lt;/p&gt;

&lt;p&gt;Agenda - &lt;br&gt;
-&amp;gt; The killer idea behind Clubhouse and its working&lt;br&gt;
-&amp;gt; Setting up &amp;amp; creating roles for the app.&lt;br&gt;
-&amp;gt; Understanding terminology - HMS SDK, rooms, etc.&lt;br&gt;
-&amp;gt; Integrating the HMS SDK to create an audio room from scratch&lt;br&gt;
-&amp;gt; QnA with 100ms team&lt;/p&gt;

</description>
      <category>ios</category>
      <category>programming</category>
      <category>hacktoberfest</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Add live video conferencing to your app in hours, not months</title>
      <dc:creator>Arpit Mishra</dc:creator>
      <pubDate>Thu, 12 Aug 2021 12:55:32 +0000</pubDate>
      <link>https://dev.to/scarneck_arpit/add-live-video-conferencing-to-your-app-in-hours-not-months-2bfo</link>
      <guid>https://dev.to/scarneck_arpit/add-live-video-conferencing-to-your-app-in-hours-not-months-2bfo</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;I’d love to share what we are building past few months with you. &lt;/p&gt;

&lt;p&gt;We launched 100ms today on the ProductHunt 🚀  -&amp;gt; &lt;a href="https://www.producthunt.com/"&gt;https://www.producthunt.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We enable developers to add live audio-video capabilities to their apps. &lt;/p&gt;

&lt;p&gt;Previously, our team built live video infrastructure at Facebook and Disney+Hotstar. &lt;/p&gt;

&lt;p&gt;Our SDK is different and efficient from existing video APIs.&lt;br&gt;
Would love to hear your thoughts about the product, support us in launch if you liked it.&lt;/p&gt;

&lt;p&gt;So, If you like our vision and what we're building, we'd grin from ear to ear if I could count on your support. 😻&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>webrtc</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
