Skip to content

Commit

Permalink
initial commit on v2
Browse files Browse the repository at this point in the history
  • Loading branch information
johndavedecano committed Mar 25, 2020
0 parents commit 216aeae
Show file tree
Hide file tree
Showing 14 changed files with 2,572 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules
*.log
Dockerfile
docker-compose.yml
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
NODE_ENV=development

# Where to store the playlist files
MEDIA_ROOT=./media

# Where is ffmpeg executable located. Run which ffmpeg
FFMPEG_PATH=/usr/bin/ffmpeg

# URL to notify when a stream starts
PUBLISH_START_NOTIFY_URL=

# URL to notify when a stream stoppes
PUBLISH_STOP_NOTIFY_URL=
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
*.log
33 changes: 33 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM node:10-alpine

WORKDIR /app

RUN apk upgrade -U \
&& apk add ca-certificates ffmpeg libva-intel-driver \
&& rm -rf /var/cache/*

COPY . /app

COPY package.json package.json

COPY yarn.lock yarn.lock

RUN yarn install --silent

RUN yarn global add nodemon

RUN echo "FFMPEG installed at: $(which ffmpeg)"
RUN echo "NODEJS installed at: $(which node)"
RUN echo "FFMPEG version is: $(ffmpeg -version)"
RUN echo "NODEJS version is: $(node -v)"
RUN echo "YARN version is: $(yarn -v)"

RUN which ffmpeg

RUN export FFMPEG_PATH=$(which ffmpeg)

RUN export MEDIA_ROOT=/app/media

EXPOSE 8000

EXPOSE 1935
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# NodeJS RTMP HLS Server

Based on node-media-server. A live streaming server that allow adaptive bitrate for HLS.

## Making it work using docker-compose

```
docker-compse build
docker-compose up
docker-compose down
```

## Making it work using regular nodejs

You have to make sure that you have ffmpeg installed on your machine

```
cp .env.example .env
yarn install
yarn start:dev # development
yarn start # production
```

### Environment Variables

```
NODE_ENV=development
# Where to store the playlist files
MEDIA_ROOT=./media
# Where is ffmpeg executable located. Run which ffmpeg
FFMPEG_PATH=/usr/bin/ffmpeg
# URL to notify when a stream starts.
# Usually used for authentication.
PUBLISH_START_NOTIFY_URL=
# URL to notify when a stream stoppes
PUBLISH_STOP_NOTIFY_URL=
```

### Installing FFMPEG on Centos

```
wget https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
tar -xf ffmpeg-release-amd64-static.tar.xz
sudo mkdir -p /usr/local/bin/ffmpeg
sudo mv ./ffmpeg-4.2.2-amd64-static/* /usr/local/bin/ffmpeg
sudo ln -s /usr/local/bin/ffmpeg/ffmpeg /usr/bin/ffmpeg
sudo ln -s /usr/local/bin/ffmpeg/ffprobe /usr/bin/ffprobe
# Verify installation
which ffmpeg && ffmpeg -version
```

## From OBS

```
Settings -> Stream
Stream Type : Custom Streaming Server
URL : rtmp://localhost:1935/live
Stream key : STREAM_NAME
```

## Accessing the live stream

```
HLS - http://localhost:8000/live/STREAM_NAME/index.m3u8
FLV - http://localhost:8000/live/STREAM_NAME.flv
WSS - ws://localhost:8000/live/STREAM_NAME.flv
RTMP - ws://localhost:8000/live/STREAM_NAME
```

## Cluster Mode

Ask me how
15 changes: 15 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: '2'
services:
media-server:
build:
context: ./
volumes:
- ./src:/app/src
- ./media:/app/media
ports:
- 8000:8000
- 1935:1935
command: ['yarn', 'start:dev']
environment:
PUBLISH_START_NOTIFY_URL: ''
PUBLISH_STOP_NOTIFY_URL: ''
19 changes: 19 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "node-rtmp-hls",
"version": "1.0.0",
"main": "./src",
"license": "MIT",
"dependencies": {
"axios": "^0.19.0",
"dotenv": "^8.2.0",
"lodash": "^4.17.15",
"mkdirp": "^0.5.1",
"node-media-server": "^2.1.4",
"nodemon": "^2.0.2",
"pm2": "^4.2.3"
},
"scripts": {
"start": "./node_modules/.bin/pm2 start src/index.js --name=node-node-rtmp-hls",
"start:dev": "./node_modules/.bin/nodemon src/index.js"
}
}
128 changes: 128 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
module.exports = {
rtmp: {
port: 1935,
chunk_size: 60000,
gop_cache: false,
ping: 30,
ping_timeout: 60,
},
http: {
port: 8000,
allow_origin: '*',
mediaroot: process.env.MEDIA_ROOT,
},
relay: {
ffmpeg: process.env.FFMPEG_PATH,
tasks: [
{
app: 'stream',
mode: 'push',
edge: 'rtmp://127.0.0.1/hls_1080p',
},
{
app: 'stream',
mode: 'push',
edge: 'rtmp://127.0.0.1/hls_720p',
},
{
app: 'stream',
mode: 'push',
edge: 'rtmp://127.0.0.1/hls_480p',
},
{
app: 'stream',
mode: 'push',
edge: 'rtmp://127.0.0.1/hls_360p',
},
],
},
trans: {
ffmpeg: '/usr/bin/ffmpeg',
tasks: [
{
app: 'hls_1080p',
hls: true,
ac: 'aac',
acParam: ['-b:a', '192k', '-ar', 48000],
vcParams: [
'-vf',
"'scale=1920:-1'",
'-b:v',
'5000k',
'-preset',
'fast',
'-profile:v',
'baseline',
'-bufsize',
'7500k',
'-tune',
'zerolatency',
],
hlsFlags: '[hls_time=10:hls_list_size=0:hls_flags=delete_segments]',
},
{
app: 'hls_720p',
hls: true,
ac: 'aac',
acParam: ['-b:a', '128k', '-ar', 48000],
vcParams: [
'-vf',
"'scale=1280:-1'",
'-b:v',
'2800k',
'-preset',
'fast',
'-profile:v',
'baseline',
'-bufsize',
'4200k',
'-tune',
'zerolatency',
],
hlsFlags: '[hls_time=10:hls_list_size=0:hls_flags=delete_segments]',
},
{
app: 'hls_480p',
hls: true,
ac: 'aac',
acParam: ['-b:a', '128k', '-ar', 48000],
vcParams: [
'-vf',
"'scale=854:-1'",
'-b:v',
'1400k',
'-preset',
'fast',
'-profile:v',
'baseline',
'-bufsize',
'2100k',
'-tune',
'zerolatency',
],
hlsFlags: '[hls_time=10:hls_list_size=0:hls_flags=delete_segments]',
},
{
app: 'hls_360p',
hls: true,
ac: 'aac',
acParam: ['-b:a', '96k', '-ar', 48000],
vcParams: [
'-vf',
"'scale=480:-1'",
'-b:v',
'800k',
'-preset',
'fast',
'-profile:v',
'baseline',
'-bufsize',
'1200k',
'-tune',
'zerolatency',
],
hlsFlags: '[hls_time=10:hls_list_size=0:hls_flags=delete_segments]',
},
],
},
}
39 changes: 39 additions & 0 deletions src/create-playlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const fs = require('fs')
const mkdirp = require('mkdirp')

const template = name => {
let line = `#EXTM3U\n#EXT-X-VERSION:3\n`
line += `#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360\n./../../hls_360p/${name}/index.m3u8\n`
line += `#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480\n./../../hls_480p/${name}/index.m3u8\n`
line += `#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720\n./../../hls_720p/${name}/index.m3u8\n`
line += `#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080\n./../../hls_1080p/${name}/index.m3u8`
return line
}

module.exports = name => {
return new Promise((resolve, reject) => {
mkdirp(`${process.env.MEDIA_ROOT}/live`, dirErr => {
if (dirErr) {
reject(dirErr.message)
return
}
const playlist = `${process.env.MEDIA_ROOT}/live/${name}.m3u8`
fs.open(playlist, 'w', (err, fd) => {
if (err) {
reject(err.message)
} else {
fs.writeFile(fd, template(name), errWrite => {
if (errWrite) {
reject(errWrite.message)
return
} else {
fs.close(fd, () => {
resolve()
})
}
})
}
})
})
})
}
12 changes: 12 additions & 0 deletions src/delete-playlist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const fs = require('fs')

module.exports = name => {
return new Promise(resolve => {
fs.unlink(`${process.env.MEDIA_ROOT}/live/${name}.m3u8`, function(err) {
if (err) {
console.log(err.message)
}
resolve()
})
})
}
3 changes: 3 additions & 0 deletions src/http-client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const axios = require('axios')

module.exports = axios.create()
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require('dotenv').config()

require('./server')
Loading

0 comments on commit 216aeae

Please sign in to comment.