Setting Up a Peer-to-Peer Connection: Client Video Streaming (Part 1 of 3)

Setting Up a Peer-to-Peer Connection: Client Video Streaming (Part 1 of 3)

Welcome to the first part of our three-part series on setting up a peer-to-peer (P2P) connection to send a video stream between two clients (a server serving as an intermediary only for initial connection set-up). We'll demonstrate this plain Javascript, HTML and CSS. Strap in, it's going to be a fun ride!

Here's the link to the repo.

In this first installment, we'll be talking about the available Web APIs for obtaining a video stream from the client. To get started, let's take a look at a piece of JavaScript code that we'll be using as a reference:

const getWebcamStream = async () => {
  const videoRef = document.getElementById('localVideo')
  try {
    const videoStream = await navigator.mediaDevices.getUserMedia({
      video: {
        facingMode: facingMode.current
      }
    });
    if (videoStream && videoRef.current) {
      // set this video stream to our video stream object.
      videoRef.srcObject = videoStream
    }
  } catch (e: any) {
    console.log(e?.message || "Could not start video stream. Have you enabled permissions?")
  }
}

Now, let's dive into the nitty-gritty details of this code and see what's happening under the hood.

Web APIs: Browser Support for Video Streaming

The code snippet above showcases a few Web APIs that work together to obtain a video stream from the client's device. Let's break them down one by one:

getUserMedia is a method available through the navigator.mediaDevices object, and it returns a Promise that resolves to the requested MediaStream object containing live audio and/or video streams. In our code snippet, we use the await keyword to fetch the video stream asynchronously.

The getUserMedia method accepts an object detailing the media constraints you want to apply to your stream. In our case, we're only interested in obtaining a video stream, so we pass { video: { facingMode: facingMode.current } }. This tells the method to grab a video stream with the specified facing mode (either user for the front camera or environment for the rear camera on mobile devices).

videoRef.current.srcObject

Once we have the video stream, we need to display it on our web page. To do this, we set the srcObject property of our video element (videoRef.current) to the obtained video stream. Now the video element knows which stream to play, and the user can see what their camera is capturing.

Stopping the Video Stream

Before we wrap up, let's take a look at another piece of JavaScript code that handles stopping the video stream:

const handleStopStream = async () => {
  const videoRef = document.getElementById('localVideo')
  const stream = videoRef.srcObject
  if (stream) {
    if ("getTracks" in stream) {
      const tracks = stream.getTracks();
      for (let i = 0; i < tracks.length; i++) {
        tracks[i].stop();
      }
      videoRef.srcObject = null;
    }
  }
}

This code snippet introduces a function called handleStopStream that stops the video stream when necessary. Let's break it down:

videoRef.srcObject

First, we check if there's an active video stream by accessing the srcObject property of the video element (videoRef). If a stream exists, we proceed to stop it.

stream.getTracks and tracks[i].stop()

As we saw earlier, getTracks returns an array of MediaStreamTrack objects representing the audio and video tracks in the stream. To stop the stream, we loop through these tracks and call the stop method on each one. This effectively stops the video stream from capturing data and frees up resources.

videoRef.srcObject = null

Once the tracks are stopped, we set the srcObject property of the video element to null. This removes the video stream from the video element and stops displaying the video on the web page.

Wrapping Up

So there you have it! With just a few lines of code and some powerful Web APIs, we can obtain a video stream from the client's device, display it on a web page, and set the stage for sharing it with other clients using P2P connections.

Stay tuned for the next part of this series, where we'll explore how to set up WebRTC using Socket.io. Until then, happy coding!