πŸ“Œ TL;DR

The tutorial is about creating an interactive live-streaming app using JavaScript with VideoSDK. It includes JavaScript, Video SDK, and a server for streaming.

You should learn this tutorial to enhance your understanding of live streaming and to gain practical skills in creating interactive live streams. The benefits of using this software configuration include the ability to create dynamic streaming experiences, engage with viewers in real-time, and customize the streaming functionality according to specific needs.

By following this tutorial, you will set up a server, integrate the VideoSDK with JavaScript, and create interactive live streams. You will have hands-on experience in building and testing a streaming application, allowing you to understand the process from start to finish.

After completing the tutorial, you will have acquired new skills in video streaming and JavaScript programming. You will be able to create your own interactive live streams, customize streaming features, and deploy video streaming applications with the Video SDK and JavaScript.

πŸ“ Prerequisites

  • VideoSDK Developer Account (Not having one? Follow VideoSDK Dashboard)
  • Have Node and NPM installed on your device.

πŸ“₯ Install Video SDK​

You can install the Video SDK using the below-mentioned npm command. Make sure you are in your app directory before running this command.

npm install @videosdk.live/js-sdk

πŸ—οΈ Structure of the project​

Your project structure should look like this.

  root
   β”œβ”€β”€ index.html
   β”œβ”€β”€ config.js
   β”œβ”€β”€ index.js

We are going to work on three files:

  • index.html : Responsible for creating basic UI.
  • config.js : Responsible for storing the token.
  • index.js : Responsible for rendering meeting view and joining the meeting.

🎨 STEP 1: Create the UI of your project

In this step, We are going to create HTML file which will have two screens join-screen and grid-screen. Copy the below content and paste it into your index.html file.

<!DOCTYPE html>
<html>
  <head> </head>

  <body>
    <div id="join-screen">
      <!-- Create new Meeting Button -->
      <button id="createMeetingBtn">Create Meeting</button>
      OR
      <!-- Join existing Meeting -->
      <input type="text" id="meetingIdTxt" placeholder="Enter Meeting id" />
      <button id="joinHostBtn">Join As Host</button>
      <button id="joinViewerBtn">Join As Viewer</button>
    </div>

    <!-- for Managing meeting status -->
    <div id="textDiv"></div>

    <div id="grid-screen" style="display: none">
      <!-- To Display MeetingId -->
      <h3 id="meetingIdHeading"></h3>
      <h3 id="hlsStatusHeading"></h3>

      <div id="speakerView" style="display: none">
        <!-- Controllers -->
        <button id="leaveBtn">Leave</button>
        <button id="toggleMicBtn">Toggle Mic</button>
        <button id="toggleWebCamBtn">Toggle WebCam</button>
        <button id="startHlsBtn">Start HLS</button>
        <button id="stopHlsBtn">Stop HLS</button>
      </div>

      <!-- render Video -->
      <div id="videoContainer"></div>
    </div>
    <script src="https://sdk.videosdk.live/js-sdk/0.0.67/videosdk.js"></script>
    <script src="config.js"></script>
    <script src="index.js"></script>

    <!-- hls lib script  -->
    <script src="https://cdn.jsdelivr.net/npm/hls.js"></script>
  </body>
</html>

πŸ”΅ Output

Video SDK Image

πŸ“² STEP 2: Implement the Join Screen of your project

Set token (that you have generated from VideoSDK Dashboard) in config.js file.

// Auth token we will use to generate a meeting and connect to it
TOKEN = "Your_Token_Here";

Now get all the elements from DOM declare the following variables in index.js file and then add Event Listener to the join and create meeting buttons.
The join screen will work as a medium to either schedule a new meeting or to join an existing meeting as a host or as a viewer.

These will have 3 buttons:
1. Join as a Host: When this button is clicked, the person will join the entered meetingId as a HOST.
2. Join as a Viewer: When this button is clicked, the person will join the entered meetingId as a VIEWER.
3. New Meeting: When this button is clicked, the person will join a new meeting as HOST.

// getting Elements from Dom
const joinHostButton = document.getElementById("joinHostBtn");
const joinViewerButton = document.getElementById("joinViewerBtn");
const leaveButton = document.getElementById("leaveBtn");
const startHlsButton = document.getElementById("startHlsBtn");
const stopHlsButton = document.getElementById("stopHlsBtn");
const toggleMicButton = document.getElementById("toggleMicBtn");
const toggleWebCamButton = document.getElementById("toggleWebCamBtn");
const createButton = document.getElementById("createMeetingBtn");
const videoContainer = document.getElementById("videoContainer");
const textDiv = document.getElementById("textDiv");
const hlsStatusHeading = document.getElementById("hlsStatusHeading");

// declare Variables
let meeting = null;
let meetingId = "";
let isMicOn = false;
let isWebCamOn = false;

const Constants = VideoSDK.Constants;

function initializeMeeting() {}

function createLocalParticipant() {}

function createVideoElement() {}

function createAudioElement() {}

function setTrack() {}

