Build a Live Event Simulator with Recorded Video

Emma Alder

Live streaming events have become more of the norm these last years. They give a sense of urgency to your content, boost online interaction between you and your users, and help reach a wider audience.

This tutorial will walk through handling video player events by building a live event simulator in Next.js.

What we will be building

The live event simulator mimics live events where we want to play a video starting from a specified time. The video starting position for the user is adjusted based on how much time has passed since the actual video started. For example, a video that is 1 hour long but that started 45 minutes ago, would have any new user that visits the page automatically start at the 45 minute mark in the video time.

Also when the recorded video is done, we want an overlay with the text ‘This stream has ended’ and our native player controls hidden.

CodeSandbox

This project was completed in a Codesandbox. To get started quickly, fork the codesandbox or run the project.

GitHub Repository

https://github.com/Iheanacho-ai/live-event-simulator

Prerequisites

To get the most out of this tutorial, we need the following:

  • A basic understanding of CSS and React.
  • Node and it’s package manager, npm. Run the command node -v && npm -v to verify we have them installed, or install them from here.
  • Alternatively, we can use another package manager, Yarn.
  • Understanding Next.js would help us follow through with this tutorial quicker, but it is not a necessity.

Setting up the Next.js application

Next.js is an open-source React framework that enables us to build server-side rendering, static web applications. With built-in CSS, Sass, and any CSS-in-JS library support, Next.js allows us to create web applications easily.

To create our Next.js app, we go to our terminal or command prompt, using the git cd command, we navigate to the directory we want our app to be created in.

1cd <name of the directory>

Next we run:

1npx create-next-app
2 # or
3 yarn create next-app

We then change directory to the app we just created and run it.

1cd < name of our app >
2 npm run dev

To see our app in our browser, go to http://localhost:3000/

Installing dependencies

In this section of the tutorial we will install React Player to handle our video and video player events.

React Player React Player is a React component for playing a variety of URLs. React Player allows us to create and personalize our video player with callback props for almost every player event. It also offers an excellent media experience in web applications.

To install React Player in our app, we run the command.

1npm install react-player
2 # or
3 yarn add react-player

Creating the video player

To create our video player, we first create a component folder in the root of our app. In this folder we will create our video-player.jsx file.

To use the ReactPlayer component, we import it from the react-library into our video-player.jsx file.

1import React from 'react'
2 import ReactPlayer from 'react-player'

This ReactPlayer component allows us to use a wide range of props. We then pass values into these props to personalize our player.

In the next piece of code, we use the url prop, the controls prop, and finally the playing prop.

The url prop takes in a value that points to the media path we are looking to play. To display the native player controls, we use the controls prop. This prop takes in a boolean value. When the controls prop set to true, the controls are displayed, and hidden when set to false. Next, we have the playing prop. The playing prop plays or pauses the media when set to true or false, respectively.

The ReactPlayer component also allows us to set the width and height of the player using the width and height prop, respectively. Failing to do so sets our video player to this default dimensions width: 640px; height: 360px; which we will use in this tutorial.

1import React from 'react'
2 import ReactPlayer from 'react-player'
3 const VideoPlayer = () => {
4 return(
5 <div className="video-player">
6 <ReactPlayer
7 url= 'https://res.cloudinary.com/amarachi-2812/video/upload/v1630370229/videoplayback_1_pr2hzi.mp4'
8 playing = {true}
9 controls = {true}
10 />
11 </div>
12 )
13 }
14
15 export const MemoizedVideoPlayer = React.memo(VideoPlayer);

We memoized the video player component to prevent any re-rendering triggered by its parent component, except a dependency changes.

<!— It looks like you are missing ‘/>’ closing the <reactplayer> div —>

Setting our playing and controls prop to true, our video player plays our video as it renders on our screen and displays our native player controls.

Next, we import our video player to our index.js file to see our video player in our browser.

1import Head from 'next/head'
2 import styles from '../styles/Home.module.css'
3 import { MemoizedVideoPlayer } from '../component/video-player';
4
5 const Home = () => {
6 return(
7 <div className={styles.container}>
8 <Head>
9 <title>Live Simulator</title>
10 <meta name="description" content="Live Event Simulator Created by Nextjs" />
11 <link rel="icon" href="/favicon.ico" />
12 </Head>
13 <main className={styles.main}>
14 <div className= 'live-event-container'>
15 <MemoizedVideoPlayer/>
16 </div>
17 </main>
18 </div>
19 )
20 }
21 export default Home;

With that we have our video player set up.

Creating the Live Simulation

To create a live simulation in our app, we start by importing React and the useState hook in our index.js file.

The useState hook allows our functional component to have states in them.

1import React, { useState } from 'react';

Next, using the useState hook, we create pieces of state to control different properties of our video player, in a Home component.

<!— Can you please mention to place this and the next piece of code within the home constant before return (..) —>

