Commit c41d2006 authored by Moe's avatar Moe 😎

Merge branch 'dev' into 'master'

Majin Buu

See merge request !55
parents 8bccd65f d6f83fff
......@@ -9,6 +9,9 @@ if ! [ -x "$(command -v opencv_version)" ]; then
else
echo "OpenCV found... : $(opencv_version)"
fi
# get tesseract repo because ubuntu repo is serving a broken version
sudo add-apt-repository ppa:alex-p/tesseract-ocr -y
sudo apt-get update
# this includes all the ones missing from OpenALPR's guide.
sudo apt install libtesseract-dev git cmake build-essential libleptonica-dev -y
sudo apt install liblog4cplus-dev libcurl3-dev -y
......
......@@ -22,7 +22,7 @@ var config = loadLib('config')(s)
//language loader
var lang = loadLib('language')(s,config)
//code test module
loadLib('codeTester')(s,config,lang,io)
loadLib('codeTester')(s,config,lang)
//basic functions
loadLib('basic')(s,config)
//video processing engine
......@@ -64,9 +64,13 @@ loadLib('ffmpeg')(s,config,function(ffmpeg){
//cluster module
loadLib('childNode')(s,config,lang,app,io)
//cloud uploaders : amazon s3, webdav, backblaze b2..
loadLib('cloudUploaders')(s,config,lang)
loadLib('uploaders')(s,config,lang)
//notifiers : discord..
loadLib('notification')(s,config,lang)
//notifiers : discord..
loadLib('rtmpserver')(s,config,lang)
//dropInEvents server (file manipulation to create event trigger)
loadLib('dropInEvents')(s,config,lang,app,io)
//custom module loader
loadLib('customAutoLoad')(s,config,lang,app,io)
//scheduling engine
......
......@@ -170,7 +170,7 @@ s.localToUtc = function(time){
return moment(time).utc()
}
s.nameToTime = function(x){x=x.replace('.webm','').replace('.mp4','').split('T'),x[1]=x[1].replace(/-/g,':');x=x.join(' ');return x;}
io = require('socket.io-client')('ws://'+config.ip+':'+config.port);//connect to master
io = require('socket.io-client')('ws://'+config.ip+':'+config.port,{transports:['websocket']});//connect to master
s.cx = function(x){x.cronKey=config.cron.key;return io.emit('cron',x)}
//emulate master socket emitter
s.tx = function(x,y){s.cx({f:'s.tx',data:x,to:y})}
......
......@@ -766,6 +766,14 @@
"example": "",
"possible": ""
},
{
"name": "detail=detector_send_video_length",
"field": "Notification Video Length",
"description": "In seconds. The length of the video that gets sent to your Notification service, like Email or Discord.",
"default": "10",
"example": "",
"possible": ""
},
{
"name": "detail=watchdog_reset",
"field": "Timeout Reset on Next Event",
......@@ -835,8 +843,16 @@
{
"name": "detail=detector_fps",
"field": "Detector Rate (FPS)",
"description": "How many frames a second to send to the motion detector; 1 is the default.",
"default": "1",
"description": "How many frames a second to send to the motion detector; 2 is the default.",
"default": "2",
"example": "",
"possible": ""
},
{
"name": "detail=detector_buffer_seconds",
"field": "Recorded Buffer",
"description": "How many seconds before the event to include in the recorded video.",
"default": "5",
"example": "",
"possible": ""
},
......
This diff is collapsed.
......@@ -48,6 +48,8 @@
"Motion GUI": "Motion GUI",
"Motion": "Motion",
"Global Detector Settings": "Global Detector Settings",
"Trigger Group to Record": "Trigger Group to Record",
"Trigger Camera Groups": "Trigger Camera Groups",
"Motion Detection": "Motion Detection",
"Object Detection": "Object Detection",
"JPEG Mode": "JPEG Mode",
......@@ -174,6 +176,15 @@
"Motion Meter": "Motion Meter",
"FFmpegTip": "FFprobe is a simple multimedia streams analyzer. You can use it to output all kinds of information about an input including duration, frame rate, frame size, etc.",
"Complete Stream URL": "Complete Stream URL",
"Primary Input": "Primary Input",
"All streams in first feed": "All streams in first feed",
"First stream in feed": "First stream in feed",
"Second stream in feed": "Second stream in feed",
"Video streams only": "Video streams only",
"Video stream only from first feed": "Video stream only from first feed",
"Audio streams only": "Audio streams only",
"Audio stream only from first feed": "Audio stream only from first feed",
"ONVIF Port": "ONVIF Port",
"ONVIF Scanner": "ONVIF Scanner",
"ONVIFEventsNotAvailable": "ONVIF Events not Available",
"ONVIFnotCompliantProfileT": "Camera is not ONVIF Profile T Compliant",
......@@ -208,8 +219,12 @@
"WebDAV": "WebDAV",
"Backblaze B2": "Backblaze B2",
"Backblaze Error": "Backblaze Error",
"SFTP (SSH File Transfer)": "SFTP (SSH File Transfer)",
"SFTP Error": "SFTP Error",
"SFTP": "SFTP",
"Could not create Bucket.": "Could not create Bucket.",
"Amazon S3": "Amazon S3",
"Wasabi Hot Cloud Storage": "Wasabi Hot Cloud Storage",
"Database": "Database",
"Database Not Found": "Database Not Found",
"User Not Found": "User Not Found",
......@@ -218,9 +233,11 @@
"Bucket": "Bucket",
"Region": "Region",
"Use Global Amazon S3 Video Storage": "Use Global Amazon S3 Video Storage",
"Use Global Wasabi Hot Cloud Storage Video Storage": "Use Global Wasabi Hot Cloud Storage Video Storage",
"Use Global Backblaze B2 Video Storage": "Use Global Backblaze B2 Video Storage",
"Use Global WebDAV Video Storage": "Use Global WebDAV Video Storage",
"Amazon S3 Upload Error": "Amazon S3 Upload Error",
"Wasabi Hot Cloud Storage Upload Error": "Wasabi Hot Cloud Storage Upload Error",
"accountId": "Account ID",
"applicationKey": "Application Key",
"aws_accessKeyId": "Access Key Id",
......@@ -261,13 +278,17 @@
"Set to Watch Only": "Set to Watch Only",
"Save as": "Save as",
"Add New": "Add New",
"Merge and Download": "Merge and Download",
"Zip and Download": "Zip and Download",
"Merge Selected Videos": "Merge Selected Videos",
"Export Selected Videos": "Export Selected Videos",
"Delete Selected Videos": "Delete Selected Videos",
"DeleteSelectedVideosMsg": "Do you want to delete these videos? You cannot recover them.",
"ExportSelectedVideosMsg": "Do you want to export these videos? It may take some time to zip and download.",
"MergeSelectedVideosMsg": "Do you want to merge these videos? It may take some time to merge and download. The moment the connection is closed the file will be deleted. Ensure you keep the browser open until it is complete.",
"clientStreamFailedattemptingReconnect": "Client side ctream check failed, attempting reconnect.",
"Export Video": "Export Video",
"Merge Video": "Merge Video",
"Delete Filter": "Delete Filter",
"confirmDeleteFilter": "Do you want to delete this filter? You cannot recover it.",
"Fix Video": "Fix Video",
......@@ -348,7 +369,7 @@
"Connected": "Connected",
"Not Saved": "Not Saved",
"Not Connected": "Not Connected",
"Lisence Plate Detector": "Lisence Plate Detector",
"License Plate Detector": "License Plate Detector",
"OpenCV Cascades": "OpenCV Cascades",
"Refresh List of Cascades": "Refresh List of Cascades",
"\"No Motion\" Detector": "\"No Motion\" Detector",
......@@ -391,6 +412,7 @@
"HLS Preset": "Preset Template",
"HLS List Size": "List Size",
"Traditional Recording": "Traditional Recording",
"Recorded Buffer": "Recorded Buffer",
"Buffer Preview": "Buffer Preview",
"HLS Start Number": "HLS Start Number",
"HLS Live Start Index": "HLS Live Start Index",
......@@ -415,6 +437,7 @@
"Image Width": "Image Width",
"Image Height": "Image Height",
"Record File Type": "Record File Type",
"Notification Video Length": "Notification Video Length",
"Video Codec": "Video Codec",
"Delete Monitor States Preset": "Delete Monitor States Preset",
"Delete Monitor State?": "Delete Monitor State",
......@@ -715,6 +738,7 @@
"Preview":"Preview",
"Websocket Connected":"Websocket Connected",
"Websocket Disconnected":"Websocket Disconnected",
"Videos Merge":"Videos Merge",
"Token":"Token",
"Channel ID":"Channel ID",
"New Authentication Token":"New Authentication Token",
......@@ -732,10 +756,13 @@
"Can use Discord Bot":"Can use Discord Bot",
"Can use WebDAV":"Can use WebDAV",
"Can use Amazon S3":"Can use Amazon S3",
"Can use SFTP":"Can use SFTP",
"Can use Wasabi Hot Cloud Storage":"Can use Wasabi Hot Cloud Storage",
"Can use LDAP":"Can use LDAP",
"Can View Logs":"Can View Logs",
"Can edit how long to keep Events":"Can edit how long to keep Events",
"Leave blank for unlimited":"Leave blank for unlimited",
"privateKey":"Private Key",
"Limited":"Limited",
"All Privileges":"All Privileges",
"LDAP":"LDAP",
......
This diff is collapsed.
......@@ -241,4 +241,15 @@ module.exports = function(s,config){
if(callback)callback()
},theTimeout)
}
Object.defineProperty(Array.prototype, 'chunk', {
value: function(chunkSize){
var temporal = [];
for (var i = 0; i < this.length; i+= chunkSize){
temporal.push(this.slice(i,i+chunkSize));
}
return temporal;
}
});
}
This diff is collapsed.
var fs = require('fs');
var execSync = require('child_process').execSync;
module.exports = function(s,config,lang,io){
module.exports = function(s,config,lang){
var onFFmpegLoaded = function(ffmpeg){
if(process.argv[2] && process.argv[2].indexOf('test') > -1){
config.testMode = true
......
......@@ -34,7 +34,7 @@ module.exports = function(s){
if(config.databaseLogs === undefined){config.databaseLogs=false}
if(config.useUTC === undefined){config.useUTC=false}
if(config.iconURL === undefined){config.iconURL = "https://shinobi.video/libs/assets/icon/apple-touch-icon-152x152.png"}
if(config.pipeAddition === undefined){config.pipeAddition=7}else{config.pipeAddition=parseInt(config.pipeAddition)}
if(config.pipeAddition === undefined){config.pipeAddition=10}else{config.pipeAddition=parseInt(config.pipeAddition)}
if(config.hideCloudSaveUrls === undefined){config.hideCloudSaveUrls = true}
if(config.insertOrphans === undefined){config.insertOrphans = true}
if(config.orphanedVideoCheckMax === undefined){config.orphanedVideoCheckMax = 20}
......@@ -51,5 +51,12 @@ module.exports = function(s){
if(config.childNodes.key === undefined)config.childNodes.key = [
'3123asdasdf1dtj1hjk23sdfaasd12asdasddfdbtnkkfgvesra3asdsd3123afdsfqw345'
];
if(config.cron.key === 'change_this_to_something_very_random__just_anything_other_than_this'){
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
console.error('!! Change your cron key in your conf.json. !!')
console.error(`!! If you're running Shinobi remotely you should do this now. !!`)
console.error('!! You can do this in the Super User panel or from terminal. !!')
console.error('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
}
return config
}
var fs = require('fs')
var execSync = require('child_process').execSync
module.exports = function(s,config,lang,app,io){
if(config.dropInEventServer === true){
if(config.dropInEventDeleteFileAfterTrigger === undefined)config.dropInEventDeleteFileAfterTrigger = true
var authenticateUser = function(username,password,callback){
var splitUsername = username.split('@')
if(splitUsername[1] !== 'Shinobi' && splitUsername[1] !== 'shinobi'){
s.sqlQuery('SELECT ke,uid FROM Users WHERE mail=? AND (pass=? OR pass=?)',[
username,
password,
s.createHash(password)
],function(err,r){
var user
if(r && r[0]){
user = r[0]
}
callback(err,user)
})
}else{
s.sqlQuery('SELECT ke,uid FROM API WHERE code=? AND ke=?',[
splitUsername[0], //code
password //ke
],function(err,r){
var apiKey
if(r && r[0]){
apiKey = r[0]
}
callback(err,apiKey)
})
}
}
var beforeMonitorsLoadedOnStartup = function(){
if(!config.dropInEventsDir){
config.dropInEventsDir = s.dir.streams + 'dropInEvents/'
}
s.dir.dropInEvents = s.checkCorrectPathEnding(config.dropInEventsDir)
//dropInEvents dir
if(!fs.existsSync(s.dir.dropInEvents)){
fs.mkdirSync(s.dir.dropInEvents)
}
}
var getDropInEventDir = function(monitorConfig){
var ke = monitorConfig.ke
var mid = monitorConfig.mid
var groupEventDropDir = s.dir.dropInEvents + ke
var monitorEventDropDir = groupEventDropDir + '/' + mid + '/'
return monitorEventDropDir
}
var onMonitorStop = function(monitorConfig){
var ke = monitorConfig.ke
var mid = monitorConfig.mid
if(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher){
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher.close()
delete(s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher)
}
var monitorEventDropDir = getDropInEventDir(monitorConfig)
if(fs.existsSync(monitorEventDropDir))execSync('rm -rf ' + monitorEventDropDir)
}
var onMonitorInit = function(monitorConfig){
onMonitorStop(monitorConfig)
var ke = monitorConfig.ke
var mid = monitorConfig.mid
var monitorEventDropDir = getDropInEventDir(monitorConfig)
var groupEventDropDir = s.dir.dropInEvents + ke
if(!fs.existsSync(groupEventDropDir)){
fs.mkdirSync(groupEventDropDir)
}
var monitorEventDropDir = groupEventDropDir + '/' + mid + '/'
if(!fs.existsSync(monitorEventDropDir)){
fs.mkdirSync(monitorEventDropDir)
}
var fileQueue = {}
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventFileQueue = fileQueue
var eventTrigger = function(eventType,filename){
var filePath = monitorEventDropDir + filename
if(filename.indexOf('.jpg') > -1 || filename.indexOf('.jpeg') > -1){
var snapPath = s.dir.streams + ke + '/' + mid + '/s.jpg'
fs.unlink(snapPath,function(err){
fs.createReadStream(filePath).pipe(fs.createWriteStream(snapPath))
s.triggerEvent({
id: mid,
ke: ke,
details: {
confidence: 100,
name: filename,
plug: "dropInEvent",
reason: "dropInEvent"
}
})
})
}else{
s.triggerEvent({
id: mid,
ke: ke,
details: {
confidence: 100,
name: filename,
plug: "dropInEvent",
reason: "ftpServer"
}
})
}
if(config.dropInEventDeleteFileAfterTrigger){
setTimeout(function(){
fs.unlink(filePath,function(err){
})
},1000 * 60 * 5)
}
}
var directoryWatch = fs.watch(monitorEventDropDir,function(eventType,filename){
if(fs.existsSync(monitorEventDropDir + filename)){
clearTimeout(fileQueue[filename])
fileQueue[filename] = setTimeout(function(){
eventTrigger(eventType,filename)
},1200)
}
})
s.group[monitorConfig.ke].mon[monitorConfig.mid].dropInEventWatcher = directoryWatch
}
// FTP Server
if(config.ftpServer === true){
if(!config.ftpServerPort)config.ftpServerPort = 21
if(!config.ftpServerUrl)config.ftpServerUrl = `ftp://0.0.0.0:${config.ftpServerPort}`
config.ftpServerUrl = config.ftpServerUrl.replace('{{PORT}}',config.ftpServerPort)
const FtpSrv = require('ftp-srv')
const ftpServer = new FtpSrv({
url: config.ftpServerUrl,
// log:{trace:function(){},error:function(){},child:function(){},info:function(){},warn:function(){}
})
ftpServer.on('login', (data, resolve, reject) => {
var username = data.username
var password = data.password
authenticateUser(username,password,function(err,user){
if(user){
resolve({root: s.dir.dropInEvents + user.ke})
}else{
// reject(new Error('Failed Authorization'))
}
})
})
ftpServer.listen().then(() => {
s.systemLog(`FTP Server running on port ${config.ftpServerPort}...`)
}).catch(function(err){
s.systemLog(err)
})
}
//add extensions
s.beforeMonitorsLoadedOnStartup(beforeMonitorsLoadedOnStartup)
s.onMonitorInit(onMonitorInit)
s.onMonitorStop(onMonitorStop)
}
// SMTP Server
// allow starting SMTP server without dropInEventServer
if(config.smtpServer === true){
var SMTPServer = require("smtp-server").SMTPServer;
if(!config.smtpServerPort && (config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl)){config.smtpServerPort = 465}else if(!config.smtpServerPort){config.smtpServerPort = 25}
var smtpOptions = {
onAuth(auth, session, callback) {
var username = auth.username
var password = auth.password
authenticateUser(username,password,function(err,user){
if(user){
callback(null, {user: user.ke})
}else{
callback(new Error(lang.failedLoginText2))
}
})
},
onRcptTo(address, session, callback) {
var split = address.address.split('@')
var monitorId = split[0]
var ke = session.user
if(s.group[ke].mon_conf[monitorId] && s.group[ke].mon[monitorId].isStarted === true){
s.triggerEvent({
id: monitorId,
ke: ke,
details: {
confidence: 100,
name: address.address,
plug: "dropInEvent",
reason: "smtpServer"
}
})
}else{
return callback(new Error(lang['No Monitor Exists with this ID.']))
}
callback()
}
}
if(config.smtpServerSsl && config.smtpServerSsl.enabled !== false || config.ssl && config.ssl.cert && config.ssl.key){
var key = config.ssl.key || fs.readFileSync(config.smtpServerSsl.key)
var cert = config.ssl.cert || fs.readFileSync(config.smtpServerSsl.cert)
smtpOptions = Object.assign(smtpOptions,{
secure: true,
key: config.ssl.key,
cert: config.ssl.cert
})
}
var server = new SMTPServer(smtpOptions)
server.listen(config.smtpServerPort,function(){
s.systemLog(`SMTP Server running on port ${config.smtpServerPort}...`)
})
}
}
......@@ -10,7 +10,7 @@ module.exports = function(s,config,lang){
var newString = string + ''
var d = Object.assign(eventData,addOps)
var detailString = s.stringJSON(d.details)
newString
newString = newString
.replace(/{{TIME}}/g,d.currentTimestamp)
.replace(/{{REGION_NAME}}/g,d.details.name)
.replace(/{{SNAP_PATH}}/g,s.dir.streams+'/'+d.ke+'/'+d.id+'/s.jpg')
......@@ -55,9 +55,6 @@ module.exports = function(s,config,lang){
s.onEventTriggerBeforeFilterExtensions.forEach(function(extender){
extender(d,filter)
})
if(s.group[d.ke].mon[d.id].open){
d.details.videoTime = s.group[d.ke].mon[d.id].open;
}
var detailString = JSON.stringify(d.details);
if(!s.group[d.ke]||!s.group[d.ke].mon[d.id]){
return s.systemLog(lang['No Monitor Found, Ignoring Request'])
......@@ -239,9 +236,25 @@ module.exports = function(s,config,lang){
frame : s.group[d.ke].mon[d.id].lastJpegDetectorFrame
})
}else{
if(currentConfig.detector_multi_trigger === '1'){
s.getCamerasForMultiTrigger(d.mon).forEach(function(monitor){
if(monitor.mid !== d.id){
s.triggerEvent({
id: monitor.mid,
ke: monitor.ke,
details: {
confidence: 100,
name: "multiTrigger",
plug: d.details.plug,
reason: d.details.reason
}
})
}
})
}
//save this detection result in SQL, only coords. not image.
if(filter.save && currentConfig.detector_save === '1'){
s.sqlQuery('INSERT INTO Events (ke,mid,details) VALUES (?,?,?)',[d.ke,d.id,detailString])
s.sqlQuery('INSERT INTO Events (ke,mid,details,time) VALUES (?,?,?,?)',[d.ke,d.id,detailString,new Date()])
}
if(currentConfig.detector_notrigger === '1'){
var detector_notrigger_timeout
......@@ -321,6 +334,9 @@ module.exports = function(s,config,lang){
s.createEventBasedRecording = function(d){
d.mon = s.group[d.ke].mon_conf[d.id]
var currentConfig = s.group[d.ke].mon[d.id].details
if(currentConfig.detector !== '1'){
return
}
var detector_timeout
if(!currentConfig.detector_timeout||currentConfig.detector_timeout===''){
detector_timeout = 10
......
......@@ -40,6 +40,11 @@ module.exports = function(s,config){
s.onTwoFactorAuthCodeNotificationExtensions.push(callback)
}
//
s.onStalePurgeLockExtensions = []
s.onStalePurgeLock = function(callback){
s.onStalePurgeLockExtensions.push(callback)
}
//
s.cloudDiskUseStartupExtensions = {}
////// EVENTS //////
......@@ -72,6 +77,11 @@ module.exports = function(s,config){
s.onMonitorStopExtensions.push(callback)
}
//
s.onMonitorSaveExtensions = []
s.onMonitorSave = function(callback){
s.onMonitorSaveExtensions.push(callback)
}
//
s.onMonitorUnexpectedExitExtensions = []
s.onMonitorUnexpectedExit = function(callback){
s.onMonitorUnexpectedExitExtensions.push(callback)
......@@ -91,6 +101,11 @@ module.exports = function(s,config){
s.onMonitorPingFailed = function(callback){
s.onMonitorPingFailedExtensions.push(callback)
}
//
s.onMonitorDiedExtensions = []
s.onMonitorDied = function(callback){
s.onMonitorDiedExtensions.push(callback)
}
///////// SYSTEM ////////
s.onProcessReadyExtensions = []
......@@ -113,4 +128,19 @@ module.exports = function(s,config){
s.onFFmpegLoadedExtensions.push(callback)
}
//
s.beforeMonitorsLoadedOnStartupExtensions = []
s.beforeMonitorsLoadedOnStartup = function(callback){
s.beforeMonitorsLoadedOnStartupExtensions.push(callback)
}
//
s.onWebSocketConnectionExtensions = []
s.onWebSocketConnection = function(callback){
s.onWebSocketConnectionExtensions.push(callback)
}
//
s.onWebSocketDisconnectionExtensions = []
s.onWebSocketDisconnection = function(callback){
s.onWebSocketDisconnectionExtensions.push(callback)
}
//
}
......@@ -135,7 +135,9 @@ module.exports = function(s,config,onFinish){
string += ' -map '+v.map
})
}else{
string += ' -map 0'
var primaryMap = '0:0'
if(e.details.primary_input && e.details.primary_input !== '')primaryMap = e.details.primary_input
string += ' -map ' + primaryMap
}
}
return string;
......@@ -645,7 +647,7 @@ module.exports = function(s,config,onFinish){
x.dimensions = e.details.stream_scale_x+'x'+e.details.stream_scale_y;
}
//record - segmenting
x.segment=' -f segment -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"';
x.segment=' -f segment -segment_format_options movflags=faststart+frag_keyframe+empty_moov -segment_atclocktime 1 -reset_timestamps 1 -strftime 1 -segment_list pipe:2 -segment_time '+(60*e.cutoff)+' "'+e.dir+'%Y-%m-%dT%H-%M-%S.'+e.ext+'"';
//record - set defaults for extension, video quality
switch(e.ext){
case'mp4':
......@@ -765,31 +767,46 @@ module.exports = function(s,config,onFinish){
//x = temporary values
x.cust_detect = ' '
//detector - plugins, motion
if(e.details.detector === '1' && e.details.detector_send_frames === '1' && e.coProcessor === false){
if(e.details.input_map_choices&&e.details.input_map_choices.detector){
var sendFramesGlobally = (e.details.detector_send_frames === '1')
var sendFramesToObjectDetector = (e.details.detector_send_frames_object !== '0' && e.details.detector_use_detect_object === '1')
if(e.details.detector === '1' && (sendFramesGlobally || sendFramesToObjectDetector) && e.coProcessor === false){
if(sendFramesGlobally && e.details.input_map_choices && e.details.input_map_choices.detector){
//add input feed map
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
}
if(!e.details.detector_fps||e.details.detector_fps===''){e.details.detector_fps=2}
if(e.details.detector_scale_x&&e.details.detector_scale_x!==''&&e.details.detector_scale_y&&e.details.detector_scale_y!==''){x.dratio=' -s '+e.details.detector_scale_x+'x'+e.details.detector_scale_y}else{x.dratio=' -s 320x240'}
if(!e.details.detector_fps || e.details.detector_fps === ''){x.detector_fps = 2}else{x.detector_fps = parseInt(e.details.detector_fps)}
if(e.details.detector_scale_x && e.details.detector_scale_x !== '' && e.details.detector_scale_y && e.details.detector_scale_y !== ''){x.dratio=' -s '+e.details.detector_scale_x+'x'+e.details.detector_scale_y}else{x.dratio=' -s 320x240'}
if(e.details.cust_detect&&e.details.cust_detect!==''){x.cust_detect+=e.details.cust_detect;}
x.detector_vf = ['fps='+e.details.detector_fps]
if(sendFramesGlobally)x.pipe += ' -r ' + x.detector_fps + x.dratio + x.cust_detect
x.detector_vf = []
if(e.cudaEnabled){
x.detector_vf.push('hwdownload,format=nv12')
}
x.detector_vf = '-vf "'+x.detector_vf.join(',')+'"'
if(e.details.detector_pam==='1'){
if(e.cudaEnabled){
if(sendFramesGlobally && x.detector_vf.length > 0)x.pipe += ' -vf "'+x.detector_vf.join(',')+'"'
var h264Output = ' -q:v 1 -an -c:v libx264 -f hls -tune zerolatency -g 1 -hls_time 2 -hls_list_size 3 -start_number 0 -live_start_index 3 -hls_allow_cache 0 -hls_flags +delete_segments+omit_endlist "'+e.sdir+'detectorStreamX.m3u8"'
if(e.details.detector_pam === '1'){
if(sendFramesGlobally && e.cudaEnabled){
x.pipe += ' -vf "hwdownload,format=nv12"'
}
x.pipe+=' -an -c:v pam -pix_fmt gray -f image2pipe -r '+e.details.detector_fps+x.cust_detect+x.dratio+' pipe:3'
if(sendFramesGlobally)x.pipe += ' -an -c:v pam -pix_fmt gray -f image2pipe pipe:3'
if(e.details.detector_use_detect_object === '1'){
//for object detection
x.pipe += s.createFFmpegMap(e,e.details.input_map_choices.detector)
x.pipe += ' -an -f singlejpeg '+x.detector_vf+x.cust_detect+x.dratio+' pipe:4';
if(e.details.detector_scale_x_object&&e.details.detector_scale_x_object!==''&&e.details.detector_scale_y_object&&e.details.detector_scale_y_object!==''){x.dobjratio=' -s '+e.details.detector_scale_x_object+'x'+e.details.detector_scale_y_object}else{x.dobjratio=x.dratio}
x.pipe += ' -r ' + x.detector_fps + x.dobjratio + x.cust_detect
if(e.details.detector_h264 === '1'){
x.pipe += h264Output
}else{
x.pipe += ' -an -f singlejpeg pipe:4'
}
}
}else if(sendFramesGlobally){
if(e.details.detector_h264 === '1'){
x.pipe += h264Output