Building Live Streaming App with Node.js and React

I’ve been working on an app which lets you live stream your desktop. It takes in an RTMP stream from the broadcaster and transcodes it into HLS stream that viewers can watch in their web browsers. In this tutorial, I’ll show how you can build your own video streaming app with Nodejs. If you are ‘I want to directly dive into code’ kind of person, I’ve created this  repository for you with setup instructions. I am going to break down this tutorial into five parts.

  • Web Server with Basic authentication
  • Setting up an RTMP server in Nodejs
  • Displaying live streams
  • Issuing streaming keys to broadcasters
  • Generating live stream thumbnails

Web Server with Basic Authentication

Let’s set up a basic node server with passport local strategy authentication. We will use MongoDB with Mongoose ODM for persistent storage. Initialize a new project by running

and install these dependencies.

In your project directory, create two folders client and server. We will place our react components inside the client directory and backend code in the server directory. For this part, we will be working inside the server directory. We are using passport.js for authentication. We have already installed passport and passport-local modules. Before we define our local strategy for authenticating users, let’s create an app.js file and add the necessary code to run a basic web server. Make sure you have MongoDB installed on your system and running as a service.

We have bootstrapped all the necessary middlewares for our application, connected to MongoDB and configured express session to use the file storage for session persistence in case of the web server restart. Now we will define our passport strategies for registering and authenticating users. Create a folder named auth with a passport.js file in it and add the following code.

We also need to define the schema for our User model. Create a database directory with  UserSchema.js file in it and add the following code.

We have three methods on our User schema. generateHash method will convert plain text password to bcrypt hash. We are using it in our passport strategy for converting plain password strings to bcrypt hash before storing them in the database. validPassword method will take in a plain text password and validate it by comparing it to bcrypt hash stored in our database. generateStreamKey method will generate a unique string that we will issue to users as their streaming key for RTMP clients.

Now that we have defined our passport strategies, added user schema and created a model from it, let’s initialize passport in app.js.

Also, register these routes in the app.js file.

Create a login.js and register.js file under routes directory where we will define these routes and use passport middleware for registration and authentication.

We are using ejs templating engine. Add login.ejs and register.ejs template to views directory and add the following code.

We are pretty much done with authentication. Now we will move onto the next part of this tutorial and set up our RTMP server.

Setting up an RTMP server

Real-Time Messaging Protocol (RTMP) was designed for high-performance transmission of video, audio, and data between broadcaster and server. Twitch, Facebook, Youtube, and many other sites who offer live streaming accepts RTMP streams and transcodes then into HTTP streams (HLS format)  before distributing them to their CDNs for high availability.

We are using node-media-server, a Node.js implementation of RTMP media server. It accepts RTMP streams and remux them to HLS/DASH using ffmpeg. Make sure you have ffmpeg installed on your system. If you are running Linux and already have ffmpeg installed, you can find your installation path by running this command from the terminal.

node-media-server recommends ffmpeg 4.x version. You can check your version by running this command.

If you don’t have ffmpeg installed and running Ubuntu, you can install it by running these commands from the terminal.

If you are running Windows, you can download ffmpeg windows builds. Add this config file to your project.

Change ffmpeg value to your own ffmpeg installation path. If you’re running windows and downloaded Windows builds from the above link, make sure you add .exe extension add the end of your path.

Also, install node-media-server by running

Create media_server.js file and add this code.

NodeMediaServer usage is pretty straight forward. It runs an RTMP server and lets you listen to connection events. You can reject an incoming connection if a streaming key is invalid.  We will be listening to its prePublish event. We will add more code inside prePublish event listener closure to reject incoming connections with invalid streaming keys in the next part of this tutorial. For now, we are accepting all incoming connection on default 1935 RTMP port. Now, all we have to do is import nms object in the app.js file and call its run method.

Download Open Broadcaster Software (OBS) and install it on your PC. Go to Settings > Stream. Select Custom service and enter rtmp://127.0.0.1:1935/live in Server input. You can leave Stream Key input empty or add a random string if it doesn’t let you save settings. Click Apply and Ok. Click start streaming button to broadcast your RTMP stream to your local server.

Head over to your terminal to see the output from your node media server. You will see an incoming stream with a few events listener logs.

Node media server exposes an API to list all connected clients. You can access it in your browser at http://127.0.0.1:8888/api/streams. Later on, we will use this API in our frontend React app to show live streaming users. You will see an output like this.