// Join Meeting As Host Button Event Listener
joinHostButton.addEventListener("click", async () => {
  document.getElementById("join-screen").style.display = "none";
  textDiv.textContent = "Joining the meeting...";

  roomId = document.getElementById("meetingIdTxt").value;
  meetingId = roomId;

  initializeMeeting(Constants.modes.CONFERENCE);
});

// Join Meeting As Viewer Button Event Listener
joinViewerButton.addEventListener("click", async () => {
  document.getElementById("join-screen").style.display = "none";
  textDiv.textContent = "Joining the meeting...";

  roomId = document.getElementById("meetingIdTxt").value;
  meetingId = roomId;

  initializeMeeting(Constants.modes.VIEWER);
});

// Create Meeting Button Event Listener
createButton.addEventListener("click", async () => {
  document.getElementById("join-screen").style.display = "none";
  textDiv.textContent = "Please wait, we are joining the meeting";

  const url = `https://api.videosdk.live/v2/rooms`;
  const options = {
    method: "POST",
    headers: { Authorization: TOKEN, "Content-Type": "application/json" },
  };

  const { roomId } = await fetch(url, options)
    .then((response) => response.json())
    .catch((error) => alert("error", error));
  meetingId = roomId;

  initializeMeeting(Constants.modes.CONFERENCE);
});

πŸ”΅ Output

Video SDK Image

🀝 STEP 3: Initialize the meeting

Initialize the meeting by mode passed to the function and only create a local participant stream when the mode is "CONFERENCE".

// Initialize meeting
function initializeMeeting(mode) {
  window.VideoSDK.config(TOKEN);

  meeting = window.VideoSDK.initMeeting({
    meetingId: meetingId, // required
    name: "Thomas Edison", // required
    mode: mode,
  });

  meeting.join();

  meeting.on("meeting-joined", () => {
    textDiv.textContent = null;

    document.getElementById("grid-screen").style.display = "block";
    document.getElementById(
      "meetingIdHeading"
    ).textContent = `Meeting Id: ${meetingId}`;

    if (meeting.hlsState === Constants.hlsEvents.HLS_STOPPED) {
      hlsStatusHeading.textContent = "HLS has not stared yet";
    } else {
      hlsStatusHeading.textContent = `HLS Status: ${meeting.hlsState}`;
    }

    if (mode === Constants.modes.CONFERENCE) {
      // we will pin the local participant if he joins in `CONFERENCE` mode
      meeting.localParticipant.pin();

      document.getElementById("speakerView").style.display = "block";
    }
  });

  meeting.on("meeting-left", () => {
    videoContainer.innerHTML = "";
  });

  meeting.on("hls-state-changed", (data) => {
    //
  });

  if (mode === Constants.modes.CONFERENCE) {
    // creating local participant
    createLocalParticipant();

    // setting local participant stream
    meeting.localParticipant.on("stream-enabled", (stream) => {
      setTrack(stream, null, meeting.localParticipant, true);
    });

    // participant joined
    meeting.on("participant-joined", (participant) => {
      if (participant.mode === Constants.modes.CONFERENCE) {
        participant.pin();

        let videoElement = createVideoElement(
          participant.id,
          participant.displayName
        );

        participant.on("stream-enabled", (stream) => {
          setTrack(stream, audioElement, participant, false);
        });

        let audioElement = createAudioElement(participant.id);
        videoContainer.appendChild(videoElement);
        videoContainer.appendChild(audioElement);
      }
    });

    // participants left
    meeting.on("participant-left", (participant) => {
      let vElement = document.getElementById(`f-${participant.id}`);
      vElement.remove(vElement);

      let aElement = document.getElementById(`a-${participant.id}`);
      aElement.remove(aElement);
    });
  }
}

πŸ”΅ Output

Video SDK Image

πŸŽ™οΈ STEP 4: Controls for the Speaker

The next step is to create SpeakerView and Controls components to manage features such as join, leave, mute, and unmute.
You'll get all the participants from meeting object and filter them for the mode set to CONFERENCE so only Speakers are shown on the screen.

// leave Meeting Button Event Listener
leaveButton.addEventListener("click", async () => {
  meeting?.leave();
  document.getElementById("grid-screen").style.display = "none";
  document.getElementById("join-screen").style.display = "block";
});

// Toggle Mic Button Event Listener
toggleMicButton.addEventListener("click", async () => {
  if (isMicOn) {
    // Disable Mic in Meeting
    meeting?.muteMic();
  } else {
    // Enable Mic in Meeting
    meeting?.unmuteMic();
  }
  isMicOn = !isMicOn;
});

// Toggle Web Cam Button Event Listener
toggleWebCamButton.addEventListener("click", async () => {
  if (isWebCamOn) {
    // Disable Webcam in Meeting
    meeting?.disableWebcam();

    let vElement = document.getElementById(`f-${meeting.localParticipant.id}`);
    vElement.style.display = "none";
  } else {
    // Enable Webcam in Meeting
    meeting?.enableWebcam();

    let vElement = document.getElementById(`f-${meeting.localParticipant.id}`);
    vElement.style.display = "inline";
  }
  isWebCamOn = !isWebCamOn;
});

