1560066980
In this tutorial, I’ll show how you can build your own video 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 not interested in going further and just want to explore the code, it’s available in this repository. I am going to break down this tutorial into five parts.
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
$ npm init
and install these dependencies.
$ npm install axios bcrypt-nodejs body-parser bootstrap config
connect-ensure-login connect-flash cookie-parser ejs express
express-session mongoose passport passport-local request
session-file-store --save-dev
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.
const express = require('express'),
Session = require('express-session'),
bodyParse = require('body-parser'),
mongoose = require('mongoose'),
middleware = require('connect-ensure-login'),
FileStore = require('session-file-store')(Session),
config = require('./config/default'),
flash = require('connect-flash'),
port = 3333,
app = express();
mongoose.connect('mongodb://127.0.0.1/nodeStream' , { useNewUrlParser: true });
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, './views'));
app.use(express.static('public'));
app.use(flash());
app.use(require('cookie-parser')());
app.use(bodyParse.urlencoded({extended: true}));
app.use(bodyParse.json({extended: true}));
app.use(Session({
store: new FileStore({
path : './server/sessions'
}),
secret: config.server.secret,
maxAge : Date().now + (60 * 1000 * 30)
}));
app.get('*', middleware.ensureLoggedIn(), (req, res) => {
res.render('index');
});
app.listen(port, () => console.log(`App listening on ${port}!`));
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.
const passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
User = require('../database/Schema').User,
shortid = require('shortid');
passport.serializeUser( (user, cb) => {
cb(null, user);
});
passport.deserializeUser( (obj, cb) => {
cb(null, obj);
});
// Passport strategy for handling user registration
passport.use('localRegister', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
(req, email, password, done) => {
User.findOne({$or: [{email: email}, {username: req.body.username}]}, (err, user) => {
if (err)
return done(err);
if (user) {
if (user.email === email) {
req.flash('email', 'Email is already taken');
}
if (user.username === req.body.username) {
req.flash('username', 'Username is already taken');
}
return done(null, false);
} else {
let user = new User();
user.email = email;
user.password = user.generateHash(password);
user.username = req.body.username;
user.stream_key = shortid.generate();
user.save( (err) => {
if (err)
throw err;
return done(null, user);
});
}
});
}));
// Passport strategy for authenticating users
passport.use('localLogin', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
(req, email, password, done) => {
User.findOne({'email': email}, (err, user) => {
if (err)
return done(err);
if (!user)
return done(null, false, req.flash('email', 'Email doesn\'t exist.'));
if (!user.validPassword(password))
return done(null, false, req.flash('password', 'Oops! Wrong password.'));
return done(null, user);
});
}));
module.exports = passport;
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.
let mongoose = require('mongoose'),
bcrypt = require('bcrypt-nodejs'),
shortid = require('shortid'),
Schema = mongoose.Schema;
let UserSchema = new Schema({
username: String,
email : String,
password: String,
stream_key : String,
});
UserSchema.methods.generateHash = (password) => {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
UserSchema.methods.validPassword = function(password){
return bcrypt.compareSync(password, this.password);
};
UserSchema.methods.generateStreamKey = () => {
return shortid.generate();
};
module.exports = UserSchema;
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.
let mongoose = require('mongoose');
exports.User = mongoose.model('User', require('./UserSchema'));
Now that we have defined our passport strategies, added user schema and created a model from it, let’s initialize passport in app.js.
// Add on the top next to imports
const passport = require('./auth/passport');
app.use(passport.initialize());
app.use(passport.session());
Also, register these routes in the app.js file.
// Register app routes
app.use('/login', require('./routes/login'));
app.use('/register', require('./routes/register'));
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.
const express = require('express'),
router = express.Router(),
passport = require('passport');
router.get('/',
require('connect-ensure-login').ensureLoggedOut(),
(req, res) => {
res.render('login', {
user : null,
errors : {
email : req.flash('email'),
password : req.flash('password')
}
});
});
router.post('/', passport.authenticate('localLogin', {
successRedirect : '/',
failureRedirect : '/login',
failureFlash : true
}));
module.exports = router;
const express = require('express'),
router = express.Router(),
passport = require('passport');
router.get('/',
require('connect-ensure-login').ensureLoggedOut(),
(req, res) => {
res.render('register', {
user : null,
errors : {
username : req.flash('username'),
email : req.flash('email')
}
});
});
router.post('/',
require('connect-ensure-login').ensureLoggedOut(),
passport.authenticate('localRegister', {
successRedirect : '/',
failureRedirect : '/register',
failureFlash : true
})
);
module.exports = router;
We are using ejs templating engine. Add login.ejs and register.ejs template to views directory and add the following code.
<!doctype html>
<html lang="en">
<% include header.ejs %>
<body>
<% include navbar.ejs %>
<div class="container app mt-5">
<h4>Login</h4>
<hr class="my-4">
<div class="row">
<form action="/login" method="post" class="col-xs-12 col-sm-12 col-md-8 col-lg-6">
<div class="form-group">
<label>Email address</label>
<input type="email" name="email" class="form-control" placeholder="Enter email" required>
<% if (errors.email.length) { %>
<small class="form-text text-danger"><%= errors.email %></small>
<% } %>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control" placeholder="Password" required>
<% if (errors.password.length) { %>
<small class="form-text text-danger"><%= errors.password %></small>
<% } %>
</div>
<div class="form-group">
<div class="leader">
Don't have an account? Register <a href="/register">here</a>.
</div>
</div>
<button type="submit" class="btn btn-dark btn-block">Login</button>
</form>
</div>
</div>
<% include footer.ejs %>
</body>
</html>
<!doctype html>
<html lang="en">
<% include header.ejs %>
<body>
<% include navbar.ejs %>
<div class="container app mt-5">
<h4>Register</h4>
<hr class="my-4">
<div class="row">
<form action="/register"
method="post"
class="col-xs-12 col-sm-12 col-md-8 col-lg-6">
<div class="form-group">
<label>Username</label>
<input type="text" name="username" class="form-control" placeholder="Enter username" required>
<% if (errors.username.length) { %>
<small class="form-text text-danger"><%= errors.username %></small>
<% } %>
</div>
<div class="form-group">
<label>Email address</label>
<input type="email" name="email" class="form-control" placeholder="Enter email" required>
<% if (errors.email.length) { %>
<small class="form-text text-danger"><%= errors.email %></small>
<% } %>
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control" placeholder="Password" required>
</div>
<div class="form-group">
<div class="leader">
Have an account? Login <a href="/login">here</a>.
</div>
</div>
<button type="submit" class="btn btn-dark btn-block">Register</button>
</form>
</div>
</div>
<% include footer.ejs %>
</body>
</html>
We are pretty much done with authentication. Now we will move onto the next part of this tutorial and set up our 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.
$ which ffmpeg
# /usr/bin/ffmpeg
node-media-server
recommends ffmpeg 4.x version. You can check your version by running this command.
$ ffmpeg --version
# ffmpeg version 4.1.3-0york1~18.04 Copyright (c) 2000-2019 the
# FFmpeg developers built with gcc 7 (Ubuntu 7.3.0-27ubuntu1~18.04)
If you don’t have ffmpeg installed and running Ubuntu, you can install it by running these commands from the terminal.
# Add PPA. If you install without PPA, it will install
# ffmpeg version 3.x.
$ sudo add-apt-repository ppa:jonathonf/ffmpeg-4
$ sudo apt install ffmpeg
If you are running Windows, you can download ffmpeg windows builds. Add this config file to your project.
const config = {
server: {
secret: 'kjVkuti2xAyF3JGCzSZTk0YWM5JhI9mgQW4rytXc'
},
rtmp_server: {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: true,
ping: 60,
ping_timeout: 30
},
http: {
port: 8888,
mediaroot: './server/media',
allow_origin: '*'
},
trans: {
ffmpeg: '/usr/bin/ffmpeg',
tasks: [
{
app: 'live',
hls: true,
hlsFlags: '[hls_time=2:hls_list_size=3:hls_flags=delete_segments]',
dash: true,
dashFlags: '[f=dash:window_size=3:extra_window_size=5]'
}
]
}
}
};
module.exports = config;
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.
const config = {
....
trans: {
ffmpeg: 'D:/ffmpeg/bin/ffmpeg.exe',
...
}
}
};
Also, install node-media-server
by running
$ npm install node-media-server --save
Create media_server.js file
and add this code.
const NodeMediaServer = require('node-media-server'),
config = require('./config/default').rtmp_server;
nms = new NodeMediaServer(config);
nms.on('prePublish', async (id, StreamPath, args) => {
let stream_key = getStreamKeyFromStreamPath(StreamPath);
console.log('[NodeEvent on prePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
});
const getStreamKeyFromStreamPath = (path) => {
let parts = path.split('/');
return parts[parts.length - 1];
};
module.exports = nms;
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.
// Add this on the top of app.js file
// next to all imports
const node_media_server = require('./media_server');
// and call run() method at the end
// file where we start our web server
node_media_server.run();
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.
{
"live": {
"0wBic-qV4": {
"publisher": {
"app": "live",
"stream": "0wBic-qV4",
"clientId": "WMZTQAEY",
"connectCreated": "2019-05-12T16:13:05.759Z",
"bytes": 33941836,
"ip": "::ffff:127.0.0.1",
"audio": {
"codec": "AAC",
"profile": "LC",
"samplerate": 44100,
"channels": 2
},
"video": {
"codec": "H264",
"width": 1920,
"height": 1080,
"profile": "High",
"level": 4.2,
"fps": 60
}
},
"subscribers": [
{
"app": "live",
"stream": "0wBic-qV4",
"clientId": "GNJ9JYJC",
"connectCreated": "2019-05-12T16:13:05.985Z",
"bytes": 33979083,
"ip": "::ffff:127.0.0.1",
"protocol": "rtmp"
}
]
}
}
}
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.
// Add import at the start of file
const User = require('./database/Schema').User;
nms.on('prePublish', async (id, StreamPath, args) => {
let stream_key = getStreamKeyFromStreamPath(StreamPath);
console.log('[NodeEvent on prePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
User.findOne({stream_key: stream_key}, (err, user) => {
if (!err) {
if (!user) {
let session = nms.getSession(id);
session.reject();
} else {
// do stuff
}
}
});
});
const getStreamKeyFromStreamPath = (path) => {
let parts = path.split('/');
return parts[parts.length - 1];
};
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.
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.
$ npm install @babel/core @babel/preset-env @babel/preset-react
babel-loader css-loader file-loader mini-css-extract-plugin
node-sass sass-loader style-loader url-loader webpack webpack-cli
react react-dom react-router-dom video.js jquery bootstrap history
popper.js
Add this webpack config to your project.
const path = require('path');
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const devMode = process.env.NODE_ENV !== 'production';
const webpack = require('webpack');
module.exports = {
entry : './client/index.js',
output : {
filename : 'bundle.js',
path : path.resolve(__dirname, 'public')
},
module : {
rules : [
{
test: /\.s?[ac]ss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { url: false, sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } }
],
},
{
test: /\.js$/,
exclude: /node_modules/,
use: "babel-loader"
},
{
test: /\.woff($|\?)|\.woff2($|\?)|\.ttf($|\?)|\.eot($|\?)|\.svg($|\?)/,
loader: 'url-loader'
},
{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'file-loader',
options: {
outputPath: '/',
},
}],
},
]
},
devtool: 'source-map',
plugins: [
new MiniCssExtractPlugin({
filename: "style.css"
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery'
})
],
mode : devMode ? 'development' : 'production',
watch : devMode,
performance: {
hints: process.env.NODE_ENV === 'production' ? "warning" : false
},
};
Add an index.js
file with the following code.
import React from "react";
import ReactDOM from 'react-dom';
import {BrowserRouter} from 'react-router-dom';
import 'bootstrap';
require('./index.scss');
import Root from './components/Root.js';
if(document.getElementById('root')){
ReactDOM.render(
<BrowserRouter>
<Root/>
</BrowserRouter>,
document.getElementById('root')
);
}
2
3
4
5
6
7
8
@import '~bootstrap/dist/css/bootstrap.css';
@import '~video.js/dist/video-js.css';
@import url('https://fonts.googleapis.com/css?family=Dosis');
html,body{
font-family: 'Dosis', sans-serif;
}
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.
import React from "react";
import {Router, Route} from 'react-router-dom';
import Navbar from './Navbar';
import LiveStreams from './LiveStreams';
import Settings from './Settings';
import VideoPlayer from './VideoPlayer';
const customHistory = require("history").createBrowserHistory();
export default class Root extends React.Component {
constructor(props){
super(props);
}
render(){
return (
<Router history={customHistory} >
<div>
<Navbar/>
<Route exact path="/" render={props => (
<LiveStreams {...props} />
)}/>
<Route exact path="/stream/:username" render={(props) => (
<VideoPlayer {...props}/>
)}/>
<Route exact path="/settings" render={props => (
<Settings {...props} />
)}/>
</div>
</Router>
)
}
}
component renders a react to hold three sub components. component will render all the live streams. will render video.js player components. component will provide an interface for generating a new streaming key.
Create LiveStreams.js component.
import React from 'react';
import axios from 'axios';
import {Link} from 'react-router-dom';
import './LiveStreams.scss';
import config from '../../server/config/default';
export default class Navbar extends React.Component {
constructor(props) {
super(props);
this.state = {
live_streams: []
}
}
componentDidMount() {
this.getLiveStreams();
}
getLiveStreams() {
axios.get('http://127.0.0.1:' + config.rtmp_server.http.port + '/api/streams')
.then(res => {
let streams = res.data;
if (typeof (streams['live'] !== 'undefined')) {
this.getStreamsInfo(streams['live']);
}
});
}
getStreamsInfo(live_streams) {
axios.get('/streams/info', {
params: {
streams: live_streams
}
}).then(res => {
this.setState({
live_streams: res.data
}, () => {
console.log(this.state);
});
});
}
render() {
let streams = this.state.live_streams.map((stream, index) => {
return (
<div className="stream col-xs-12 col-sm-12 col-md-3 col-lg-4" key={index}>
<span className="live-label">LIVE</span>
<Link to={'/stream/' + stream.username}>
<div className="stream-thumbnail">
<img src={'/thumbnails/' + stream.stream_key + '.png'}/>
</div>
</Link>
<span className="username">
<Link to={'/stream/' + stream.username}>
{stream.username}
</Link>
</span>
</div>
);
});
return (
<div className="container mt-5">
<h4>Live Streams</h4>
<hr className="my-4"/>
<div className="streams row">
{streams}
</div>
</div>
)
}
}
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.
const express = require('express'),
router = express.Router(),
User = require('../database/Schema').User;
router.get('/info',
require('connect-ensure-login').ensureLoggedIn(),
(req, res) => {
if(req.query.streams){
let streams = JSON.parse(req.query.streams);
let query = {$or: []};
for (let stream in streams) {
if (!streams.hasOwnProperty(stream)) continue;
query.$or.push({stream_key : stream});
}
User.find(query,(err, users) => {
if (err)
return;
if (users) {
res.json(users);
}
});
}
});
module.exports = router;
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.
app.use('/streams', require('./routes/streams'));
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.
import React from 'react';
import videojs from 'video.js'
import axios from 'axios';
import config from '../../server/config/default';
export default class VideoPlayer extends React.Component {
constructor(props) {
super(props);
this.state = {
stream: false,
videoJsOptions: null
}
}
componentDidMount() {
axios.get('/user', {
params: {
username: this.props.match.params.username
}
}).then(res => {
this.setState({
stream: true,
videoJsOptions: {
autoplay: false,
controls: true,
sources: [{
src: 'http://127.0.0.1:' + config.rtmp_server.http.port + '/live/' + res.data.stream_key + '/index.m3u8',
type: 'application/x-mpegURL'
}],
fluid: true,
}
}, () => {
this.player = videojs(this.videoNode, this.state.videoJsOptions, function onPlayerReady() {
console.log('onPlayerReady', this)
});
});
})
}
componentWillUnmount() {
if (this.player) {
this.player.dispose()
}
}
render() {
return (
<div className="row">
<div className="col-xs-12 col-sm-12 col-md-10 col-lg-8 mx-auto mt-5">
{this.state.stream ? (
<div data-vjs-player>
<video ref={node => this.videoNode = node} className="video-js vjs-big-play-centered"/>
</div>
) : ' Loading ... '}
</div>
</div>
)
}
}
On component mount, we retrieve the user’s streaming key to initiate an HLS stream inside video.js player.
Create Settings.js component.
import React from 'react';
import axios from 'axios';
export default class Navbar extends React.Component {
constructor(props){
super(props);
this.state = {
stream_key : ''
};
this.generateStreamKey = this.generateStreamKey.bind(this);
}
componentDidMount() {
this.getStreamKey();
}
generateStreamKey(e){
axios.post('/settings/stream_key')
.then(res => {
this.setState({
stream_key : res.data.stream_key
});
})
}
getStreamKey(){
axios.get('/settings/stream_key')
.then(res => {
this.setState({
stream_key : res.data.stream_key
});
})
}
render() {
return (
<React.Fragment>
<div className="container mt-5">
<h4>Streaming Key</h4>
<hr className="my-4"/>
<div className="col-xs-12 col-sm-12 col-md-8 col-lg-6">
<div className="row">
<h5>{this.state.stream_key}</h5>
</div>
<div className="row">
<button
className="btn btn-dark mt-2"
onClick={this.generateStreamKey}>
Generate a new key
</button>
</div>
</div>
</div>
<div className="container mt-5">
<h4>How to Stream</h4>
<hr className="my-4"/>
<div className="col-12">
<div className="row">
<p>
You can use <a target="_blank" href="https://obsproject.com/">OBS</a> or
<a target="_blank" href="https://www.xsplit.com/">XSplit</a> to Live stream. If you're
using OBS, go to Settings > Stream and select Custom from service dropdown. Enter
<b>rtmp://127.0.0.1:1935/live</b> in server input field. Also, add your stream key.
Click apply to save.
</p>
</div>
</div>
</div>
</React.Fragment>
)
}
}
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 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.
const express = require('express'),
router = express.Router(),
User = require('../database/Schema').User,
shortid = require('shortid');
router.get('/stream_key',
require('connect-ensure-login').ensureLoggedIn(),
(req, res) => {
User.findOne({email: req.user.email}, (err, user) => {
if (!err) {
res.json({
stream_key: user.stream_key
})
}
});
});
router.post('/stream_key',
require('connect-ensure-login').ensureLoggedIn(),
(req, res) => {
User.findOneAndUpdate({
email: req.user.email
}, {
stream_key: shortid.generate()
}, {
upsert: true,
new: true,
}, (err, user) => {
if (!err) {
res.json({
stream_key: user.stream_key
})
}
});
});
module.exports = router;
We using shortid module for generating unique strings. Register these routes in the app.js file.
app.use('/settings', require('./routes/settings'));
In components, we are displaying thumbnail images for live streams.
render() {
let streams = this.state.live_streams.map((stream, index) => {
return (
<div className="stream col-xs-12 col-sm-12 col-md-3 col-lg-4" key={index}>
<span className="live-label">LIVE</span>
<Link to={'/stream/' + stream.username}>
<div className="stream-thumbnail">
<img src={'/thumbnails/' + stream.stream_key + '.png'}/>
</div>
</Link>
<span className="username">
<Link to={'/stream/' + stream.username}>
{stream.username}
</Link>
</span>
</div>
);
});
return (
<div className="container mt-5">
<h4>Live Streams</h4>
<hr className="my-4"/>
<div className="streams row">
{streams}
</div>
</div>
)
}
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
.
const spawn = require('child_process').spawn,
config = require('../config/default'),
cmd = config.rtmp_server.trans.ffmpeg;
const generateStreamThumbnail = (stream_key) => {
const args = [
'-y',
'-i', 'http://127.0.0.1:8888/live/'+stream_key+'/index.m3u8',
'-ss', '00:00:01',
'-vframes', '1',
'-vf', 'scale=-2:300',
'server/thumbnails/'+stream_key+'.png',
];
spawn(cmd, args, {
detached: true,
stdio: 'ignore'
}).unref();
};
module.exports = {
generateStreamThumbnail : generateStreamThumbnail
};
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.
nms.on('prePublish', async (id, StreamPath, args) => {
let stream_key = getStreamKeyFromStreamPath(StreamPath);
console.log('[NodeEvent on prePublish]', `id=${id} StreamPath=${StreamPath} args=${JSON.stringify(args)}`);
User.findOne({stream_key: stream_key}, (err, user) => {
if (!err) {
if (!user) {
let session = nms.getSession(id);
session.reject();
} else {
helpers.generateStreamThumbnail(stream_key);
}
}
});
});
To generate fresh thumbnails, we will run a cron job and call this helper method from it.
const CronJob = require('cron').CronJob,
request = require('request'),
helpers = require('../helpers/helpers'),
config = require('../config/default'),
port = config.rtmp_server.http.port;
const job = new CronJob('*/5 * * * * *', function () {
request
.get('http://127.0.0.1:' + port + '/api/streams', function (error, response, body) {
let streams = JSON.parse(body);
if (typeof (streams['live'] !== undefined)) {
let live_streams = streams['live'];
for (let stream in live_streams) {
if (!live_streams.hasOwnProperty(stream)) continue;
helpers.generateStreamThumbnail(stream);
}
}
});
}, null, true);
module.exports = job;
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.
// Add import at the start of file
const thumbnail_generator = require('./cron/thumbnails');
// Call start method at the end of file
thumbnail_generator.start();
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.
#node-js #reactjs
1598839687
If you are undertaking a mobile app development for your start-up or enterprise, you are likely wondering whether to use React Native. As a popular development framework, React Native helps you to develop near-native mobile apps. However, you are probably also wondering how close you can get to a native app by using React Native. How native is React Native?
In the article, we discuss the similarities between native mobile development and development using React Native. We also touch upon where they differ and how to bridge the gaps. Read on.
Let’s briefly set the context first. We will briefly touch upon what React Native is and how it differs from earlier hybrid frameworks.
React Native is a popular JavaScript framework that Facebook has created. You can use this open-source framework to code natively rendering Android and iOS mobile apps. You can use it to develop web apps too.
Facebook has developed React Native based on React, its JavaScript library. The first release of React Native came in March 2015. At the time of writing this article, the latest stable release of React Native is 0.62.0, and it was released in March 2020.
Although relatively new, React Native has acquired a high degree of popularity. The “Stack Overflow Developer Survey 2019” report identifies it as the 8th most loved framework. Facebook, Walmart, and Bloomberg are some of the top companies that use React Native.
The popularity of React Native comes from its advantages. Some of its advantages are as follows:
Are you wondering whether React Native is just another of those hybrid frameworks like Ionic or Cordova? It’s not! React Native is fundamentally different from these earlier hybrid frameworks.
React Native is very close to native. Consider the following aspects as described on the React Native website:
Due to these factors, React Native offers many more advantages compared to those earlier hybrid frameworks. We now review them.
#android app #frontend #ios app #mobile app development #benefits of react native #is react native good for mobile app development #native vs #pros and cons of react native #react mobile development #react native development #react native experience #react native framework #react native ios vs android #react native pros and cons #react native vs android #react native vs native #react native vs native performance #react vs native #why react native #why use react native
1589634120
Never heard of Node.js? Node.js is an accessible asynchronous environment based on Javascript which contains several core modules helpful for performing various tasks. Node.js is famous worldwide due to its efficiency and being open-source, it brings a lot to the table. Node.js allows the developers to handle multiple requests on a single thread and thereby allowing them more breathing space.
Node.js handles data using two approaches – Buffered and Streamed. In the buffered approach, you have to write the entire data before the receiver may read it. Such an approach doesn’t support its asynchronous paradigm. When it comes to the Streamed approach, the information starts the interpreting process as soon as you enter it.
Before you read further, we would like to inform you that this article is about streams. Streams are an essential part of the Node.js environment. What it stream, and what do they do? What are the different types of streams? We have tried to cover several important questions that may help you in understanding Node.js Streams. Let’s get started.
#nodejs #streams in node.js #using streams in node js #node.js streams #node.js tutorial #data streams
1623231790
Nowadays, people don’t stick around a TV and are glued to it in order to watch their favorite movie, live football match in their living room which can convert into a cinema hall or a stadium. With all the attractiveness and attention of cable television that it has obtained in recent years, the interest is slowly disappearing among the users with the arrival of live video streaming apps. The most interesting fact is that one can easily enjoy their favorite TV shows, movies without any kind of hindrance often in the form of commercials. Let’s make one thing clear.
These live video streaming apps aren’t just meant for entertainment and fun purposes. It can be quite useful for businesses as per as personal purposes. For example, it can prove to be quite vital in case of live video conferencing, offering live tutorials by the teachers to the students and another type of demonstration, providing training lessons, teaching skills, and also act as a safety device to keep a keen eye on the pets or children. So, let’s get deeper into the features and cost of developing a live video streaming app.
FEATURES OF LIVE VIDEO STREAMING APP
1. Creation of Registration
The registration of the users is the most basic and first requirement whenever the customer is looking for a live video streaming app. The user needs to be provided with different options in order to register his account. It may include options such as registration through mobile number using verifications through OTP or email address. Along with this, you can also introduce the option of registering to the app via social media platforms like Facebook, etc. And also keep in mind that you should include the option where any user can easily recover his forgotten password. It is the most crucial part of phone app development.
2. Creating a Profile of User
Having a wide range of inventory that consists of the personal information of the signed users is always worthy. So, your next step is an app developer is to actually create a platform where your customer can easily fill in his profile which includes his name, mobile number, email address, preference of genre of videos like movies, sports, etc. subscription list, and all.
3. Search Feature and Suggested Videos
The mobile app developers need to attach a search box within the app which allows the user to easily search for any particular video in a given category. Now, these search categories have to be quite wider. They need to be based on the age factor, interest, location as well as language of the broadcast as well as number of total views and so on.
4. Feedback and Review
You know that the popularity of any video often increases by the total number of users watching it and it also offers their valuable feedback in a particular form of comments and ratings. The users often provide their feedback whether they liked the video or not and also make comments on the quality of the video and even if the video ran smoothly without any type of buffering. Any mobile application development company developing such an app needs to inculcate this feature.
5. Settings based on Sharing
In this current era of the internet, most of the ambiguous videos often become viral in a short span of time like a wildfire. How is it actually be possible for any video to get viewed by millions of people within a few minutes after its upload? Well, it occurs due to the sharing of the video on different platforms like WhatsApp, Facebook, Twitter, Instagram etc. But most of the users like to maintain their own privacy when it comes to sharing of the content. So, you have to consider that you provide some kind of privacy-related settings. Users should be allowed to choose whether he wishes to share the details or not.
6. Tracking based on Geo-Location
Since you are actually creating apps for live video streaming, geolocation tracking can be added which is quite helpful. It allows you to trace or obtain the location of the current broadcast easily. One can easily cite the instance of Facebook Live which allows the user to share the current location with their friends easily.
7. Making Regular Notifications
One of the key steps you should take in order to keep boosting your traffic is implementing Push Notification during app development. You know that if a particular user likes any type of video category, like Sports, then he would like to wait for the uploading of next video of the match that he can watch. So, if the user has subscribed, then you can easily send him particular notification information that a relevant new video has appeared and been uploaded.
COST OF DEVELOPMENT OF LIVE VIDEO STREAMING APP
The development of such an app requires a development team that is involved in a project that often consists of the same professionals that are often involved in any app design. There is a project manager who manages as well as supervises the entire project and even designates the different roles to the team members. Then there are app developers who have experience in iOS and Android app development.
There is the vital contribution of UX and UI designers as well as backend team members and then the quality app testers which are also required. All these different people play their own decisive role in making a successful live video streaming app.
The entire cost of the development of such as an actually depends on the mobile app development company you are hiring. The cost of hiring the company and their professionals, time of development, features to be included etc. plays a key role in finding the right cost of development. An approximate figure of development of live video streaming app starts from $12,000 to $15,000. However, the final cost can be dependent on different components which are required to develop such an app. There is a cost involved with the server as well as third-party applications which isn’t included in this price.
CONCLUSION
Live video streaming app is the latest trend in the market. At present, most people like to remain connected with the things that are happening all over the world without being around their television sets, but through these wonderful apps. Also, these apps are not just a mere source of entertainment, but they also offer valuable information and helps in taking the business forward. In the years to come, the demand for these apps will certainly rise, and mobile app developers have to find new innovative methods and techniques to make things much more interesting to the end-users.
#live streaming app cost #video streaming app #cost to build a live streaming app #live streaming app development
1622719015
Front-end web development has been overwhelmed by JavaScript highlights for quite a long time. Google, Facebook, Wikipedia, and most of all online pages use JS for customer side activities. As of late, it additionally made a shift to cross-platform mobile development as a main technology in React Native, Nativescript, Apache Cordova, and other crossover devices.
Throughout the most recent couple of years, Node.js moved to backend development as well. Designers need to utilize a similar tech stack for the whole web project without learning another language for server-side development. Node.js is a device that adjusts JS usefulness and syntax to the backend.
Node.js isn’t a language, or library, or system. It’s a runtime situation: commonly JavaScript needs a program to work, however Node.js makes appropriate settings for JS to run outside of the program. It’s based on a JavaScript V8 motor that can run in Chrome, different programs, or independently.
The extent of V8 is to change JS program situated code into machine code — so JS turns into a broadly useful language and can be perceived by servers. This is one of the advantages of utilizing Node.js in web application development: it expands the usefulness of JavaScript, permitting designers to coordinate the language with APIs, different languages, and outside libraries.
Of late, organizations have been effectively changing from their backend tech stacks to Node.js. LinkedIn picked Node.js over Ruby on Rails since it took care of expanding responsibility better and decreased the quantity of servers by multiple times. PayPal and Netflix did something comparative, just they had a goal to change their design to microservices. We should investigate the motivations to pick Node.JS for web application development and when we are planning to hire node js developers.
The principal thing that makes Node.js a go-to environment for web development is its JavaScript legacy. It’s the most well known language right now with a great many free devices and a functioning local area. Node.js, because of its association with JS, immediately rose in ubiquity — presently it has in excess of 368 million downloads and a great many free tools in the bundle module.
Alongside prevalence, Node.js additionally acquired the fundamental JS benefits:
In addition, it’s a piece of a well known MEAN tech stack (the blend of MongoDB, Express.js, Angular, and Node.js — four tools that handle all vital parts of web application development).
This is perhaps the most clear advantage of Node.js web application development. JavaScript is an unquestionable requirement for web development. Regardless of whether you construct a multi-page or single-page application, you need to know JS well. On the off chance that you are now OK with JavaScript, learning Node.js won’t be an issue. Grammar, fundamental usefulness, primary standards — every one of these things are comparable.
In the event that you have JS designers in your group, it will be simpler for them to learn JS-based Node than a totally new dialect. What’s more, the front-end and back-end codebase will be basically the same, simple to peruse, and keep up — in light of the fact that they are both JS-based.
There’s another motivation behind why Node.js got famous so rapidly. The environment suits well the idea of microservice development (spilling stone monument usefulness into handfuls or many more modest administrations).
Microservices need to speak with one another rapidly — and Node.js is probably the quickest device in information handling. Among the fundamental Node.js benefits for programming development are its non-obstructing algorithms.
Node.js measures a few demands all at once without trusting that the first will be concluded. Many microservices can send messages to one another, and they will be gotten and addressed all the while.
Node.js was worked in view of adaptability — its name really says it. The environment permits numerous hubs to run all the while and speak with one another. Here’s the reason Node.js adaptability is better than other web backend development arrangements.
Node.js has a module that is liable for load adjusting for each running CPU center. This is one of numerous Node.js module benefits: you can run various hubs all at once, and the environment will naturally adjust the responsibility.
Node.js permits even apportioning: you can part your application into various situations. You show various forms of the application to different clients, in light of their age, interests, area, language, and so on. This builds personalization and diminishes responsibility. Hub accomplishes this with kid measures — tasks that rapidly speak with one another and share a similar root.
What’s more, Node’s non-hindering solicitation handling framework adds to fast, letting applications measure a great many solicitations.
Numerous designers consider nonconcurrent to be one of the two impediments and benefits of Node.js web application development. In Node, at whatever point the capacity is executed, the code consequently sends a callback. As the quantity of capacities develops, so does the number of callbacks — and you end up in a circumstance known as the callback damnation.
In any case, Node.js offers an exit plan. You can utilize systems that will plan capacities and sort through callbacks. Systems will associate comparable capacities consequently — so you can track down an essential component via search or in an envelope. At that point, there’s no compelling reason to look through callbacks.
So, these are some of the top benefits of Nodejs in web application development. This is how Nodejs is contributing a lot to the field of web application development.
I hope now you are totally aware of the whole process of how Nodejs is really important for your web project. If you are looking to hire a node js development company in India then I would suggest that you take a little consultancy too whenever you call.
Good Luck!
#node.js development company in india #node js development company #hire node js developers #hire node.js developers in india #node.js development services #node.js development
1616671994
If you look at the backend technology used by today’s most popular apps there is one thing you would find common among them and that is the use of NodeJS Framework. Yes, the NodeJS framework is that effective and successful.
If you wish to have a strong backend for efficient app performance then have NodeJS at the backend.
WebClues Infotech offers different levels of experienced and expert professionals for your app development needs. So hire a dedicated NodeJS developer from WebClues Infotech with your experience requirement and expertise.
So what are you waiting for? Get your app developed with strong performance parameters from WebClues Infotech
For inquiry click here: https://www.webcluesinfotech.com/hire-nodejs-developer/
Book Free Interview: https://bit.ly/3dDShFg
#hire dedicated node.js developers #hire node.js developers #hire top dedicated node.js developers #hire node.js developers in usa & india #hire node js development company #hire the best node.js developers & programmers