<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Chetan VideoSDK</title>
    <description>The latest articles on Forem by Chetan VideoSDK (@chetan_videosdk).</description>
    <link>https://forem.com/chetan_videosdk</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%2F1243263%2F64671c22-8c79-41b7-b41d-107c2120bceb.png</url>
      <title>Forem: Chetan VideoSDK</title>
      <link>https://forem.com/chetan_videosdk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/chetan_videosdk"/>
    <language>en</language>
    <item>
      <title>Integrate Collaborative Whiteboard in React JS Video Call App?</title>
      <dc:creator>Chetan VideoSDK</dc:creator>
      <pubDate>Mon, 15 Apr 2024 07:06:59 +0000</pubDate>
      <link>https://forem.com/chetan_videosdk/integrate-collaborative-whiteboard-in-react-js-video-call-app-46be</link>
      <guid>https://forem.com/chetan_videosdk/integrate-collaborative-whiteboard-in-react-js-video-call-app-46be</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&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%2Fassets.videosdk.live%2Fstatic-assets%2Fghost%2F2024%2F04%2FCollaborative-Whiteboard.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%2Fassets.videosdk.live%2Fstatic-assets%2Fghost%2F2024%2F04%2FCollaborative-Whiteboard.png" alt="How to Integrate Collaborative Whiteboard in React JS Video Call App?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding a Collaborative Whiteboard feature to your React JS Video Call App not only enhances collaboration but also boosts productivity. This feature allows users to brainstorm ideas, sketch diagrams, and annotate documents in real-time while engaged in video calls. By seamlessly integrating this feature, your app facilitates smoother communication and empowers users to visualize concepts, fostering a more interactive and engaging experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits of using Whiteboard:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced Collaboration&lt;/strong&gt; : Users can visually illustrate concepts, making communication more effective.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increased Productivity&lt;/strong&gt; : Whiteboarding allows for on-the-fly problem-solving and idea generation, reducing the need for separate tools or meetings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual Learning&lt;/strong&gt; : Visual aids help convey complex ideas more clearly, catering to different learning styles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remote Work Facilitation&lt;/strong&gt; : Especially beneficial for remote teams, enabling seamless collaboration despite geographical barriers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documented Discussions&lt;/strong&gt; : Whiteboard content can be saved for future reference, ensuring that valuable insights aren't lost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Cases of Whiteboard:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Education&lt;/strong&gt; : Teachers can explain complex topics visually, engaging students in interactive lessons.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business Meetings&lt;/strong&gt; : Teams can brainstorm strategies, visualize data, and plan projects together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design Reviews&lt;/strong&gt; : Designers can share concepts and receive feedback in real time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical Support&lt;/strong&gt; : Support teams can troubleshoot issues by visually demonstrating solutions to customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Transform your React JS video call app into a dynamic platform for collaborative innovation, driving productivity and creativity to new heights. Follow the below tutorial and build a React JS Video Calling App with the Collaborative Whiteboard feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Getting Started with VideoSDK
&lt;/h2&gt;

&lt;p&gt;To take advantage of the chat functionality, we must use the capabilities that the VideoSDK offers. Before diving into the implementation steps, let's ensure you complete the necessary prerequisites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a VideoSDK Account
&lt;/h3&gt;

&lt;p&gt;Go to your &lt;a href="https://www.videosdk.live/signup?utm_source=organicseo&amp;amp;utm_medium=devto&amp;amp;utm_campaign=whiteboard_react_js" rel="noopener noreferrer"&gt;VideoSDK dashboard&lt;/a&gt; and sign up if you don't have an account. This account gives you access to the required Video SDK token, which acts as an authentication key that allows your application to interact with VideoSDK functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generate your Auth Token
&lt;/h3&gt;

&lt;p&gt;Visit your &lt;a href="https://www.videosdk.live/signup?utm_source=organicseo&amp;amp;utm_medium=devto&amp;amp;utm_campaign=whiteboard_react_js" rel="noopener noreferrer"&gt;VideoSDK dashboard&lt;/a&gt; and navigate to the "API Key" section to generate your auth token. This token is crucial in authorizing your application to use VideoSDK features. For a more visual understanding of the account creation and token generation process, consider referring to the &lt;a href="https://docs.videosdk.live/android/guide/video-and-audio-calling-api-sdk/authentication-and-token#1-generating-token-from-dashboard" rel="noopener noreferrer"&gt;provided tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites and Setup
&lt;/h3&gt;

