Substream can now be used for On-Demand Live Stream on Dashboard
parent
ea5120fded
commit
3cf38a7843
|
|
@ -765,6 +765,10 @@ module.exports = function(s,config,lang){
|
|||
"name": lang['HLS (includes Audio)'],
|
||||
"value": "hls",
|
||||
"info": "Similar method to facebook live streams. <b>Includes audio</b> if input provides it. There is a delay of about 4-6 seconds because this method records segments then pushes them to the client rather than push as while it creates them."
|
||||
},
|
||||
{
|
||||
"name": lang.useSubStreamOnlyWhenWatching,
|
||||
"value": "useSubstream",
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1286,11 +1290,15 @@ module.exports = function(s,config,lang){
|
|||
isAdvanced: true,
|
||||
"isSection": true,
|
||||
"id": "monSectionSubstream",
|
||||
"blockquote": lang.substreamText,
|
||||
"blockquoteClass": 'global_tip',
|
||||
"info": [
|
||||
{
|
||||
"name": lang['Connection'],
|
||||
"color": "orange",
|
||||
id: "monSectionSubstreamInput",
|
||||
"blockquote": lang.substreamConnectionText,
|
||||
"blockquoteClass": 'global_tip',
|
||||
isSection: true,
|
||||
isFormGroupGroup: true,
|
||||
"info": [
|
||||
|
|
@ -1522,6 +1530,8 @@ module.exports = function(s,config,lang){
|
|||
"name": lang['Output'],
|
||||
"color": "blue",
|
||||
id: "monSectionSubstreamOutput",
|
||||
"blockquote": lang.substreamOutputText,
|
||||
"blockquoteClass": 'global_tip',
|
||||
isSection: true,
|
||||
isFormGroupGroup: true,
|
||||
"info": [
|
||||
|
|
@ -1538,10 +1548,6 @@ module.exports = function(s,config,lang){
|
|||
"name": lang.Poseidon,
|
||||
"value": "mp4",
|
||||
},
|
||||
{
|
||||
"name": lang["RTMP Stream"],
|
||||
"value": "rtmp",
|
||||
},
|
||||
{
|
||||
"name": lang['MJPEG'],
|
||||
"value": "mjpeg",
|
||||
|
|
@ -1557,18 +1563,6 @@ module.exports = function(s,config,lang){
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"field": lang['Server URL'],
|
||||
"name": `detail-substream-output="rtmp_server_url"`,
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_rtmp",
|
||||
"example": "rtmp://live-api.facebook.com:80/rtmp/",
|
||||
},
|
||||
{
|
||||
"field": lang['Stream Key'],
|
||||
"name": `detail-substream-output="rtmp_stream_key"`,
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_rtmp",
|
||||
"example": "1111111111?ds=1&a=xxxxxxxxxx",
|
||||
},
|
||||
{
|
||||
"field": lang['# of Allow MJPEG Clients'],
|
||||
"name": `detail-substream-output="stream_mjpeg_clients"`,
|
||||
|
|
@ -1580,7 +1574,7 @@ module.exports = function(s,config,lang){
|
|||
"name": `detail-substream-output="stream_vcodec"`,
|
||||
"description": "Video codec for streaming.",
|
||||
"default": "copy",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
"fieldType": "select",
|
||||
"selector": "h_hls_v_channel_SUBSTREAM_FIELDS",
|
||||
"possible": [
|
||||
|
|
@ -1663,7 +1657,7 @@ module.exports = function(s,config,lang){
|
|||
"default": "",
|
||||
"example": "",
|
||||
"fieldType": "select",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
"possible": [
|
||||
{
|
||||
"name": lang.Auto,
|
||||
|
|
@ -1735,7 +1729,7 @@ module.exports = function(s,config,lang){
|
|||
"description": "Low number means higher quality. Higher number means less quality.",
|
||||
"default": "15",
|
||||
"example": "1",
|
||||
"form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
// "form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_mjpeg h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_jsmpeg h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
"possible": "1-23"
|
||||
},
|
||||
|
|
@ -1757,7 +1751,7 @@ module.exports = function(s,config,lang){
|
|||
"name": "detail-substream-output=stream_fps",
|
||||
"field": lang['Frame Rate'],
|
||||
"description": "The speed in which frames are displayed to clients, in Frames Per Second. Be aware there is no default. This can lead to high bandwidth usage.",
|
||||
"form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
// "form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_mjpeg h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_jsmpeg h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
},
|
||||
{
|
||||
|
|
@ -1767,7 +1761,7 @@ module.exports = function(s,config,lang){
|
|||
"fieldType": "number",
|
||||
"numberMin": "1",
|
||||
"example": "640",
|
||||
"form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
// "form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_mjpeg h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_jsmpeg h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
},
|
||||
{
|
||||
|
|
@ -1777,7 +1771,7 @@ module.exports = function(s,config,lang){
|
|||
"fieldType": "number",
|
||||
"numberMin": "1",
|
||||
"example": "480",
|
||||
"form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
// "form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_mjpeg h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_jsmpeg h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
},
|
||||
{
|
||||
|
|
@ -1785,7 +1779,7 @@ module.exports = function(s,config,lang){
|
|||
"field": lang["Rotate"],
|
||||
"description": "Change the viewing angle of the video stream.",
|
||||
"fieldType": "select",
|
||||
"form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
// "form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_mjpeg h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_jsmpeg h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
"possible": [
|
||||
{
|
||||
|
|
@ -1818,7 +1812,7 @@ module.exports = function(s,config,lang){
|
|||
"name": "detail-substream-output=svf",
|
||||
"field": lang["Video Filter"],
|
||||
"description": "Place FFMPEG video filters in this box to affect the streaming portion. No spaces.",
|
||||
"form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
// "form-group-class-pre-layer": "h_hls_v_channel_SUBSTREAM_FIELDS_input h_hls_v_channel_SUBSTREAM_FIELDS_libx264 h_hls_v_channel_SUBSTREAM_FIELDS_libx265 h_hls_v_channel_SUBSTREAM_FIELDS_h264_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_hevc_nvenc h_hls_v_channel_SUBSTREAM_FIELDS_no",
|
||||
"form-group-class": "h_st_channel_SUBSTREAM_FIELDS_input h_st_channel_SUBSTREAM_FIELDS_mjpeg h_st_channel_SUBSTREAM_FIELDS_hls h_st_channel_SUBSTREAM_FIELDS_rtmp h_st_channel_SUBSTREAM_FIELDS_jsmpeg h_st_channel_SUBSTREAM_FIELDS_flv h_st_channel_SUBSTREAM_FIELDS_mp4 h_st_channel_SUBSTREAM_FIELDS_h264",
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,8 +23,14 @@
|
|||
"Use Raw Snapshot": "Use Raw Snapshot",
|
||||
"Login": "Login",
|
||||
"Substream": "Substream",
|
||||
"Use Substream": "Use Substream",
|
||||
"useSubStreamOnlyWhenWatching": "Only When Watching, Use Substream",
|
||||
"substreamText": "This is an On-Demand method of viewing the Live Stream. You can make it so the viewing process is available only when someone is watching or to be used for switching between Low and High Resolution.",
|
||||
"substreamConnectionText": "You can leave the Connection detail as-is if you want it to use the main Connection information set above.",
|
||||
"substreamOutputText": "Here you can set the On-Demand Stream's configuration. Learn about <a href='https://hub.shinobi.video/articles/view/Eug1dxIdhwY6zTw' target='_blank'>latency of Stream types here.</a>",
|
||||
"Toggle Substream": "Toggle Substream",
|
||||
"Output": "Output",
|
||||
"SubstreamNotConfigured": "Substream not configured. Open your Monitor Settings and configure it.",
|
||||
"Substream Process": "Substream Process",
|
||||
"Welcome": "Welcome!",
|
||||
"API Key Action Failed": "API Key Action Failed",
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ module.exports = async (s,config,lang,onFinish) => {
|
|||
|
||||
s.ffmpeg = function(e){
|
||||
try{
|
||||
const activeMonitor = s.group[e.ke].activeMonitors[e.mid]
|
||||
const ffmpegCommand = [`-progress pipe:5`];
|
||||
([
|
||||
buildMainInput(e),
|
||||
const allOutputs = [
|
||||
buildMainStream(e),
|
||||
buildJpegApiOutput(e),
|
||||
buildMainRecording(e),
|
||||
|
|
@ -36,45 +36,52 @@ module.exports = async (s,config,lang,onFinish) => {
|
|||
buildMainDetector(e),
|
||||
buildEventRecordingOutput(e),
|
||||
buildTimelapseOutput(e),
|
||||
]).forEach(function(commandStringPart){
|
||||
ffmpegCommand.push(commandStringPart)
|
||||
})
|
||||
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
|
||||
extender(e,ffmpegCommand)
|
||||
})
|
||||
const stdioPipes = createPipeArray(e)
|
||||
const ffmpegCommandString = ffmpegCommand.join(' ')
|
||||
//hold ffmpeg command for log stream
|
||||
s.group[e.ke].activeMonitors[e.mid].ffmpeg = sanitizedFfmpegCommand(e,ffmpegCommandString)
|
||||
//clean the string of spatial impurities and split for spawn()
|
||||
const ffmpegCommandParsed = splitForFFPMEG(ffmpegCommandString)
|
||||
try{
|
||||
fs.unlinkSync(e.sdir + 'cmd.txt')
|
||||
}catch(err){
|
||||
|
||||
}
|
||||
fs.writeFileSync(e.sdir + 'cmd.txt',JSON.stringify({
|
||||
cmd: ffmpegCommandParsed,
|
||||
pipes: stdioPipes.length,
|
||||
rawMonitorConfig: s.group[e.ke].rawMonitorConfigurations[e.id],
|
||||
globalInfo: {
|
||||
config: config,
|
||||
isAtleatOneDetectorPluginConnected: s.isAtleatOneDetectorPluginConnected
|
||||
}
|
||||
},null,3),'utf8')
|
||||
var cameraCommandParams = [
|
||||
config.monitorDaemonPath ? config.monitorDaemonPath : __dirname + '/cameraThread/singleCamera.js',
|
||||
config.ffmpegDir,
|
||||
e.sdir + 'cmd.txt'
|
||||
]
|
||||
const cameraProcess = spawn('node',cameraCommandParams,{detached: true,stdio: stdioPipes})
|
||||
if(config.debugLog === true && config.debugLogMonitors === true){
|
||||
cameraProcess.stderr.on('data',(data) => {
|
||||
console.log(`${e.ke} ${e.mid}`)
|
||||
console.log(data.toString())
|
||||
];
|
||||
if(allOutputs.filter(output => !!output).length > 0){
|
||||
([
|
||||
buildMainInput(e),
|
||||
]).concat(allOutputs).forEach(function(commandStringPart){
|
||||
ffmpegCommand.push(commandStringPart)
|
||||
})
|
||||
s.onFfmpegCameraStringCreationExtensions.forEach(function(extender){
|
||||
extender(e,ffmpegCommand)
|
||||
})
|
||||
const stdioPipes = createPipeArray(e)
|
||||
const ffmpegCommandString = ffmpegCommand.join(' ')
|
||||
//hold ffmpeg command for log stream
|
||||
activeMonitor.ffmpeg = sanitizedFfmpegCommand(e,ffmpegCommandString)
|
||||
//clean the string of spatial impurities and split for spawn()
|
||||
const ffmpegCommandParsed = splitForFFPMEG(ffmpegCommandString)
|
||||
try{
|
||||
fs.unlinkSync(e.sdir + 'cmd.txt')
|
||||
}catch(err){
|
||||
|
||||
}
|
||||
fs.writeFileSync(e.sdir + 'cmd.txt',JSON.stringify({
|
||||
cmd: ffmpegCommandParsed,
|
||||
pipes: stdioPipes.length,
|
||||
rawMonitorConfig: s.group[e.ke].rawMonitorConfigurations[e.id],
|
||||
globalInfo: {
|
||||
config: config,
|
||||
isAtleatOneDetectorPluginConnected: s.isAtleatOneDetectorPluginConnected
|
||||
}
|
||||
},null,3),'utf8')
|
||||
var cameraCommandParams = [
|
||||
config.monitorDaemonPath ? config.monitorDaemonPath : __dirname + '/cameraThread/singleCamera.js',
|
||||
config.ffmpegDir,
|
||||
e.sdir + 'cmd.txt'
|
||||
]
|
||||
const cameraProcess = spawn('node',cameraCommandParams,{detached: true,stdio: stdioPipes})
|
||||
if(config.debugLog === true && config.debugLogMonitors === true){
|
||||
cameraProcess.stderr.on('data',(data) => {
|
||||
console.log(`${e.ke} ${e.mid}`)
|
||||
console.log(data.toString())
|
||||
})
|
||||
}
|
||||
return cameraProcess
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
return cameraProcess
|
||||
}catch(err){
|
||||
s.systemLog(err)
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ module.exports = (s,config,lang) => {
|
|||
const createStreamChannel = function(e,number,channel){
|
||||
//`e` is the monitor object
|
||||
//`x` is an object used to contain temporary values.
|
||||
const channelStreamDirectory = !isNaN(parseInt(number)) ? `${e.sdir}channel${number}/` : e.sdir
|
||||
const channelStreamDirectory = !isNaN(parseInt(number)) ? `${e.sdir || s.getStreamsDirectory(e)}channel${number}/` : e.sdir
|
||||
if(channelStreamDirectory !== e.sdir && !fs.existsSync(channelStreamDirectory)){
|
||||
try{
|
||||
fs.mkdirSync(channelStreamDirectory)
|
||||
|
|
@ -377,7 +377,7 @@ module.exports = (s,config,lang) => {
|
|||
//x = temporary values
|
||||
const streamFlags = []
|
||||
const streamType = e.details.stream_type ? e.details.stream_type : 'hls'
|
||||
if(streamType !== 'jpeg'){
|
||||
if(streamType !== 'jpeg' && streamType !== 'useSubstream'){
|
||||
const isCudaEnabled = hasCudaEnabled(e)
|
||||
const streamFilters = []
|
||||
const videoCodecisCopy = e.details.stream_vcodec === 'copy'
|
||||
|
|
|
|||
132
libs/monitor.js
132
libs/monitor.js
|
|
@ -28,6 +28,9 @@ module.exports = function(s,config,lang){
|
|||
cameraDestroy,
|
||||
monitorConfigurationMigrator,
|
||||
attachStreamChannelHandlers,
|
||||
setActiveViewer,
|
||||
destroySubstreamProcess,
|
||||
attachMainProcessHandlers,
|
||||
} = require('./monitor/utils.js')(s,config,lang)
|
||||
const {
|
||||
addEventDetailsToString,
|
||||
|
|
@ -55,7 +58,7 @@ module.exports = function(s,config,lang){
|
|||
if(!activeMonitor.contentWriter){activeMonitor.contentWriter={}};
|
||||
if(!activeMonitor.childNodeStreamWriters){activeMonitor.childNodeStreamWriters={}};
|
||||
if(!activeMonitor.eventBasedRecording){activeMonitor.eventBasedRecording={}};
|
||||
if(!activeMonitor.watch){activeMonitor.watch={}};
|
||||
if(!activeMonitor.watch){activeMonitor.watch = []};
|
||||
if(!activeMonitor.fixingVideos){activeMonitor.fixingVideos={}};
|
||||
// if(!activeMonitor.viewerConnection){activeMonitor.viewerConnection={}};
|
||||
// if(!activeMonitor.viewerConnectionCount){activeMonitor.viewerConnectionCount=0};
|
||||
|
|
@ -140,7 +143,7 @@ module.exports = function(s,config,lang){
|
|||
return x.ar;
|
||||
}
|
||||
s.getStreamsDirectory = (monitor) => {
|
||||
return s.dir.streams + monitor.ke + '/' + monitor.mid + '/'
|
||||
return s.dir.streams + monitor.ke + '/' + (monitor.mid || monitor.id) + '/'
|
||||
}
|
||||
s.getRawSnapshotFromMonitor = function(monitor,options){
|
||||
return new Promise((resolve,reject) => {
|
||||
|
|
@ -761,55 +764,8 @@ module.exports = function(s,config,lang){
|
|||
code: e.wantedStatusCode
|
||||
});
|
||||
//on unexpected exit restart
|
||||
s.group[e.ke].activeMonitors[e.id].spawn_exit = function(){
|
||||
if(s.group[e.ke].activeMonitors[e.id].isStarted === true){
|
||||
if(e.details.loglevel!=='quiet'){
|
||||
s.userLog(e,{type:lang['Process Unexpected Exit'],msg:{msg:lang.unexpectedExitText,cmd:s.group[e.ke].activeMonitors[e.id].ffmpeg}});
|
||||
}
|
||||
fatalError(e,'Process Unexpected Exit');
|
||||
scanForOrphanedVideos(e,{
|
||||
forceCheck: true,
|
||||
checkMax: 2
|
||||
})
|
||||
s.onMonitorUnexpectedExitExtensions.forEach(function(extender){
|
||||
extender(Object.assign(s.group[e.ke].rawMonitorConfigurations[e.id],{}),e)
|
||||
})
|
||||
}
|
||||
}
|
||||
s.group[e.ke].activeMonitors[e.id].spawn.on('end',s.group[e.ke].activeMonitors[e.id].spawn_exit)
|
||||
s.group[e.ke].activeMonitors[e.id].spawn.on('exit',s.group[e.ke].activeMonitors[e.id].spawn_exit)
|
||||
s.group[e.ke].activeMonitors[e.id].spawn.on('error',function(er){
|
||||
s.userLog(e,{type:'Spawn Error',msg:er});fatalError(e,'Spawn Error')
|
||||
})
|
||||
s.userLog(e,{type:lang['Process Started'],msg:{cmd:s.group[e.ke].activeMonitors[e.id].ffmpeg}})
|
||||
if(s.isWin === false){
|
||||
var strippedHost = s.stripAuthFromHost(e)
|
||||
var sendProcessCpuUsage = function(){
|
||||
s.getMonitorCpuUsage(e,function(percent){
|
||||
s.group[e.ke].activeMonitors[e.id].currentCpuUsage = percent
|
||||
s.tx({
|
||||
f: 'camera_cpu_usage',
|
||||
ke: e.ke,
|
||||
id: e.id,
|
||||
percent: percent
|
||||
},'MON_STREAM_'+e.ke+e.id)
|
||||
})
|
||||
}
|
||||
clearInterval(s.group[e.ke].activeMonitors[e.id].getMonitorCpuUsage)
|
||||
s.group[e.ke].activeMonitors[e.id].getMonitorCpuUsage = setInterval(function(){
|
||||
if(e.details.skip_ping !== '1'){
|
||||
connectionTester.test(strippedHost,e.port,2000,function(err,response){
|
||||
if(response.success){
|
||||
sendProcessCpuUsage()
|
||||
}else{
|
||||
launchMonitorProcesses(e)
|
||||
}
|
||||
})
|
||||
}else{
|
||||
sendProcessCpuUsage()
|
||||
}
|
||||
},1000 * 60)
|
||||
}
|
||||
if(s.group[e.ke].activeMonitors[e.id].spawn)attachMainProcessHandlers(e)
|
||||
return s.group[e.ke].activeMonitors[e.id].spawn
|
||||
}
|
||||
const createEventCounter = function(monitor){
|
||||
if(monitor.details.detector_obj_count === '1'){
|
||||
|
|
@ -1257,27 +1213,29 @@ module.exports = function(s,config,lang){
|
|||
if(pingResponse.success === true){
|
||||
activeMonitor.isRecording = true
|
||||
try{
|
||||
createCameraFfmpegProcess(e)
|
||||
createCameraStreamHandlers(e)
|
||||
var mainProcess = createCameraFfmpegProcess(e)
|
||||
createEventCounter(e)
|
||||
if(e.type === 'dashcam' || e.type === 'socket'){
|
||||
setTimeout(function(){
|
||||
activeMonitor.allowStdinWrite = true
|
||||
s.txToDashcamUsers({
|
||||
f : 'enable_stream',
|
||||
ke : e.ke,
|
||||
mid : e.id
|
||||
},e.ke)
|
||||
},30000)
|
||||
}
|
||||
if(
|
||||
e.functionMode === 'record' ||
|
||||
e.type === 'mjpeg' ||
|
||||
e.type === 'h264' ||
|
||||
e.type === 'local'
|
||||
){
|
||||
catchNewSegmentNames(e)
|
||||
cameraFilterFfmpegLog(e)
|
||||
if(mainProcess){
|
||||
createCameraStreamHandlers(e)
|
||||
if(e.type === 'dashcam' || e.type === 'socket'){
|
||||
setTimeout(function(){
|
||||
activeMonitor.allowStdinWrite = true
|
||||
s.txToDashcamUsers({
|
||||
f : 'enable_stream',
|
||||
ke : e.ke,
|
||||
mid : e.id
|
||||
},e.ke)
|
||||
},30000)
|
||||
}
|
||||
if(
|
||||
e.functionMode === 'record' ||
|
||||
e.type === 'mjpeg' ||
|
||||
e.type === 'h264' ||
|
||||
e.type === 'local'
|
||||
){
|
||||
catchNewSegmentNames(e)
|
||||
cameraFilterFfmpegLog(e)
|
||||
}
|
||||
}
|
||||
clearTimeout(activeMonitor.onMonitorStartTimer)
|
||||
activeMonitor.onMonitorStartTimer = setTimeout(() => {
|
||||
|
|
@ -1547,26 +1505,24 @@ module.exports = function(s,config,lang){
|
|||
s.initiateMonitorObject({ke:e.ke,mid:e.id})
|
||||
switch(e.functionMode){
|
||||
case'watch_on'://live streamers - join
|
||||
if(!cn.monitorsCurrentlyWatching){cn.monitorsCurrentlyWatching = {}}
|
||||
if(!cn.monitorsCurrentlyWatching[e.id]){cn.monitorsCurrentlyWatching[e.id]={ke:e.ke}}
|
||||
s.group[e.ke].activeMonitors[e.id].watch[cn.id]={};
|
||||
var numberOfViewers = Object.keys(s.group[e.ke].activeMonitors[e.id].watch).length
|
||||
s.tx({
|
||||
viewers: numberOfViewers,
|
||||
ke: e.ke,
|
||||
id: e.id
|
||||
},'MON_'+e.ke+e.id)
|
||||
if(!cn.monitorsCurrentlyWatching){cn.monitorsCurrentlyWatching = {}}
|
||||
if(!cn.monitorsCurrentlyWatching[e.id]){cn.monitorsCurrentlyWatching[e.id]={ke:e.ke}}
|
||||
setActiveViewer(e.ke,e.id,cn.id,true)
|
||||
s.group[e.ke].activeMonitors[e.id].allowDestroySubstream = false
|
||||
clearTimeout(s.group[e.ke].activeMonitors[e.id].noViewerCountDisableSubstream)
|
||||
break;
|
||||
case'watch_off'://live streamers - leave
|
||||
if(cn.monitorsCurrentlyWatching){delete(cn.monitorsCurrentlyWatching[e.id])}
|
||||
var numberOfViewers = 0
|
||||
delete(s.group[e.ke].activeMonitors[e.id].watch[cn.id]);
|
||||
numberOfViewers = Object.keys(s.group[e.ke].activeMonitors[e.id].watch).length
|
||||
s.tx({
|
||||
viewers: numberOfViewers,
|
||||
ke: e.ke,
|
||||
id: e.id
|
||||
},'MON_'+e.ke+e.id)
|
||||
let currentCount = setActiveViewer(e.ke,e.id,cn.id,false)
|
||||
s.debugLog(currentCount,currentCount === 0,!!s.group[e.ke].activeMonitors[e.id].subStreamProcess)
|
||||
if(currentCount === 0 && s.group[e.ke].activeMonitors[e.id].subStreamProcess){
|
||||
clearTimeout(s.group[e.ke].activeMonitors[e.id].noViewerCountDisableSubstream)
|
||||
s.group[e.ke].activeMonitors[e.id].noViewerCountDisableSubstream = setTimeout(function(){
|
||||
s.debugLog('closed')
|
||||
s.group[e.ke].activeMonitors[e.id].allowDestroySubstream = true
|
||||
destroySubstreamProcess(s.group[e.ke].activeMonitors[e.id])
|
||||
},5000)
|
||||
}
|
||||
break;
|
||||
case'restart'://restart monitor
|
||||
s.sendMonitorStatus({
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const treekill = require('tree-kill');
|
|||
const spawn = require('child_process').spawn;
|
||||
const events = require('events');
|
||||
const Mp4Frag = require('mp4frag');
|
||||
const streamViewerCountTimeouts = {}
|
||||
module.exports = (s,config,lang) => {
|
||||
const {
|
||||
createPipeArray,
|
||||
|
|
@ -17,6 +18,10 @@ module.exports = (s,config,lang) => {
|
|||
const processKill = (proc) => {
|
||||
const response = {ok: true}
|
||||
return new Promise((resolve,reject) => {
|
||||
if(!proc){
|
||||
resolve(response)
|
||||
return
|
||||
}
|
||||
function sendError(err){
|
||||
response.ok = false
|
||||
response.err = err
|
||||
|
|
@ -94,13 +99,17 @@ module.exports = (s,config,lang) => {
|
|||
if(activeMonitor.onChildNodeExit){
|
||||
activeMonitor.onChildNodeExit()
|
||||
}
|
||||
activeMonitor.spawn.stdio.forEach(function(stdio){
|
||||
try{
|
||||
stdio.unpipe()
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
try{
|
||||
activeMonitor.spawn.stdio.forEach(function(stdio){
|
||||
try{
|
||||
stdio.unpipe()
|
||||
}catch(err){
|
||||
console.log(err)
|
||||
}
|
||||
})
|
||||
}catch(err){
|
||||
// s.debugLog(err)
|
||||
}
|
||||
if(activeMonitor.mp4frag){
|
||||
var mp4FragChannels = Object.keys(activeMonitor.mp4frag)
|
||||
mp4FragChannels.forEach(function(channel){
|
||||
|
|
@ -116,6 +125,7 @@ module.exports = (s,config,lang) => {
|
|||
}else{
|
||||
processKill(proc).then((response) => {
|
||||
s.debugLog(`cameraDestroy`,response)
|
||||
activeMonitor.allowDestroySubstream = true
|
||||
destroySubstreamProcess(activeMonitor).then((response) => {
|
||||
if(response.hadSubStream)s.debugLog(`cameraDestroy`,response.closeResponse)
|
||||
})
|
||||
|
|
@ -209,13 +219,19 @@ module.exports = (s,config,lang) => {
|
|||
const spawnSubstreamProcess = function(e){
|
||||
// e = monitorConfig
|
||||
try{
|
||||
const monitorConfig = s.group[e.ke].rawMonitorConfigurations[e.mid]
|
||||
const groupKey = e.ke
|
||||
const monitorId = e.mid
|
||||
const monitorConfig = Object.assign({},s.group[groupKey].rawMonitorConfigurations[monitorId])
|
||||
const monitorDetails = monitorConfig.details
|
||||
const activeMonitor = s.group[e.ke].activeMonitors[e.mid]
|
||||
const channelNumber = 1 + (monitorDetails.stream_channels || []).length
|
||||
const ffmpegCommand = [`-progress pipe:5`];
|
||||
const logLevel = monitorDetails.loglevel ? e.details.loglevel : 'warning'
|
||||
const stdioPipes = createPipeArray({}, 2)
|
||||
const substreamConfig = monitorConfig.details.substream
|
||||
substreamConfig.input.type = !substreamConfig.input.fulladdress ? monitorConfig.type : substreamConfig.input.type || monitorConfig.details.rtsp_transport
|
||||
substreamConfig.input.fulladdress = substreamConfig.input.fulladdress || s.buildMonitorUrl(monitorConfig)
|
||||
substreamConfig.input.rtsp_transport = substreamConfig.input.rtsp_transport || monitorConfig.details.rtsp_transport
|
||||
const {
|
||||
inputAndConnectionFields,
|
||||
outputFields,
|
||||
|
|
@ -360,6 +376,92 @@ module.exports = (s,config,lang) => {
|
|||
ffmpegProcess.stdio[pipeNumber].on('data',frameToStreamAdded)
|
||||
}
|
||||
}
|
||||
function setActiveViewer(groupKey,monitorId,connectionId,isBeingAdded){
|
||||
const viewerList = s.group[groupKey].activeMonitors[monitorId].watch;
|
||||
if(isBeingAdded){
|
||||
if(viewerList.indexOf(connectionId) > -1)viewerList.push(connectionId);
|
||||
}else{
|
||||
viewerList.splice(viewerList.indexOf(connectionId), 1)
|
||||
}
|
||||
const numberOfViewers = viewerList.length
|
||||
s.tx({
|
||||
f: 'viewer_count',
|
||||
viewers: numberOfViewers,
|
||||
ke: groupKey,
|
||||
id: monitorId
|
||||
},'MON_' + groupKey + monitorId)
|
||||
return numberOfViewers;
|
||||
}
|
||||
function setTimedActiveViewerForHttp(req){
|
||||
const groupKey = req.params.ke
|
||||
const connectionId = req.params.auth
|
||||
const loggedInUser = s.group[groupKey].users[connectionId]
|
||||
if(!loggedInUser){
|
||||
const monitorId = req.params.id
|
||||
const viewerList = s.group[groupKey].activeMonitors[monitorId].watch
|
||||
const theViewer = viewerList[connectionId]
|
||||
if(!theViewer){
|
||||
setActiveViewer(groupKey,monitorId,connectionId,true)
|
||||
}
|
||||
clearTimeout(streamViewerCountTimeouts[req.originalUrl])
|
||||
streamViewerCountTimeouts[req.originalUrl] = setTimeout(() => {
|
||||
setActiveViewer(groupKey,monitorId,connectionId,false)
|
||||
},5000)
|
||||
}else{
|
||||
s.debugLog(`User is Logged in, Don't add to viewer count`);
|
||||
}
|
||||
}
|
||||
function attachMainProcessHandlers(e){
|
||||
s.group[e.ke].activeMonitors[e.id].spawn_exit = function(){
|
||||
if(s.group[e.ke].activeMonitors[e.id].isStarted === true){
|
||||
if(e.details.loglevel!=='quiet'){
|
||||
s.userLog(e,{type:lang['Process Unexpected Exit'],msg:{msg:lang.unexpectedExitText,cmd:s.group[e.ke].activeMonitors[e.id].ffmpeg}});
|
||||
}
|
||||
fatalError(e,'Process Unexpected Exit');
|
||||
scanForOrphanedVideos(e,{
|
||||
forceCheck: true,
|
||||
checkMax: 2
|
||||
})
|
||||
s.onMonitorUnexpectedExitExtensions.forEach(function(extender){
|
||||
extender(Object.assign(s.group[e.ke].rawMonitorConfigurations[e.id],{}),e)
|
||||
})
|
||||
}
|
||||
}
|
||||
s.group[e.ke].activeMonitors[e.id].spawn.on('end',s.group[e.ke].activeMonitors[e.id].spawn_exit)
|
||||
s.group[e.ke].activeMonitors[e.id].spawn.on('exit',s.group[e.ke].activeMonitors[e.id].spawn_exit)
|
||||
s.group[e.ke].activeMonitors[e.id].spawn.on('error',function(er){
|
||||
s.userLog(e,{type:'Spawn Error',msg:er});fatalError(e,'Spawn Error')
|
||||
})
|
||||
s.userLog(e,{type:lang['Process Started'],msg:{cmd:s.group[e.ke].activeMonitors[e.id].ffmpeg}})
|
||||
// if(s.isWin === false){
|
||||
// var strippedHost = s.stripAuthFromHost(e)
|
||||
// var sendProcessCpuUsage = function(){
|
||||
// s.getMonitorCpuUsage(e,function(percent){
|
||||
// s.group[e.ke].activeMonitors[e.id].currentCpuUsage = percent
|
||||
// s.tx({
|
||||
// f: 'camera_cpu_usage',
|
||||
// ke: e.ke,
|
||||
// id: e.id,
|
||||
// percent: percent
|
||||
// },'MON_STREAM_'+e.ke+e.id)
|
||||
// })
|
||||
// }
|
||||
// clearInterval(s.group[e.ke].activeMonitors[e.id].getMonitorCpuUsage)
|
||||
// s.group[e.ke].activeMonitors[e.id].getMonitorCpuUsage = setInterval(function(){
|
||||
// if(e.details.skip_ping !== '1'){
|
||||
// connectionTester.test(strippedHost,e.port,2000,function(err,response){
|
||||
// if(response.success){
|
||||
// sendProcessCpuUsage()
|
||||
// }else{
|
||||
// launchMonitorProcesses(e)
|
||||
// }
|
||||
// })
|
||||
// }else{
|
||||
// sendProcessCpuUsage()
|
||||
// }
|
||||
// },1000 * 60)
|
||||
// }
|
||||
}
|
||||
return {
|
||||
cameraDestroy: cameraDestroy,
|
||||
createSnapshot: createSnapshot,
|
||||
|
|
@ -369,5 +471,8 @@ module.exports = (s,config,lang) => {
|
|||
spawnSubstreamProcess: spawnSubstreamProcess,
|
||||
destroySubstreamProcess: destroySubstreamProcess,
|
||||
attachStreamChannelHandlers: attachStreamChannelHandlers,
|
||||
setActiveViewer: setActiveViewer,
|
||||
setTimedActiveViewerForHttp: setTimedActiveViewerForHttp,
|
||||
attachMainProcessHandlers: attachMainProcessHandlers,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -841,8 +841,6 @@ module.exports = function(s,config,lang,app,io){
|
|||
const activeMonitor = s.group[groupKey].activeMonitors[monitorId]
|
||||
const substreamConfig = monitorConfig.details.substream
|
||||
if(
|
||||
substreamConfig.input.fulladdress &&
|
||||
substreamConfig.input.type &&
|
||||
substreamConfig.output
|
||||
){
|
||||
if(!activeMonitor.subStreamProcess){
|
||||
|
|
|
|||
|
|
@ -527,15 +527,17 @@ function initiateLiveGridPlayer(monitor,subStreamChannel){
|
|||
})
|
||||
}
|
||||
//initiate signal check
|
||||
var signalCheckInterval = (isNaN(loadedMonitor.details.signal_check) ? 10 : parseFloat(loadedMonitor.details.signal_check)) * 1000 * 60
|
||||
if(signalCheckInterval > 0){
|
||||
clearInterval(loadedPlayer.signal)
|
||||
loadedPlayer.signal = setInterval(function(){
|
||||
signalCheckLiveStream({
|
||||
mid: monitorId,
|
||||
checkSpeed: 1000,
|
||||
})
|
||||
},signalCheckInterval);
|
||||
if(streamType !== 'useSubstream'){
|
||||
var signalCheckInterval = (isNaN(loadedMonitor.details.signal_check) ? 10 : parseFloat(loadedMonitor.details.signal_check)) * 1000 * 60
|
||||
if(signalCheckInterval > 0){
|
||||
clearInterval(loadedPlayer.signal)
|
||||
loadedPlayer.signal = setInterval(function(){
|
||||
signalCheckLiveStream({
|
||||
mid: monitorId,
|
||||
checkSpeed: 1000,
|
||||
})
|
||||
},signalCheckInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
function revokeVideoPlayerUrl(monitorId){
|
||||
|
|
@ -819,15 +821,6 @@ $(document).ready(function(e){
|
|||
.resize(function(){
|
||||
resetAllLiveGridDimensionsInMemory()
|
||||
})
|
||||
.on('click','.toggle-substream',function(){
|
||||
var monitorId = $(this).parents('[data-mid]').attr('data-mid')
|
||||
var monitor = loadedMonitors[monitorId]
|
||||
if(monitor.subStreamToggleLock)return false;
|
||||
monitor.subStreamToggleLock = true
|
||||
$.getJSON(getApiPrefix(`toggleSubstream`) + '/' + monitor.mid,function(data){
|
||||
monitor.subStreamToggleLock = false
|
||||
})
|
||||
})
|
||||
.on('click','.launch-live-grid-monitor',function(){
|
||||
var monitorId = $(this).parents('[data-mid]').attr('data-mid')
|
||||
// if(isMobile){
|
||||
|
|
@ -998,9 +991,17 @@ $(document).ready(function(e){
|
|||
break;
|
||||
case'monitor_watch_on':
|
||||
var monitorId = d.mid || d.id
|
||||
var loadedMonitor = loadedMonitors[monitorId]
|
||||
var subStreamChannel = d.subStreamChannel
|
||||
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel)
|
||||
saveLiveGridBlockOpenState(monitorId,$user.ke,1)
|
||||
if(!loadedMonitor.subStreamChannel && loadedMonitor.details.stream_type === 'useSubstream'){
|
||||
toggleSubStream(monitorId,function(){
|
||||
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel)
|
||||
saveLiveGridBlockOpenState(monitorId,$user.ke,1)
|
||||
})
|
||||
}else{
|
||||
drawLiveGridBlock(loadedMonitors[monitorId],subStreamChannel)
|
||||
saveLiveGridBlockOpenState(monitorId,$user.ke,1)
|
||||
}
|
||||
break;
|
||||
case'mode_jpeg_off':
|
||||
window.jpegModeOn = false
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ function generateDefaultMonitorSettings(){
|
|||
"aduration": "",
|
||||
"probesize": "",
|
||||
"stream_loop": null,
|
||||
"rtsp_transport": "tcp",
|
||||
"rtsp_transport": "",
|
||||
"accelerator": "0",
|
||||
"hwaccel": null,
|
||||
"hwaccel_vcodec": "",
|
||||
|
|
@ -244,8 +244,8 @@ function generateDefaultMonitorSettings(){
|
|||
"stream_v_br": "",
|
||||
"stream_a_br": "",
|
||||
"stream_fps": "",
|
||||
"stream_scale_x": "",
|
||||
"stream_scale_y": "",
|
||||
"stream_scale_x": "640",
|
||||
"stream_scale_y": "480",
|
||||
"stream_rotate": null,
|
||||
"svf": "",
|
||||
"cust_stream": ""
|
||||
|
|
|
|||
|
|
@ -171,7 +171,21 @@ function runTestDetectionTrigger(monitorId,callback){
|
|||
})
|
||||
}
|
||||
function toggleSubStream(monitorId,callback){
|
||||
var monitor = loadedMonitors[monitorId]
|
||||
var substreamConfig = monitor.details.substream
|
||||
var isSubStreamConfigured = !!substreamConfig.output;
|
||||
if(!isSubStreamConfigured){
|
||||
new PNotify({
|
||||
type: 'warning',
|
||||
title: lang['Invalid Settings'],
|
||||
text: lang.SubstreamNotConfigured,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if(monitor.subStreamToggleLock)return false;
|
||||
monitor.subStreamToggleLock = true
|
||||
$.getJSON(getApiPrefix() + '/toggleSubstream/'+$user.ke+'/'+monitorId,function(d){
|
||||
monitor.subStreamToggleLock = false
|
||||
debugLog(d)
|
||||
if(callback)callback()
|
||||
})
|
||||
|
|
@ -200,7 +214,7 @@ function playAudioAlert(){
|
|||
|
||||
function buildStreamUrl(monitorId){
|
||||
var monitor = loadedMonitors[monitorId]
|
||||
var streamURL
|
||||
var streamURL = ''
|
||||
var streamType = safeJsonParse(monitor.details).stream_type
|
||||
switch(streamType){
|
||||
case'jpeg':
|
||||
|
|
@ -221,6 +235,9 @@ function buildStreamUrl(monitorId){
|
|||
case'b64':
|
||||
streamURL = 'Websocket'
|
||||
break;
|
||||
case'useSubstream':
|
||||
streamURL = lang['Use Substream']
|
||||
break;
|
||||
}
|
||||
if(!streamURL){
|
||||
$.each(onBuildStreamUrlExtensions,function(n,extender){
|
||||
|
|
|
|||
Loading…
Reference in New Issue