Introduction

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.

Building a JavaScript live video streaming app, it's crucial to consider various aspects to ensure a seamless user experience. While we delve into real-time streaming, incorporating real-time messaging can foster interactivity among users during live sessions. Additionally, exploring features like,

  1. Mute/unmute their mic for a seamless audio experience.
  2. The on/off camera functionality ensures control over video visibility.
  3. Screen sharing enables participants to display their screen content effortlessly.
  4. Active speaker indication highlights the current speaker for better communication.
  5. The image capture feature allows users to take snapshots during the stream.

Moreover, discussing integration with media services platforms and optimizing for low latency enhances streaming quality. Security measures, such as API keys and stream keys, safeguard streaming sessions. By incorporating these elements and exploring open-source javascript livestreaming templates so, you can create a comprehensive and customizable live-streaming experience for your users.

πŸ“ Prerequisites for Building Your Javascript Live Video Streaming App

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

πŸ“₯ Install Video SDK​

Run the following npm command in your app directory.

npm install @videosdk.live/js-sdk

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

Your project directory should include:

  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: Set Up Your Project's User Interface

Create the HTML structure with join and grid screens in your index.html file. Include controls for managing meetings and initiating live streams with HLS (HTTP Live Streaming).

<!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 up token authentication in the config.js file for creating and joining meetings. Implement DOM manipulations in index.js to facilitate session joining as a host or viewer. Β (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 and 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 your meeting

Configure the meeting settings based on the session type (host or viewer) and initiate media streams. Here, focus on low-latency configurations to enhance real-time interaction.

// 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

Detail the process for setting up speaker controls, such as muting/unmuting and camera management. Explain how these controls impact the streaming experience, especially in a low-latency environment.

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: Configure Media Elements for Speakers

Guide on creating and managing audio and video elements for displaying both local and remote participants effectively. Highlight synchronization techniques for audio and video streams to ensure seamless playback.

// 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: Set Up the Viewer View for Live Streaming

Implement viewer-specific functionalities using HLS. Discuss the role of media servers in managing and distributing live content efficiently

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 downstream URL 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