1const Home = () => {
2 const [startTime, setStartTime] = useState(1633538276355);
3 const [controls, setControls] = useState(true);
4 const [ended, setEnded] = useState(false);
5 const [duration, setDuration] = useState(null);
6 const [playing, setPlaying] = useState(true);
7
8 return (
9 <div className={styles.container}>
10
11 </div>
12 );
13};
14
15export default Home;
  • startTime : Set to an initial value of 1630430724714, this is the time in milliseconds we want our ‘live event’ to start (we shall discuss how to find the time in milliseconds in the functions section of this tutorial below).
  • controls : Set to an initial value of true, this property sets the controls prop on the ReactPlayer component.
  • ended : Set to an initial value of false. It would be set to true when the video ends. We will use the ended variable to conditionally render our overlay.
  • duration : Stores the duration of the video we are playing in seconds.
  • playing : Controls when our video starts or stops playing, it will be set to an initial value of true as we want our video to be playing when it mounts, unless the video has ended.

Next, our control functions.

1let date = new Date();
2 let currentTime = date.getTime()
3 let timePlayed = ( currentTime - startTime ) % 1000;
4
5 const endVideo = () => {
6 if (controls === false && ended === true) {
7 if (playing === false) {
8 return;
9 } else {
10 setPlaying(false);
11 }
12 } else {
13 setControls(false);
14 setEnded(true);
15 setPlaying(false);
16 }
17 };
18
19 const videoDuration = num => {
20 setDuration( num )
21 }
22
23 if (timePlayed > duration){
24 endVideo()
25 }
26
27 const restartLive = () => {
28 let newDate = new Date();
29 let newStartTime = newDate.getTime();
30 setStartTime(newStartTime);
31 setEnded(false);
32 setPlaying(true);
33 setControls(true);
34 };

Getting the current time in milliseconds

The date variable in the code block above gets the current date using the JavaScript new Date() method. We then get the current time in milliseconds using the JavaScript getTime() method, and store it in the currentTime variable. The timePlayed variable gets the difference between the time our ‘live event’ started and the current time in seconds, this variable will be used to tell our video player what time in the player we want to seek to.

What happens when our video ends? The endVideo function will get called when our video ends, even long after. The endVideo function has an if else block. This if else conditional checks if the controls variable is already false and the ended variable true, if they are, we go further to check if the playing variable is false, if our code passes all these checks, we want to return from the function.

If, however, our code does not pass all these checks, we want to set

  • playing to false to stop playing the video
  • controls to false to hide the native player controls.

We created a videoDuration function that takes in a num parameter, this parameter represents the duration of the media. This videoDuration function uses the num parameter to update the duration state variable.

Next, we have an if block of code, this checks if the timePlayed variable (containing how much time has passed since the start of the live) is greater than the duration. If it is, we run the endVideo function.

Restarting the video

The restartLive function is used to restart the live event simulation. The newDate variable gets the date the restartLive function ran, and the newStartTime variable stores the exact time in milliseconds this function ran. We pass in the newStartTime to the setStartTime function to reset the startTime variable. Finally, we set our ended variable to false, as we just started our simulation over again.

Next, we add a button to run this restartLive function, with the onClick event handler.

1<button className= 'reset-button' onClick = {restartLive}>Restart Live Simulation</button>

Our index.js file should like this.

The module imports and methods

1import Head from "next/head";
2 import React, { useState } from "react";
3 import { MemoizedVideoPlayer } from "../component/video-player";
4 import styles from "../styles/Home.module.css";
5
6 const Home = () => {
7 const [startTime, setStartTime] = useState(1633538276355);
8 const [controls, setControls] = useState(true);
9 const [ended, setEnded] = useState(false);
10 const [duration, setDuration] = useState(null);
11 const [playing, setPlaying] = useState(true);
12
13 let date = new Date();
14 let currentTime = date.getTime();
15 let timePlayed = (currentTime - startTime) % 1000;
16
17
18 const endVideo = () => {
19 if (controls === false && ended === true) {
20 if (playing === false) {
21 return;
22 } else {
23 setPlaying(false);
24 }
25 } else {
26 setControls(false);
27 setEnded(true);
28 setPlaying(false);
29 }
30 };
31
32 const restartLive = () => {
33 let newDate = new Date();
34 let newStartTime = newDate.getTime();
35 setStartTime(newStartTime);
36 setEnded(false);
37 setPlaying(true);
38 setControls(true);
39 };
40
41 const videoDuration = (num) => {
42 setDuration(num);
43 };
44
45 if (timePlayed > duration) {
46 endVideo();
47 }

The rendered component

1return (
2 <div className={styles.container}>
3 <Head>
4 <title>Live Simulator</title>
5 <meta
6 name="description"
7 content="Live Event Simulator Created by Next.js"
8 />
9 <link rel="icon" href="/favicon.ico" />
10 </Head>
11 <main className={styles.main}>
12 <div className="live-event-container">
13 {/* Our VideoPlayer component */}
14 <MemoizedVideoPlayer
15 ended={ended}
16 timePlayed={timePlayed}
17 controls={controls}
18 endVideo={endVideo}
19 playing={playing}
20 videoDuration={videoDuration}
21 />
22 </div>
23 {/* Our Restart button */}
24 <button className="reset-button" onClick={restartLive}>
25 Restart Live Simulation
26 </button>
27 </main>
28 </div>
29 );
30 };
31
32 export default Home;