Our backend is pretty much ready. Its a working HTTP/RTMP/HLS streaming server. However, we still need to validate incoming RTMP connections to ensure that only authenticated user’s streams are accepted. Add this code to your prePublish event listener closure.

Inside closure, we are querying the database to find a user with the streaming key. If it belongs to a user, we would simply let them connect and publish their stream. Otherwise, we reject the incoming RTMP connection.

In the next part of this tutorial, we will build a basic React frontend to allow users to view live streams, generate and view their streaming keys.

Displaying Live Streams

For this part, we will be working in the client directory. Since its a react app, we will be using webpack and necessary loaders to transpile JSX into browser ready JavaScript.  Install these modules.

Add this webpack config to your project.

Add an index.js file with the following code.

We are using react-router for routing and bootstrap on the frontend along with  video.js for displaying live streams. Add components directory with Root.js file in it and add the following code.

<Root/> component renders a react <Router/> to hold three sub <Route/> components.  <LiveStreams/> component will render all the live streams. <VideoPlayer/> will render video.js player components. <Settings/> component will provide an interface for generating a new streaming key.

Create LiveStreams.js  component.

After our component mounts, we are making a call to NMS API to retrieve all the connected clients. NMS API does not have much information about the user other than their streaming key through which they are connected to our RTMP server. We will use the streaming key to query our database to get users records. In getStreamsInfo method, we are making an XHR request to /streams/info which we have not yet defined.  Create a server/routes/streams.js file and add the following code to it. We will pass on the streams returned from the NMS API to our backend to retrieve information about connected clients.

We are querying the database to select all the users with matched streaming keys that we retrieved from NMS API and return them as a JSON response. Register this route in the app.js file.

In the end, we are rendering live streams with username and thumbnails. We will generate thumbnails for our streams in the last part of this tutorial. These thumbnails are linked to the individual pages where HLS streams are played inside a video.js player component. Create VideoPlayer.js component.

On component mount, we retrieve the user’s streaming key to initiate an HLS stream inside video.js player.

Issuing streaming keys to broadcasters

Create Settings.js component.

Inside our passport’s local strategy, when a user successfully registers, we create a new user record with a unique streaming key. If a user visits /settings route, they will be able to view their existing key. When components mounts, we make an XHR call to the backend to retrieve user’s existing streaming key and render it inside our <Settings/> component.

Users can generate a new key by clicking Generate a new key button which makes an XHR call to the backend to create a new key, save it to user collection and also return it so that it can be rendered inside the component. We need to define both GET and POST /settings/stream_key routes. Create a server/routes/settings.js file and add the following code.

We using shortid module for generating unique strings. Register these routes in the app.js  file.

Generating Live Stream Thumbnails

In <LiveStreams/> components, we are displaying thumbnail images for live streams.

We will be generating these thumbnails whenever a new stream connects to our server. We will run a cron job to generate new thumbnails for live streams every 5 seconds. Add this helper method inside server/helpers/helpers.js.

We are passing the streaming key to generateStreamThumbnail. It spawns a detached ffmpeg process to generate thumbnail image from HLS stream. We will call this helper method inside prePublish closure after validating the streaming key.

To generate fresh thumbnails, we will run a cron job and call this helper method from it.

This cron job will execute every 5 seconds, retrieve active streams from NMS API and generate thumbnails for each stream using the streaming key. Import this job inside the app.js  file and run it.

Our real-time live streaming app is ready. I might have missed some details in this tutorial, but you can access complete code in this repository. If you run into an issue, please report it by creating a new issue in the repository so that it can be addressed. Setup and usage instructions are available in the repository’s readme file.

Here’s a demo of our app.

Share this post

