Make streaming work & much more elegant
This commit is contained in:
		@@ -10,7 +10,6 @@ router.get('/', getById);
 | 
			
		||||
router.put('/', update);
 | 
			
		||||
router.delete('/', _delete);
 | 
			
		||||
router.post('/osd', _osd);
 | 
			
		||||
router.post('/stream', _stream);
 | 
			
		||||
 | 
			
		||||
module.exports = router;
 | 
			
		||||
 | 
			
		||||
@@ -49,10 +48,3 @@ function _osd(req, res, next) {
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _stream(req, res, next) {
 | 
			
		||||
    console.log("Stream Requester ip: " + req.ip);
 | 
			
		||||
    cameraService.stream(req.body)
 | 
			
		||||
        .then((streamPort) => res.json(streamPort))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,5 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const Stream = require('../stream/videoStream');
 | 
			
		||||
const db = require(path.resolve(__dirname, '../_helpers/db.js'));
 | 
			
		||||
const mongoose = require('mongoose');
 | 
			
		||||
const cameraHelper = require(path.resolve(__dirname, '../_helpers/cameraHelper.js'));
 | 
			
		||||
@@ -13,8 +12,7 @@ module.exports = {
 | 
			
		||||
    getById: _getById,
 | 
			
		||||
    update: _update,
 | 
			
		||||
    delete: _delete,
 | 
			
		||||
    osd: _osd,
 | 
			
		||||
    stream: _stream
 | 
			
		||||
    osd: _osd
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function _getAll() {
 | 
			
		||||
@@ -101,27 +99,3 @@ async function _osd({id, option}) {
 | 
			
		||||
    }
 | 
			
		||||
    return await socket.sendCmd(id, osdHex);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _stream({id, width = 1920, height = 1080}) {
 | 
			
		||||
    const camera = await Camera.findById(id).lean();
 | 
			
		||||
 | 
			
		||||
    // In case a seperate user wants to reach same camera stream
 | 
			
		||||
    let inUse;
 | 
			
		||||
    try {
 | 
			
		||||
        inUse = await cameraHelper.checkPortUse(camera.streamPort);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!inUse) {
 | 
			
		||||
        const stream = new Stream({
 | 
			
		||||
            name: camera.name || "stream: " + camera.rtsp,
 | 
			
		||||
            url: 'rtsp://' + camera.rtsp,
 | 
			
		||||
            port: camera.streamPort,
 | 
			
		||||
            width: width,
 | 
			
		||||
            height: height
 | 
			
		||||
        });
 | 
			
		||||
        stream.start();
 | 
			
		||||
    }
 | 
			
		||||
    return camera.streamPort;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
    "connectionString": "mongodb://your-mongo-address/db-name",
 | 
			
		||||
    "connectionString": "mongodb://localhost:27017/ptzoptics",
 | 
			
		||||
    "secret": "your db-secret"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,38 +0,0 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const ffmpegPath = require('@ffmpeg-installer/ffmpeg').path;
 | 
			
		||||
const child_process = require('child_process');
 | 
			
		||||
const EventEmitter = require('events');
 | 
			
		||||
const spawn = require('cross-spawn');
 | 
			
		||||
 | 
			
		||||
class Mpeg1Muxer extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    constructor(options) {
 | 
			
		||||
        super(options);
 | 
			
		||||
 | 
			
		||||
        this.url = options.url;
 | 
			
		||||
        this.width = options.width;
 | 
			
		||||
        // this.stream = child_process.spawn(ffmpegPath, ['-y', '-loglevel', 'quiet', "-rtsp_transport", "tcp", "-i", this.url, '-vf', 'yadif', '-f', 'mpegts', '-r', '30',  '-codec:v', 'mpeg1video', '-codec:a', 'mp2', '-b:a', '128k', '-b:v', '4096k', '-muxdelay', '0', '-', './app/stream/stream.ts'], {
 | 
			
		||||
        //     detached: false
 | 
			
		||||
        // });
 | 
			
		||||
        this.stream = child_process.spawn(ffmpegPath, ['-y', '-loglevel', 'quiet', "-rtsp_transport", "tcp", "-i", this.url, '-filter:v', 'scale=1280:-1', '-f', 'mpegts', '-r', '30',  '-codec:v', 'mpeg1video', '-codec:a', 'mp2', '-b:a', '128k', '-b:v', '1500k', '-', './app/stream/stream.ts'], {
 | 
			
		||||
            detached: false
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.inputStreamStarted = true;
 | 
			
		||||
        this.stream.stdout.on('data', (data) => { return this.emit('mpeg1data', data); });
 | 
			
		||||
        this.stream.stderr.on('data', (data) => { return this.emit('ffmpegError', data); });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stop() {
 | 
			
		||||
        try {
 | 
			
		||||
            this.stream.stdout.removeAllListeners();
 | 
			
		||||
        } catch(err) {
 | 
			
		||||
            console.log("Muxer: " + err);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.stream.kill();
 | 
			
		||||
        this.stream = undefined;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = Mpeg1Muxer;
 | 
			
		||||
							
								
								
									
										21
									
								
								app/stream/stream.controller.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								app/stream/stream.controller.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
 | 
			
		||||
const db = require(path.resolve(__dirname, '../_helpers/db.js'));
 | 
			
		||||
const Camera = db.Camera;
 | 
			
		||||
 | 
			
		||||
const { proxy, scriptUrl } = require('rtsp-relay')(router);
 | 
			
		||||
 | 
			
		||||
console.log(`Connecting to camera: rtsp://${Camera.rtsp}`);
 | 
			
		||||
 | 
			
		||||
const handler = proxy({
 | 
			
		||||
    url: `rtsp://10.0.1.3:554/1`,
 | 
			
		||||
    // if your RTSP stream need credentials, include them in the URL as above
 | 
			
		||||
    verbose: false,
 | 
			
		||||
    additionalFlags: ['-q', '1']
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
router.ws('/', handler);
 | 
			
		||||
 | 
			
		||||
module.exports = router;
 | 
			
		||||
@@ -1,124 +0,0 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
 | 
			
		||||
// Thanks to:  https://github.com/Wifsimster/node-rtsp-stream-es6
 | 
			
		||||
const WebSocket = require('ws');
 | 
			
		||||
const EventEmitter = require('events');
 | 
			
		||||
const STREAM_MAGIC_BYTES = "jsmp";
 | 
			
		||||
const Mpeg1Muxer = require('./mpeg1muxer');
 | 
			
		||||
 | 
			
		||||
class VideoStream extends EventEmitter {
 | 
			
		||||
 | 
			
		||||
    constructor(options) {
 | 
			
		||||
        super(options);
 | 
			
		||||
        this.name = options.name;
 | 
			
		||||
        this.url = options.url;
 | 
			
		||||
        this.width = options.width;
 | 
			
		||||
        this.height = options.height;
 | 
			
		||||
        this.port = options.port;
 | 
			
		||||
        this.stream = void 0;
 | 
			
		||||
        this.stream2Socket();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stream2Socket() {
 | 
			
		||||
        this.server = new WebSocket.Server({
 | 
			
		||||
            port: this.port
 | 
			
		||||
        });
 | 
			
		||||
        this.server.on('connection', (socket) => {
 | 
			
		||||
            console.log(`New connection: ${this.name}`);
 | 
			
		||||
            let streamHeader = new Buffer(8);
 | 
			
		||||
            streamHeader.write(STREAM_MAGIC_BYTES);
 | 
			
		||||
            streamHeader.writeUInt16BE(this.width, 4);
 | 
			
		||||
            streamHeader.writeUInt16BE(this.height, 6);
 | 
			
		||||
            socket.send(streamHeader);
 | 
			
		||||
 | 
			
		||||
            socket.on('close', () => {
 | 
			
		||||
                console.log(`${this.name} disconnected !`);
 | 
			
		||||
                if (this.server.clients.length <= 0) {
 | 
			
		||||
                    this.mpeg1Muxer.stop();
 | 
			
		||||
                    this.server.close();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.on('camdata', (data) => {
 | 
			
		||||
            for (let i in this.server.clients) {
 | 
			
		||||
                let client = this.server.clients[i];
 | 
			
		||||
                if (client.readyState === WebSocket.OPEN) {
 | 
			
		||||
                    client.send(data);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    onSocketConnect(socket) {
 | 
			
		||||
        let streamHeader = new Buffer(8);
 | 
			
		||||
        streamHeader.write(STREAM_MAGIC_BYTES);
 | 
			
		||||
        streamHeader.writeUInt16BE(this.width, 4);
 | 
			
		||||
        streamHeader.writeUInt16BE(this.height, 6);
 | 
			
		||||
        socket.send(streamHeader, {
 | 
			
		||||
            binary: true
 | 
			
		||||
        });
 | 
			
		||||
        console.log(`New connection: ${this.name} - ${this.wsServer.clients.length} total`);
 | 
			
		||||
        return socket.on("close", function(code, message) {
 | 
			
		||||
            return console.log(`${this.name} disconnected - ${this.wsServer.clients.length} total`);
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    start() {
 | 
			
		||||
        this.mpeg1Muxer = new Mpeg1Muxer({
 | 
			
		||||
            url: this.url,
 | 
			
		||||
            width: this.width
 | 
			
		||||
        });
 | 
			
		||||
        this.mpeg1Muxer.on('mpeg1data', (data) => {
 | 
			
		||||
            return this.emit('camdata', data);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let gettingInputData = false;
 | 
			
		||||
        let gettingOutputData = false;
 | 
			
		||||
        let inputData = [];
 | 
			
		||||
        let outputData = [];
 | 
			
		||||
 | 
			
		||||
        this.mpeg1Muxer.on('ffmpegError', (data) => {
 | 
			
		||||
            data = data.toString();
 | 
			
		||||
            if (data.indexOf('Input #') !== -1) {
 | 
			
		||||
                gettingInputData = true;
 | 
			
		||||
            }
 | 
			
		||||
            if (data.indexOf('Output #') !== -1) {
 | 
			
		||||
                gettingInputData = false;
 | 
			
		||||
                gettingOutputData = true;
 | 
			
		||||
            }
 | 
			
		||||
            if (data.indexOf('frame') === 0) {
 | 
			
		||||
                gettingOutputData = false;
 | 
			
		||||
            }
 | 
			
		||||
            if (gettingInputData) {
 | 
			
		||||
                inputData.push(data.toString());
 | 
			
		||||
                let size = data.match(/\d+x\d+/);
 | 
			
		||||
                if (size != null) {
 | 
			
		||||
                    size = size[0].split('x');
 | 
			
		||||
                    if (this.width == null) {
 | 
			
		||||
                        this.width = parseInt(size[0], 10);
 | 
			
		||||
                    }
 | 
			
		||||
                    if (this.height == null) {
 | 
			
		||||
                        return this.height = parseInt(size[1], 10);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        this.mpeg1Muxer.on('ffmpegError', (data) => {
 | 
			
		||||
            return global.process.stderr.write(data);
 | 
			
		||||
        });
 | 
			
		||||
        return this;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    stop(serverCloseCallback) {
 | 
			
		||||
        this.server.close(serverCloseCallback);
 | 
			
		||||
        this.server.removeAllListeners();
 | 
			
		||||
        this.server = undefined;
 | 
			
		||||
 | 
			
		||||
        this.mpeg1Muxer.stop();
 | 
			
		||||
        this.mpeg1Muxer.removeAllListeners();
 | 
			
		||||
        this.mpeg1Muxer = undefined;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = VideoStream;
 | 
			
		||||
		Reference in New Issue
	
	Block a user