Cctv recording and playback (work in progress)

The node is unaware of what is happening on the client side. It simply receives the playlist and sends it to the client side where there is an attempt to play it.

If you wanted to handle the client side event on the video element, you can add the event listener to detect ended and send a message back using scope.send. If you added this little piece of code on the controls, you could detect the event and send a message and somehow load the next video from your list. Of course, this would make it change for all viewers since the dashboard is designed to be single user.

const video = document.getElementById(msg.videoId);

            if (video) {

                video.addEventListener('ended', () => {
                    scope.send('it ended');
                });

This successfully sent a message to my debug panel.

It would need more work so that to only detects the ended event when you want it to, maybe with some checkbox control. Just a proof of concept at this point...

1 Like

Works great, really useful. Understood, it is events from the HTMLMediaElement you have to watch. Many useful and interesting:

1 Like

In this situation, it might be better to set the onended property with the callback function vs adding the event listener. This will help to ensure that you have only added the callback a single time. And instead of removing the event listener, you can just delete the onended property (or maybe set to null or call removeAttribute, can't remember right now at the top of my head).

Well, both works fine, why is one better than the other?
Best regards, Walter

            if (video) {

                video.onended = function() {myEnd()};

                function myEnd() {
                    scope.send('the end');
                }

                video.addEventListener('ended', () => {
                scope.send('it ended');
                });

                video.addEventListener('playing', () => {
                scope.send('it plays');
                });

                video.addEventListener('pause', () => {
                scope.send('it paused');
                });

Assigning it as a property only allows 1 handler, whereas adding the event listener will allow more than 1 handler. If you are only need a single callback, which is most likely the case for this particular situation, then using the property might be simpler and you won't accidentally add too many event listeners. Other than that, I don't know if one is better or just a matter of preference.

1 Like

Is anybody here using nginx along with the cctv stuff? I have been deep diving nginx over the last month and found some interesting techniques to offload some burden from the node-red instance, in particular when playing back recordings and using nginx's slice module. Hopefully I can get a small change to node-red core to allow this to work. If anybody is interested in nginx integration with the cctv things, then maybe we can start a new thread for that. Let me know.

1 Like

If you found something interesting, please yes explain it a new thread :wink:

1 Like

How is this project that involves the recordings?

I'm going to fall into those possibilities now that I've managed to make it work. The RTSP camera on the system.

To my knowledge, this worked when I added & worked with a sample to my flow (using that RedBull stream, but it should work with your camera(s) as well in the same way

1 Like

Who is the user who creates these folders so I can give this mkdir permission?

I'm a little lost on this item of code ??

[{"id":"f3874cf4a963908d","type":"subflow","name":"recording","info":"","category":"","in":[{"x":80,"y":160,"wires":[{"id":"d0615cb16ccf58fb"}]}],"out":[{"x":320,"y":120,"wires":[{"id":"d0615cb16ccf58fb","port":0}]}],"env":[{"name":"STATIC_PATH","type":"str","value":"/run/media/system/surveillance1/cctv/recordings"},{"name":"STATIC_ROOT","type":"str","value":"/static/recordings"}],"meta":{},"color":"#DDAA99","status":{"x":320,"y":200,"wires":[{"id":"d0615cb16ccf58fb","port":1}]}},{"id":"d0615cb16ccf58fb","type":"function","z":"f3874cf4a963908d","name":"recording","func":"const { payload, payload: { length }, duration, topic } = msg;\n\nconst [ , id, , type ] = topic.split('/');\n\nconst recording = context.get('recording');\n\nif (type === 'seg' || type === 'pre') {\n\n    const { mp4WriteStream = {}, m3u8WriteStream = {}, mp4Name, bytesWritten, segments, playlist } = recording;\n\n    if (mp4WriteStream.writable && m3u8WriteStream.writable) {\n\n        // write live (seg) or previous (pre) segments to existing writeStreams\n\n        mp4WriteStream.write(payload);\n\n        m3u8WriteStream.write(`#EXTINF:${duration}\\n#EXT-X-BYTERANGE:${length}@${bytesWritten}\\n${mp4Name}\\n`);\n\n        recording.bytesWritten += length;\n\n        recording.segments++;\n\n        // only send playlist after the first segment is written so that hls.js does not throw Hls.Events.ERROR, Hls.ErrorTypes.NETWORK_ERROR, Hls.ErrorDetails.LEVEL_EMPTY_ERROR\n\n        if (segments === 0) {\n\n            return { payload: playlist };\n\n        }\n\n    } else {\n\n        node.error(`mp4WriteStream and m3u8WriteStream must be writable`);\n\n    }\n\n    return;\n\n}\n\nif (type === 'init') {\n\n    // create new or end existing write streams based on buffer length\n\n    if (length) {\n\n        const date = new Date();\n\n        const year = date.getFullYear();\n\n        const month = (date.getMonth() + 1).toString().padStart(2, '0');\n\n        const day = date.getDate().toString().padStart(2, '0');\n\n        const hours = date.getHours().toString().padStart(2, '0');\n\n        const minutes = date.getMinutes().toString().padStart(2, '0');\n\n        const seconds = date.getSeconds().toString().padStart(2, '0');\n\n        // create name based on timestamp\n\n        const baseName = `${hours}h.${minutes}m.${seconds}s`; // 00h.00m.00s\n\n        const mp4Name = `${baseName}.mp4`; // 00h.00m.00s.mp4\n\n        const m3u8Name = `${baseName}.m3u8`; // 00h.00m.00s.m3u8\n\n        const baseRoot = `${env.get('STATIC_ROOT')}/${id}`;\n\n        const basePath = `${env.get('STATIC_PATH')}/${id}`;\n\n        // create dir base on timestamp\n\n        const fullDir = `${basePath}/${year}/${month}/${day}`; // basePath/yyyy/mm/dd\n\n        const { mkdirSync, createWriteStream, writeFile } = fs;\n\n        const newDir = mkdirSync(fullDir, { recursive: true });\n\n        newDir && writeFile(`${basePath}/listing.txt`, `${year}/${month}/${day}\\n`, { encoding: 'utf8', flag: 'a' }, (err) => { err && node.error(err) });\n\n        writeFile(`${fullDir}/listing.txt`, `${m3u8Name}\\n`, { encoding: 'utf8', flag: 'a' }, (err) => { err && node.error(err) });\n\n        // create new write streams for mp4 and playlist\n       \n        const mp4WriteStream = createWriteStream(`${fullDir}/${mp4Name}`)\n            .on('error', (err) => {\n                node.error(err);\n            });\n\n        const m3u8WriteStream = createWriteStream(`${fullDir}/${m3u8Name}`)\n            .on('error', (err) => {\n                node.error(err);\n            });\n\n        mp4WriteStream.write(payload);\n\n        m3u8WriteStream.write(`#EXTM3U\\n#EXT-X-VERSION:7\\n#EXT-X-PLAYLIST-TYPE:EVENT\\n#EXT-X-TARGETDURATION:${Math.round(duration) || 1}\\n#EXT-X-MEDIA-SEQUENCE:0\\n#EXT-X-MAP:URI=\"${mp4Name}\",BYTERANGE=\"${length}@0\"\\n`);\n\n        recording.mp4WriteStream = mp4WriteStream;\n\n        recording.m3u8WriteStream = m3u8WriteStream;\n\n        recording.mp4Name = mp4Name;\n\n        recording.bytesWritten = length;\n\n        recording.segments = 0;\n\n        // save playlist\n\n        recording.playlist = `${baseRoot}/${year}/${month}/${day}/${m3u8Name}`;\n\n        // send status\n\n        return [ null, { payload: { fill: 'green', shape: \"dot\", text: `${id}/${year}/${month}/${day}/${baseName}` } }];\n\n    } else {\n\n        // end existing writeStreams\n\n        const { mp4WriteStream = {}, m3u8WriteStream = {} } = recording;\n\n        if (mp4WriteStream.writable) {\n\n            mp4WriteStream.end();\n\n        }\n\n        if (m3u8WriteStream.writable) {\n\n            m3u8WriteStream.end('#EXT-X-ENDLIST\\n');\n\n        }\n\n        return;\n\n    }\n\n}","outputs":2,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\n\ncontext.set('recording', {});","finalize":"// Code added here will be run when the\n// node is being stopped or re-deployed.\n\nconst { mp4WriteStream = {}, m3u8WriteStream = {} } = context.get('recording');\n\nif (mp4WriteStream.writable) {\n\n    mp4WriteStream.end();\n\n}\n\nif (m3u8WriteStream.writable) {\n\n    m3u8WriteStream.end('#EXT-X-ENDLIST\\n');\n\n}\n\ncontext.set('recording', undefined);","libs":[{"var":"fs","module":"fs"}],"x":200,"y":160,"wires":[[],[]]},{"id":"32d61e0f.0490b2","type":"subflow","name":"stderr","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"211d46bf.a79c7a"}]}],"out":[],"env":[],"color":"#DDAA99","status":{"x":280,"y":120,"wires":[{"id":"211d46bf.a79c7a","port":1}]}},{"id":"211d46bf.a79c7a","type":"function","z":"32d61e0f.0490b2","name":"stderr","func":"// convert payload for Buffer to String and split by newlines\n\nconst stderr = msg.payload.toString().split('\\n');\n\nnode.send([ { stderr }, { payload: { fill: 'red', text: `${new Date().toString()}` } } ]);","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":80,"wires":[["c6e5408.f19c4c"],[]]},{"id":"c6e5408.f19c4c","type":"debug","z":"32d61e0f.0490b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"stderr","targetType":"msg","statusVal":"","statusType":"auto","x":330,"y":40,"wires":[]},{"id":"9b59c1a2222210b8","type":"inject","z":"5273a6de.b59318","name":"start","props":[{"p":"action","v":"{\"command\":\"start\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":"5","topic":"","x":450,"y":340,"wires":[["7a8c3f95b486b8f9"]]},{"id":"b4a8c0bc44499078","type":"inject","z":"5273a6de.b59318","name":"restart","props":[{"p":"action","v":"{\"command\":\"restart\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":"1","topic":"","x":450,"y":380,"wires":[["7a8c3f95b486b8f9"]]},{"id":"7dde9f15d2aa5933","type":"inject","z":"5273a6de.b59318","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":450,"y":420,"wires":[["7a8c3f95b486b8f9"]]},{"id":"58b77ee9a2e29092","type":"mp4frag","z":"5273a6de.b59318","name":"","outputs":2,"basePath":"telemetria","serveHttp":"false","serveIo":"false","hlsPlaylistSize":4,"hlsPlaylistExtra":0,"autoStart":"true","preBuffer":"0","timeLimit":"-1","repeated":"false","statusData":"playlist","x":1010,"y":100,"wires":[[],["571c8af49dfc40ae"]]},{"id":"a8741b7c88d88102","type":"subflow:32d61e0f.0490b2","z":"5273a6de.b59318","name":"","x":910,"y":340,"wires":[]},{"id":"ef6008f4d7425c22","type":"inject","z":"5273a6de.b59318","name":"write restart","props":[{"p":"action","v":"{\"command\":\"restart\",\"subject\":\"write\",\"preBuffer\":1,\"timeLimit\":-1,\"repeated\":false}","vt":"json"}],"repeat":"","crontab":"*/15 0-23 * * *","once":false,"onceDelay":"5","topic":"","x":750,"y":60,"wires":[["58b77ee9a2e29092"]]},{"id":"e23111864612f2c5","type":"inject","z":"5273a6de.b59318","name":"write stop","props":[{"p":"action","v":"{\"command\":\"stop\",\"subject\":\"write\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":760,"y":100,"wires":[["58b77ee9a2e29092"]]},{"id":"571c8af49dfc40ae","type":"subflow:f3874cf4a963908d","z":"5273a6de.b59318","name":"","x":1320,"y":180,"wires":[["e2da3c3cee315665"]]},{"id":"e2da3c3cee315665","type":"ui_mp4frag","z":"5273a6de.b59318","name":"","group":"9a076144e0b8c687","order":1,"width":"20","height":"14","readyPoster":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAMAAACtqHJCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzgwMTE3NDA3MjA2ODExODIyQUIzREQ3RTA0MDAyNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRkU5MkM4RDJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRkU5MkM4QzJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6Rjc3RjExNzQwNzIwNjgxMTgyMkFDQTkzQjdGQkY1MzAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDM4MDExNzQwNzIwNjgxMTgyMkFCM0REN0UwNDAwMjQiLz4gPC9yZGY6RGVzY3Jp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","errorPoster":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAMAAACtqHJCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzgwMTE3NDA3MjA2ODExODIyQUIzREQ3RTA0MDAyNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRkU5MkM5MTJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRkU5MkM5MDJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6Rjc3RjExNzQwNzIwNjgxMTgyMkFDQTkzQjdGQkY1MzAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDM4MDExNzQwNzIwNjgxMTgyMkFCM0REN0UwNDAwMjQiLz4gPC9yZGY6RGVzY3Jp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","hlsJsConfig":"{\"startPosition\":0,\"liveDurationInfinity\":false,\"progressive\":false}","autoplay":"true","unload":"false","threshold":0.1,"controls":"true","muted":"true","players":["hls.js","hls","socket.io","mp4"],"x":1330,"y":240,"wires":[["9ec4f3ad80a3ec74"]]},{"id":"9ec4f3ad80a3ec74","type":"ui_template","z":"5273a6de.b59318","group":"9a076144e0b8c687","name":"video controls","order":15,"width":"20","height":"1","format":"<div layout-fill layout=\"row\">\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"toggleControls()\">\n            <md-icon class=\"material-icons\">smart_button</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"playPause()\">\n            <md-icon class=\"material-icons\">play_arrow</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"forwardFive()\">\n            <md-icon class=\"material-icons\">forward_5</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"replayFive()\">\n            <md-icon class=\"material-icons\">replay_5</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"decreaseRate()\">\n            <md-icon class=\"material-icons\">slow_motion_video</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"increaseRate()\">\n            <md-icon class=\"material-icons\">speed</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"toggleMuted()\">\n            <md-icon class=\"material-icons\">volume_mute</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"videoFullscreen()\">\n            <md-icon class=\"material-icons\">fullscreen</md-icon>\n        </md-button>\n    </div>\n\n</div>\n\n<script>\n    ((scope) => {\n\n    const unwatchMsg = scope.$watch('msg', (msg) => {\n\n        if (msg && msg.videoId) {\n\n            const video = document.getElementById(msg.videoId);\n            \n            if (video) {\n\n                const rates = [ 0.1, 0.2, 0.5, 1, 2, 5, 10];\n\n                let rateIndex = 3;\n\n                const requestFullscreen = video.webkitRequestFullscreen || video.webkitEnterFullScreen || video.mozRequestFullScreen || video.requestFullscreen;\n\n                scope.toggleControls = () => {\n\n                    video.controls = !video.controls;\n\n                }\n\n                scope.playPause = () => {\n\n                    video.paused ? video.play() : video.pause();\n\n                }\n\n                scope.toggleMuted = () => {\n\n                    video.muted = !video.muted;\n\n                }\n\n                scope.videoFullscreen = () => {\n\n                    requestFullscreen.call(video);\n\n                }\n\n                scope.videoDisplay = () => {\n\n                    video.style.display = video.style.display === 'none' ? 'initial' : 'none';\n\n                }\n\n                scope.increaseRate = () => {\n\n                    rateIndex++;\n\n                    if (rateIndex > 6) {\n\n                        rateIndex = 6;\n\n                    }\n\n                    video.playbackRate = rates[rateIndex];\n\n                }\n\n                scope.decreaseRate = () => {\n                \n                    rateIndex--;\n                \n                    if (rateIndex < 0) {\n\n                        rateIndex = 0;\n                    \n                    }\n                \n                    video.playbackRate = rates[rateIndex];\n                \n                }\n\n                scope.forwardFive = () => {\n                \n                    video.currentTime += 5;\n                \n                }\n\n                scope.replayFive = () => {\n                \n                    video.currentTime -= 5;\n                \n                }\n\n                unwatchMsg();\n\n            }\n\n        }\n\n    });\n\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1340,"y":340,"wires":[[]]},{"id":"cfa37a1fd41f4570","type":"group","z":"5273a6de.b59318","name":"Camera IP - Protocolo RTSP","style":{"stroke":"#ffC000","fill":"#ffefbf","label":true,"color":"#6f2fa0"},"nodes":["7a8c3f95b486b8f9","4cc7afbbcc802243","652bde80360a1fe1","d1112dfcbfef615b","2858e68578995d73","379c223a6ef2f8ea","adffeae04f85fed5","96e79d1eccb9df9d"],"x":74,"y":139,"w":1132,"h":122},{"id":"7a8c3f95b486b8f9","type":"ffmpeg","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","outputs":3,"cmdPath":"ffmpeg","cmdArgs":"[]","cmdOutputs":2,"killSignal":"SIGTERM","x":750,"y":200,"wires":[["4cc7afbbcc802243","58b77ee9a2e29092"],["4cc7afbbcc802243","58b77ee9a2e29092"],["a8741b7c88d88102"]]},{"id":"4cc7afbbcc802243","type":"mp4frag","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","outputs":2,"basePath":"teste","serveHttp":"true","serveIo":"true","hlsPlaylistSize":"6","hlsPlaylistExtra":"2","autoStart":"false","preBuffer":1,"timeLimit":10000,"repeated":"false","statusData":"playlist","x":920,"y":200,"wires":[["652bde80360a1fe1"],[]]},{"id":"652bde80360a1fe1","type":"ui_mp4frag","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","group":"46d6d60673638810","order":1,"width":22,"height":13,"readyPoster":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB2aWV3Qm94PSIwIDAgODAwMCA2MDAwIj48dGV4dCBmaWxsPSIjMGYwIiBzdHJva2Utd2lkdGg9IjAiIGR4PSIwIiBkeT0iMCIgZm9udC1mYW1pbHk9IiZxdW90O2UyY3B1d3p4YldsMTo6OlJvYm90byZxdW90OyIgZm9udC1zaXplPSI2MDAiIGZvbnQtd2VpZ2h0PSI3MDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKCAxMDExLjI0IDMyMDQuODgpIj48dHNwYW4geT0iMCIgc3Ryb2tlLXdpZHRoPSIwIiBmb250LXdlaWdodD0iNzAwIj48IVtDREFUQVsKVmlkZW8gUGxheWJhY2sgUmVhZHkKXV0+PC90c3Bhbj48L3RleHQ+PC9zdmc+Cg==","errorPoster":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB2aWV3Qm94PSIwIDAgODAwMCA2MDAwIj48dGV4dCBmaWxsPSJyZWQiIHN0cm9rZS13aWR0aD0iMCIgZHg9IjAiIGR5PSIwIiBmb250LWZhbWlseT0iJnF1b3Q7ZW42OXdhSUtnN0MxOjo6Um9ib3RvJnF1b3Q7IiBmb250LXNpemU9IjYwMCIgZm9udC13ZWlnaHQ9IjcwMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoIDExODMuOTYgMzIwNC44OCkiPjx0c3BhbiB5PSIwIiBzdHJva2Utd2lkdGg9IjAiIGZvbnQtd2VpZ2h0PSI3MDAiPjwhW0NEQVRBWwpWaWRlbyBQbGF5YmFjayBFcnJvcgpdXT48L3RzcGFuPjwvdGV4dD48L3N2Zz4K","hlsJsConfig":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":5,\"maxBufferLength\":10,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","autoplay":"true","unload":"true","threshold":0.1,"controls":"true","muted":"true","players":["socket.io","hls.js","hls","mp4"],"x":1110,"y":200,"wires":[[]]},{"id":"d1112dfcbfef615b","type":"ui_button","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","group":"46d6d60673638810","order":1,"width":0,"height":0,"passthru":false,"label":"Start","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":150,"y":180,"wires":[["adffeae04f85fed5"]]},{"id":"2858e68578995d73","type":"ui_button","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","group":"46d6d60673638810","order":1,"width":0,"height":0,"passthru":false,"label":"Stop","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":150,"y":220,"wires":[["379c223a6ef2f8ea"]]},{"id":"379c223a6ef2f8ea","type":"change","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"stop\",\"args\":[\"-loglevel\",\"quiet\",\"-rtsp_transport\",\"tcp\",\"-i\",\"rtsp://belov:belov12345678@192.168.1.129:554/cam/realmonitor?channel=1&subtype=0\",\"-an\",\"-c:v\",\"copy\",\"-f\",\"mp4\",\"-movflags\",\"+frag_every_frame+empty_moov+default_base_moof\",\"-min_frag_duration\",\"1000000\",\"pipe:1\"]}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":220,"wires":[["7a8c3f95b486b8f9"]]},{"id":"adffeae04f85fed5","type":"change","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","rules":[{"t":"set","p":"URLCAM","pt":"msg","to":"#:(persistent)::URLCAM_conf","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":180,"wires":[["96e79d1eccb9df9d"]]},{"id":"96e79d1eccb9df9d","type":"change","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"quiet\",\"-rtsp_transport\",\"tcp\",\"-i\", $.URLCAM,\"-an\",\"-c:v\",\"copy\",\"-f\",\"mp4\",\"-movflags\",\"+frag_every_frame+empty_moov+default_base_moof\",\"-min_frag_duration\",\"1000000\",\"pipe:1\"]}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":180,"wires":[["7a8c3f95b486b8f9"]]},{"id":"46d6d60673638810","type":"ui_group","name":"","tab":"9eb9a9fcd56859ba","order":1,"disp":true,"width":"22","collapse":false,"className":""},{"id":"9eb9a9fcd56859ba","type":"ui_tab","name":"Camera Mergulhador","icon":"dashboard","order":2,"disabled":false,"hidden":false},{"id":"9a076144e0b8c687","type":"ui_group","name":"shared","tab":"e671ec4ca9656cab","order":1,"disp":true,"width":"20","collapse":false,"className":""},{"id":"e671ec4ca9656cab","type":"ui_tab","name":"shared","icon":"dashboard","disabled":false,"hidden":false}]

I solved with chmod 777 in dir.

Is possible send me link or here, with code the sample with updated ?

I try is not sucess

[{"id":"f3874cf4a963908d","type":"subflow","name":"recording","info":"","category":"","in":[{"x":80,"y":160,"wires":[{"id":"d0615cb16ccf58fb"}]}],"out":[{"x":320,"y":120,"wires":[{"id":"d0615cb16ccf58fb","port":0}]}],"env":[{"name":"STATIC_PATH","type":"str","value":"/run/media/system/surveillance1/cctv/recordings"},{"name":"STATIC_ROOT","type":"str","value":"/static/recordings"}],"meta":{},"color":"#DDAA99","status":{"x":320,"y":200,"wires":[{"id":"d0615cb16ccf58fb","port":1}]}},{"id":"d0615cb16ccf58fb","type":"function","z":"f3874cf4a963908d","name":"recording","func":"const { payload, payload: { length }, duration, topic } = msg;\n\nconst [ , id, , type ] = topic.split('/');\n\nconst recording = context.get('recording');\n\nif (type === 'seg' || type === 'pre') {\n\n    const { mp4WriteStream = {}, m3u8WriteStream = {}, mp4Name, bytesWritten, segments, playlist } = recording;\n\n    if (mp4WriteStream.writable && m3u8WriteStream.writable) {\n\n        // write live (seg) or previous (pre) segments to existing writeStreams\n\n        mp4WriteStream.write(payload);\n\n        m3u8WriteStream.write(`#EXTINF:${duration}\\n#EXT-X-BYTERANGE:${length}@${bytesWritten}\\n${mp4Name}\\n`);\n\n        recording.bytesWritten += length;\n\n        recording.segments++;\n\n        // only send playlist after the first segment is written so that hls.js does not throw Hls.Events.ERROR, Hls.ErrorTypes.NETWORK_ERROR, Hls.ErrorDetails.LEVEL_EMPTY_ERROR\n\n        if (segments === 0) {\n\n            return { payload: playlist };\n\n        }\n\n    } else {\n\n        node.error(`mp4WriteStream and m3u8WriteStream must be writable`);\n\n    }\n\n    return;\n\n}\n\nif (type === 'init') {\n\n    // create new or end existing write streams based on buffer length\n\n    if (length) {\n\n        const date = new Date();\n\n        const year = date.getFullYear();\n\n        const month = (date.getMonth() + 1).toString().padStart(2, '0');\n\n        const day = date.getDate().toString().padStart(2, '0');\n\n        const hours = date.getHours().toString().padStart(2, '0');\n\n        const minutes = date.getMinutes().toString().padStart(2, '0');\n\n        const seconds = date.getSeconds().toString().padStart(2, '0');\n\n        // create name based on timestamp\n\n        const baseName = `${hours}h.${minutes}m.${seconds}s`; // 00h.00m.00s\n\n        const mp4Name = `${baseName}.mp4`; // 00h.00m.00s.mp4\n\n        const m3u8Name = `${baseName}.m3u8`; // 00h.00m.00s.m3u8\n\n        const baseRoot = `${env.get('STATIC_ROOT')}/${id}`;\n\n        const basePath = `${env.get('STATIC_PATH')}/${id}`;\n\n        // create dir base on timestamp\n\n        const fullDir = `${basePath}/${year}/${month}/${day}`; // basePath/yyyy/mm/dd\n\n        const { mkdirSync, createWriteStream, writeFile } = fs;\n\n        const newDir = mkdirSync(fullDir, { recursive: true });\n\n        newDir && writeFile(`${basePath}/listing.txt`, `${year}/${month}/${day}\\n`, { encoding: 'utf8', flag: 'a' }, (err) => { err && node.error(err) });\n\n        writeFile(`${fullDir}/listing.txt`, `${m3u8Name}\\n`, { encoding: 'utf8', flag: 'a' }, (err) => { err && node.error(err) });\n\n        // create new write streams for mp4 and playlist\n       \n        const mp4WriteStream = createWriteStream(`${fullDir}/${mp4Name}`)\n            .on('error', (err) => {\n                node.error(err);\n            });\n\n        const m3u8WriteStream = createWriteStream(`${fullDir}/${m3u8Name}`)\n            .on('error', (err) => {\n                node.error(err);\n            });\n\n        mp4WriteStream.write(payload);\n\n        m3u8WriteStream.write(`#EXTM3U\\n#EXT-X-VERSION:7\\n#EXT-X-PLAYLIST-TYPE:EVENT\\n#EXT-X-TARGETDURATION:${Math.round(duration) || 1}\\n#EXT-X-MEDIA-SEQUENCE:0\\n#EXT-X-MAP:URI=\"${mp4Name}\",BYTERANGE=\"${length}@0\"\\n`);\n\n        recording.mp4WriteStream = mp4WriteStream;\n\n        recording.m3u8WriteStream = m3u8WriteStream;\n\n        recording.mp4Name = mp4Name;\n\n        recording.bytesWritten = length;\n\n        recording.segments = 0;\n\n        // save playlist\n\n        recording.playlist = `${baseRoot}/${year}/${month}/${day}/${m3u8Name}`;\n\n        // send status\n\n        return [ null, { payload: { fill: 'green', shape: \"dot\", text: `${id}/${year}/${month}/${day}/${baseName}` } }];\n\n    } else {\n\n        // end existing writeStreams\n\n        const { mp4WriteStream = {}, m3u8WriteStream = {} } = recording;\n\n        if (mp4WriteStream.writable) {\n\n            mp4WriteStream.end();\n\n        }\n\n        if (m3u8WriteStream.writable) {\n\n            m3u8WriteStream.end('#EXT-X-ENDLIST\\n');\n\n        }\n\n        return;\n\n    }\n\n}","outputs":2,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\n\ncontext.set('recording', {});","finalize":"// Code added here will be run when the\n// node is being stopped or re-deployed.\n\nconst { mp4WriteStream = {}, m3u8WriteStream = {} } = context.get('recording');\n\nif (mp4WriteStream.writable) {\n\n    mp4WriteStream.end();\n\n}\n\nif (m3u8WriteStream.writable) {\n\n    m3u8WriteStream.end('#EXT-X-ENDLIST\\n');\n\n}\n\ncontext.set('recording', undefined);","libs":[{"var":"fs","module":"fs"}],"x":200,"y":160,"wires":[[],[]]},{"id":"32d61e0f.0490b2","type":"subflow","name":"stderr","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"211d46bf.a79c7a"}]}],"out":[],"env":[],"color":"#DDAA99","status":{"x":280,"y":120,"wires":[{"id":"211d46bf.a79c7a","port":1}]}},{"id":"211d46bf.a79c7a","type":"function","z":"32d61e0f.0490b2","name":"stderr","func":"// convert payload for Buffer to String and split by newlines\n\nconst stderr = msg.payload.toString().split('\\n');\n\nnode.send([ { stderr }, { payload: { fill: 'red', text: `${new Date().toString()}` } } ]);","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":170,"y":80,"wires":[["c6e5408.f19c4c"],[]]},{"id":"c6e5408.f19c4c","type":"debug","z":"32d61e0f.0490b2","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"stderr","targetType":"msg","statusVal":"","statusType":"auto","x":330,"y":40,"wires":[]},{"id":"9b59c1a2222210b8","type":"inject","z":"5273a6de.b59318","name":"start","props":[{"p":"action","v":"{\"command\":\"start\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":"5","topic":"","x":150,"y":320,"wires":[["adffeae04f85fed5"]]},{"id":"b4a8c0bc44499078","type":"inject","z":"5273a6de.b59318","name":"restart","props":[{"p":"action","v":"{\"command\":\"restart\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":"1","topic":"","x":150,"y":360,"wires":[["7a8c3f95b486b8f9"]]},{"id":"7dde9f15d2aa5933","type":"inject","z":"5273a6de.b59318","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":150,"y":400,"wires":[["7a8c3f95b486b8f9"]]},{"id":"58b77ee9a2e29092","type":"mp4frag","z":"5273a6de.b59318","name":"","outputs":2,"basePath":"telemetria","serveHttp":"false","serveIo":"false","hlsPlaylistSize":4,"hlsPlaylistExtra":0,"autoStart":"true","preBuffer":"0","timeLimit":"-1","repeated":"false","statusData":"playlist","x":1010,"y":100,"wires":[[],["571c8af49dfc40ae"]]},{"id":"a8741b7c88d88102","type":"subflow:32d61e0f.0490b2","z":"5273a6de.b59318","name":"","x":910,"y":340,"wires":[]},{"id":"ef6008f4d7425c22","type":"inject","z":"5273a6de.b59318","name":"write restart","props":[{"p":"action","v":"{\"command\":\"restart\",\"subject\":\"write\",\"preBuffer\":1,\"timeLimit\":-1,\"repeated\":false}","vt":"json"}],"repeat":"","crontab":"*/15 0-23 * * *","once":false,"onceDelay":"5","topic":"","x":750,"y":60,"wires":[["58b77ee9a2e29092"]]},{"id":"e23111864612f2c5","type":"inject","z":"5273a6de.b59318","name":"write stop","props":[{"p":"action","v":"{\"command\":\"stop\",\"subject\":\"write\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":760,"y":100,"wires":[["58b77ee9a2e29092"]]},{"id":"571c8af49dfc40ae","type":"subflow:f3874cf4a963908d","z":"5273a6de.b59318","name":"","env":[{"name":"STATIC_PATH","value":"/var/www/arq","type":"str"},{"name":"STATIC_ROOT","value":"/var/www/arq","type":"str"}],"x":1320,"y":180,"wires":[["e2da3c3cee315665"]]},{"id":"e2da3c3cee315665","type":"ui_mp4frag","z":"5273a6de.b59318","name":"","group":"9a076144e0b8c687","order":1,"width":"20","height":"14","readyPoster":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAMAAACtqHJCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzgwMTE3NDA3MjA2ODExODIyQUIzREQ3RTA0MDAyNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRkU5MkM4RDJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRkU5MkM4QzJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6Rjc3RjExNzQwNzIwNjgxMTgyMkFDQTkzQjdGQkY1MzAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDM4MDExNzQwNzIwNjgxMTgyMkFCM0REN0UwNDAwMjQiLz4gPC9yZGY6RGVzY3Jp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","errorPoster":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAMAAACtqHJCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMzgwMTE3NDA3MjA2ODExODIyQUIzREQ3RTA0MDAyNCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRkU5MkM5MTJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRkU5MkM5MDJCNTkxMUVCQjJCOTg0MEQ0QThCQ0YyQyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6Rjc3RjExNzQwNzIwNjgxMTgyMkFDQTkzQjdGQkY1MzAiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDM4MDExNzQwNzIwNjgxMTgyMkFCM0REN0UwNDAwMjQiLz4gPC9yZGY6RGVzY3Jp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","hlsJsConfig":"{\"startPosition\":0,\"liveDurationInfinity\":false,\"progressive\":false}","autoplay":"true","unload":"false","threshold":0.1,"controls":"true","muted":"true","players":["hls.js","hls","socket.io","mp4"],"x":1330,"y":340,"wires":[["9ec4f3ad80a3ec74"]]},{"id":"9ec4f3ad80a3ec74","type":"ui_template","z":"5273a6de.b59318","group":"9a076144e0b8c687","name":"video controls","order":15,"width":"20","height":"1","format":"<div layout-fill layout=\"row\">\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"toggleControls()\">\n            <md-icon class=\"material-icons\">smart_button</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"playPause()\">\n            <md-icon class=\"material-icons\">play_arrow</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"forwardFive()\">\n            <md-icon class=\"material-icons\">forward_5</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"replayFive()\">\n            <md-icon class=\"material-icons\">replay_5</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"decreaseRate()\">\n            <md-icon class=\"material-icons\">slow_motion_video</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"increaseRate()\">\n            <md-icon class=\"material-icons\">speed</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"toggleMuted()\">\n            <md-icon class=\"material-icons\">volume_mute</md-icon>\n        </md-button>\n    </div>\n\n    <div layout-fill>\n        <md-button layout-fill class=\"md-raised\" ng-click=\"videoFullscreen()\">\n            <md-icon class=\"material-icons\">fullscreen</md-icon>\n        </md-button>\n    </div>\n\n</div>\n\n<script>\n    ((scope) => {\n\n    const unwatchMsg = scope.$watch('msg', (msg) => {\n\n        if (msg && msg.videoId) {\n\n            const video = document.getElementById(msg.videoId);\n            \n            if (video) {\n\n                const rates = [ 0.1, 0.2, 0.5, 1, 2, 5, 10];\n\n                let rateIndex = 3;\n\n                const requestFullscreen = video.webkitRequestFullscreen || video.webkitEnterFullScreen || video.mozRequestFullScreen || video.requestFullscreen;\n\n                scope.toggleControls = () => {\n\n                    video.controls = !video.controls;\n\n                }\n\n                scope.playPause = () => {\n\n                    video.paused ? video.play() : video.pause();\n\n                }\n\n                scope.toggleMuted = () => {\n\n                    video.muted = !video.muted;\n\n                }\n\n                scope.videoFullscreen = () => {\n\n                    requestFullscreen.call(video);\n\n                }\n\n                scope.videoDisplay = () => {\n\n                    video.style.display = video.style.display === 'none' ? 'initial' : 'none';\n\n                }\n\n                scope.increaseRate = () => {\n\n                    rateIndex++;\n\n                    if (rateIndex > 6) {\n\n                        rateIndex = 6;\n\n                    }\n\n                    video.playbackRate = rates[rateIndex];\n\n                }\n\n                scope.decreaseRate = () => {\n                \n                    rateIndex--;\n                \n                    if (rateIndex < 0) {\n\n                        rateIndex = 0;\n                    \n                    }\n                \n                    video.playbackRate = rates[rateIndex];\n                \n                }\n\n                scope.forwardFive = () => {\n                \n                    video.currentTime += 5;\n                \n                }\n\n                scope.replayFive = () => {\n                \n                    video.currentTime -= 5;\n                \n                }\n\n                unwatchMsg();\n\n            }\n\n        }\n\n    });\n\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":true,"templateScope":"local","className":"","x":1340,"y":440,"wires":[[]]},{"id":"cfa37a1fd41f4570","type":"group","z":"5273a6de.b59318","name":"Camera IP - Protocolo RTSP","style":{"stroke":"#ffC000","fill":"#ffefbf","label":true,"color":"#6f2fa0"},"nodes":["7a8c3f95b486b8f9","4cc7afbbcc802243","652bde80360a1fe1","d1112dfcbfef615b","2858e68578995d73","379c223a6ef2f8ea","adffeae04f85fed5","96e79d1eccb9df9d"],"x":74,"y":139,"w":1132,"h":122},{"id":"7a8c3f95b486b8f9","type":"ffmpeg","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","outputs":3,"cmdPath":"ffmpeg","cmdArgs":"[]","cmdOutputs":2,"killSignal":"SIGTERM","x":750,"y":200,"wires":[["4cc7afbbcc802243","58b77ee9a2e29092"],["4cc7afbbcc802243","58b77ee9a2e29092"],["a8741b7c88d88102"]]},{"id":"4cc7afbbcc802243","type":"mp4frag","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","outputs":2,"basePath":"teste","serveHttp":"true","serveIo":"true","hlsPlaylistSize":"6","hlsPlaylistExtra":"2","autoStart":"false","preBuffer":1,"timeLimit":10000,"repeated":"false","statusData":"playlist","x":920,"y":200,"wires":[["652bde80360a1fe1"],[]]},{"id":"652bde80360a1fe1","type":"ui_mp4frag","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","group":"46d6d60673638810","order":1,"width":22,"height":13,"readyPoster":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB2aWV3Qm94PSIwIDAgODAwMCA2MDAwIj48dGV4dCBmaWxsPSIjMGYwIiBzdHJva2Utd2lkdGg9IjAiIGR4PSIwIiBkeT0iMCIgZm9udC1mYW1pbHk9IiZxdW90O2UyY3B1d3p4YldsMTo6OlJvYm90byZxdW90OyIgZm9udC1zaXplPSI2MDAiIGZvbnQtd2VpZ2h0PSI3MDAiIHRyYW5zZm9ybT0idHJhbnNsYXRlKCAxMDExLjI0IDMyMDQuODgpIj48dHNwYW4geT0iMCIgc3Ryb2tlLXdpZHRoPSIwIiBmb250LXdlaWdodD0iNzAwIj48IVtDREFUQVsKVmlkZW8gUGxheWJhY2sgUmVhZHkKXV0+PC90c3Bhbj48L3RleHQ+PC9zdmc+Cg==","errorPoster":"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHNoYXBlLXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiB2aWV3Qm94PSIwIDAgODAwMCA2MDAwIj48dGV4dCBmaWxsPSJyZWQiIHN0cm9rZS13aWR0aD0iMCIgZHg9IjAiIGR5PSIwIiBmb250LWZhbWlseT0iJnF1b3Q7ZW42OXdhSUtnN0MxOjo6Um9ib3RvJnF1b3Q7IiBmb250LXNpemU9IjYwMCIgZm9udC13ZWlnaHQ9IjcwMCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoIDExODMuOTYgMzIwNC44OCkiPjx0c3BhbiB5PSIwIiBzdHJva2Utd2lkdGg9IjAiIGZvbnQtd2VpZ2h0PSI3MDAiPjwhW0NEQVRBWwpWaWRlbyBQbGF5YmFjayBFcnJvcgpdXT48L3RzcGFuPjwvdGV4dD48L3N2Zz4K","hlsJsConfig":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":5,\"maxBufferLength\":10,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","autoplay":"true","unload":"true","threshold":0.1,"controls":"true","muted":"true","players":["socket.io","hls.js","hls","mp4"],"x":1110,"y":200,"wires":[[]]},{"id":"d1112dfcbfef615b","type":"ui_button","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","group":"46d6d60673638810","order":1,"width":0,"height":0,"passthru":false,"label":"Start","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":150,"y":180,"wires":[["adffeae04f85fed5"]]},{"id":"2858e68578995d73","type":"ui_button","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","group":"46d6d60673638810","order":1,"width":0,"height":0,"passthru":false,"label":"Stop","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"","payloadType":"str","topic":"topic","topicType":"msg","x":150,"y":220,"wires":[["379c223a6ef2f8ea"]]},{"id":"379c223a6ef2f8ea","type":"change","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"stop\",\"args\":[\"-loglevel\",\"quiet\",\"-rtsp_transport\",\"tcp\",\"-i\",\"rtsp://belov:belov12345678@192.168.1.129:554/cam/realmonitor?channel=1&subtype=0\",\"-an\",\"-c:v\",\"copy\",\"-f\",\"mp4\",\"-movflags\",\"+frag_every_frame+empty_moov+default_base_moof\",\"-min_frag_duration\",\"1000000\",\"pipe:1\"]}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":320,"y":220,"wires":[["7a8c3f95b486b8f9"]]},{"id":"adffeae04f85fed5","type":"change","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","rules":[{"t":"set","p":"URLCAM","pt":"msg","to":"#:(persistent)::URLCAM_conf","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":180,"wires":[["96e79d1eccb9df9d"]]},{"id":"96e79d1eccb9df9d","type":"change","z":"5273a6de.b59318","g":"cfa37a1fd41f4570","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"quiet\",\"-rtsp_transport\",\"tcp\",\"-i\", $.URLCAM,\"-an\",\"-c:v\",\"copy\",\"-f\",\"mp4\",\"-movflags\",\"+frag_every_frame+empty_moov+default_base_moof\",\"-min_frag_duration\",\"1000000\",\"pipe:1\"]}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":180,"wires":[["7a8c3f95b486b8f9"]]},{"id":"46d6d60673638810","type":"ui_group","name":"","tab":"9eb9a9fcd56859ba","order":1,"disp":true,"width":"22","collapse":false,"className":""},{"id":"9eb9a9fcd56859ba","type":"ui_tab","name":"Camera Mergulhador","icon":"dashboard","order":2,"disabled":false,"hidden":false},{"id":"9a076144e0b8c687","type":"ui_group","name":"shared","tab":"e671ec4ca9656cab","order":1,"disp":true,"width":"20","collapse":false,"className":""},{"id":"e671ec4ca9656cab","type":"ui_tab","name":"shared","icon":"dashboard","disabled":false,"hidden":false}]

I had to re-read some of the previous posts from last year to remember what we did.

  • Firstly, you have to setup static hosting from node-red by configuring httpStatic in the setting.js file.
  • Then you have to set the values of static path/root in the subflow to match those values.

The current problem is that your system is setup differently than mine and you probably do not have an external usb 6tb hard drive mounted at /run/media/system/surveillance1 as I do. The values are configurable so that you can make it match your specific setup of where you would want to write the data.

To clarify, I have a hard drive mounted at a location that I am using to write large quantities of video.

Filesystem      Size  Used Avail Use% Mounted on
/dev/root        30G  9.3G   19G  34% /
devtmpfs        3.5G     0  3.5G   0% /dev
tmpfs           3.7G  904K  3.7G   1% /dev/shm
tmpfs           1.5G  932K  1.5G   1% /run
tmpfs           5.0M  4.0K  5.0M   1% /run/lock
/dev/mmcblk0p1  253M   31M  222M  13% /boot
/dev/sda1       5.5T  4.2T 1015G  81% /run/media/system/surveillance1
tmpfs           739M     0  739M   0% /run/user/1000

So, in the settings.js file, I added some configuration to tell node-red to map a location there and host files from it in the httpStatic sections.

httpStaticRoot: '/static/',
httpStatic: [
        { path: '/run/media/system/surveillance1/cctv/recordings/', root: '/recordings/' }
    ],

Using those values, I added that to the subflow node so that I can write to the correct location.

Currently, I am writing video 24/7 for 11 main streams and it seems to work ok.

2 Likes

@allacmc
What is not yet working??

Hey Kevin,

Is this a turn key solution now or you have provided just the (excellent) building blocks ?

We are just deploying a number of security cameras around our place and would like to integrate them into Node Red.

I run my Node Red in a Virtual machine under ESXI so could easily spawn a dedicated machine for this if required

All of my storage is centralised and will be made available to the Node Red host through NFS shares

Craig

Sorry jumpin in

Turn-Key and Turn-Key, well you have to follow the instructions given and import the flow sample (one per camera) and make the modifications to match their url's etc. (In addition I did on my own some experiments how to handle and play the recordings in a convenient way from the browser. So far that worked excellent bur unfortunately all my cameras are usb types so I just archived my setup for future, next house or camera upgrading, whatever happens first)

Myself I do have a house-keeping script that erases older recordings. Since your drive is reaching the upper limit, do you have something similar as well?

It's definitely closer to building blocks than it is to turn-key. Although, I have ensured that any library I have published is designed to be cross platform for linux, macos, and windows.

Dealing with the local police department yesterday reminded of how turn-key it is NOT when I was trying to extract video footage that caught the getaway driver's vehicle of someone that stole a car from the dealership on the other side of the apartment complex. And that reminds me, I only setup the 24/7 recording after the previous car theft occurred at the same location and I had no recordings available. I guess necessity is the mother of all invention. In fact, I only ever set up cameras many years ago due to an ex girlfriend who was threatening my property while I had to be away at work. And then I wasn't satisfied with the "off the shelf" camera systems and started working on my own stuff. It's funny life works.

Thanks for jumping in.

I do, but it's not perfect. When I updated it a while back I broke something and my empty folders no longer get deleted. I still have time to fix it since they don't take up much space.


The use percent gets run once per hour, while the removal script run once per night to delete anything older than 2 weeks. I used the standard fs module but my hunch is that your cleanup script is made in python?

(Cctv recording and playback (work in progress) - #39 by allacmc)

Is it possible for you to add a sample code? of the nodes with the recording feature?

The last ones I got don't have the node updated, it's missing.

That's correct, :smile: but the idea and concept is more or less the same, I keep the last 10 days

1 Like