33 thoughts on “Building Live Streaming App with Node.js and React”

  1. This blog is impressive specially for beginners .The whole bunch is gathered in a single page 👍👍👍

  2. Hey, i have rtsp and http streams of Ip cameras,i am able to view them using websockets but that is causing delay in video. Can you suggest me what to do.

    1. Hi Ayushi, could you help me with that? I need to make a simple page where i can live stream from rtsp url in reactjs. Im unable to solve it. Please and thank youp

  3. Hello – will this work for multicast UDP to be streamed to the browser? UDP could be converted to HLS or http?

    Thanks.

  4. Thank you for this guide!
    Can you tell me about performance of your approach? ex: 1000 active user per second

  5. I got as far as ‘Create media_server.js file’, which doesn’t tell me where to put it.

    Then it seems like it’s supposed to allow OBS to connect? But I don’t even see a webserver being started?

    This guide looked promising, but maybe it’s over my head. I’m used to making simple pages on apache2 servers. Any better guides than this?

    Also get some errors from npm about dependencies being missing. And not everything this guide requested I install even exists.

    OS: Debian 9

  6. I followed the instructions and could never get the streams to show up. I thought maybe I did an incorrect step. So I downloaded the git repo and started it up with the same outcome. The Server is receiving the stream, but the playable live stream never appears under “Live Streams” I just get a blank page.

  7. I’m looking into possibility of making OBS-like software using Electron. Would this kind of thing theoretically be possible with minimum involvement of C++ coding for node?
    I’m currently researching possibilities with screen/window/directshow captures using ffmpeg and mixing multiple video streams into one.

  8. I was trying to deploy to Heroku a working alike version of your application. It actually renders the page without css styling and with the following error: Uncaught SyntaxError: Unexpected token ‘<'. When I click Login button page goes white and nothing else happens. Do you know how to make it work?

      1. Thanks, I eventually configured it a little bit different and got it working on Heroku. I have a client listeing to port 3000 with create-react-app, and my server listening to the same port as your source code: 3333. I get to se the livestreams thumbnails. I didn’t use the ejs engine template as I had everything going on in my react front end. However the only problem that I have is that when I click in a thumbnail image, it takes me to the VideoPlayer component, but the page is in blank, and in the console I get the following error:

        GET http://localhost:8888/live/Uv8HUwMU/index.m3u8 404 (Not Found)

        I’ve checked the whole repo, and I left the server part that has to do with the streaming the same. But I don’t know what’s wrong. If you could give me a hint, I would be very much greatful. Best.

        1. Nevermind. It works perfectly in localhost. Only problem is, Heroku seems to block port 1935 and it obs fails to even connect to heroku.

          1. @Diego Salas heroku will provide you a dynamic port in env.PORT variable, use that instead of hardcoding 1935 port like this.
            const port = env.PORT || 1935;

        2. please can i have a look at your code, i am also trying to deploy to heroku and am having issues with the path to the ffmpeg even though i have add ffmpeg as a buildpack

  9. Love the project; it works as intended! Is there a possibility of having a Public Page with all of the stream listed, but that does not require to login or register? Perhaps, the public page can be on a different port than 3333…

  10. This was almost a good guide, but not sure why the stream is not working and OBS and this guide don’t help much in the realm of troubleshooting.

    Looks nice, but based on commits from repo and the steps made with no explanation this guide is best for those who are familiar with every line of code used and not so much for new people.

  11. Hi, I tring, but it apper an error:

    [rtmp play] Stream not found. id=31MPQJFQ streamPath=/live/ streamId=1

    How to fix this?

  12. Hi,

    Congratulations and thanks for your work.
    I’m trying to run it on the computer. I would fix some mistakes. But ffmpeg does not start. I couldn’t fix this error. I am reviewing the codes. but I couldn’t find using require (‘@ ffmpeg-installer / ffmpeg’) section. Can you give an idea?

    Thanks.

  13. I created everything just right. In the first test everything went well with the obs connecting, but then it is connecting and disconnecting automatically and infinitely

    1. Great post, had no issues setting up following the tutorial and cross referencing your github repo for it. Thank you!

  14. hello i have this error : (node:73404) UnhandledPromiseRejectionWarning: Error: spawn /usr/local/Cellar/ffmpeg EACCES
    could you help me ?
    thx

  15. Hello great tutorial thatk you it works perfect on localhost but if i but in real server (ubuntu nginx) it creates the folder under live with stream_key but wont create any file inside it OBS wont fail and log from server noting special idk whats wrong any idea ? ffmpeg ver 4.3-2

  16. This blog post was really helpful, I was able to create a live stream server for my NGO.
    I will like to know how to scale the live streaming to support more users on a Digital Ocean Droplet (Say up to 2000 users at a time)

  17. Pretty good tutorial,
    But how to protect the stream from reading?
    So only registered / paid subscription users can watch it.

    Thanks.

    1. On the audience route to the livestream, just make sure the loggedInUser() function is called. (I feel like I saw it in there, if it wasn’t that route, it was the redirect after login… do the same thing). I’m about to fork this and toy around. I’ll update with my findings.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.