const fs = require('fs'); const treekill = require('tree-kill'); const spawn = require('child_process').spawn; module.exports = (s,config,lang) => { const { splitForFFPMEG, } = require('../ffmpeg/utils.js')(s,config,lang) const getUpdateableFields = require('./updatedFields.js') const processKill = (proc) => { const response = {ok: true} return new Promise((resolve,reject) => { function sendError(err){ response.ok = false response.err = err resolve(response) } try{ proc.stdin.write("q\r\n") setTimeout(() => { if(proc && proc.kill){ if(s.isWin){ spawn("taskkill", ["/pid", proc.pid, '/t']) }else{ proc.kill('SIGTERM') } setTimeout(function(){ try{ proc.kill() resolve(response) }catch(err){ s.debugLog(err) sendError(err) } },1000) } },1000) }catch(err){ s.debugLog(err) sendError(err) } }) } const cameraDestroy = function(e,p){ if( s.group[e.ke] && s.group[e.ke].activeMonitors[e.id] && s.group[e.ke].activeMonitors[e.id].spawn !== undefined ){ const activeMonitor = s.group[e.ke].activeMonitors[e.id]; const proc = s.group[e.ke].activeMonitors[e.id].spawn; if(proc){ activeMonitor.allowStdinWrite = false s.txToDashcamUsers({ f : 'disable_stream', ke : e.ke, mid : e.id },e.ke) // if(activeMonitor.p2pStream){activeMonitor.p2pStream.unpipe();} try{ proc.removeListener('end',activeMonitor.spawn_exit); proc.removeListener('exit',activeMonitor.spawn_exit); delete(activeMonitor.spawn_exit); }catch(er){ } } if(activeMonitor.audioDetector){ activeMonitor.audioDetector.stop() delete(activeMonitor.audioDetector) } activeMonitor.firstStreamChunk = {} clearTimeout(activeMonitor.recordingChecker); delete(activeMonitor.recordingChecker); clearTimeout(activeMonitor.streamChecker); delete(activeMonitor.streamChecker); clearTimeout(activeMonitor.checkSnap); delete(activeMonitor.checkSnap); clearTimeout(activeMonitor.watchdog_stop); delete(activeMonitor.watchdog_stop); // delete(activeMonitor.secondaryDetectorOutput); delete(activeMonitor.detectorFrameSaveBuffer); clearTimeout(activeMonitor.recordingSnapper); clearInterval(activeMonitor.getMonitorCpuUsage); clearInterval(activeMonitor.objectCountIntervals); delete(activeMonitor.onvifConnection) if(activeMonitor.onChildNodeExit){ activeMonitor.onChildNodeExit() } activeMonitor.spawn.stdio.forEach(function(stdio){ try{ stdio.unpipe() }catch(err){ console.log(err) } }) if(activeMonitor.mp4frag){ var mp4FragChannels = Object.keys(activeMonitor.mp4frag) mp4FragChannels.forEach(function(channel){ activeMonitor.mp4frag[channel].removeAllListeners() delete(activeMonitor.mp4frag[channel]) }) } if(config.childNodes.enabled === true && config.childNodes.mode === 'child' && config.childNodes.host){ s.cx({f:'clearCameraFromActiveList',ke:e.ke,id:e.id}) } if(activeMonitor.childNode){ s.cx({f:'kill',d:s.cleanMonitorObject(e)},activeMonitor.childNodeId) }else{ processKill(proc).then((response) => { s.debugLog(`cameraDestroy`,response) }) } } } const createSnapshot = (options) => { const url = options.url const streamDir = options.streamDir || s.dir.streams const inputOptions = options.input || [] const outputOptions = options.output || [] return new Promise((resolve,reject) => { if(!url){ resolve(null); return } const completeRequest = () => { fs.readFile(temporaryImageFile,(err,imageBuffer) => { fs.unlink(temporaryImageFile,(err) => { if(err){ s.debugLog(err) } }) if(err){ s.debugLog(err) } resolve(imageBuffer) }) } const temporaryImageFile = streamDir + s.gid(5) + '.jpg' const ffmpegCmd = splitForFFPMEG(`-y -loglevel warning -re ${inputOptions.join(' ')} -i "${url}" ${outputOptions.join(' ')} -f image2 -an -frames:v 1 "${temporaryImageFile}"`) const snapProcess = spawn('ffmpeg',ffmpegCmd,{detached: true}) snapProcess.stderr.on('data',function(data){ // s.debugLog(data.toString()) }) snapProcess.on('close',async function(data){ clearTimeout(snapProcessTimeout) completeRequest() }) var snapProcessTimeout = setTimeout(function(){ processKill(snapProcess).then((response) => { s.debugLog(`createSnapshot-snapProcessTimeout`,response) completeRequest() }) },5000) }) } const addCredentialsToStreamLink = (options) => { const streamUrl = options.url const username = options.username const password = options.password const urlParts = streamUrl.split('://') urlParts[0] = 'http' return ['rtsp','://',`${username}:${password}@`,urlParts[1]].join('') } const monitorConfigurationMigrator = (monitor) => { // converts the old style to the new style. const updatedFields = getUpdateableFields() const fieldKeys = Object.keys(updatedFields) fieldKeys.forEach((oldKey) => { if(oldKey === 'details'){ const detailKeys = Object.keys(updatedFields.details) detailKeys.forEach((oldKey) => { if(oldKey === 'stream_channels'){ if(monitor.details.stream_channels){ const channelUpdates = updatedFields.details.stream_channels const channelKeys = Object.keys(channelUpdates) const streamChannels = s.parseJSON(monitor.details.stream_channels) || [] streamChannels.forEach(function(channel,number){ channelKeys.forEach((oldKey) => { const newKey = channelUpdates[oldKey] monitor.details.stream_channels[number][newKey] = streamChannels[number][oldKey] ? streamChannels[number][oldKey] : monitor.details.stream_channels[number][newKey] // delete(e.details.stream_channels[number][oldKey]) }) }) } }else{ const newKey = updatedFields.details[oldKey] monitor.details[newKey] = monitor.details[oldKey] ? monitor.details[oldKey] : monitor.details[newKey] // delete(monitor.details[oldKey]) } }) }else{ const newKey = updatedFields[oldKey] monitor[newKey] = monitor[oldKey] ? monitor[oldKey] : monitor[newKey] // delete(monitor[oldKey]) } }) } return { cameraDestroy: cameraDestroy, createSnapshot: createSnapshot, processKill: processKill, addCredentialsToStreamLink: addCredentialsToStreamLink, monitorConfigurationMigrator: monitorConfigurationMigrator, } }