// Start Hls Button Event Listener
startHlsButton.addEventListener("click", async () => {
  meeting?.startHls({
    layout: {
      type: "SPOTLIGHT",
      priority: "PIN",
      gridSize: "20",
    },
    theme: "LIGHT",
    mode: "video-and-audio",
    quality: "high",
    orientation: "landscape",
  });
});

// Stop Hls Button Event Listener
stopHlsButton.addEventListener("click", async () => {
  meeting?.stopHls();
});

πŸ”Š STEP 5: Speaker Media Elements

In this step, You will have to create a function that helps you create audio and video elements to display local and remote participants for the speaker. You will also have to set the appropriate media track based on whether it's a video or audio.

// creating video element
function createVideoElement(pId, name) {
  let videoFrame = document.createElement("div");
  videoFrame.setAttribute("id", `f-${pId}`);

  //create video
  let videoElement = document.createElement("video");
  videoElement.classList.add("video-frame");
  videoElement.setAttribute("id", `v-${pId}`);
  videoElement.setAttribute("playsinline", true);
  videoElement.setAttribute("width", "300");
  videoFrame.appendChild(videoElement);

  let displayName = document.createElement("div");
  displayName.innerHTML = `Name : ${name}`;

  videoFrame.appendChild(displayName);
  return videoFrame;
}

// creating audio element
function createAudioElement(pId) {
  let audioElement = document.createElement("audio");
  audioElement.setAttribute("autoPlay", "false");
  audioElement.setAttribute("playsInline", "true");
  audioElement.setAttribute("controls", "false");
  audioElement.setAttribute("id", `a-${pId}`);
  audioElement.style.display = "none";
  return audioElement;
}

// creating local participant
function createLocalParticipant() {
  let localParticipant = createVideoElement(
    meeting.localParticipant.id,
    meeting.localParticipant.displayName
  );
  videoContainer.appendChild(localParticipant);
}

// setting media track
function setTrack(stream, audioElement, participant, isLocal) {
  if (stream.kind == "video") {
    isWebCamOn = true;
    const mediaStream = new MediaStream();
    mediaStream.addTrack(stream.track);
    let videoElm = document.getElementById(`v-${participant.id}`);
    videoElm.srcObject = mediaStream;
    videoElm
      .play()
      .catch((error) =>
        console.error("videoElem.current.play() failed", error)
      );
  }
  if (stream.kind == "audio") {
    if (isLocal) {
      isMicOn = true;
    } else {
      const mediaStream = new MediaStream();
      mediaStream.addTrack(stream.track);
      audioElement.srcObject = mediaStream;
      audioElement
        .play()
        .catch((error) => console.error("audioElem.play() failed", error));
    }
  }
}

πŸ”΅ Output

Video SDK Image

πŸ‘€ STEP 6: Implementing ViewerView

When the host starts live streaming, viewers will be able to see it.
To implement player view, You need to use hls.js. It will be helpful to play hls stream. You have already added the script of hls.js in index.html file. Now on the hls-state-changed event, when participant mode is set to VIEWER and the status of HLS is HLS_PLAYABLE, Pass the downstreamUrl to the hls.js and play it.

// Initialize meeting
function initializeMeeting() {
  // ...

  // hls-state-chnaged event
  meeting.on("hls-state-changed", (data) => {
    const { status } = data;

    hlsStatusHeading.textContent = `HLS Status: ${status}`;

    if (mode === Constants.modes.VIEWER) {
      if (status === Constants.hlsEvents.HLS_PLAYABLE) {
        const { downstreamUrl } = data;
        let video = document.createElement("video");
        video.setAttribute("width", "100%");
        video.setAttribute("muted", "false");
        // enableAutoPlay for browser autoplay policy
        video.setAttribute("autoplay", "true");

        if (Hls.isSupported()) {
          var hls = new Hls();
          hls.loadSource(downstreamUrl);
          hls.attachMedia(video);
          hls.on(Hls.Events.MANIFEST_PARSED, function () {
            video.play();
          });
        } else if (video.canPlayType("application/vnd.apple.mpegurl")) {
          video.src = downstreamUrl;
          video.addEventListener("canplay", function () {
            video.play();
          });
        }

        videoContainer.appendChild(video);
      }

      if (status === Constants.hlsEvents.HLS_STOPPING) {
        videoContainer.innerHTML = "";
      }
    }
  });
}
Video SDK Image

You're done with the implementation of a customized live-streaming app in JavaScript using VideoSDK.

πŸ‘ Conclusion

  • You have configured a server and integrated the Video SDK with JavaScript to create interactive live streams.
  • You have gained practical skills in live streaming and JavaScript programming.
  • You can now create your own customized streaming experiences, engage with viewers in real-time, and deploy video streaming applications.
  • Go ahead and create advanced features like real-time messaging, screen-sharing, and others. Browse our documentation.
  • If you face any problems, Feel free to join our Discord community.

πŸ“š More JavaScript Resources