init
This commit is contained in:
		
							
								
								
									
										180
									
								
								app/_helpers/cameraHelper.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								app/_helpers/cameraHelper.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const http = require('http');
 | 
			
		||||
const tcpPortUsed =require('tcp-port-used');
 | 
			
		||||
const db = require(path.resolve(__dirname, './db.js'));
 | 
			
		||||
const Camera = db.Camera;
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    getDeviceModel,
 | 
			
		||||
    checkPortUse,
 | 
			
		||||
    createNewCameraStreamPort
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function checkPortUse(port) {
 | 
			
		||||
    return tcpPortUsed.check(port, '127.0.0.1')
 | 
			
		||||
        .then((inuse) => {
 | 
			
		||||
            return inuse;
 | 
			
		||||
        })
 | 
			
		||||
        .catch(err => {
 | 
			
		||||
            throw err;
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getDeviceModel(ip) {
 | 
			
		||||
    const options = {
 | 
			
		||||
        hostname: ip,
 | 
			
		||||
        path: '/cgi-bin/param.cgi?get_serial_number',
 | 
			
		||||
        method: 'GET'
 | 
			
		||||
    };
 | 
			
		||||
    return sendCgiReq(options)
 | 
			
		||||
        .then((res) => {
 | 
			
		||||
            const rawData = res.toString().replace(/\n/g, '').toUpperCase();
 | 
			
		||||
            return calcCamModel(rawData, rawData.substring(0, 1), rawData.substring(0, 2))
 | 
			
		||||
                .then(res => {
 | 
			
		||||
                    return res;
 | 
			
		||||
                })
 | 
			
		||||
                .catch(err => {
 | 
			
		||||
                    throw err;
 | 
			
		||||
                });
 | 
			
		||||
        })
 | 
			
		||||
        .catch(err => {
 | 
			
		||||
            throw err;
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function calcCamModel(serial, first_letter, twoFirst_letter) {
 | 
			
		||||
    let model = '';
 | 
			
		||||
    // Check 12x
 | 
			
		||||
    if (["1", "A", "B", "C", "D", "N", "O"].includes(first_letter)) {
 | 
			
		||||
        model = "PT12X-";
 | 
			
		||||
        (first_letter === "1") ? model += serial.slice(3, 6) + "-XX-" + serial.slice(8, 10) + checkPoe(serial.slice(10)) : model += newSerialAnsBuilder(serial);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check 20x
 | 
			
		||||
    if (["2", "E", "F", "G", "H", "P", "Q"].includes(first_letter) && twoFirst_letter !== "PT" ) {
 | 
			
		||||
        model = "PT20X-";
 | 
			
		||||
        (first_letter === "2") ? model += serial.slice(3, 6) + "-XX-" + serial.slice(8, 10) + checkPoe(serial.slice(10)) : model += newSerialAnsBuilder(serial);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check 30x
 | 
			
		||||
    if (["W", "X", "R", "S"].includes(first_letter)) {
 | 
			
		||||
        model = "PT30X-" + newSerialAnsBuilder(serial);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Check zcams
 | 
			
		||||
    if (["J", "U", "I", "T"].includes(first_letter)) {
 | 
			
		||||
        (["J", "U"].includes(first_letter)) ? model = "PT20X-ZCAM-" : model = "PTVL-ZCAM";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('PT' == twoFirst_letter) {
 | 
			
		||||
        (serial.slice(0, 4) === "PTVL") ? model = "PTVL-ZCAM-" : model = "PT20X-ZCAM-";
 | 
			
		||||
    }
 | 
			
		||||
    return [model, serial];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function newSerialAnsBuilder(serial) {
 | 
			
		||||
    let ans = '';
 | 
			
		||||
    switch(serial.substring(0, 1)) {
 | 
			
		||||
        // 12X
 | 
			
		||||
        case "A":
 | 
			
		||||
        case "B":
 | 
			
		||||
            ans = "SDI-XX-G2" + checkPoe(serial.slice(1));
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "C":
 | 
			
		||||
        case "D":
 | 
			
		||||
            ans = "USB-XX-G2";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "N":
 | 
			
		||||
        case "O":
 | 
			
		||||
            ans = "SDI-XX-G2 POE";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        // 20X
 | 
			
		||||
        case "E":
 | 
			
		||||
        case "F":
 | 
			
		||||
            ans = "SDI-XX-G2" + checkPoe(serial.slice(1));
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "G":
 | 
			
		||||
        case "H":
 | 
			
		||||
            ans = "USB-XX-G2";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "P":
 | 
			
		||||
        case "Q":
 | 
			
		||||
            ans = "SDI-XX-G2 POE";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        // 30X
 | 
			
		||||
        case "W":
 | 
			
		||||
        case "X":
 | 
			
		||||
            ans = "SDI-XX-G2 POE";
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        case "R":
 | 
			
		||||
        case "S":
 | 
			
		||||
            ans = "NDI-XX-G2";
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
    return ans;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function checkPoe (serialNum) {
 | 
			
		||||
    if (serialNum <= "B1025000") {
 | 
			
		||||
        return '';
 | 
			
		||||
    } else if (serialNum >= "B1025001" && serialNum <= "D0129000") {
 | 
			
		||||
        return "-POE";
 | 
			
		||||
    } else if(serialNum >= "D0129001") {
 | 
			
		||||
        return "-POE";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function createNewCameraStreamPort() {
 | 
			
		||||
    let port = 5000;
 | 
			
		||||
    let cameraStreamPorts;
 | 
			
		||||
    try {
 | 
			
		||||
        // Returns all saved camera stream ports
 | 
			
		||||
        cameraStreamPorts = await currentCameraStreamPorts();
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    while (cameraStreamPorts.includes(port)) {
 | 
			
		||||
        ++port;
 | 
			
		||||
    }
 | 
			
		||||
    return port;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function currentCameraStreamPorts() {
 | 
			
		||||
    return Camera.find().select('streamPort').lean()
 | 
			
		||||
        .then((cameras) => {
 | 
			
		||||
            return cameras.map((camera) => {
 | 
			
		||||
                return camera.streamPort;
 | 
			
		||||
            });
 | 
			
		||||
        })
 | 
			
		||||
        .catch((err) => {
 | 
			
		||||
            throw err;
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sendCgiReq(options) {
 | 
			
		||||
    return new Promise(function(resolve, reject) {
 | 
			
		||||
        const req = http.request(options, (res) => {
 | 
			
		||||
            let rawData = '';
 | 
			
		||||
            res.setEncoding('utf8');
 | 
			
		||||
            res.on('data', (chunk) => {
 | 
			
		||||
                rawData += chunk;
 | 
			
		||||
            });
 | 
			
		||||
            res.on('end', () => {
 | 
			
		||||
                resolve(rawData);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
        req.on('error', function(err) {
 | 
			
		||||
            reject(err);
 | 
			
		||||
        });
 | 
			
		||||
        req.end();
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								app/_helpers/db.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								app/_helpers/db.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const config = require(path.resolve(__dirname, '../config.json'));
 | 
			
		||||
const mongoose = require('mongoose');
 | 
			
		||||
 | 
			
		||||
mongoose.connect(config.connectionString, {useNewUrlParser: true });
 | 
			
		||||
mongoose.Promise = global.Promise;
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    Camera: require('../camera/camera.model')
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										9
									
								
								app/_helpers/error-handler.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/_helpers/error-handler.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
module.exports = errorHandler;
 | 
			
		||||
 | 
			
		||||
function errorHandler(err, req, res, next) {
 | 
			
		||||
    console.log(err);
 | 
			
		||||
    if (typeof err === 'string') {
 | 
			
		||||
        return res.status(409).json({message: err});
 | 
			
		||||
    }
 | 
			
		||||
    return res.status(500).json({message: err.message});
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										85
									
								
								app/_helpers/ptzHelper.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								app/_helpers/ptzHelper.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    translateDirection,
 | 
			
		||||
    hexStrToNum,
 | 
			
		||||
    numToHexStr,
 | 
			
		||||
    sanitizeSpeed,
 | 
			
		||||
    getCurrentPos
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function translateDirection(direction) {
 | 
			
		||||
    let hexStr;
 | 
			
		||||
    switch(direction.toLowerCase()) {
 | 
			
		||||
        case "stop":
 | 
			
		||||
            hexStr =  "0303FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "up":
 | 
			
		||||
            hexStr =  "0301FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "down":
 | 
			
		||||
            hexStr =  "0302FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "right":
 | 
			
		||||
            hexStr =  "0203FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "left":
 | 
			
		||||
            hexStr =  "0103FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "upleft":
 | 
			
		||||
            hexStr =  "0101FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "upright":
 | 
			
		||||
            hexStr =  "0201FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "downleft":
 | 
			
		||||
            hexStr =  "0102FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "downright":
 | 
			
		||||
            hexStr =  "0202FF";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The direction " + direction + " is not a valid movement direction";
 | 
			
		||||
    }
 | 
			
		||||
    return hexStr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function hexStrToNum(str) {
 | 
			
		||||
    return parseInt(str, 16);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function numToHexStr(num) {
 | 
			
		||||
    if (num == 0) {
 | 
			
		||||
        return '00';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (typeof num === 'string') {
 | 
			
		||||
        num = parseInt(num);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return num.toString(16).toUpperCase().split('').reduce(function(str, char) {
 | 
			
		||||
        return '0' + char;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sanitizeSpeed(pan, tilt) {
 | 
			
		||||
    let sanitizedPan, sanitizedTilt;
 | 
			
		||||
 | 
			
		||||
    if (pan >= 1 && pan <= 18) {
 | 
			
		||||
        sanitizedPan = ("0" + pan).slice(-2);
 | 
			
		||||
    } else {
 | 
			
		||||
        throw 'The pan speed value must be greater than or equal to 1 and less than or equal to 18';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (tilt >= 1 && tilt <= 14) {
 | 
			
		||||
        sanitizedTilt = ("0" + pan).slice(-2);
 | 
			
		||||
    } else {
 | 
			
		||||
        throw 'The tilt speed value must be greater than or equal to 1 and less than or equal to 14';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return [sanitizedPan, sanitizedTilt];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getCurrentPos(id) {
 | 
			
		||||
    return numToHexStr(await socket.sendCmd(id, "81090612FF"));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										81
									
								
								app/_helpers/socket.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								app/_helpers/socket.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const db = require(path.resolve(__dirname, '../_helpers/db.js'));
 | 
			
		||||
const Camera = db.Camera;
 | 
			
		||||
const net = require('net');
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    sendCmd: _sendCmd
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function _sendCmd(id, cmd) {
 | 
			
		||||
    const camera = await Camera.findById(id);
 | 
			
		||||
    const buffer = Buffer.from(cmd, 'hex');
 | 
			
		||||
 | 
			
		||||
    return new Promise((resolve, reject) => {
 | 
			
		||||
        const socket = new net.Socket({allowHalfOpen: true});
 | 
			
		||||
        const conn = net.createConnection(Number(camera.port), camera.ip);
 | 
			
		||||
        conn.setNoDelay();
 | 
			
		||||
        conn.setEncoding('hex');
 | 
			
		||||
 | 
			
		||||
        conn.on('connect', () => {
 | 
			
		||||
            conn.write(buffer);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        conn.on('error', (error) => {
 | 
			
		||||
            reject(error);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        conn.on('data', (buf) => {
 | 
			
		||||
            if (conn.bytesRead >= 3) {
 | 
			
		||||
                conn.end();
 | 
			
		||||
                decode(buf.toString('hex'))
 | 
			
		||||
                    .then((res) => {
 | 
			
		||||
                        resolve(res);
 | 
			
		||||
                    })
 | 
			
		||||
                    .catch(err => {
 | 
			
		||||
                        reject(err);
 | 
			
		||||
                    });
 | 
			
		||||
            } else {
 | 
			
		||||
                reject("Unusual Camera Response: " + buf.toString('hex') + " connection bytes Read: " + conn.bytesRead);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function decode(hexStr) {
 | 
			
		||||
    let decoded = '';
 | 
			
		||||
    switch (hexStr) {
 | 
			
		||||
        case "9041ff":
 | 
			
		||||
        case "9042ff":
 | 
			
		||||
            decoded = "Command Accepted";
 | 
			
		||||
            break;
 | 
			
		||||
        case "9051ff":
 | 
			
		||||
        case "9041ff9051ff":
 | 
			
		||||
            decoded = "Socket1 Cmd Done";
 | 
			
		||||
            break;
 | 
			
		||||
        case "9052ff":
 | 
			
		||||
        case "9042ff9052ff":
 | 
			
		||||
            decoded = "Socket2 Cmd Done";
 | 
			
		||||
            break;
 | 
			
		||||
        case "906002ff":
 | 
			
		||||
            throw "Command Syntax Error";
 | 
			
		||||
        case "906003ff":
 | 
			
		||||
            throw "Command Buffer Full";
 | 
			
		||||
        case "906104ff":
 | 
			
		||||
            throw "Socket1 Cmd Cancelled";
 | 
			
		||||
        case "906204ff":
 | 
			
		||||
            throw "Socket2 Cmd Cancelled";
 | 
			
		||||
        case "906105ff":
 | 
			
		||||
        case "906205ff":
 | 
			
		||||
            throw "No Socket";
 | 
			
		||||
        case "906141ff":
 | 
			
		||||
            throw "Socket1 Cmd Not Executable";
 | 
			
		||||
        case "906241ff":
 | 
			
		||||
            throw "Socket2 Cmd Not Executable";
 | 
			
		||||
        default:
 | 
			
		||||
            throw "Unusual Camera Response: " + hexStr;
 | 
			
		||||
    }
 | 
			
		||||
    return decoded;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										58
									
								
								app/camera/camera.controller.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								app/camera/camera.controller.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
const cameraService = require(path.resolve(__dirname, './camera.service'));
 | 
			
		||||
 | 
			
		||||
router.post('/create', _create);
 | 
			
		||||
router.get('/cameras', getAll);
 | 
			
		||||
router.get('/', getById);
 | 
			
		||||
router.put('/', update);
 | 
			
		||||
router.delete('/', _delete);
 | 
			
		||||
router.post('/osd', _osd);
 | 
			
		||||
router.post('/stream', _stream);
 | 
			
		||||
 | 
			
		||||
module.exports = router;
 | 
			
		||||
 | 
			
		||||
function _create(req, res, next) {
 | 
			
		||||
    cameraService.create(req.body)
 | 
			
		||||
        .then((camera) => res.json(camera))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function update(req, res, next) {
 | 
			
		||||
    cameraService.update(req.body.id)
 | 
			
		||||
        .then((camera) => res.json(camera))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getAll(req, res, next) {
 | 
			
		||||
    cameraService.getAll()
 | 
			
		||||
        .then((cameras) => res.json(cameras))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function getById(req, res, next) {
 | 
			
		||||
    cameraService.getById(req.body.id)
 | 
			
		||||
        .then((camera) => res.json(camera))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _delete(req, res, next) {
 | 
			
		||||
    cameraService.delete(req.body.id)
 | 
			
		||||
        .then((id) => res.json(id))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _osd(req, res, next) {
 | 
			
		||||
    cameraService.osd(req.body)
 | 
			
		||||
        .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));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										30
									
								
								app/camera/camera.model.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/camera/camera.model.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
 | 
			
		||||
const mongoose = require('mongoose');
 | 
			
		||||
const Schema = mongoose.Schema;
 | 
			
		||||
 | 
			
		||||
const schema = new Schema({
 | 
			
		||||
    _id: Schema.Types.ObjectId,
 | 
			
		||||
    ip: {type: String, unique: true, required: true},
 | 
			
		||||
    port: {type: String, required: true},
 | 
			
		||||
    rtsp: {type: String, required: true},
 | 
			
		||||
    model: {type: String, required: true},
 | 
			
		||||
    serial: {type: String, required: true},
 | 
			
		||||
    name: {type: String, unique: true},
 | 
			
		||||
    streamPort: {type: Number, unique: true},
 | 
			
		||||
    presets: [
 | 
			
		||||
        {
 | 
			
		||||
            memNum: {type: Number, max: 127},
 | 
			
		||||
            name: {type: String},
 | 
			
		||||
            location: {
 | 
			
		||||
                pan: {type: String},
 | 
			
		||||
                tilt: {type: String},
 | 
			
		||||
                focus: {type: String},
 | 
			
		||||
                zoom: {type: String}
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
schema.set('toJson', {virtuals: true});
 | 
			
		||||
module.exports = mongoose.model('Camera', schema);
 | 
			
		||||
							
								
								
									
										127
									
								
								app/camera/camera.service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								app/camera/camera.service.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
/*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'));
 | 
			
		||||
const socket = require(path.resolve(__dirname, '../_helpers/socket.js'));
 | 
			
		||||
const Camera = db.Camera;
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    create: _create,
 | 
			
		||||
    getAll: _getAll,
 | 
			
		||||
    getById: _getById,
 | 
			
		||||
    update: _update,
 | 
			
		||||
    delete: _delete,
 | 
			
		||||
    osd: _osd,
 | 
			
		||||
    stream: _stream
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function _getAll() {
 | 
			
		||||
    try {
 | 
			
		||||
        return await Camera.find().lean();
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _getById(id) {
 | 
			
		||||
    try {
 | 
			
		||||
        return await Camera.findById(id).lean();
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _create(cameraParams) {
 | 
			
		||||
    if (await Camera.findOne({ip: cameraParams.ip})) {
 | 
			
		||||
        throw 'There is already a camera with ip ' + cameraParams.ip;
 | 
			
		||||
    } else {
 | 
			
		||||
        const modelSerialArr = await cameraHelper.getDeviceModel(cameraParams.ip);
 | 
			
		||||
        const camId = new mongoose.Types.ObjectId();
 | 
			
		||||
        const streamPort = await cameraHelper.createNewCameraStreamPort();
 | 
			
		||||
        return new Camera({
 | 
			
		||||
            ...cameraParams,
 | 
			
		||||
            _id: camId,
 | 
			
		||||
            model: modelSerialArr[0],
 | 
			
		||||
            serial: modelSerialArr[1],
 | 
			
		||||
            streamPort: streamPort
 | 
			
		||||
        })
 | 
			
		||||
        .save()
 | 
			
		||||
        .then((camera) => {
 | 
			
		||||
            return camera.toObject();
 | 
			
		||||
        }).catch((err) => {
 | 
			
		||||
            throw err;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _update(cameraParams) {
 | 
			
		||||
    try {
 | 
			
		||||
        return await Camera.findByIdAndUpdate(cameraParams._id, cameraParams, {new: true}).save();
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _delete(id) {
 | 
			
		||||
    try {
 | 
			
		||||
        return await Camera.findOneAndDelete(id);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _osd({id, option}) {
 | 
			
		||||
    let osdHex = "8101";
 | 
			
		||||
    switch(option.toLowerCase()) {
 | 
			
		||||
        case "openToggle":
 | 
			
		||||
            osdHex += "043F025FFF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "up":
 | 
			
		||||
            osdHex += "06010E0E0301FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "down":
 | 
			
		||||
            osdHex += "06010E0E0302FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "left":
 | 
			
		||||
            osdHex += "06010E0E0103FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "right":
 | 
			
		||||
            osdHex += "06010E0E0203FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "enter":
 | 
			
		||||
            osdHex += "060605FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "return":
 | 
			
		||||
            osdHex += "060604FF";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The OSD option " + option + " is not a recognizable OSD 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								app/config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								app/config.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
    "connectionString": "mongodb://your-mongo-address/db-name",
 | 
			
		||||
    "secret": "your db-secret"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										112
									
								
								app/image/image.controller.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								app/image/image.controller.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
const imageService = require(path.resolve(__dirname, './image.service.js'));
 | 
			
		||||
module.exports = router;
 | 
			
		||||
 | 
			
		||||
router.post('/bright', _brightness);
 | 
			
		||||
router.post('/contrast', _contrast);
 | 
			
		||||
router.post('/wb', _whiteBalance);
 | 
			
		||||
router.post('/rgain', _rgain);
 | 
			
		||||
router.post('/bgain', _bgain);
 | 
			
		||||
router.post('/shutter', _shutter);
 | 
			
		||||
router.post('/iris', _iris);
 | 
			
		||||
router.post('/gain', _gain);
 | 
			
		||||
router.post('/backLight', _backLight);
 | 
			
		||||
router.post('/bw', _blackWhite);
 | 
			
		||||
router.post('/flicker', _flicker);
 | 
			
		||||
router.post('/imgFlip', _imgFlip);
 | 
			
		||||
router.post('/colorHue', _colorHue);
 | 
			
		||||
router.post('/ae', _autoExp);
 | 
			
		||||
router.post('/save', _save);
 | 
			
		||||
 | 
			
		||||
function _brightness(req, res, next) {
 | 
			
		||||
    imageService.brightness(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _contrast(req, res, next) {
 | 
			
		||||
    imageService.contrast(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _whiteBalance(req, res, next) {
 | 
			
		||||
    imageService.whiteBalance(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _rgain(req, res, next) {
 | 
			
		||||
    imageService.rgain(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _bgain(req, res, next) {
 | 
			
		||||
    imageService.bgain(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _shutter(req, res, next) {
 | 
			
		||||
    imageService.shutter(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _iris(req, res, next) {
 | 
			
		||||
    imageService.iris(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _gain(req, res, next) {
 | 
			
		||||
    imageService.gain(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _backLight(req, res, next) {
 | 
			
		||||
    imageService.backLight(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _blackWhite(req, res, next) {
 | 
			
		||||
    imageService.blackWhite(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _flicker(req, res, next) {
 | 
			
		||||
    imageService.flicker(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _imgFlip(req, res, next) {
 | 
			
		||||
    imageService.imgFlip(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _colorHue(req, res, next) {
 | 
			
		||||
    imageService.colorHue(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _autoExp(req, res, next) {
 | 
			
		||||
    imageService.ae(req.body)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _save(req, res, next) {
 | 
			
		||||
    imageService.save(req.body.id)
 | 
			
		||||
        .then((socket) => res.json(socket))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										479
									
								
								app/image/image.service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										479
									
								
								app/image/image.service.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,479 @@
 | 
			
		||||
 /*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const db = require(path.resolve(__dirname, '../_helpers/db.js'));
 | 
			
		||||
const ptzHelper = require(path.resolve(__dirname, '../_helpers/ptzHelper.js'));
 | 
			
		||||
const socket = require(path.resolve(__dirname, '../_helpers/socket.js'));
 | 
			
		||||
const Camera = db.Camera;
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    brightness: _brightness,
 | 
			
		||||
    contrast: _contrast,
 | 
			
		||||
    whiteBalance: whiteBal,
 | 
			
		||||
    rgain: _rgain,
 | 
			
		||||
    bgain: _bgain,
 | 
			
		||||
    shutter: _shutter,
 | 
			
		||||
    iris: _iris,
 | 
			
		||||
    gain: _gain,
 | 
			
		||||
    backLight: _backLight,
 | 
			
		||||
    blackWhite: _blackWhite,
 | 
			
		||||
    flicker: _flicker,
 | 
			
		||||
    imgFlip: _imgFlip,
 | 
			
		||||
    colorHue: _colorHue,
 | 
			
		||||
    ae: _autoExp,
 | 
			
		||||
    save: saveSetting
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function _brightness({id, pos}) {
 | 
			
		||||
    let brightnessHex = "810104A10000";
 | 
			
		||||
    let brightnessPos = ptzHelper.numToHexStr(pos).padStart(4, "0");
 | 
			
		||||
    brightnessHex += brightnessPos + "FF";
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, brightnessHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _contrast(contrastParams) {
 | 
			
		||||
    const {id, pos} = contrastParams;
 | 
			
		||||
    let contrastHex = "810104A20000";
 | 
			
		||||
    let contrastPos = ptzHelper.numToHexStr(pos).padStart(4, "0");
 | 
			
		||||
    contrastHex += contrastPos + 'FF';
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, contrastHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function whiteBal({id, option, mode}) {
 | 
			
		||||
    let wbHex = "810104";
 | 
			
		||||
 | 
			
		||||
    if (mode.toLowerCase() === 'wbmode') {
 | 
			
		||||
        switch(option.toLowerCase()) {
 | 
			
		||||
            case "auto":
 | 
			
		||||
                wbHex += "3500FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "indoor":
 | 
			
		||||
                wbHex += "3501FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "outdoor":
 | 
			
		||||
                wbHex  += "3502FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "onepush":
 | 
			
		||||
                wbHex += "3503FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "manual":
 | 
			
		||||
                wbHex += "3505FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "onepush-trigger":
 | 
			
		||||
                wbHex += "1005FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The white balance mode " + option + " is not a recognizable white balance mode.";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (mode.toLowerCase() === 'awbsenstivity') {
 | 
			
		||||
        switch(option.toLowerCase()) {
 | 
			
		||||
            case "high":
 | 
			
		||||
                wbHex += "A900FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "normal":
 | 
			
		||||
                wbHex += "A901FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "low":
 | 
			
		||||
                wbHex += "A902FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The auto white balance mode " + option + " is not a recognizable auto white balance mode.";
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        throw mode + "is not a recognizable white balance method.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, wbHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _rgain({id, mode, option = null, pos = null}) {
 | 
			
		||||
    let rgainHex = "810104";
 | 
			
		||||
 | 
			
		||||
    if (mode.toLowerCase() === 'standard') {
 | 
			
		||||
        switch (option.toLowerCase()) {
 | 
			
		||||
            case "reset":
 | 
			
		||||
                rgainHex += "0300FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "up":
 | 
			
		||||
                rgainHex += "0302FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "down":
 | 
			
		||||
                rgainHex += "0303FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The rgain option " + option + " is not a recognizable standard rgain option";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (mode.toLowerCase() === 'direct') {
 | 
			
		||||
        rgainHex = "430000" + ptzHelper.numToHexStr(pos).padStart(4, "0") + "FF";
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The rgain mode " + mode + " is not a recognizable rgain method.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, rgainHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _bgain({id, mode, option = null, pos = null}) {
 | 
			
		||||
    let bgainHex = "810104";
 | 
			
		||||
 | 
			
		||||
    if (mode.toLowerCase() === 'standard') {
 | 
			
		||||
        switch (option.toLowerCase()) {
 | 
			
		||||
            case "reset":
 | 
			
		||||
                bgainHex += "0400FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "up":
 | 
			
		||||
                bgainHex += "0402FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "down":
 | 
			
		||||
                bgainHex += "0403FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The bgain option " + option + " is not a recognizable standard bgain option";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (mode.toLowerCase() === 'direct') {
 | 
			
		||||
        rgainHex = "430000" + ptzHelper.numToHexStr(pos).padStart(4, "0") + "FF";
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The bgain mode " + mode + " is not a recognizable bgain method.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, bgainHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _shutter({id, option, mode}) {
 | 
			
		||||
    let shutterHex = "8101040A";
 | 
			
		||||
    if (mode.toLowerCase() === 'standard') {
 | 
			
		||||
        switch (option.toLowerCase()) {
 | 
			
		||||
            case "reset":
 | 
			
		||||
                shutterHex += "00FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "up":
 | 
			
		||||
                shutterHex += "02FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "down":
 | 
			
		||||
                shutterHex += "03FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The shutter option " + option + " is not a recognizable standard shutter option";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (mode.toLowerCase() === 'direct') {
 | 
			
		||||
        shutterHex += "0000";
 | 
			
		||||
        switch (option) {
 | 
			
		||||
            case "1/30":
 | 
			
		||||
                shutterHex += "0001FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/60":
 | 
			
		||||
                shutterHex += "0002FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/90":
 | 
			
		||||
                shutterHex += "0003FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/100":
 | 
			
		||||
                shutterHex += "0004FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/125":
 | 
			
		||||
                shutterHex += "0005FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/180":
 | 
			
		||||
                shutterHex += "0006FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/250":
 | 
			
		||||
                shutterHex += "0007FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/350":
 | 
			
		||||
                shutterHex += "0008FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/500":
 | 
			
		||||
                shutterHex += "0009FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/725":
 | 
			
		||||
                shutterHex += "000AFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/1000":
 | 
			
		||||
                shutterHex += "000BFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/1500":
 | 
			
		||||
                shutterHex += "000CFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/2000":
 | 
			
		||||
                shutterHex += "000DFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/3000":
 | 
			
		||||
                shutterHex += "000EFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/4000":
 | 
			
		||||
                shutterHex += "000FFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/6000":
 | 
			
		||||
                shutterHex += "0100FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "1/10000":
 | 
			
		||||
                shutterHex += "0101FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The shutter option " + option + " is not a recognizable direct shutter option";
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The shutter mode " + mode + " is not a recognizable shutter mode";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, shutterHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
    console.log("reached end");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _iris({id, mode, option}) {
 | 
			
		||||
    let irisHex = "8101040B";
 | 
			
		||||
 | 
			
		||||
    if (mode.toLowerCase() === 'standard') {
 | 
			
		||||
        switch (option.toLowerCase()) {
 | 
			
		||||
            case "reset":
 | 
			
		||||
                irisHex += "00FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "up":
 | 
			
		||||
                irisHex += "02FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "down":
 | 
			
		||||
                irisHex += "03FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The iris option " + option + " is not a recognizable standard iris option";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (mode.toLowerCase() === 'direct') {
 | 
			
		||||
        irisHex += "0000";
 | 
			
		||||
        switch (option.toLowerCase()) {
 | 
			
		||||
            case "close":
 | 
			
		||||
                irisHex += "0000FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f11":
 | 
			
		||||
                irisHex += "0006FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f9.6":
 | 
			
		||||
                irisHex += "0007FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f8.0":
 | 
			
		||||
                irisHex += "0008FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f6.8":
 | 
			
		||||
                irisHex += "0009FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f5.6":
 | 
			
		||||
                irisHex += "000AFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f4.8":
 | 
			
		||||
                irisHex += "000BFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f4.0":
 | 
			
		||||
                irisHex += "000CFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f3.4":
 | 
			
		||||
                irisHex += "000DFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f2.8":
 | 
			
		||||
                irisHex += "000EFF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f2.0":
 | 
			
		||||
                irisHex += "0100FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "f1.8":
 | 
			
		||||
                irisHex += "0200FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The iris direct option " + option + " is not a recognizable option";
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The iris option " + option + " is not a recognizable direct iris option";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, irisHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _gain({id, mode, option = null, pos = null}) {
 | 
			
		||||
    let gainHex = "8101040C";
 | 
			
		||||
    if (mode.toLowerCase() === "standard") {
 | 
			
		||||
        switch(option.toLowerCase()) {
 | 
			
		||||
            case "reset":
 | 
			
		||||
                gainHex += "00FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "up":
 | 
			
		||||
                gainHex += "02FF";
 | 
			
		||||
                break;
 | 
			
		||||
            case "down":
 | 
			
		||||
                gainHex += "03FF";
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                throw "The gain option " + option + " is not a recognizable standard gain option";
 | 
			
		||||
        }
 | 
			
		||||
    } else if (mode === "direct") {
 | 
			
		||||
        gainHex += "0000" + ptzHelper.numToHexStr(pos).padStart(4, "0") + "FF";
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The gain mode " + mode + " is not a recognizable gain mode";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, gainHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _backLight({id, option}) {
 | 
			
		||||
    let backlightHex = "81010433";
 | 
			
		||||
    if (option.toLowerCase() === 'on') {
 | 
			
		||||
        backlightHex += "02FF";
 | 
			
		||||
    } else if (option.toLowerCase() === 'off') {
 | 
			
		||||
        backlightHex += "03FF";
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The back light option " + option + " is not a recognizable back light option";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, backlightHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _blackWhite({id, option}) {
 | 
			
		||||
    let bwHex = "81010463";
 | 
			
		||||
 | 
			
		||||
    if (option.toLowerCase() === 'on') {
 | 
			
		||||
        bwHex += "04FF";
 | 
			
		||||
    } else if (option.toLowerCase() === 'off') {
 | 
			
		||||
        bwHex += "00FF";
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The back light option " + option + " is not a recognizable back light option";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, bwHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _flicker({id, option}) {
 | 
			
		||||
    let flickerHex = "810104230";
 | 
			
		||||
    switch(option.toLowerCase()) {
 | 
			
		||||
        case "off":
 | 
			
		||||
            flickerHex += "0FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "50hz":
 | 
			
		||||
            flickerHex += "1FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case "60hz":
 | 
			
		||||
            flickerHex += "2FF";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The flicker option " + option + " is not a recognizable standard flicker option";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, flickerHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _imgFlip({id, mode, option}) {
 | 
			
		||||
    let imgFlipHex = "810104";
 | 
			
		||||
 | 
			
		||||
    if (mode.toLowerCase() === 'lr') {
 | 
			
		||||
        imgFlipHex += '61';
 | 
			
		||||
    } else if (mode.toLowerCase() === 'pf') {
 | 
			
		||||
        imgFlipHex += '66';
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The image flip mode " + mode + " is not a recognizable image flip mode";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (option.toLowerCase() === 'off') {
 | 
			
		||||
        imgFlipHex += '03FF';
 | 
			
		||||
    } else if (option.toLowerCase() === 'on') {
 | 
			
		||||
        imgFlipHex += '02FF';
 | 
			
		||||
    } else {
 | 
			
		||||
        throw "The image flip option " + option + " is not a recognizable image flip option";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, imgFlipHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _colorHue({id, pos}) {
 | 
			
		||||
    let colorHueHex = '8101044F';
 | 
			
		||||
    let colorHuePos = ptzHelper.numToHexStr(pos).padStart(8, "0");
 | 
			
		||||
    colorHueHex += colorHuePos + "FF";
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, colorHueHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _autoExp({id, option}) {
 | 
			
		||||
    let aeHex = '81010439';
 | 
			
		||||
 | 
			
		||||
    switch (option.toLowerCase()) {
 | 
			
		||||
        case "fullauto":
 | 
			
		||||
            aeHex += '00FF';
 | 
			
		||||
            break;
 | 
			
		||||
        case "manual":
 | 
			
		||||
            aeHex += '03FF';
 | 
			
		||||
            break;
 | 
			
		||||
        case "shutter":
 | 
			
		||||
            aeHex += '0AFF';
 | 
			
		||||
            break;
 | 
			
		||||
        case "iris":
 | 
			
		||||
            aeHex += '0BFF';
 | 
			
		||||
            break;
 | 
			
		||||
        case "bright":
 | 
			
		||||
            aeHex += '0DFF';
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The autoexp option " + option + " is not a recognizable autoexp option";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, aeHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function saveSetting(id) {
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, "81010604FF");
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								app/ptz/ptz.controller.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/ptz/ptz.controller.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const express = require('express');
 | 
			
		||||
const router = express.Router();
 | 
			
		||||
const ptzService = require(path.resolve(__dirname, './ptz.service.js'));
 | 
			
		||||
 | 
			
		||||
module.exports = router;
 | 
			
		||||
 | 
			
		||||
router.post('/motion', _motion);
 | 
			
		||||
router.post('/presets', _presets);
 | 
			
		||||
router.post('/focus', _focus);
 | 
			
		||||
router.post('/zoom', _zoom);
 | 
			
		||||
 | 
			
		||||
function _presets(req, res, next) {
 | 
			
		||||
    ptzService.preset(req.body)
 | 
			
		||||
        .then((response) => res.send(response))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _motion(req, res, next) {
 | 
			
		||||
    ptzService.motion(req.body)
 | 
			
		||||
        .then((response) => res.send(response))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _focus(req, res, next) {
 | 
			
		||||
    ptzService.focus(req.body)
 | 
			
		||||
        .then((response) => res.send(response))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _zoom(req, res, next) {
 | 
			
		||||
    ptzService.zoom(req.body)
 | 
			
		||||
        .then((response) => res.send(response))
 | 
			
		||||
        .catch(err => next(err));
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										194
									
								
								app/ptz/ptz.service.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								app/ptz/ptz.service.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,194 @@
 | 
			
		||||
/*jshint esversion: 6 */
 | 
			
		||||
const path = require('path');
 | 
			
		||||
const db = require(path.resolve(__dirname, '../_helpers/db.js'));
 | 
			
		||||
const ptzHelper = require(path.resolve(__dirname, '../_helpers/ptzHelper.js'));
 | 
			
		||||
const socket = require(path.resolve(__dirname, '../_helpers/socket.js'));
 | 
			
		||||
const Camera = db.Camera;
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    preset: _presets,
 | 
			
		||||
    motion: _motion,
 | 
			
		||||
    focus: _focus,
 | 
			
		||||
    zoom: _zoom
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
async function _presets({id, mode, speed = null, memNum = null}) {
 | 
			
		||||
    let memCmd;
 | 
			
		||||
 | 
			
		||||
    switch(mode.toLowerCase()) {
 | 
			
		||||
        case 'speed':
 | 
			
		||||
            const recallSpeed = ptzHelper.numToHexStr(speed).padStart(2, "0");
 | 
			
		||||
            memCmd = "81010601" + recallSpeed + "FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case 'call':
 | 
			
		||||
            memCmd = "8101043F02" + ptzHelper.numToHexStr(memNum).padStart(2, "0") + "FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case 'set':
 | 
			
		||||
            memCmd = "8101043F01" + ptzHelper.numToHexStr(memNum).padStart(2, "0") + "FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case 'reset':
 | 
			
		||||
            memCmd = "8101043F00" + ptzHelper.numToHexStr(memNum).padStart(2, "0") + "FF";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The preset mode " + mode + " is not a recognizable preset method.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, memCmd);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _motion({id, mode, pan = null, tilt = null, panSpeed = null, tiltSpeed = null, direction = null}) {
 | 
			
		||||
    if (mode === 'absolute' || mode === 'relative' || mode === 'standard') {
 | 
			
		||||
        panTiltSpeedArr = ptzHelper.sanitizeSpeed(panSpeed, tiltSpeed);
 | 
			
		||||
        if (mode === 'absolute' || mode === 'relative') {
 | 
			
		||||
            pan = ptzHelper.numToHexStr(pan);
 | 
			
		||||
            tilt = ptzHelper.numToHexStr(tilt);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let motionHex;
 | 
			
		||||
    switch(mode.toLowerCase()) {
 | 
			
		||||
        case 'home':
 | 
			
		||||
            motionHex = "81010604FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case 'absolute':
 | 
			
		||||
            motionHex = "81010602" + panTiltSpeedArr[0] + panTiltSpeedArr[1] + pan + tilt +"FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case 'relative':
 | 
			
		||||
            motionHex = "81010603" + panTiltSpeedArr[0] + panTiltSpeedArr[1] + pan + tilt +"FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case 'standard':
 | 
			
		||||
            motionHex = "81010601" + panTiltSpeedArr[0] + panTiltSpeedArr[1] + ptzHelper.translateDirection(direction);
 | 
			
		||||
            break;
 | 
			
		||||
        case 'current':
 | 
			
		||||
            motionHex = "81090612FF";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The option " + mode + " is not a recognizable motion method.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, motionHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _focus({id, mode, option = null, focusPos = null, intensity = null}) {
 | 
			
		||||
    let focusHex = '';
 | 
			
		||||
    switch(mode.toLowerCase()) {
 | 
			
		||||
        case 'standard':
 | 
			
		||||
            focusHex = "81010408";
 | 
			
		||||
 | 
			
		||||
            if (option === 'stop') {
 | 
			
		||||
                focusHex += "00FF";
 | 
			
		||||
            } else if (option === 'tele') {
 | 
			
		||||
                focusHex += "03FF";
 | 
			
		||||
            } else if (option === 'wide') {
 | 
			
		||||
                focusHex += "02FF";
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "The focus option " + option + " is not a recognizable standard focus option";
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 'variable':
 | 
			
		||||
            focusHex = "81010408";
 | 
			
		||||
            if (option === 'tele') {
 | 
			
		||||
                focusHex += '3' + intensity + "FF";
 | 
			
		||||
            } else if (option === 'wide') {
 | 
			
		||||
                focusHex += '2' + intensity + "FF";
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "The focus option " + option + " is not a recognizable variable focus option";
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 'direct':
 | 
			
		||||
            focusPos = numToHexStr(focusPos).split('').reduce((str, char) => {
 | 
			
		||||
                return '0' + char;
 | 
			
		||||
            });
 | 
			
		||||
            focusPos = focusPos.padStart(8, "0");
 | 
			
		||||
            focusHex = "81010448" + focusPos + "FF";
 | 
			
		||||
            break;
 | 
			
		||||
        case 'focusmode':
 | 
			
		||||
            focusHex = "810";
 | 
			
		||||
            if (option === 'auto') {
 | 
			
		||||
                focusHex += "1043802FF";
 | 
			
		||||
            } else if (option === 'manual') {
 | 
			
		||||
                focusHex += "1043803FF";
 | 
			
		||||
            } else if (option === 'toggle') {
 | 
			
		||||
                focusHex += "1043810FF";
 | 
			
		||||
            } else if (option === 'lock') {
 | 
			
		||||
                focusHex += "A046802FF";
 | 
			
		||||
            } else if (option === 'unlock') {
 | 
			
		||||
                focusHex += "A046803FF";
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "The focus option " + option + " is not a recognizable focus mode option";
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 'afzone':
 | 
			
		||||
            focusHex = "810104AA";
 | 
			
		||||
            if (option === 'top') {
 | 
			
		||||
                focusHex += "00FF";
 | 
			
		||||
            } else if (option === 'center') {
 | 
			
		||||
                focusHex += "01FF";
 | 
			
		||||
            } else if (option === 'bottom') {
 | 
			
		||||
                focusHex += "02FF";
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "The focus option " + option + " is not a recognizable auto focus-zone option";
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The focus mode " + mode + " is not a recognizable focus method.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, focusHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function _zoom({id, mode, option = null, zoomPos = null, intensity}) {
 | 
			
		||||
    let zoomHex = '';
 | 
			
		||||
 | 
			
		||||
    switch (mode) {
 | 
			
		||||
        case 'standard':
 | 
			
		||||
            zoomHex = "81010407";
 | 
			
		||||
            if (option === "stop") {
 | 
			
		||||
                zoomHex += "00FF";
 | 
			
		||||
            } else if (option === "tele") {
 | 
			
		||||
                zoomHex += "02FF";
 | 
			
		||||
            } else if (option === 'wide') {
 | 
			
		||||
                zoomHex += "03FF";
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "The zoom option " + option + " is not a recognizable standard zoom option";
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 'variable':
 | 
			
		||||
            zoomHex = "81010407";
 | 
			
		||||
            if (option === 'tele') {
 | 
			
		||||
                zoomHex += '2' + intensity + "FF";
 | 
			
		||||
            } else if (option === 'wide') {
 | 
			
		||||
                zoomHex += '3' + intensity + "FF";
 | 
			
		||||
            } else {
 | 
			
		||||
                throw "The zoom option " + option + " is not a recognizable variable zoom option";
 | 
			
		||||
            }
 | 
			
		||||
            break;
 | 
			
		||||
        case 'direct':
 | 
			
		||||
            zoomPos = numToHexStr(zoomPos).split('').reduce((str, char) => {
 | 
			
		||||
                return '0' + char;
 | 
			
		||||
            });
 | 
			
		||||
            zoomPos = zoomPos.padStart(8, "0");
 | 
			
		||||
            zoomHex = "81010447" + zoomPos + "FF";
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            throw "The zoom mode " + mode + " is not a recognizable zoom method.";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
        return await socket.sendCmd(id, zoomHex);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        throw err;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										38
									
								
								app/stream/mpeg1muxer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								app/stream/mpeg1muxer.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
/*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;
 | 
			
		||||
							
								
								
									
										124
									
								
								app/stream/videoStream.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								app/stream/videoStream.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
/*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