Setting Up a Peer-to-Peer Connection: Client Video Streaming (Part 1 of 3)
- Brian Baliach
- 07 May 2023
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:
navigator.mediaDevices.getUserMedia
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!