&lt;p&gt;Before proceeding, ensure that your development environment meets the following requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VideoSDK Developer Account (Not having one?, follow &lt;strong&gt;&lt;a href="https://app.videosdk.live/" rel="noopener noreferrer"&gt;VideoSDK Dashboard&lt;/a&gt;&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;Basic understanding of React.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.npmjs.com/package/@videosdk.live/react-sdk" rel="noopener noreferrer"&gt;React VideoSDK&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Make sure Node and NPM are installed on your device.&lt;/li&gt;
&lt;li&gt;Basic understanding of Hooks (useState, useRef, useEffect)&lt;/li&gt;
&lt;li&gt;React Context API (optional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow the steps to create the environment necessary to add video calls to your app. You can also find the code sample for &lt;a href="https://github.com/videosdk-live/quickstart/tree/main/react-rtc" rel="noopener noreferrer"&gt;Quickstart here&lt;/a&gt;.&lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start#create-new-react-app" rel="noopener noreferrer"&gt;​&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a new React App using the below command.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npx create-react-app videosdk-rtc-react-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛠️ Install VideoSDK&lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start#install-videosdk" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It is necessary to set up VideoSDK within your project before going into the details of integrating the Screen Share feature. Installing VideoSDK using NPM or Yarn will depend on the needs of your project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For NPM
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ npm install "@videosdk.live/react-sdk"

//For the Participants Video
$ npm install "react-player"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;For Yarn
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ yarn add "@videosdk.live/react-sdk"

//For the Participants Video
$ yarn add "react-player"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are going to use functional components to leverage React's reusable component architecture. There will be components for users, videos and controls (mic, camera, leave) over the video.&lt;/p&gt;

&lt;h3&gt;
  
  
  App Architecture
&lt;/h3&gt;

&lt;p&gt;The App will contain a &lt;code&gt;MeetingView&lt;/code&gt; component which includes a &lt;code&gt;ParticipantView&lt;/code&gt; component which will render the participant's name, video, audio, etc. It will also have a &lt;code&gt;Controls&lt;/code&gt; component that will allow the user to perform operations like leave and toggle media.&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%2Fd4ty2q8rf89z87fw63qy.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%2Fd4ty2q8rf89z87fw63qy.png" alt="How to Integrate Collaborative Whiteboard in React JS Video Call App?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You will be working on the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API.js: Responsible for handling API calls such as generating unique meetingId and token&lt;/li&gt;
&lt;li&gt;App.js: Responsible for rendering &lt;code&gt;MeetingView&lt;/code&gt; and joining the meeting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎥 Essential Steps to Implement Video Calling Functionality
&lt;/h2&gt;

&lt;p&gt;To add video capability to your React application, you must first complete a sequence of prerequisites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Get started with API.js
&lt;/h3&gt;

&lt;p&gt;Before moving on, you must create an API request to generate a unique meetingId. You will need an authentication token, which you can create either through the &lt;a href="https://github.com/videosdk-live/videosdk-rtc-api-server-examples" rel="noopener noreferrer"&gt;videosdk-rtc-api-server-examples&lt;/a&gt; or directly from the &lt;a href="https://app.videosdk.live/api-keys" rel="noopener noreferrer"&gt;VideoSDK Dashboard&lt;/a&gt; for developers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//This is the Auth token, you will use it to generate a meeting and connect to it
export const authToken = "&amp;lt;Generated-from-dashbaord&amp;gt;";
// API call to create a meeting
export const createMeeting = async ({ token }) =&amp;gt; {
  const res = await fetch(`https://api.videosdk.live/v2/rooms`, {
    method: "POST",
    headers: {
      authorization: `${authToken}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({}),
  });
  //Destructuring the roomId from the response
  const { roomId } = await res.json();
  return roomId;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;API.js&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Wireframe App.js with all the components&lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start#step-2-wireframe-appjs-with-all-the-components" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;To build up a wireframe of App.js, you need to use VideoSDK Hooks and Context Providers. VideoSDK provides MeetingProvider, MeetingConsumer, useMeeting, and useParticipant hooks.&lt;/p&gt;

&lt;p&gt;First, you need to understand the &lt;strong&gt;Context Provider&lt;/strong&gt; and Consumer. Context is primarily used when some data needs to be accessible by many components at different nesting levels.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MeetingProvider&lt;/strong&gt; : This is the Context Provider. It accepts value &lt;code&gt;config&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt; as props. The Provider component accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers. Providers can be nested to override values deeper within the tree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MeetingConsumer&lt;/strong&gt; : This is the Context Consumer. All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;useMeeting&lt;/strong&gt; : This is the meeting hook API. It includes all the information related to meetings such as join/leave, enable/disable the mic or webcam, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;useParticipant&lt;/strong&gt; : This is the participant hook API. It is responsible for handling all the events and props related to one particular participant such as &lt;strong&gt;name&lt;/strong&gt; , &lt;strong&gt;webcamStream&lt;/strong&gt; , &lt;strong&gt;micStream&lt;/strong&gt; , etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Meeting Context provides a way to listen for any changes that occur when a participant joins the meeting or makes modifications to their microphone, camera, and other settings.&lt;/p&gt;

&lt;p&gt;Begin by making a few changes to the code in the &lt;strong&gt;App.js&lt;/strong&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;import "./App.css";
import React, { useEffect, useMemo, useRef, useState } from "react";
import {
  MeetingProvider,
  MeetingConsumer,
  useMeeting,
  useParticipant,
} from "@videosdk.live/react-sdk";
import { authToken, createMeeting } from "./API";
import ReactPlayer from "react-player";

function JoinScreen({ getMeetingAndToken }) {
  return null;
}

function ParticipantView(props) {
  return null;
}

function Controls(props) {
  return null;
}

function MeetingView(props) {
  return null;
}

function App() {
  const [meetingId, setMeetingId] = useState(null);

  //Getting the meeting id by calling the api we just wrote
  const getMeetingAndToken = async (id) =&amp;gt; {
    const meetingId =
      id == null ? await createMeeting({ token: authToken }) : id;
    setMeetingId(meetingId);
  };

  //This will set Meeting Id to null when meeting is left or ended
  const onMeetingLeave = () =&amp;gt; {
    setMeetingId(null);
  };

  return authToken &amp;amp;&amp;amp; meetingId ? (
    &amp;lt;MeetingProvider
      config={{
        meetingId,
        micEnabled: true,
        webcamEnabled: true,
        name: "C.V. Raman",
      }}
      token={authToken}
    &amp;gt;
      &amp;lt;MeetingView meetingId={meetingId} onMeetingLeave={onMeetingLeave} /&amp;gt;
    &amp;lt;/MeetingProvider&amp;gt;
  ) : (
    &amp;lt;JoinScreen getMeetingAndToken={getMeetingAndToken} /&amp;gt;
  );
}

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Implement Join Screen&lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start#step-3-implement-join-screen" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The join screen will serve as a medium to either schedule a new meeting or join an existing one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function JoinScreen({ getMeetingAndToken }) {
  const [meetingId, setMeetingId] = useState(null);
  const onClick = async () =&amp;gt; {
    await getMeetingAndToken(meetingId);
  };
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;input
        type="text"
        placeholder="Enter Meeting Id"
        onChange={(e) =&amp;gt; {
          setMeetingId(e.target.value);
        }}
      /&amp;gt;
      &amp;lt;button onClick={onClick}&amp;gt;Join&amp;lt;/button&amp;gt;
      {" or "}
      &amp;lt;button onClick={onClick}&amp;gt;Create Meeting&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Output
&lt;/h4&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%2Ffk2j4uancnagxrq2mwp7.jpeg" 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%2Ffk2j4uancnagxrq2mwp7.jpeg" alt="How to Integrate Collaborative Whiteboard in React JS Video Call App?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Implement MeetingView and Controls&lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start#step-4-implement-meetingview-and-controls" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The next step is to create &lt;code&gt;MeetingView&lt;/code&gt; and &lt;code&gt;Controls&lt;/code&gt; components to manage features such as join, leave, mute, and unmute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function MeetingView(props) {
  const [joined, setJoined] = useState(null);
  //Get the method which will be used to join the meeting.
  //We will also get the participants list to display all participants
  const { join, participants } = useMeeting({
    //callback for when meeting is joined successfully
    onMeetingJoined: () =&amp;gt; {
      setJoined("JOINED");
    },
    //callback for when meeting is left
    onMeetingLeft: () =&amp;gt; {
      props.onMeetingLeave();
    },
  });
  const joinMeeting = () =&amp;gt; {
    setJoined("JOINING");
    join();
  };

  return (
    &amp;lt;div className="container"&amp;gt;
      &amp;lt;h3&amp;gt;Meeting Id: {props.meetingId}&amp;lt;/h3&amp;gt;
      {joined &amp;amp;&amp;amp; joined == "JOINED" ? (
        &amp;lt;div&amp;gt;
          &amp;lt;Controls /&amp;gt;
          //For rendering all the participants in the meeting
          {[...participants.keys()].map((participantId) =&amp;gt; (
            &amp;lt;ParticipantView
              participantId={participantId}
              key={participantId}
            /&amp;gt;
          ))}
        &amp;lt;/div&amp;gt;
      ) : joined &amp;amp;&amp;amp; joined == "JOINING" ? (
        &amp;lt;p&amp;gt;Joining the meeting...&amp;lt;/p&amp;gt;
      ) : (
        &amp;lt;button onClick={joinMeeting}&amp;gt;Join&amp;lt;/button&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}

function Controls() {
  const { leave, toggleMic, toggleWebcam } = useMeeting();
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; leave()}&amp;gt;Leave&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; toggleMic()}&amp;gt;toggleMic&amp;lt;/button&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; toggleWebcam()}&amp;gt;toggleWebcam&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Control Component&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Output of Controls Component
&lt;/h4&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%2F0b3nywv3wib94qhel716.jpeg" 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%2F0b3nywv3wib94qhel716.jpeg" alt="How to Integrate Collaborative Whiteboard in React JS Video Call App?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Implement Participant View&lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/quick-start#step-5-implement-participant-view" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Before implementing the participant view, you need to understand a couple of concepts.&lt;/p&gt;

&lt;h4&gt;
  
  
  5.1 Forwarding Ref for mic and camera
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;useRef&lt;/code&gt; hook is responsible for referencing the audio and video components. It will be used to play and stop the audio and video of the participant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const webcamRef = useRef(null);
const micRef = useRef(null);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Forwarding Ref for mic and camera&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  5.2 useParticipant Hook
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;useParticipant&lt;/code&gt; hook is responsible for handling all the properties and events of one particular participant who joined the meeting. It will take participantId as an argument.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { webcamStream, micStream, webcamOn, micOn } = useParticipant(
  props.participantId
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5.3 MediaStream API
&lt;/h4&gt;

&lt;p&gt;The MediaStream API is beneficial for adding a MediaTrack to the audio/video tag, enabling the playback of audio or video.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const webcamRef = useRef(null);
const mediaStream = new MediaStream();
mediaStream.addTrack(webcamStream.track);

webcamRef.current.srcObject = mediaStream;
webcamRef.current
  .play()
  .catch((error) =&amp;gt; console.error("videoElem.current.play() failed", error));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  5.4 Implement &lt;code&gt;ParticipantView&lt;/code&gt;​
&lt;/h4&gt;

&lt;p&gt;Now you can use both of the hooks and the API to create &lt;code&gt;ParticipantView&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function ParticipantView(props) {
  const micRef = useRef(null);
  const { webcamStream, micStream, webcamOn, micOn, isLocal, displayName } =
    useParticipant(props.participantId);

  const videoStream = useMemo(() =&amp;gt; {
    if (webcamOn &amp;amp;&amp;amp; webcamStream) {
      const mediaStream = new MediaStream();
      mediaStream.addTrack(webcamStream.track);
      return mediaStream;
    }
  }, [webcamStream, webcamOn]);

  useEffect(() =&amp;gt; {
    if (micRef.current) {
      if (micOn &amp;amp;&amp;amp; micStream) {
        const mediaStream = new MediaStream();
        mediaStream.addTrack(micStream.track);

        micRef.current.srcObject = mediaStream;
        micRef.current
          .play()
          .catch((error) =&amp;gt;
            console.error("videoElem.current.play() failed", error)
          );
      } else {
        micRef.current.srcObject = null;
      }
    }
  }, [micStream, micOn]);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;p&amp;gt;
        Participant: {displayName} | Webcam: {webcamOn ? "ON" : "OFF"} | Mic:{" "}
        {micOn ? "ON" : "OFF"}
      &amp;lt;/p&amp;gt;
      &amp;lt;audio ref={micRef} autoPlay playsInline muted={isLocal} /&amp;gt;
      {webcamOn &amp;amp;&amp;amp; (
        &amp;lt;ReactPlayer
          //
          playsinline // extremely crucial prop
          pip={false}
          light={false}
          controls={false}
          muted={true}
          playing={true}
          //
          url={videoStream}
          //
          height={"300px"}
          width={"300px"}
          onError={(err) =&amp;gt; {
            console.log(err, "participant video error");
          }}
        /&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can check out the complete &lt;a href="https://github.com/videosdk-live/quickstart/tree/main/react-rtc" rel="noopener noreferrer"&gt;quick start example here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Integrate Collaborative Whiteboard (Canvas Drawing)
&lt;/h2&gt;

&lt;p&gt;When in a meeting, it can be very handy to draw and share your views with all the collaborators. To achieve this, you can develop a drawing board shared in real time using the publish-subscribe mechanism. If you are not familiar with the PubSub mechanism the &lt;code&gt;usePubSub hook&lt;/code&gt;, you can &lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/collaboration-in-meeting/pubsub" rel="noopener noreferrer"&gt;follow this guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementing Collaborative Whiteboard&lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/collaboration-in-meeting/canvas-drawing-using-pubsub#implementing-canvas-drawing" rel="noopener noreferrer"&gt;​&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;To implement the Whiteboard (Canvas Drawing) feature, you need to use a third-party library that provides an easy solution for drawing and rendering on the canvas.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, install all the dependencies.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i "@shawngoh87/react-sketch-canvas"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;With the dependencies installed, make a new &lt;code&gt;Canvas&lt;/code&gt; component which will be placed in the &lt;code&gt;MeetingView&lt;/code&gt; component, and also add a basic whiteboard (canvas) to it.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { ReactSketchCanvas } from "@shawngoh87/react-sketch-canvas";

const MeetingView = () =&amp;gt; {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;WhiteboardView /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

const WhiteboardView = () =&amp;gt; {
  //Define a refernce for the canvas
  const canvasRef = useRef();

  //Define the props required by the canvas element used
  const canvasProps = {
    width: "100%",
    height: "500px",
    backgroundImage:
      "https://upload.wikimedia.org/wikipedia/commons/7/70/Graph_paper_scan_1600x1000_%286509259561%29.jpg",
    preserveBackgroundImageAspectRatio: "none",
    strokeWidth: 4,
    eraserWidth: 5,
    strokeColor: "#000000",
    canvasColor: "#FFFFFF",
    allowOnlyPointerType: "all",
    withViewBox: false,
  };
  return (
    &amp;lt;div&amp;gt;
      //Adding the actual canvas object
      &amp;lt;ReactSketchCanvas ref={canvasRef} {...canvasProps} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;With this, your canvas(whiteboard) is ready for drawing. If you draw something on your board, other participants won't be able to see those drawings yet. To share your drawings with others, use the &lt;code&gt;usePubSub&lt;/code&gt; hook. Get the &lt;code&gt;publish()&lt;/code&gt; method from the &lt;code&gt;usePubSub&lt;/code&gt; hook for the topic &lt;code&gt;WHITEBOARD&lt;/code&gt; to send your drawings to all the participants in the meeting.&lt;/li&gt;
&lt;li&gt;The data you need to send to all the participants is the strokes you are drawing, so you will send a stringified JSON to everyone in the message.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { usePubSub } from "@videosdk.live/react-sdk";

const WhiteboardView = () =&amp;gt; {
  //.. other declarations

  const { publish } = usePubSub("WHITEBOARD");

  // This callback from the canvas component will give us the stroke json we need to share
  const onStroke = (stroke, isEraser) =&amp;gt; {
    // We will be setting the `persist:true` so that all the strokes
    // are available for the participants who have recently joined
    publish(JSON.stringify(stroke), { persist: true });
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;ReactSketchCanvas ref={canvasRef} onStroke={onStroke} {...canvasProps} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Even after publishing, the drawings won't appear to other participants because they need to redraw the strokes received from others. This involves handling the &lt;code&gt;onMessageReceived&lt;/code&gt; event and the &lt;code&gt;onOldMessagesReceived&lt;/code&gt; event.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;The data received in these events will be &lt;code&gt;stringified JSON&lt;/code&gt;, which need to be parsed before drawing.&lt;/li&gt;
&lt;li&gt;Additionally, to avoid redrawing the strokes created by the local participant, an extra check is implemented to determine whether the stroke was drawn by the local participant or not.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { useMeeting,usePubSub } from "@videosdk.live/react-sdk";

const WhiteboardView = () =&amp;gt; {
  //.. other declarations

  const { localParticipant } = useMeeting();

  const { publish } = usePubSub("WHITEBOARD", {
    onMessageReceived: (message) =&amp;gt; {
      //Check if the stroke is from remote participant only
      if (message.senderId !== localParticipant.id) {
        canvasRef.current.loadPaths(JSON.parse(message.message));
      }
    },
    onOldMessagesReceived: (messages) =&amp;gt; {
      messages.map((message) =&amp;gt; {
        canvasRef.current.loadPaths(JSON.parse(message.message));
      });
    },
  });

  //This callback from the canvas component will give us the stroke json we need to share
  const onStroke = (stroke, isEraser) =&amp;gt; {
    ...
  };

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;ReactSketchCanvas ref={canvasRef} onStroke={onStroke} {...canvasProps} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! You've successfully integrated a whiteboard feature into your React.js video-calling application.&lt;/p&gt;

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

&lt;p&gt;In conclusion, integrating a Collaborative Whiteboard into your React JS Video Call App enriches communication and collaboration experiences. By providing users with a versatile platform to brainstorm, illustrate ideas, and annotate documents in real time, this feature enhances productivity, fosters creativity, and facilitates seamless remote collaboration. Whether used in educational settings, business meetings, design reviews, or technical support sessions. Embrace this innovative feature today and enhance your video call app with a Collaborative Whiteboard from &lt;a href="https://www.videosdk.live/" rel="noopener noreferrer"&gt;VideoSDK&lt;/a&gt;. Elevate your users' experience and drive success in diverse contexts.&lt;/p&gt;

&lt;p&gt;To unlock the full potential of VideoSDK and create easy-to-use video experiences, developers are encouraged to &lt;a href="https://www.videosdk.live/signup?utm_source=organicseo&amp;amp;utm_medium=devto&amp;amp;utm_campaign=whiteboard_react_js" rel="noopener noreferrer"&gt;Sign up with VideoSDK&lt;/a&gt; today and Get 10000 minutes free to take the video app to the next level&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>programming</category>
    </item>
    <item>
      <title>1-on-1 Video Chat App with Java on Android</title>
      <dc:creator>Chetan VideoSDK</dc:creator>
      <pubDate>Thu, 14 Mar 2024 05:12:51 +0000</pubDate>
      <link>https://forem.com/chetan_videosdk/1-on-1-video-chat-app-with-java-on-android-2khc</link>
      <guid>https://forem.com/chetan_videosdk/1-on-1-video-chat-app-with-java-on-android-2khc</guid>
      <description>&lt;p&gt;In this tutorial, You'll learn about a 1-on-1 Video Chat Application. Also, a Step-by-Step Guide for building an Android video chat application using Java and VideoSDK.&lt;/p&gt;

&lt;p&gt;Remote communication has become the most important part of communication after the pandemic. It will likely play a significant role in the future too. Today's mobile applications often include voice or video chat functionality. But it's extremely complex and takes a lot of time to build. This is where &lt;a href="https://www.videosdk.live/" rel="noopener noreferrer"&gt;VideoSDK&lt;/a&gt; comes into the picture.&lt;/p&gt;

&lt;p&gt;VideoSDK is a platform that allows developers to create rich in-app experiences such as embedding real-time video, voice, real-time recording, live streaming, and real-time messaging.&lt;/p&gt;

&lt;p&gt;VideoSDK is available for JavaScript, ReactJS, React-Native, IOS, Android, and Flutter to be integrated seamlessly. VideoSDK also provides a Pre-built SDK that enables the opportunity to integrate real-time communication with your application in just 10 minutes!&lt;/p&gt;

&lt;p&gt;Let's create a 1-on-1 video chat/call app using VideoSDK. But first, we need to create a VideoSDK account and generate the token.&lt;/p&gt;

&lt;h2&gt;
  
  
  Requirement
&lt;/h2&gt;

&lt;p&gt;First of all, Your development environment should meet the following requirements:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oracle.com/in/java/technologies/downloads/" rel="noopener noreferrer"&gt;Java Development Kit&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://developer.android.com/studio" rel="noopener noreferrer"&gt;Android Studio&lt;/a&gt; 3.0 or later.&lt;br&gt;
Android SDK API Level 21 or higher.&lt;br&gt;
A mobile device that runs Android 5.0 or later.&lt;/p&gt;
&lt;h2&gt;
  
  
  Setup the project
&lt;/h2&gt;
&lt;h3&gt;
  
  
  STEP 1: Create a new project
&lt;/h3&gt;

&lt;p&gt;Let’s start by creating a new project. In Android Studio, create a new project with an Empty Activity.&lt;/p&gt;

&lt;p&gt;Then, provide a name. We will name it as OneToOneDemo.&lt;/p&gt;
&lt;h3&gt;
  
  
  STEP 2: Integrate VideoSDK
&lt;/h3&gt;

&lt;p&gt;Add the repository to &lt;code&gt;settings.gradle&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;dependencyResolutionManagement{
  repositories {
    // ...
    google()
    mavenCentral()
    maven { url 'https://jitpack.io' }
    maven { url "https://maven.aliyun.com/repository/jcenter" }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following dependencies in &lt;code&gt;build.gradle&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;dependencies {
  implementation 'live.videosdk:rtc-android-sdk:0.1.13'

  // library to perform Network call to generate a meeting id
  implementation 'com.amitshekhar.android:android-networking:1.0.2'

  // other app dependencies
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your project has set &lt;code&gt;android.useAndroidX = true&lt;/code&gt;, then set &lt;code&gt;android.enableJetifier = true&lt;/code&gt; in the &lt;code&gt;gradle.properties&lt;/code&gt; file to migrate your project to AndroidX and avoid duplicate class conflict.&lt;/p&gt;

&lt;h3&gt;
  
  
  STEP 3: Add permissions to your project
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;/app/Manifests/AndroidManifest.xml&lt;/code&gt;, add the following permissions after &lt;code&gt;&amp;lt;/application&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;uses-permission android:name="android.permission.RECORD_AUDIO" /&amp;gt;
&amp;lt;uses-permission android:name="android.permission.INTERNET" /&amp;gt;
&amp;lt;uses-permission android:name="android.permission.CAMERA" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  STEP 4: Getting started with the code!
&lt;/h3&gt;

&lt;p&gt;(a) 📋 Structure of project&lt;br&gt;
We'll create two screens. The first screen will be the &lt;code&gt;Joining screen&lt;/code&gt; that allows users to create/join the meeting, and another one is &lt;code&gt;Meeting screen&lt;/code&gt; that will show participants like WhatsApp views.&lt;/p&gt;

&lt;p&gt;Our project structure would look 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;   app
   ├── java
   │    ├── packagename
   │         ├── JoinActivity
   │         ├── MeetingActivity
   ├── res
   │    ├── layout
   │    │    ├── activity_join.xml
   │    │    ├── activity_meeting.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;(b) Creating Joining Screen&lt;/strong&gt;&lt;br&gt;
Create a new Activity named &lt;code&gt;JoinActivity&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(c) Creating UI for Joining the Screen&lt;/strong&gt;&lt;br&gt;
The Joining screen will include :&lt;/p&gt;

&lt;p&gt;Create Button - Creates a new meeting.&lt;br&gt;
TextField for MeetingID - Contains the &lt;code&gt;meetingId&lt;/code&gt;you want to join.&lt;br&gt;
Join Button - Joins the meeting with the &lt;code&gt;meetingId&lt;/code&gt;provided.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/app/res/layout/activity_join.xml file&lt;/code&gt;, replace the content with the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".JoinActivity"&amp;gt;

    &amp;lt;com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/material_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:contentInsetStart="0dp"
        android:background="?attr/colorPrimary"
        app:titleTextColor="@color/white" /&amp;gt;

    &amp;lt;LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical"&amp;gt;


        &amp;lt;Button
            android:id="@+id/btnCreateMeeting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:text="Create Meeting" /&amp;gt;

        &amp;lt;TextView
            style="@style/TextAppearance.AppCompat.Headline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="OR" /&amp;gt;

        &amp;lt;com.google.android.material.textfield.TextInputLayout          style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginVertical="16dp"
            android:hint="Enter Meeting ID"&amp;gt;

            &amp;lt;EditText
                android:id="@+id/etMeetingId"
                android:layout_width="250dp"
                android:layout_height="wrap_content" /&amp;gt;
        &amp;lt;/com.google.android.material.textfield.TextInputLayout&amp;gt;

        &amp;lt;Button
            android:id="@+id/btnJoinMeeting"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Join Meeting" /&amp;gt;
    &amp;lt;/LinearLayout&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;(d) Integration of Create Meeting API&lt;/strong&gt;&lt;br&gt;
You need to create a field &lt;code&gt;sampleToken&lt;/code&gt;in &lt;code&gt;JoinActivity&lt;/code&gt;that holds the generated token from the &lt;a href="https://app.videosdk.live/api-keys" rel="noopener noreferrer"&gt;VideoSDK dashboard&lt;/a&gt;. This token will be used in the VideoSDK config as well as generating meetingId.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JoinActivity extends AppCompatActivity {

  //Replace with the token you generated from the VideoSDK Dashboard
  private String sampleToken = ""; 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    //...
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Join Button &lt;code&gt;onClick&lt;/code&gt;the event, it will navigate to &lt;code&gt;MeetingActivity&lt;/code&gt;with token and meetingId.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JoinActivity extends AppCompatActivity {

  //Replace with the token you generated from the VideoSDK Dashboard
  private String sampleToken =""; 

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_join);

    final Button btnCreate = findViewById(R.id.btnCreateMeeting);
    final Button btnJoin = findViewById(R.id.btnJoinMeeting);
    final EditText etMeetingId = findViewById(R.id.etMeetingId);

    //set title
    Toolbar toolbar = findViewById(R.id.material_toolbar);
    toolbar.setTitle("OneToOneDemo");
    setSupportActionBar(toolbar);

    btnCreate.setOnClickListener(v -&amp;gt; {
      // we will explore this method in the next step
      createMeeting(sampleToken);
    });

    btnJoin.setOnClickListener(v -&amp;gt; {
      Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
      intent.putExtra("token", sampleToken);
      intent.putExtra("meetingId", etMeetingId.getText().toString());
      startActivity(intent);
    });
  }

  private void createMeeting(String token) {
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the Create Button, under &lt;code&gt;createMeeting&lt;/code&gt;method we will generate &lt;code&gt;meetingId&lt;/code&gt;by calling API and navigating to &lt;code&gt;MeetingActivity&lt;/code&gt;with token and generated meetingId.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JoinActivity extends AppCompatActivity {
  //...onCreate

  private void createMeeting(String token) {
      // we will make an API call to VideoSDK Server to get a roomId
      AndroidNetworking.post("https://api.videosdk.live/v2/rooms")
        .addHeaders("Authorization", token) //we will pass the token in the Headers
        .build()
        .getAsJSONObject(new JSONObjectRequestListener() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    // response will contain `roomId`
                    final String meetingId = response.getString("roomId");

                    // starting the MeetingActivity with received roomId and our sampleToken
                    Intent intent = new Intent(JoinActivity.this, MeetingActivity.class);
                    intent.putExtra("token", sampleToken);
                    intent.putExtra("meetingId", meetingId);
                    startActivity(intent);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onError(ANError anError) {
                anError.printStackTrace();
                Toast.makeText(JoinActivity.this, anError.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our App is completely based on audio and video commutation, that's why we need to ask for runtime permissions &lt;code&gt;RECORD_AUDIO&lt;/code&gt; and &lt;code&gt;CAMERA&lt;/code&gt;. So, we will implement permission logic on &lt;code&gt;JoinActivity&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class JoinActivity extends AppCompatActivity {
  private static final int PERMISSION_REQ_ID = 22;

  private static final String[] REQUESTED_PERMISSIONS = {
    Manifest.permission.RECORD_AUDIO,
    Manifest.permission.CAMERA
  };

  private boolean checkSelfPermission(String permission, int requestCode) {
    if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(this, REQUESTED_PERMISSIONS, requestCode);
      return false;
    }
    return true;
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    //... button listeneres
    checkSelfPermission(REQUESTED_PERMISSIONS[0], PERMISSION_REQ_ID);
    checkSelfPermission(REQUESTED_PERMISSIONS[1], PERMISSION_REQ_ID);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll get &lt;code&gt;Unresolved reference: MeetingActivity&lt;/code&gt; error, but don't worry. It will be solved automatically once you create &lt;code&gt;MeetingActivity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We're done with the Joining screen now, and it is time to create the participant's view in the Meeting screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  STEP 5: Creating Meeting Screen
&lt;/h3&gt;

&lt;p&gt;Create a new Activity named &lt;code&gt;MeetingActivity&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(a) Creating the UI for Meeting Screen&lt;/strong&gt;&lt;br&gt;
In &lt;code&gt;/app/res/layout/activity_meeting.xml&lt;/code&gt; file, replace the content with the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?xml version="1.0" encoding="utf-8"?&amp;gt;
&amp;lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MeetingActivity"&amp;gt;

    &amp;lt;com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/material_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/black"
        app:contentInsetStart="0dp"
        app:titleTextColor="@color/white"&amp;gt;

        &amp;lt;LinearLayout
            android:id="@+id/meetingLayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="14dp"
            android:layout_marginTop="10dp"
            android:orientation="horizontal"&amp;gt;

            &amp;lt;RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"&amp;gt;

                &amp;lt;TextView
                    android:id="@+id/txtMeetingId"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center"
                    android:fontFamily="sans-serif-medium"
                    android:textColor="@color/white"
                    android:textFontWeight="600"
                    android:textSize="16sp" /&amp;gt;

                &amp;lt;ImageButton
                    android:id="@+id/btnCopyContent"
                    android:layout_width="22dp"
                    android:layout_height="22sp"
                    android:layout_marginLeft="7dp"
                    android:layout_toRightOf="@+id/txtMeetingId"
                    android:backgroundTint="@color/black"
                    android:src="@drawable/ic_outline_content_copy_24" /&amp;gt;

            &amp;lt;/RelativeLayout&amp;gt;

        &amp;lt;/LinearLayout&amp;gt;

        &amp;lt;LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="end"
            android:layout_marginEnd="10dp"&amp;gt;

            &amp;lt;ImageButton
                android:id="@+id/btnSwitchCameraMode"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@color/black"
                android:contentDescription="Switch Camera mode"
                android:src="@drawable/ic_baseline_flip_camera_android_24" /&amp;gt;

        &amp;lt;/LinearLayout&amp;gt;

    &amp;lt;/com.google.android.material.appbar.MaterialToolbar&amp;gt;

    &amp;lt;FrameLayout
        android:id="@+id/participants_frameLayout"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="@color/black"&amp;gt;

        &amp;lt;androidx.cardview.widget.CardView
            android:id="@+id/ParticipantCard"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="12dp"
            android:layout_marginTop="3dp"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="3dp"
            android:backgroundTint="#2B3034"
            android:visibility="gone"
            app:cardCornerRadius="8dp"
            app:strokeColor="#2B3034"&amp;gt;

            &amp;lt;ImageView
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:layout_gravity="center"
                android:src="@drawable/ic_baseline_person_24" /&amp;gt;

            &amp;lt;live.videosdk.rtc.android.VideoView
                android:id="@+id/participantView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="gone" /&amp;gt;

        &amp;lt;/androidx.cardview.widget.CardView&amp;gt;

        &amp;lt;FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"&amp;gt;

        &amp;lt;/FrameLayout&amp;gt;

        &amp;lt;androidx.cardview.widget.CardView
            android:id="@+id/LocalCard"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginLeft="12dp"
            android:layout_marginTop="3dp"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="3dp"
            android:backgroundTint="#1A1C22"
            app:cardCornerRadius="8dp"
            app:strokeColor="#1A1C22"&amp;gt;

            &amp;lt;ImageView
                android:id="@+id/localParticipant_img"
                android:layout_width="150dp"
                android:layout_height="150dp"
                android:layout_gravity="center"
                android:src="@drawable/ic_baseline_person_24" /&amp;gt;

            &amp;lt;live.videosdk.rtc.android.VideoView
                android:id="@+id/localView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:visibility="gone" /&amp;gt;

        &amp;lt;/androidx.cardview.widget.CardView&amp;gt;

    &amp;lt;/FrameLayout&amp;gt;

    &amp;lt;!-- add bottombar here--&amp;gt;

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottomAppbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:animateLayoutChanges="true"
        android:backgroundTint="@color/black"
        android:gravity="center_horizontal"
        android:paddingVertical="5dp"
        tools:ignore="BottomAppBar"&amp;gt;

        &amp;lt;RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingStart="16dp"
            android:paddingEnd="16dp"&amp;gt;

            &amp;lt;com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnLeave"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:contentDescription="Leave Meeting"
                android:src="@drawable/ic_end_call"
                app:backgroundTint="#FF5D5D"
                app:fabSize="normal"
                app:tint="@color/white" /&amp;gt;

            &amp;lt;com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnMic"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="90dp"
                android:layout_toEndOf="@+id/btnLeave"
                android:contentDescription="Toggle Mic"
                android:src="@drawable/ic_mic_off"
                app:backgroundTint="@color/white"
                app:borderWidth="1dp"
                app:fabSize="normal" /&amp;gt;

            &amp;lt;com.google.android.material.floatingactionbutton.FloatingActionButton
                android:id="@+id/btnWebcam"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="90dp"
                android:layout_toEndOf="@+id/btnMic"
                android:backgroundTint="@color/white"
                android:contentDescription="Toggle Camera"
                android:src="@drawable/ic_video_camera_off"
                app:backgroundTint="@color/white"
                app:borderWidth="1dp"
                app:fabSize="normal" /&amp;gt;

        &amp;lt;/RelativeLayout&amp;gt;

    &amp;lt;/com.google.android.material.bottomappbar.BottomAppBar&amp;gt;


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

&lt;/div&gt;



&lt;p&gt;Copy the required icons from &lt;a href="https://github.com/videosdk-live/videosdk-rtc-android-kotlin-sdk-example/tree/one-to-one-demo/app/src/main/res/drawable" rel="noopener noreferrer"&gt;here &lt;/a&gt;and paste into your project's &lt;code&gt;res/drawable&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(b) Initializing the Meeting&lt;/strong&gt;&lt;br&gt;
After getting the token and &lt;code&gt;meetingId&lt;/code&gt;from &lt;code&gt;JoinActivity&lt;/code&gt;, we need to...&lt;/p&gt;

&lt;p&gt;Initialize VideoSDK&lt;br&gt;
Configure VideoSDK with the token.&lt;br&gt;
Initialize the meeting with required params such as &lt;code&gt;meetingId&lt;/code&gt;, &lt;code&gt;participantName&lt;/code&gt;, &lt;code&gt;micEnabled&lt;/code&gt;, &lt;code&gt;webcamEnabled&lt;/code&gt;, &lt;code&gt;participantId&lt;/code&gt;and map of &lt;code&gt;CustomStreamTrack&lt;/code&gt;.&lt;br&gt;
Join the room with &lt;code&gt;meeting.join()&lt;/code&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MeetingActivity extends AppCompatActivity {

    private static Meeting meeting;
    private boolean micEnabled = true;
    private boolean webcamEnabled = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_meeting);

        //
        Toolbar toolbar = findViewById(R.id.material_toolbar);
        toolbar.setTitle("");
        setSupportActionBar(toolbar);

        //
        String token = getIntent().getStringExtra("token");
        final String meetingId = getIntent().getStringExtra("meetingId");

        // set participant name
        String localParticipantName = "Alex";

        // Initialize VideoSDK
        VideoSDK.initialize(getApplicationContext());

        // pass the token generated from api server
        VideoSDK.config(token);

        // create a new meeting instance
        meeting = VideoSDK.initMeeting(
                MeetingActivity.this, meetingId, localParticipantName,
                micEnabled, webcamEnabled, null, null
        );

        // join the meeting
        if (meeting != null) meeting.join();

        //
        TextView textMeetingId = findViewById(R.id.txtMeetingId);
        textMeetingId.setText(meetingId);

     // copy meetingId to clipboard
         ((ImageButton) findViewById(R.id.btnCopyContent)).setOnClickListener(v -&amp;gt; copyTextToClipboard(meetingId));

   }

      private void copyTextToClipboard(String text) {
        ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText("Copied text", text);
        clipboard.setPrimaryClip(clip);

        Toast.makeText(MeetingActivity.this, "Copied to clipboard!", Toast.LENGTH_SHORT).show();
    }

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  STEP 6: Handle Local Participant Media
&lt;/h3&gt;

&lt;p&gt;We need to implement clicks for the following &lt;code&gt;Views&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;Mic Button&lt;br&gt;
Webcam Button&lt;br&gt;
Switch Camera Button&lt;br&gt;
Leave Button&lt;br&gt;
Add the following implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MeetingActivity extends AppCompatActivity {

    private FloatingActionButton btnWebcam, btnMic, btnLeave;
    private ImageButton btnSwitchCameraMode;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);

    //
    btnMic = findViewById(R.id.btnMic);
    btnWebcam = findViewById(R.id.btnWebcam);
    btnLeave = findViewById(R.id.btnLeave);
    btnSwitchCameraMode = findViewById(R.id.btnSwitchCameraMode);

    //...

    // actions
    setActionListeners();
  }

  private void setActionListeners() {
        // Toggle mic
        btnMic.setOnClickListener(view -&amp;gt; toggleMic());

        // Toggle webcam
        btnWebcam.setOnClickListener(view -&amp;gt; toggleWebCam());

        // Leave meeting
        btnLeave.setOnClickListener(view -&amp;gt; {
            // this will make the local participant leave the meeting
            meeting.leave();
        });

        // Switch camera
        btnSwitchCameraMode.setOnClickListener(view -&amp;gt; {
            //a participant can change stream from front/rear camera during the meeting.
            meeting.changeWebcam();
        });

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

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void toggleMic() {
        if (micEnabled) {
            // this will mute the local participant's mic
            meeting.muteMic();
        } else {
            // this will unmute the local participant's mic
            meeting.unmuteMic();
        }
        micEnabled = !micEnabled;
        // change mic icon according to micEnable status
        toggleMicIcon();
    }

    @SuppressLint("ResourceType")
    private void toggleMicIcon() {
        if (micEnabled) {
            btnMic.setImageResource(R.drawable.ic_mic_on);
            btnMic.setColorFilter(Color.WHITE);
            Drawable buttonDrawable = btnMic.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.TRANSPARENT);
            btnMic.setBackground(buttonDrawable);

        } else {
            btnMic.setImageResource(R.drawable.ic_mic_off);
            btnMic.setColorFilter(Color.BLACK);
            Drawable buttonDrawable = btnMic.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.WHITE);
            btnMic.setBackground(buttonDrawable);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void toggleWebCam() {
        if (webcamEnabled) {
            // this will disable the local participant webcam
            meeting.disableWebcam();
        } else {
            // this will enable the local participant webcam
            meeting.enableWebcam();
        }
        webcamEnabled = !webcamEnabled;
        // change webCam icon according to webcamEnabled status
        toggleWebcamIcon();
    }

    @SuppressLint("ResourceType")
    private void toggleWebcamIcon() {
        if (webcamEnabled) {
            btnWebcam.setImageResource(R.drawable.ic_video_camera);
            btnWebcam.setColorFilter(Color.WHITE);
            Drawable buttonDrawable = btnWebcam.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.TRANSPARENT);
            btnWebcam.setBackground(buttonDrawable);

        } else {
            btnWebcam.setImageResource(R.drawable.ic_video_camera_off);
            btnWebcam.setColorFilter(Color.BLACK);
            Drawable buttonDrawable = btnWebcam.getBackground();
            buttonDrawable = DrawableCompat.wrap(buttonDrawable);
            //the color is a direct color int and not a color resource
            if (buttonDrawable != null) DrawableCompat.setTint(buttonDrawable, Color.WHITE);
            btnWebcam.setBackground(buttonDrawable);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;**(a) Setting up Local participant view&lt;br&gt;
To set up participant view, we have to implement all the methods of the &lt;code&gt;ParticipantEventListener&lt;/code&gt;Abstract Class and add the listener to &lt;code&gt;Participant&lt;/code&gt;class using the &lt;code&gt;addEventListener()&lt;/code&gt; method of &lt;code&gt;Participant&lt;/code&gt;Class. &lt;code&gt;ParticipantEventListener&lt;/code&gt;class has two methods :&lt;/p&gt;

&lt;p&gt;&lt;code&gt;onStreamEnabled&lt;/code&gt;- Whenever a participant enables mic/webcam in the meeting, This event will be triggered and return &lt;code&gt;Stream&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;onStreamDisabled&lt;/code&gt; - Whenever a participant disables mic/webcam in the meeting, This event will be triggered and return &lt;code&gt;Stream&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MeetingActivity extends AppCompatActivity {

    private VideoView localView;
    private VideoView participantView;
    private CardView localCard, participantCard;
    private ImageView localParticipantImg;

  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);

    //... 

    localCard = findViewById(R.id.LocalCard);
    participantCard = findViewById(R.id.ParticipantCard);
    localView = findViewById(R.id.localView);
    participantView = findViewById(R.id.localParticipant);
    localParticipantImg = findViewById(R.id.localParticipant_img);

    //...

    // setup local participant view
    setLocalListeners();
  }

  private void setLocalListeners() {
        meeting.getLocalParticipant().addEventListener(new ParticipantEventListener() {
            @Override
            public void onStreamEnabled(Stream stream) {
                if (stream.getKind().equalsIgnoreCase("video")) {
                    VideoTrack track = (VideoTrack) stream.getTrack();
                    localView.setVisibility(View.VISIBLE);
                    localView.addTrack(track);
                    localView.setZOrderMediaOverlay(true);
                    localCard.bringToFront();
                }
            }

            @Override
            public void onStreamDisabled(Stream stream) {
                if (stream.getKind().equalsIgnoreCase("video")) {
                    localView.removeTrack();
                    localView.setVisibility(View.GONE);
                }
            }
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;(b) Setting up Remote participant view&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private final ParticipantEventListener participantEventListener = new ParticipantEventListener() {
        // trigger when participant enabled mic/webcam
        @Override
        public void onStreamEnabled(Stream stream) {
            if (stream.getKind().equalsIgnoreCase("video")) {
                localView.setZOrderMediaOverlay(true);
                localCard.bringToFront();
                VideoTrack track = (VideoTrack) stream.getTrack();
                participantView.setVisibility(View.VISIBLE);
                participantView.addTrack(track);
            }
        }

        // trigger when participant disabled mic/webcam
        @Override
        public void onStreamDisabled(Stream stream) {
            if (stream.getKind().equalsIgnoreCase("video")) {
                participantView.removeTrack();
                participantView.setVisibility(View.GONE);
            }
        }
    };

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;(c) Handle meeting events &amp;amp; manage participant's view&lt;/strong&gt;&lt;br&gt;
Add &lt;code&gt;MeetingEventListener&lt;/code&gt;for listening events such as Meeting Join/Left and Participant Join/Lef.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class MeetingActivity extends AppCompatActivity {
  @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_meeting);
    //...

    // handle meeting events
    meeting.addEventListener(meetingEventListener);
  }

  private final MeetingEventListener meetingEventListener = new MeetingEventListener() {
        @Override
        public void onMeetingJoined() {
            // change mic,webCam icon after meeting successfully joined
            toggleMicIcon();
            toggleWebcamIcon();
        }

        @Override
        public void onMeetingLeft() {
            if (!isDestroyed()) {
                Intent intent = new Intent(MeetingActivity.this, JoinActivity.class);
                startActivity(intent);
                finish();
            }
        }

        @Override
        public void onParticipantJoined(Participant participant) {
            // Display local participant as miniView when other participant joined
            changeLocalParticipantView(true);
            Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " joined",
                    Toast.LENGTH_SHORT).show();
            participant.addEventListener(participantEventListener);
        }

        @Override
        public void onParticipantLeft(Participant participant) {
            // Display local participant as largeView when other participant left
            changeLocalParticipantView(false);
            Toast.makeText(MeetingActivity.this, participant.getDisplayName() + " left",
                    Toast.LENGTH_SHORT).show();
        }
    };
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;changeLocalParticipantView(isMiniView: Boolean)&lt;/code&gt; function first checks whether the video of a local participant is displayed as a MiniView or a LargeView.&lt;br&gt;
If the meeting has only one participant (local participant), then the local participant is displayed as LargeView.&lt;br&gt;
When another participant (other than the local participant) joins, &lt;code&gt;changeLocalParticipantView(true)&lt;/code&gt; is called. As a result, the local participant is shown as MiniView, while the other participant is shown as LargeView.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void changeLocalParticipantView(boolean isMiniView) {
        if(isMiniView)
        {
            // show localCard as miniView
            localCard.setLayoutParams(new CardView.LayoutParams(300, 430, Gravity.RIGHT | Gravity.BOTTOM));
            ViewGroup.MarginLayoutParams cardViewMarginParams = (ViewGroup.MarginLayoutParams) localCard.getLayoutParams();
            cardViewMarginParams.setMargins(30, 0, 60, 40);
            localCard.requestLayout();
            // set height-width of localParticipant_img
            localParticipantImg.setLayoutParams(new FrameLayout.LayoutParams(150, 150, Gravity.CENTER));
            participantCard.setVisibility(View.VISIBLE);
        }else{
            // show localCard as largeView
            localCard.setLayoutParams(new CardView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
            ViewGroup.MarginLayoutParams cardViewMarginParams = (ViewGroup.MarginLayoutParams) localCard.getLayoutParams();
            cardViewMarginParams.setMargins(30, 5, 30, 30);
            localCard.requestLayout();
            // set height-width of localParticipant_img
            localParticipantImg.setLayoutParams(new FrameLayout.LayoutParams(400, 400, Gravity.CENTER));
            participantCard.setVisibility(View.GONE);
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;(d) Destroying everything&lt;/strong&gt;&lt;br&gt;
We need to release resources when the app is closed and is no longer being used. Override the &lt;code&gt;onDestroy&lt;/code&gt;with the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected void onDestroy() {
        if(meeting !=null)
        {
            meeting.removeAllListeners();
            meeting.getLocalParticipant().removeAllListeners();
            meeting.leave();
            meeting = null;
        }
        if (participantView != null) {
            participantView.setVisibility(View.GONE);
            participantView.releaseSurfaceViewRenderer();
        }

        if (localView != null) {
            localView.setVisibility(View.GONE);
            localView.releaseSurfaceViewRenderer();
        }

        super.onDestroy();
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;java.lang.IllegalStateException: This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_SUPPORT_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead.&lt;/code&gt;&lt;br&gt;
If you face this error at Runtime, Include these lines in &lt;code&gt;theme.xml&lt;/code&gt; file.&lt;br&gt;
&lt;code&gt;&amp;lt;item name="windowActionBar"&amp;gt;false&amp;lt;/item&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;code&gt;&amp;lt;item name="windowNoTitle"&amp;gt;true&amp;lt;/item&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  App Demo
&lt;/h2&gt;

&lt;p&gt;Tadaa!! Our app is ready. Easy, isn't it?&lt;br&gt;
Install and run the app on two different devices and make sure that they are connected to the internet.&lt;/p&gt;

&lt;p&gt;This app only supports 2 participants, it does not manage more than 2 participants. If you want to handle more than 2 participant then check out our Group call example &lt;a href="https://dev.tortc-android-java-sdk-example"&gt;HERE&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;In this blog, we have learned what videoSDK is, how to obtain an access token from the &lt;a href="https://app.videosdk.live/api-keys" rel="noopener noreferrer"&gt;VideoSDK Dashboard&lt;/a&gt;, and how to create a one-to-one video chat app with the VideoSDK.&lt;br&gt;
Go ahead and create advanced features like screen-sharing, chat, and others. &lt;a href="https://docs.videosdk.live/" rel="noopener noreferrer"&gt;Browse Our Documentation&lt;/a&gt;.&lt;br&gt;
To see the full implementation of the app, check out this &lt;a href="https://github.com/videosdk-live/videosdk-rtc-android-java-sdk-example/tree/one-to-one-demo" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;.&lt;br&gt;
If you face any problems or have questions, please join our &lt;a href="https://discord.com/invite/Gpmj6eCq5u" rel="noopener noreferrer"&gt;Discord Community&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>videosdk</category>
      <category>videochatapp</category>
      <category>androiddev</category>
      <category>javadevelopment</category>
    </item>
    <item>
      <title>What is HTTP Live Streaming? How Does HLS Work?</title>
      <dc:creator>Chetan VideoSDK</dc:creator>
      <pubDate>Tue, 16 Jan 2024 10:17:20 +0000</pubDate>
      <link>https://forem.com/video-sdk/what-is-http-live-streaming-how-does-hls-work-40ke</link>
      <guid>https://forem.com/video-sdk/what-is-http-live-streaming-how-does-hls-work-40ke</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;What is HTTP Live Streaming (HLS)?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;HTTP Live Streaming (HLS) is a widely adopted streaming protocol developed by Apple Inc. to transmit audio and video content over the internet. It operates on a client-server model, delivering a seamless and adaptive streaming experience. HLS has gained popularity due to its compatibility with various devices and browsers, making it an ideal choice for content delivery.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Purpose of HLS&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;HTTP Live Streaming&lt;/strong&gt; serves the purpose of delivering high-quality streaming content while adapting to fluctuating network conditions. It achieves this by dynamically adjusting the bitrate based on the viewer's internet speed, ensuring a smooth playback experience without interruptions or buffering.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Advantages of HLS over Other Streaming Protocols&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Adaptive Bitrate Streaming:&lt;/strong&gt; HLS's adaptive streaming capability allows the client device to switch between different bitrate streams based on available network bandwidth. This ensures optimal playback quality regardless of the user's internet speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wide Compatibility:&lt;/strong&gt; HTTP Live Streaming is supported by a broad range of devices, including iOS and Android devices, web browsers, and more. This widespread compatibility makes it a versatile choice for content delivery.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced Buffering:&lt;/strong&gt; The segmented nature of HLS, with each segment containing a few seconds of content, minimizes buffering times. This results in a smoother streaming experience, especially when network conditions vary.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How Does HLS Work?&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;HTTP Live Streaming (HLS) Architecture Overview&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;HTTP Live Streaming&lt;/strong&gt; operates on a client-server model, where the server generates a series of media files and playlists that the client downloads and plays sequentially. The key components of HLS include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Encoding:&lt;/strong&gt; This involves reformatting the video data to ensure compatibility and seamless interpretation across various devices. HLS mandates the utilization of H.264 or H.265 encoding for this purpose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Segmenting:&lt;/strong&gt; The video is systematically divided into segments, each typically spanning a few seconds. Although the segment length is flexible, the default duration was 6 seconds until 2016. Simultaneously, HLS generates an index file detailing the order of these video segments. Additionally, HTTP Live Streaming generates multiple duplicate sets of segments, each at different quality levels such as 480p, 720p, 1080p, and so forth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manifest Files:&lt;/strong&gt; These playlists provide information about available streams and their characteristics. The master playlist lists different variants of the content at varying bitrates, while media playlists contain URLs of individual media segments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playlists:&lt;/strong&gt; Playlists act as an index, specifying the order and characteristics of the media segments. The master playlist guides the client in selecting the appropriate variant, while media playlists provide the actual URLs of the segments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN (Content Delivery Network):&lt;/strong&gt; CDNs distribute media content efficiently to users worldwide, reducing latency and enhancing streaming performance.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Adaptive Bitrate Streaming in HLS&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Adaptive bitrate streaming is a fundamental concept in &lt;strong&gt;HTTP Live Streaming&lt;/strong&gt;. The client continuously monitors network conditions and selects the appropriate bitrate variant for playback. If the network speed decreases, the client switches to a lower bitrate, preventing buffering issues. Conversely, if the network improves, the client can switch to a higher bitrate for improved video quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What are the Advantages of HTTP Live Streaming?&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Adaptive Streaming for Varying Network Conditions
&lt;/h3&gt;

&lt;p&gt;HLS's adaptive streaming ensures a consistent viewing experience, even in challenging network conditions. Users can enjoy content without interruptions, as the protocol dynamically adjusts the quality to match the available bandwidth.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Wide Compatibility with Devices and Browsers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;HLS's compatibility extends to a wide range of devices and browsers, making it an accessible solution for content providers. Whether users are on mobile devices or desktops, HLS ensures seamless playback across platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Enhanced User Experience with Reduced Buffering&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The segmented nature of HLS and adaptive bitrate streaming contribute to a superior user experience. Reduced buffering times and the ability to adapt to varying network speeds make HTTP Live Streaming a reliable choice for streaming services aiming to provide high-quality content delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How VideoSDK Enhances HTTP Live Streaming (HLS)?&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Introduction to VideoSDK&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.videosdk.live/" rel="noopener noreferrer"&gt;VideoSDK&lt;/a&gt;is a powerful set of real-time audio and video SDKs that empower developers to seamlessly integrate audio-video conferencing and live streaming into web and mobile applications. It offers complete flexibility, scalability, and control over the streaming experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Features and Capabilities of VideoSDK in HTTP Live Streaming (HLS)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Interactive Livestream Communication:&lt;/strong&gt; VideoSDK facilitates &lt;a href="https://www.videosdk.live/audio-video-conferencing" rel="noopener noreferrer"&gt;real-time audio and video communication&lt;/a&gt;, enabling interactive and engaging experiences for users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; VideoSDK is designed to scale effortlessly, ensuring that streaming applications can accommodate a growing user base without compromising performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility and Control:&lt;/strong&gt; Developers using VideoSDK have granular control over the streaming process, allowing them to tailor the experience to meet specific requirements.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Benefits of Using VideoSDK for HTTP Live Streaming&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Integration:&lt;/strong&gt; VideoSDK seamlessly integrates with HLS, providing developers with the tools needed to enhance streaming capabilities within their applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customization:&lt;/strong&gt; VideoSDK allows developers to customize the streaming experience, ensuring it aligns with the unique requirements of their applications.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability:&lt;/strong&gt; With VideoSDK, developers can build robust and reliable streaming applications, ensuring a consistent and high-quality experience for end-users.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Technical Aspects of VideoSDK and HLS Integration&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;API Documentation and Developer Resources&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Developers can leverage comprehensive API documentation provided by &lt;a href="https://docs.videosdk.live/" rel="noopener noreferrer"&gt;VideoSDK to integrate HLS&lt;/a&gt; seamlessly. The documentation includes detailed guides, code samples, and support resources to facilitate a smooth integration process.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Compatibility with Different Platforms and Devices&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;VideoSDK ensures cross-platform compatibility, enabling developers to create applications that work seamlessly on a variety of devices, including smartphones, tablets, and desktops. This versatility is crucial for reaching a broad audience.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Performance Optimization Tips for HLS Streaming with VideoSDK&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To optimize HLS streaming performance with VideoSDK, developers should consider the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Bandwidth Adaptation:&lt;/strong&gt; Leverage VideoSDK's capabilities to adjust bandwidth dynamically, aligning with HLS's adaptive streaming for an optimal user experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caching and Content Delivery:&lt;/strong&gt; Implement effective caching mechanisms and leverage CDNs to enhance content delivery speed and reduce latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality of Service (QoS) Monitoring:&lt;/strong&gt; Integrate tools for monitoring QoS to track streaming performance and identify areas for improvement.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Best Practices for Implementing HLS Streaming&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Tips for Optimizing HLS Streaming Performance&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use Efficient Codecs:&lt;/strong&gt; Employ modern and efficient video and audio codecs to ensure high-quality streaming at lower bitrates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimize Segmentation:&lt;/strong&gt; Adjust the duration of media segments to balance between reducing buffering and minimizing the number of requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN Optimization:&lt;/strong&gt; Choose a reliable CDN and optimize its configuration to ensure efficient content delivery.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Ensuring Compatibility Across Devices and Browsers&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Browser Support:&lt;/strong&gt; Verify the compatibility of HTTP Live Streaming with popular browsers and implement fallback options for non-supporting environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Device Testing:&lt;/strong&gt; Conduct thorough testing on various devices to ensure a consistent streaming experience across platforms.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Security Considerations for HLS Streaming&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Encryption:&lt;/strong&gt; Implement encryption to secure the content and prevent unauthorized access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokenization:&lt;/strong&gt; Use tokenization to control access to streaming content and protect against unauthorized sharing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Does VideoSDK support HTTP live streaming?
&lt;/h2&gt;

&lt;p&gt;Yes, VideoSDK seamlessly supports HTTP Live Streaming (HLS), providing developers with the capability to integrate adaptive and high-quality audio-video streaming into their web and mobile applications effortlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can I use HLS with VideoSDK in my application?
&lt;/h2&gt;

&lt;p&gt;Absolutely! VideoSDK is designed to seamlessly integrate with HLS, offering enhanced capabilities for real-time audio-video integration. By following &lt;a href="https://docs.videosdk.live/" rel="noopener noreferrer"&gt;API documentation,&lt;/a&gt; you can easily incorporate VideoSDK into your web or mobile app that supports HLS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does VideoSDK provide support for different web and mobile frameworks?
&lt;/h2&gt;

&lt;p&gt;Yes, VideoSDK is designed to be versatile and supports integration with a variety of web and mobile frameworks. Whether you are using &lt;a href="https://docs.videosdk.live/react/guide/video-and-audio-calling-api-sdk/concept-and-architecture" rel="noopener noreferrer"&gt;React&lt;/a&gt;, &lt;a href="https://docs.videosdk.live/react-native/guide/video-and-audio-calling-api-sdk/quick-start-ILS" rel="noopener noreferrer"&gt;React Native&lt;/a&gt;, &lt;a href="https://docs.videosdk.live/flutter/guide/video-and-audio-calling-api-sdk/quick-start-ils" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt;&lt;a href="https://docs.videosdk.live/javascript/guide/video-and-audio-calling-api-sdk/concept-and-architecture" rel="noopener noreferrer"&gt;JavaScript&lt;/a&gt;, &lt;a href="https://docs.videosdk.live/ios/guide/video-and-audio-calling-api-sdk/getting-started" rel="noopener noreferrer"&gt;iOS&lt;/a&gt;, or &lt;a href="https://docs.videosdk.live/android/guide/video-and-audio-calling-api-sdk/concept-and-architecture" rel="noopener noreferrer"&gt;Android&lt;/a&gt;, VideoSDK offers compatibility and flexibility for a wide range of development environments.&lt;/p&gt;

&lt;p&gt;Have questions about integrating HTTP Live Streaming and VideoSDK? Our team offers &lt;a href="https://www.videosdk.live/contact?utm_source=blog&amp;amp;utm_medium=google&amp;amp;utm_campaign=organic" rel="noopener noreferrer"&gt;expert advice&lt;/a&gt; tailored to your unique needs. Unlock the full potential—&lt;a href="https://www.videosdk.live/blog/what-is-http-live-streaming?utm_source=blog&amp;amp;utm_medium=google&amp;amp;utm_campaign=organic" rel="noopener noreferrer"&gt;sign up&lt;/a&gt;now to access resources and join our &lt;a href="https://discord.com/invite/Qfm8j4YAUJ?utm_source=blog&amp;amp;utm_medium=google&amp;amp;utm_campaign=organic" rel="noopener noreferrer"&gt;developer community&lt;/a&gt;. Schedule a demo to see features in action and discover how our solutions meet your streaming app needs.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