See the complete code block in this Github Gist here.

Passing the variables to our React Player

We pass our timePlayed variable, controls variables, endVideo function and our videoDuration function to the MemoizedVideoPlayer component housing our ReactPlayer component.

1<MemoizedVideoPlayer timePlayed ={timePlayed} controls={controls} endVideo={endVideo} playing={playing} videoDuration={videoDuration} />

In our video-player.jsx file, we define a player variable, we use this variable later to set the ref on our ReactPlayer, we do this to reference the component.

Next we pass the variables we received to their respective props, the controls variable, to the controls prop and the playing variable, to the playing prop.

The ReactPlayer component allows us to use an onEnded callback prop which runs the function passed into it when the video ends, so we pass in the endVideo function.

The onStart callback prop takes in a function that runs when the video is mounted and ready to play. We use this, to call the getDuration() method on the player object we get from the ReactPlayer component. This method is used to get the duration of the video in seconds.

Next we write another function using the seekTo() method on the player object. The seekTo() method takes in a time parameter in seconds that indicates where we want to skip to in the video. We then pass the timePlayed variable (the difference between the current time and the start time in seconds) as the time parameter into the seekTo() method.

Our video-player.jsx should look like this.

1import React from "react";
2 import ReactPlayer from "react-player";
3 let player;
4 const VideoPlayer = ({
5 endVideo,
6 timePlayed,
7 controls,
8 playing,
9 videoDuration
10 }) => (
11 <div className="video-player">
12 <ReactPlayer
13 ref={(ref) => {
14 player = ref;
15 }}
16 url="https://res.cloudinary.com/amarachi-2812/video/upload/v1630370229/videoplayback_1_pr2hzi.mp4"
17 playing={playing}
18 controls={controls}
19 onStart={() => {
20 videoDuration(player.getDuration);
21 player.seekTo(timePlayed);
22 }}
23 onEnded={endVideo}
24 />
25 </div>
26 );
27 export const MemoizedVideoPlayer = React.memo(VideoPlayer);

Creating the video overlay

To make our overlay, we create a video-overlay.jsx file in the components folder.

For our overlay, we use a div element, a header and a p tag.

1import React from 'react';
2 const VideoOverlay = () => (
3 <div className = 'video-overlay'>
4 <h2>Live Event has finished</h2>
5 <p>Click the button below to restart simulation</p>
6 </div>
7 );
8 export default VideoOverlay;

In the styles folder, there is a global.css stylesheet. In this, we write the styling for our app with the following content:

1.video-overlay{
2 width: 640px;
3 height: 360px;
4 position: absolute;
5 top: 18.5%;
6 bottom: 0;
7 background-color: rgba(0,0,0, 0.6);
8 color: #fff;
9 font-family: 'Nunito Sans', sans-serif;
10 text-align: center;
11 }

The video-overlay div has a height of 640px and a width of 360px. These are the measurement of the video player. Next, we give the overlay a position property of ‘absolute’, setting its position relative to its parent.

The top and bottom properties tell us the distance we want our element to start. This tutorial has our properties top to 18.5% and bottom set to 0, indicating that we wish our overlay to begin at the top of our video player and end at the bottom.

We give the overlay a background-color property background-color: rgba(0, 0, 0, 0.6). This value gives us a light black background. The color property indicates what text color we want in our overlay, font-family indicates what font we want and the text-align: center to move our text to the center of the overlay.

We also style our reset button with:

1.reset-button{
2 width: 250px;
3 height: 30px;
4 border-radius: 5px;
5 background-color: #111;
6 outline: none;
7 color: #fff;
8 }

Selectively rendering the overlay

In the Home component we render the VideoOverlay component with:

1import VideoOverlay from "../component/video-overlay";
2// other imports go in here
3
4const Home = () => {
5 // state and method definitions
6
7 return (
8 <div className={styles.container}>
9 {/* Head component */}
10
11 <main className={styles.main}>
12 <div className="live-event-container">
13 {/* Our VideoPlayer component */}
14
15 <MemoizedVideoPlayer
16 ended={ended}
17 timePlayed={timePlayed}
18 controls={controls}
19 endVideo={endVideo}
20 playing={playing}
21 videoDuration={videoDuration}
22 />
23 {ended ? <VideoOverlay /> : null}
24 </div>
25
26 {/* Our Restart button */}
27 </main>
28 </div>
29 );
30};
31
32export default Home;

In the block of code above, we use a ternary operator to render our overlay depending on if the value of the ended variable is true.

With that, we have created our Live Event Simulator.

Here was it looks like :

Conclusion

In this article, we created a video player and handled player events with React Player. We specified what should happen when a user starts watching the 'live video' and how to place them at a specific point of the video depending on how much time has passed since the video began. Try to modify the video’s start time relative to your current time and see how far into the live video you get.

Resources

You may also find these resources helpful.

Emma Alder

Technical Writer at Hackmamba.io

Technical writer at Hackmamba.io