[beta testing] nodes for live streaming mp4

Hi Kevin,
Changing the basePath during runtime should cause a clearing of the buffer, a restart of the node with the new basePath (delete this: There is no need to support adding & removing routes during runtime).

It's not needed to buffer segments if no basePath is set, I might be expressing myself wrong when I say "the node should just idling, waiting for a basePath to be set"

If you pass a mp4 buffer from ffmpeg to the mp4frag node not having a basePath set, it should not use that, just discard the buffers

EDIT: I think in this way, there will not be many, if any, way the user can break it

I did a little overhaul to the http routes. It should not affect functionality, just cleaning up and organizing a bit. Ultimately, I would like to make socket .io an optional dependency. Also, i would like to make it so that you can pick which type of resources you want to serve, such as selecting video.mp4, hls.m3u8, socket .io, or none of the above (if only needing to pipe out the mp4 buffer from the 2nd port). I ran out of time for that and do not have an answer to the basePath idea.

I ask, as an option for your project, do you definitely need to define the basePath or could it be generated randomly or based on the node id? As my node is currently structured, it really needs that info when it is first initialized. If you wanted the alternative of random string or node id, then i could make that happen much sooner.

For, node-red-contrib-mp4frag 0.1.0-beta.4, you can use the value id in the basePath property so it will automatically use the node's id when making the http routes, etc. That will make it easier to make copies since each node using that value will automatically use its own unique id.


Screen Shot 2021-08-22 at 7.16.33 PM

2 Likes

Excellent @kevinGodell !
That works fine, that is perfect! I can use that to initiate the number of cameras I need without having to configure the mp4frag basePath upfront. Once initiated I grab the created playlist and link that to the camera name I have assigned. This also allows me to create a "mp4frag subflow" and simplify my flow substantially. See sample flow below
Thanks a lot Kevin!

[{"id":"8416c1e7.d93fa","type":"subflow","name":"mp4frag subflow","info":"","category":"","in":[{"x":120,"y":200,"wires":[{"id":"8db549fb.34ab78"},{"id":"e6f3017c.f2a61"}]}],"out":[{"x":740,"y":80,"wires":[{"id":"e6f3017c.f2a61","port":0}]},{"x":740,"y":160,"wires":[{"id":"d4e5a7b6.9dcaf8","port":0}]},{"x":740,"y":240,"wires":[{"id":"d4e5a7b6.9dcaf8","port":1}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"d4e5a7b6.9dcaf8","type":"mp4frag","z":"8416c1e7.d93fa","name":"","migrate":2e-9,"hlsPlaylistSize":4,"hlsPlaylistExtra":0,"basePath":"id","repeated":"false","timeLimit":10000,"preBuffer":1,"x":490,"y":200,"wires":[["4b282ea8.2e198"],[]]},{"id":"8db549fb.34ab78","type":"ffmpeg-spawn","z":"8416c1e7.d93fa","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[]","cmdOutputs":1,"killSignal":"SIGTERM","x":280,"y":200,"wires":[["d4e5a7b6.9dcaf8"],["d4e5a7b6.9dcaf8"]]},{"id":"4b282ea8.2e198","type":"switch","z":"8416c1e7.d93fa","name":"","property":"payload.hlsPlaylist","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":490,"y":140,"wires":[["e6f3017c.f2a61"]]},{"id":"e6f3017c.f2a61","type":"join","z":"8416c1e7.d93fa","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":290,"y":80,"wires":[[]]},{"id":"e62acd5b.7efa6","type":"inject","z":"472fc173.1567","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":520,"wires":[["a2fb58f8.816ce8"]]},{"id":"697d86dc.54cca8","type":"inject","z":"472fc173.1567","name":"TearsOfSteel","props":[{"p":"action","v":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"error\",\"-nostats\",\"-f\",\"hls\",\"-http_multiple\",\"1\",\"-re\",\"-i\",\"http://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/hls/TearsOfSteel.m3u8\",\"-c:v\",\"copy\",\"-c:a\",\"aac\",\"-f\",\"mp4\",\"-movflags\",\"+frag_keyframe+empty_moov+default_base_moof\",\"pipe:1\",\"-progress\",\"pipe:3\"]}","vt":"json"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"1","topic":"","payload":"camera nbr x","payloadType":"str","x":610,"y":460,"wires":[["a2fb58f8.816ce8"]]},{"id":"9f9de01a.8de4e","type":"debug","z":"472fc173.1567","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1050,"y":420,"wires":[]},{"id":"a2fb58f8.816ce8","type":"subflow:8416c1e7.d93fa","z":"472fc173.1567","x":850,"y":480,"wires":[["9f9de01a.8de4e"],["2b9b7cbb.24c1e4"],["fe20ef24.742ba"]]},{"id":"fe20ef24.742ba","type":"debug","z":"472fc173.1567","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1050,"y":540,"wires":[]},{"id":"2b9b7cbb.24c1e4","type":"ui_mp4frag","z":"472fc173.1567","name":"","group":"9a72bc97.c7009","order":0,"width":6,"height":4,"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":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":5,\"maxBufferLength\":10,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","autoplay":"true","unload":"true","threshold":0.1,"controls":"true","muted":"true","players":["hls.js","socket.io","hls","mp4"],"x":1070,"y":480,"wires":[[]]},{"id":"49d41ac4.d434c4","type":"inject","z":"472fc173.1567","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":610,"y":720,"wires":[["63c8c410.ee5dec"]]},{"id":"d716bc87.211ee","type":"ui_mp4frag","z":"472fc173.1567","name":"","group":"9a72bc97.c7009","order":0,"width":6,"height":4,"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":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":5,\"maxBufferLength\":10,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","autoplay":"true","unload":"true","threshold":0.1,"controls":"true","muted":"true","players":["hls.js","socket.io","hls","mp4"],"x":1070,"y":680,"wires":[[]]},{"id":"dcb859aa.755488","type":"inject","z":"472fc173.1567","name":"RedBull","props":[{"p":"action","v":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"error\",\"-nostats\",\"-f\",\"hls\",\"-http_multiple\",\"1\",\"-re\",\"-i\",\"http://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_1660.m3u8\",\"-c:v\",\"copy\",\"-c:a\",\"aac\",\"-f\",\"mp4\",\"-movflags\",\"+frag_keyframe+empty_moov+default_base_moof\",\"pipe:1\",\"-progress\",\"pipe:3\"]}","vt":"json"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"1","topic":"","payload":"camera nbr y","payloadType":"str","x":610,"y":660,"wires":[["63c8c410.ee5dec"]]},{"id":"63c8c410.ee5dec","type":"subflow:8416c1e7.d93fa","z":"472fc173.1567","name":"","x":850,"y":680,"wires":[["c887f485.c227f8"],["d716bc87.211ee"],["86c8a150.04057"]]},{"id":"c887f485.c227f8","type":"debug","z":"472fc173.1567","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1050,"y":620,"wires":[]},{"id":"86c8a150.04057","type":"debug","z":"472fc173.1567","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1050,"y":740,"wires":[]},{"id":"9a72bc97.c7009","type":"ui_group","name":"Test","tab":"53358f7d.907ac","order":1,"disp":true,"width":"6","collapse":false},{"id":"53358f7d.907ac","type":"ui_tab","name":"Test","icon":"dashboard","disabled":false,"hidden":false}]
3 Likes

Ok, I get it now. The way I was initializing the node was not allowing it to be very portable. I only recently started using subflows and had never tested my own nodes inside subflows. I will be sure to maintain compatibility going forward. Thanks for the clarification.

I'm not sure if I have such very advanced subflows but this is how I now have been able to reduce. For each source, I have a tab like below. All configuration params are currently in the inject button but I plan to move that to some external configuration data set of some kind. The flow below is just a sub component of a larger flow system and handles the startup of the stream as well as incoming commands to start recordings and/or take snapshots to be forwarded to various consumers

The benefit now is that this flow is easy to reduce, you just copy the tab as many times as you have ip sources and change the related params in the inject node. I tried to put even more of the nodes into the subflows but some things I could not figure out, I had to keep them outside for the time being. Maybe I could even merge the two subflows into one but for readability, it's not so bad to keep them separated

[{"id":"eb003283.c28bf","type":"subflow","name":"HSS_mp4frag subflow","info":"","category":"","in":[{"x":100,"y":200,"wires":[{"id":"62ea8138.a1ecc"}]}],"out":[{"x":840,"y":200,"wires":[{"id":"43fff5e2.62047c","port":1}]},{"x":840,"y":320,"wires":[{"id":"832a7a83.3b8028","port":0}]},{"x":360,"y":400,"wires":[{"id":"62ea8138.a1ecc","port":0},{"id":"62ea8138.a1ecc","port":1}]}],"env":[],"meta":{},"color":"#DDAA99","status":{"x":1140,"y":400,"wires":[{"id":"f8c3cfdc.2e315","port":0},{"id":"9814715a.7517e","port":0}]}},{"id":"43fff5e2.62047c","type":"mp4frag","z":"eb003283.c28bf","name":"","migrate":2e-9,"hlsPlaylistSize":4,"hlsPlaylistExtra":0,"basePath":"id","repeated":"false","timeLimit":10000,"preBuffer":1,"x":570,"y":160,"wires":[["5e775a0d.d46554","1f3151ed.cd8ebe"],[]]},{"id":"1b11edc3.1378a2","type":"ffmpeg-spawn","z":"eb003283.c28bf","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[]","cmdOutputs":1,"killSignal":"SIGTERM","x":540,"y":240,"wires":[["43fff5e2.62047c"],["43fff5e2.62047c"]]},{"id":"5e775a0d.d46554","type":"switch","z":"eb003283.c28bf","name":"","property":"payload.hlsPlaylist","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":510,"y":400,"wires":[["a09fcfcc.52157"]]},{"id":"a09fcfcc.52157","type":"join","z":"eb003283.c28bf","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":510,"y":320,"wires":[["832a7a83.3b8028"]]},{"id":"62ea8138.a1ecc","type":"switch","z":"eb003283.c28bf","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"Snapshot","vt":"str"},{"t":"eq","v":"Recording","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":230,"y":200,"wires":[["43fff5e2.62047c"],["43fff5e2.62047c"],["1b11edc3.1378a2","a09fcfcc.52157"]]},{"id":"832a7a83.3b8028","type":"function","z":"eb003283.c28bf","name":"","func":"let hss = global.get(\"hss.cameras\");\nlet cam = msg.payload[0];\nlet url = msg.payload[1].hlsPlaylist;\n//node.warn(url);\nlet camera = hss[cam];\ncamera.url = url;\n//node.warn(camera.url);\nhss[cam] = camera;\n//node.warn(hss[cam]);\nglobal.set(\"hss.cameras\", hss);\nmsg.payload = hss[cam];\nmsg.camera = cam;\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":320,"wires":[["f8c3cfdc.2e315"]]},{"id":"f8c3cfdc.2e315","type":"change","z":"eb003283.c28bf","name":"the url","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":810,"y":400,"wires":[[]]},{"id":"1f3151ed.cd8ebe","type":"switch","z":"eb003283.c28bf","name":"","property":"payload","propertyType":"msg","rules":[{"t":"empty"}],"checkall":"true","repair":false,"outputs":1,"x":870,"y":120,"wires":[["9814715a.7517e"]]},{"id":"9814715a.7517e","type":"change","z":"eb003283.c28bf","name":"the url","rules":[{"t":"set","p":"payload","pt":"msg","to":"stopped","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1010,"y":120,"wires":[[]]},{"id":"26ed31e3.2fdffe","type":"subflow","name":"Recordings & Snapshots","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"d72ee6ae.b58e28"}]}],"out":[{"x":600,"y":320,"wires":[{"id":"d0b16878.6536b8","port":0}]},{"x":600,"y":380,"wires":[{"id":"736eff4.ea988","port":0}]},{"x":600,"y":760,"wires":[{"id":"a6a407d6.5d4148","port":0}]},{"x":600,"y":820,"wires":[{"id":"a6a407d6.5d4148","port":1}]},{"x":600,"y":880,"wires":[{"id":"a6a407d6.5d4148","port":2}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"4bf8e63f.4271a8","type":"file","z":"26ed31e3.2fdffe","name":"Save file","filename":"","appendNewline":false,"createDir":false,"overwriteFile":"false","encoding":"none","x":400,"y":260,"wires":[["d0b16878.6536b8"]]},{"id":"d0b16878.6536b8","type":"trigger","z":"26ed31e3.2fdffe","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"5","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":400,"y":320,"wires":[["736eff4.ea988"]]},{"id":"f7cfc57.a602638","type":"function","z":"26ed31e3.2fdffe","name":"","func":"let ts = flow.get('ts') || undefined;\n\nif (ts == undefined) {\n    //get new time stamp\n    const d = new Date();\n\n    let year = d.getFullYear().toString();\n    let month = (d.getMonth() + 1).toString();\n    if (month.length < 2) {\n        month = '0' + month;\n    }\n    let day = d.getDate().toString();\n    if (day.length < 2) {\n        day = '0' + day;\n    }\n    let hour = d.getHours().toString();\n    if (hour.length < 2) {\n        hour = '0' + hour;\n    }\n    let minute = d.getMinutes().toString();\n    if (minute.length < 2) {\n        minute = '0' + minute;\n    }\n    let seconds = d.getSeconds().toString();\n    if (seconds.length < 2) {\n        seconds = '0' + seconds;\n    }\n\n    ts = year + '-' + month + '-' + day + '-' + hour + minute + seconds;\n    flow.set('ts', ts);\n}\n\n// build the payload for recording\nvar hss = global.get(\"hss\");\nlet fn = hss.http_path + \"/hss_media/recordings/\" + ts + '.' + hss.recording_format;\nglobal.set(\"hss.latest_recording\", fn);\nconst { payload, action } = msg;\n\nif (Buffer.isBuffer(payload)) {\n    msg.filename = fn;\n    return msg;\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":200,"wires":[["4bf8e63f.4271a8"]]},{"id":"38926008.f5cfd","type":"change","z":"26ed31e3.2fdffe","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"stop\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":620,"wires":[[]]},{"id":"31706103.689ede","type":"trigger","z":"26ed31e3.2fdffe","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"1","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":400,"y":680,"wires":[["38926008.f5cfd"]]},{"id":"736eff4.ea988","type":"change","z":"26ed31e3.2fdffe","name":"Recording done","rules":[{"t":"set","p":"topic","pt":"msg","to":"RECORDING","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"Recording for IP source done","tot":"str"},{"t":"delete","p":"ts","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":380,"wires":[[]]},{"id":"a6a407d6.5d4148","type":"switch","z":"26ed31e3.2fdffe","name":"","property":"notification_type","propertyType":"global","rules":[{"t":"eq","v":"Snapshot","vt":"str"},{"t":"eq","v":"Telegram","vt":"str"},{"t":"eq","v":"Email","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":390,"y":820,"wires":[[],[],[]]},{"id":"ba6d86bf.bf3f88","type":"delay","z":"26ed31e3.2fdffe","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":420,"y":740,"wires":[["a6a407d6.5d4148"]]},{"id":"d72ee6ae.b58e28","type":"switch","z":"26ed31e3.2fdffe","name":"","property":"writeMode","propertyType":"global","rules":[{"t":"eq","v":"recording","vt":"str"},{"t":"eq","v":"snapshot","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":190,"y":80,"wires":[["f7cfc57.a602638","73993c36.5d8b04"],["be2268d5.bb84a8"]]},{"id":"32900339.aea3ac","type":"comment","z":"26ed31e3.2fdffe","name":"Create mp4 file and image required for making the thumbnail","info":"","x":560,"y":160,"wires":[]},{"id":"c6dbcf14.e78bc","type":"comment","z":"26ed31e3.2fdffe","name":"Create jpg image snapshot","info":"","x":450,"y":520,"wires":[]},{"id":"8c08be50.ead39","type":"file","z":"26ed31e3.2fdffe","name":"","filename":"","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"none","x":750,"y":560,"wires":[[]]},{"id":"7a9a3954.3459c8","type":"function","z":"26ed31e3.2fdffe","name":"","func":"// build the payload for recording thumb nail\nvar hss = global.get(\"hss\");\nlet fn = hss.latest_recording.split(hss.recording_format)[0] + hss.recording_format + '.jpg';\n\nconst { payload, action } = msg;\n\nif (Buffer.isBuffer(payload)) {\n        msg.filename = fn;\n        return msg;\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":500,"wires":[["8c08be50.ead39"]]},{"id":"96d62f8a.517a","type":"change","z":"26ed31e3.2fdffe","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"stop\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":320,"wires":[["d97a4806.92b288"]]},{"id":"753ee470.28695c","type":"trigger","z":"26ed31e3.2fdffe","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"1","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":760,"y":380,"wires":[["96d62f8a.517a"]]},{"id":"ba9c87a7.1071e8","type":"delay","z":"26ed31e3.2fdffe","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":760,"y":440,"wires":[["7a9a3954.3459c8"]]},{"id":"d97a4806.92b288","type":"ffmpeg-spawn","z":"26ed31e3.2fdffe","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[\"-i\",\"pipe:0\",\"-f\",\"image2pipe\",\"pipe:1\"]","cmdOutputs":1,"killSignal":"SIGTERM","x":760,"y":260,"wires":[[],["753ee470.28695c","ba9c87a7.1071e8"]]},{"id":"be2268d5.bb84a8","type":"ffmpeg-spawn","z":"26ed31e3.2fdffe","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[\"-i\",\"pipe:0\",\"-f\",\"image2pipe\",\"pipe:1\"]","cmdOutputs":1,"killSignal":"SIGTERM","x":420,"y":560,"wires":[[],["31706103.689ede","ba6d86bf.bf3f88"]]},{"id":"73993c36.5d8b04","type":"delay","z":"26ed31e3.2fdffe","name":"","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":750,"y":200,"wires":[["d97a4806.92b288"]]},{"id":"50ed257.677f8dc","type":"tab","label":"IP CAM","disabled":false,"info":""},{"id":"879d3f10.ec9b1","type":"inject","z":"50ed257.677f8dc","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":390,"y":440,"wires":[["3d29d905.6efda6"]]},{"id":"345eaad9.172a66","type":"link in","z":"50ed257.677f8dc","name":"IP_Record_IN","links":["2bf661f0.22225e","7be3e5c2.4b909c"],"x":95,"y":180,"wires":[["d40da575.efc358"]]},{"id":"1e9db784.31f758","type":"inject","z":"50ed257.677f8dc","name":"TearsOfSteel","props":[{"p":"action","v":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"error\",\"-nostats\",\"-f\",\"hls\",\"-http_multiple\",\"1\",\"-re\",\"-i\",\"http://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/hls/TearsOfSteel.m3u8\",\"-c:v\",\"copy\",\"-c:a\",\"aac\",\"-f\",\"mp4\",\"-movflags\",\"+frag_keyframe+empty_moov+default_base_moof\",\"pipe:1\",\"-progress\",\"pipe:3\"]}","vt":"json"},{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"cam9","payloadType":"str","x":370,"y":380,"wires":[["3d29d905.6efda6"]]},{"id":"45151bef.478344","type":"comment","z":"50ed257.677f8dc","name":"HSS mp4frag video sources and recording","info":"","x":240,"y":40,"wires":[]},{"id":"c641e406.a7d448","type":"comment","z":"50ed257.677f8dc","name":"Switch to trigger recordings","info":"","x":200,"y":140,"wires":[]},{"id":"afb78f7f.3e5d6","type":"link in","z":"50ed257.677f8dc","name":"IP_Snapshot_IN","links":["c88b87e1.f21c88","5ec907a2.8da2c8","384bbc7d.b10064","1a01ed2f.d1a463","70fcd40e.1dd2bc","cb480b0b.f22248","9bae2e46.deaa8"],"x":95,"y":300,"wires":[["96e4bb8b.55df88"]]},{"id":"d56bcde6.7387d","type":"comment","z":"50ed257.677f8dc","name":"Switch to trigger snapshots","info":"","x":200,"y":260,"wires":[]},{"id":"96e4bb8b.55df88","type":"change","z":"50ed257.677f8dc","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"start\",\"repeated\":false,\"timeLimit\":1,\"preBuffer\":3}","tot":"json"},{"t":"set","p":"notification_type","pt":"global","to":"topic","tot":"msg"},{"t":"set","p":"writeMode","pt":"global","to":"snapshot","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":220,"y":300,"wires":[["2d92a4a2.a2fabc"]]},{"id":"d40da575.efc358","type":"change","z":"50ed257.677f8dc","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"start\",\"timeLimit\":0,\"repeated\":false,\"preBuffer\":$globalContext(\"recording_duration\")}","tot":"jsonata"},{"t":"set","p":"writeMode","pt":"global","to":"recording","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Recording","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":220,"y":180,"wires":[["4f66394b.0789b8"]]},{"id":"9cfb2750.e2c338","type":"subflow:26ed31e3.2fdffe","z":"50ed257.677f8dc","x":950,"y":240,"wires":[["3d4d5c03.736554"],["dc602675.e7d388"],["6bb6ba9e.15fb34"],["e7da0b83.7cff68"],["4ddece85.fce61"]]},{"id":"27abd8e6.7afc28","type":"comment","z":"50ed257.677f8dc","name":"Version 11","info":"v11 2021/08/25 - WK\n- reworked ip cam flow tab using subflows\n\nv10 2021/08/19 - WK\n- reworked ip cam flow tab\n\nv09 2021/08/16 - WK\n- starting sources at init\n\nv08 2021/08/15 - WK\n- using 'hss.latest_recording' when setting file name of thumb nails\n\nv07 2021/08/08 - WK\n- using 'recording_format' for setting compatible format for video files\n\nv06 2021/08/07 - WK\n- added thumb nails to recordings\n\nv05 2021/08/04 - WK\n- adopting to use browsing of recordings & snapshots\n\nv04 2021/07/31 - WK\n- restructuring and design modifications\n\nv03 2021/07/29 - WK\n- added support for triggering of telegrams and emails\n\nv02 2021/07/29 - WK\n- first version supporting triggering of snapshots\n\nv01 2021/07/09 - WK\n- first version supporting mp4frag video sources and triggering of recording\n\n\n","x":140,"y":80,"wires":[]},{"id":"6bb6ba9e.15fb34","type":"link out","z":"50ed257.677f8dc","name":"to_snapshot_ip_cams","links":["d3160b3d.d839c8"],"x":1155,"y":240,"wires":[]},{"id":"4ddece85.fce61","type":"link out","z":"50ed257.677f8dc","name":"to_email_ip_cams","links":["14c6814f.7c12bf"],"x":1155,"y":320,"wires":[]},{"id":"e7da0b83.7cff68","type":"link out","z":"50ed257.677f8dc","name":"to_telegram_ip_cams","links":["6098bf0f.b236e"],"x":1155,"y":280,"wires":[]},{"id":"3d4d5c03.736554","type":"link out","z":"50ed257.677f8dc","name":"to_recording_done","links":["d6065808.b6b018"],"x":1155,"y":160,"wires":[]},{"id":"dc602675.e7d388","type":"link out","z":"50ed257.677f8dc","name":"to_show_notification","links":["63e8b119.a294d"],"x":1155,"y":200,"wires":[]},{"id":"3d29d905.6efda6","type":"subflow:eb003283.c28bf","z":"50ed257.677f8dc","name":"","x":620,"y":240,"wires":[["9cfb2750.e2c338"],["a8881dfc.aa0bb","5b906553.b6483c"],["734e9b05.181cc4"]]},{"id":"a8881dfc.aa0bb","type":"debug","z":"50ed257.677f8dc","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":890,"y":380,"wires":[]},{"id":"734e9b05.181cc4","type":"debug","z":"50ed257.677f8dc","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":890,"y":440,"wires":[]},{"id":"5b906553.b6483c","type":"change","z":"50ed257.677f8dc","name":"","rules":[{"t":"set","p":"mp4frag_url","pt":"flow","to":"payload.url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":940,"y":320,"wires":[[]]},{"id":"4f66394b.0789b8","type":"switch","z":"50ed257.677f8dc","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"mp4frag_url","vt":"flow"}],"checkall":"true","repair":false,"outputs":1,"x":390,"y":180,"wires":[["3d29d905.6efda6"]]},{"id":"2d92a4a2.a2fabc","type":"switch","z":"50ed257.677f8dc","name":"","property":"url","propertyType":"msg","rules":[{"t":"eq","v":"mp4frag_url","vt":"flow"}],"checkall":"true","repair":false,"outputs":1,"x":390,"y":300,"wires":[["3d29d905.6efda6"]]}]

Yes, I noticed that too. And thanks for responding to my question that I had subsequently deleted since I found the docs on subflows. The following links show the important parts for passing data into a subflow for usage by the internal nodes.

So, if a node inside of a subflow uses a string property, you can insert ${someName} into the input box. And then on the subflow instance, you can set the property someName and it will be automatically inserted. Currently, it won't work with node-red-contrib-mp4frag since I do string replacement to remove any character that is not a-z0-9_.(case insensitive) since I need it to be a simple url which will prevent the $ and {, and } from being entered. But in my experiments, it is possible to do the string replacement using the subflow instance property. I would just have to adjust how I am handling the input.

in node-red-contrib-mp4frag:

mp4frag subflow:

1 Like

Just ran some quick tests on passing env into subflow. It seems that when using the string ${replacement} options, the regular client side validations in the editor do not occur. This would then make it difficult to report back to the end user that bad info was passed from the subflow instance into the node until it was submitted to the server side.

edit: An alternative is that I can loosen the validation in the editor and instead of erroring out the bad input when passed to the server side, I could just default to using the node's id and give a warning about the bad characters, etc. To error or not to error?

Hmmm, that is the question… :rofl:

1 Like

Just my opinion here; I think it will do fine as is with using the id. What is the actual target for setting the basePath explicitly? All I currently see is a need for the consumer of the playlist(s) to get info about the valid url's. I have solved it by setting a flow variable from inside the subflow and this makes it very easy. You do not have to care setting the basePath yourself, you will get it by reading the flow variable

Below is my updated flow, I finally figured out how to set a flow variable from a subflow that allowed me to make the subflow even more generic


[{"id":"eb003283.c28bf","type":"subflow","name":"HSS_mp4frag subflow","info":"","category":"","in":[{"x":140,"y":200,"wires":[{"id":"ce08be31.5a64b"}]}],"out":[{"x":1100,"y":240,"wires":[{"id":"43fff5e2.62047c","port":1}]},{"x":1100,"y":320,"wires":[{"id":"832a7a83.3b8028","port":0}]},{"x":620,"y":400,"wires":[{"id":"62ea8138.a1ecc","port":0},{"id":"62ea8138.a1ecc","port":1}]}],"env":[],"meta":{},"color":"#DDAA99","status":{"x":1280,"y":400,"wires":[{"id":"9814715a.7517e","port":0},{"id":"f8c3cfdc.2e315","port":0}]}},{"id":"43fff5e2.62047c","type":"mp4frag","z":"eb003283.c28bf","name":"","migrate":2e-9,"hlsPlaylistSize":"5","hlsPlaylistExtra":"1","basePath":"id","repeated":"false","timeLimit":"-1","preBuffer":"1","x":830,"y":160,"wires":[["5e775a0d.d46554","1f3151ed.cd8ebe"],[]]},{"id":"1b11edc3.1378a2","type":"ffmpeg-spawn","z":"eb003283.c28bf","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[]","cmdOutputs":1,"killSignal":"SIGTERM","x":800,"y":240,"wires":[["43fff5e2.62047c"],["43fff5e2.62047c"]]},{"id":"5e775a0d.d46554","type":"switch","z":"eb003283.c28bf","name":"","property":"payload.hlsPlaylist","propertyType":"msg","rules":[{"t":"nempty"}],"checkall":"true","repair":false,"outputs":1,"x":770,"y":400,"wires":[["a09fcfcc.52157"]]},{"id":"a09fcfcc.52157","type":"join","z":"eb003283.c28bf","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"","count":"2","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"","reduceFixup":"","x":770,"y":320,"wires":[["832a7a83.3b8028"]]},{"id":"62ea8138.a1ecc","type":"switch","z":"eb003283.c28bf","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"Snapshot","vt":"str"},{"t":"eq","v":"Recording","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":3,"x":490,"y":200,"wires":[["43fff5e2.62047c"],["43fff5e2.62047c"],["1b11edc3.1378a2","a09fcfcc.52157"]]},{"id":"832a7a83.3b8028","type":"function","z":"eb003283.c28bf","name":"","func":"let hss = global.get(\"hss.cameras\");\nlet cam = msg.payload[0];\nlet url = msg.payload[1].hlsPlaylist;\n//node.warn(url);\nlet camera = hss[cam];\ncamera.url = url;\n//node.warn(camera.url);\nhss[cam] = camera;\n//node.warn(hss[cam]);\nglobal.set(\"hss.cameras\", hss);\nmsg.payload = hss[cam];\nmsg.camera = cam;\nflow.set(\"$parent.mp4frag_url\", camera.url);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":960,"y":320,"wires":[["f8c3cfdc.2e315"]]},{"id":"f8c3cfdc.2e315","type":"change","z":"eb003283.c28bf","name":"the url","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.url","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1070,"y":400,"wires":[[]]},{"id":"1f3151ed.cd8ebe","type":"switch","z":"eb003283.c28bf","name":"","property":"payload","propertyType":"msg","rules":[{"t":"empty"}],"checkall":"true","repair":false,"outputs":1,"x":1130,"y":120,"wires":[["9814715a.7517e"]]},{"id":"9814715a.7517e","type":"change","z":"eb003283.c28bf","name":"the url","rules":[{"t":"set","p":"payload","pt":"msg","to":"stopped","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1130,"y":180,"wires":[[]]},{"id":"ce08be31.5a64b","type":"function","z":"eb003283.c28bf","name":"","func":"if(msg.payload == flow.get(\"$parent.mp4frag_url\")){\n    return msg;    \n}\n\nif(msg.url == flow.get(\"$parent.mp4frag_url\")){\n    return msg;    \n}\n\nif(msg.action){\n    return msg;    \n}\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":200,"wires":[["62ea8138.a1ecc"]]},{"id":"26ed31e3.2fdffe","type":"subflow","name":"Recordings & Snapshots","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"d72ee6ae.b58e28"}]}],"out":[{"x":600,"y":320,"wires":[{"id":"d0b16878.6536b8","port":0}]},{"x":600,"y":380,"wires":[{"id":"736eff4.ea988","port":0}]},{"x":600,"y":760,"wires":[{"id":"a6a407d6.5d4148","port":0}]},{"x":600,"y":820,"wires":[{"id":"a6a407d6.5d4148","port":1}]},{"x":600,"y":880,"wires":[{"id":"a6a407d6.5d4148","port":2}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"4bf8e63f.4271a8","type":"file","z":"26ed31e3.2fdffe","name":"Save file","filename":"","appendNewline":false,"createDir":false,"overwriteFile":"false","encoding":"none","x":400,"y":260,"wires":[["d0b16878.6536b8"]]},{"id":"d0b16878.6536b8","type":"trigger","z":"26ed31e3.2fdffe","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"5","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":400,"y":320,"wires":[["736eff4.ea988"]]},{"id":"f7cfc57.a602638","type":"function","z":"26ed31e3.2fdffe","name":"","func":"let ts = flow.get('ts') || undefined;\n\nif (ts == undefined) {\n    //get new time stamp\n    const d = new Date();\n\n    let year = d.getFullYear().toString();\n    let month = (d.getMonth() + 1).toString();\n    if (month.length < 2) {\n        month = '0' + month;\n    }\n    let day = d.getDate().toString();\n    if (day.length < 2) {\n        day = '0' + day;\n    }\n    let hour = d.getHours().toString();\n    if (hour.length < 2) {\n        hour = '0' + hour;\n    }\n    let minute = d.getMinutes().toString();\n    if (minute.length < 2) {\n        minute = '0' + minute;\n    }\n    let seconds = d.getSeconds().toString();\n    if (seconds.length < 2) {\n        seconds = '0' + seconds;\n    }\n\n    ts = year + '-' + month + '-' + day + '-' + hour + minute + seconds;\n    flow.set('ts', ts);\n}\n\n// build the payload for recording\nvar hss = global.get(\"hss\");\nlet fn = hss.http_path + \"/hss_media/recordings/\" + ts + '.' + hss.recording_format;\nglobal.set(\"hss.latest_recording\", fn);\nconst { payload, action } = msg;\n\nif (Buffer.isBuffer(payload)) {\n    msg.filename = fn;\n    return msg;\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":200,"wires":[["4bf8e63f.4271a8"]]},{"id":"38926008.f5cfd","type":"change","z":"26ed31e3.2fdffe","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"stop\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":620,"wires":[[]]},{"id":"31706103.689ede","type":"trigger","z":"26ed31e3.2fdffe","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"1","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":400,"y":680,"wires":[["38926008.f5cfd"]]},{"id":"736eff4.ea988","type":"change","z":"26ed31e3.2fdffe","name":"Recording done","rules":[{"t":"set","p":"topic","pt":"msg","to":"RECORDING","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"Recording for IP source done","tot":"str"},{"t":"delete","p":"ts","pt":"flow"}],"action":"","property":"","from":"","to":"","reg":false,"x":420,"y":380,"wires":[[]]},{"id":"a6a407d6.5d4148","type":"switch","z":"26ed31e3.2fdffe","name":"","property":"notification_type","propertyType":"global","rules":[{"t":"eq","v":"Snapshot","vt":"str"},{"t":"eq","v":"Telegram","vt":"str"},{"t":"eq","v":"Email","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":390,"y":820,"wires":[[],[],[]]},{"id":"ba6d86bf.bf3f88","type":"delay","z":"26ed31e3.2fdffe","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":420,"y":740,"wires":[["a6a407d6.5d4148"]]},{"id":"d72ee6ae.b58e28","type":"switch","z":"26ed31e3.2fdffe","name":"","property":"writeMode","propertyType":"global","rules":[{"t":"eq","v":"recording","vt":"str"},{"t":"eq","v":"snapshot","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":190,"y":80,"wires":[["f7cfc57.a602638","73993c36.5d8b04"],["be2268d5.bb84a8"]]},{"id":"32900339.aea3ac","type":"comment","z":"26ed31e3.2fdffe","name":"Create mp4 file and image required for making the thumbnail","info":"","x":560,"y":160,"wires":[]},{"id":"c6dbcf14.e78bc","type":"comment","z":"26ed31e3.2fdffe","name":"Create jpg image snapshot","info":"","x":450,"y":520,"wires":[]},{"id":"8c08be50.ead39","type":"file","z":"26ed31e3.2fdffe","name":"","filename":"","appendNewline":false,"createDir":false,"overwriteFile":"true","encoding":"none","x":750,"y":560,"wires":[[]]},{"id":"7a9a3954.3459c8","type":"function","z":"26ed31e3.2fdffe","name":"","func":"// build the payload for recording thumb nail\nvar hss = global.get(\"hss\");\nlet fn = hss.latest_recording.split(hss.recording_format)[0] + hss.recording_format + '.jpg';\n\nconst { payload, action } = msg;\n\nif (Buffer.isBuffer(payload)) {\n        msg.filename = fn;\n        return msg;\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":760,"y":500,"wires":[["8c08be50.ead39"]]},{"id":"96d62f8a.517a","type":"change","z":"26ed31e3.2fdffe","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"command\":\"stop\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":760,"y":320,"wires":[["d97a4806.92b288"]]},{"id":"753ee470.28695c","type":"trigger","z":"26ed31e3.2fdffe","name":"","op1":"","op2":"true","op1type":"nul","op2type":"bool","duration":"1","extend":false,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":760,"y":380,"wires":[["96d62f8a.517a"]]},{"id":"ba9c87a7.1071e8","type":"delay","z":"26ed31e3.2fdffe","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"2","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"x":760,"y":440,"wires":[["7a9a3954.3459c8"]]},{"id":"d97a4806.92b288","type":"ffmpeg-spawn","z":"26ed31e3.2fdffe","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[\"-i\",\"pipe:0\",\"-f\",\"image2pipe\",\"pipe:1\"]","cmdOutputs":1,"killSignal":"SIGTERM","x":760,"y":260,"wires":[[],["753ee470.28695c","ba9c87a7.1071e8"]]},{"id":"be2268d5.bb84a8","type":"ffmpeg-spawn","z":"26ed31e3.2fdffe","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[\"-i\",\"pipe:0\",\"-f\",\"image2pipe\",\"pipe:1\"]","cmdOutputs":1,"killSignal":"SIGTERM","x":420,"y":560,"wires":[[],["31706103.689ede","ba6d86bf.bf3f88"]]},{"id":"73993c36.5d8b04","type":"delay","z":"26ed31e3.2fdffe","name":"","pauseType":"delay","timeout":"100","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":750,"y":200,"wires":[["d97a4806.92b288"]]},{"id":"f6109710.d8a6f8","type":"tab","label":"IP CAM","disabled":false,"info":""},{"id":"eb4bb64c.4552b8","type":"inject","z":"f6109710.d8a6f8","name":"RedBull","props":[{"p":"action","v":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"error\",\"-nostats\",\"-f\",\"hls\",\"-http_multiple\",\"1\",\"-re\",\"-i\",\"http://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_1660.m3u8\",\"-c:v\",\"copy\",\"-c:a\",\"aac\",\"-f\",\"mp4\",\"-movflags\",\"+frag_keyframe+empty_moov+default_base_moof\",\"pipe:1\",\"-progress\",\"pipe:3\"]}","vt":"json"},{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"cam10","payloadType":"str","x":240,"y":380,"wires":[["92372af2.8768e8"]]},{"id":"ea10bfe5.94116","type":"comment","z":"f6109710.d8a6f8","name":"HSS mp4frag video sources and recording","info":"","x":240,"y":40,"wires":[]},{"id":"e6391835.e71298","type":"inject","z":"f6109710.d8a6f8","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"},{"p":"reset","v":"true","vt":"bool"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"str","x":250,"y":440,"wires":[["92372af2.8768e8"]]},{"id":"bcb75531.43e018","type":"link in","z":"f6109710.d8a6f8","name":"IP_Record_IN","links":["2bf661f0.22225e","7be3e5c2.4b909c"],"x":95,"y":180,"wires":[["2bb8f8c.3e42608"]]},{"id":"386d1225.d06e9e","type":"comment","z":"f6109710.d8a6f8","name":"Switch to trigger recordings","info":"","x":200,"y":140,"wires":[]},{"id":"acd24006.15a31","type":"link in","z":"f6109710.d8a6f8","name":"IP_Snapshot_IN","links":["c88b87e1.f21c88","5ec907a2.8da2c8","384bbc7d.b10064","1a01ed2f.d1a463","70fcd40e.1dd2bc","cb480b0b.f22248","9bae2e46.deaa8"],"x":95,"y":300,"wires":[["4ea540d8.44dbb"]]},{"id":"89bed23a.79d6","type":"comment","z":"f6109710.d8a6f8","name":"Switch to trigger snapshots","info":"","x":200,"y":260,"wires":[]},{"id":"4ea540d8.44dbb","type":"change","z":"f6109710.d8a6f8","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"start\",\"repeated\":false,\"timeLimit\":1,\"preBuffer\":3}","tot":"json"},{"t":"set","p":"notification_type","pt":"global","to":"topic","tot":"msg"},{"t":"set","p":"writeMode","pt":"global","to":"snapshot","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":220,"y":300,"wires":[["92372af2.8768e8"]]},{"id":"2bb8f8c.3e42608","type":"change","z":"f6109710.d8a6f8","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"start\",\"timeLimit\":0,\"repeated\":false,\"preBuffer\":$globalContext(\"recording_duration\")}","tot":"jsonata"},{"t":"set","p":"writeMode","pt":"global","to":"recording","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"Recording","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":220,"y":180,"wires":[["92372af2.8768e8"]]},{"id":"3e76f2e5.91c06e","type":"subflow:26ed31e3.2fdffe","z":"f6109710.d8a6f8","name":"","env":[],"x":850,"y":240,"wires":[["ee727ce0.462e1"],["549f383.b0397c8"],["e90e5c6d.3fa32"],["2a612cef.a1c174"],["b4094f7a.23147"]]},{"id":"e1ec99ba.6208d8","type":"comment","z":"f6109710.d8a6f8","name":"Version 11","info":"v11 2021/08/25 - WK\n- reworked ip cam flow tab using subflows\n\nv10 2021/08/19 - WK\n- reworked ip cam flow tab\n\nv09 2021/08/16 - WK\n- starting sources at init\n\nv08 2021/08/15 - WK\n- using 'hss.latest_recording' when setting file name of thumb nails\n\nv07 2021/08/08 - WK\n- using 'recording_format' for setting compatible format for video files\n\nv06 2021/08/07 - WK\n- added thumb nails to recordings\n\nv05 2021/08/04 - WK\n- adopting to use browsing of recordings & snapshots\n\nv04 2021/07/31 - WK\n- restructuring and design modifications\n\nv03 2021/07/29 - WK\n- added support for triggering of telegrams and emails\n\nv02 2021/07/29 - WK\n- first version supporting triggering of snapshots\n\nv01 2021/07/09 - WK\n- first version supporting mp4frag video sources and triggering of recording\n\n\n","x":140,"y":80,"wires":[]},{"id":"e90e5c6d.3fa32","type":"link out","z":"f6109710.d8a6f8","name":"to_snapshot_ip_cams","links":["d3160b3d.d839c8"],"x":1055,"y":240,"wires":[]},{"id":"b4094f7a.23147","type":"link out","z":"f6109710.d8a6f8","name":"to_email_ip_cams","links":["14c6814f.7c12bf"],"x":1055,"y":320,"wires":[]},{"id":"2a612cef.a1c174","type":"link out","z":"f6109710.d8a6f8","name":"to_telegram_ip_cams","links":["6098bf0f.b236e"],"x":1055,"y":280,"wires":[]},{"id":"ee727ce0.462e1","type":"link out","z":"f6109710.d8a6f8","name":"to_recording_done","links":["d6065808.b6b018"],"x":1055,"y":160,"wires":[]},{"id":"549f383.b0397c8","type":"link out","z":"f6109710.d8a6f8","name":"to_show_notification","links":["63e8b119.a294d"],"x":1055,"y":200,"wires":[]},{"id":"92372af2.8768e8","type":"subflow:eb003283.c28bf","z":"f6109710.d8a6f8","name":"","env":[],"x":520,"y":240,"wires":[["3e76f2e5.91c06e"],["f9c75cdd.79d5"],["68efcc06.d63004"]]},{"id":"f9c75cdd.79d5","type":"debug","z":"f6109710.d8a6f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":380,"wires":[]},{"id":"68efcc06.d63004","type":"debug","z":"f6109710.d8a6f8","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":790,"y":440,"wires":[]}]

2 Likes

Thanks. I was hoping you would feel that way.

Well, the short answer is that i didn't know any better and my wife made me do it.

But really, I needed an easy way to remember the url so that I can play the videos directly in an amazon fire stick because my wife said it would be nice to have the cameras on the tv. That was before I knew how to use many features already built-in to node-red, such as http in -> template -> http response, etc.

Also, I am trying to keep it api friendly. My ultimate plans is to make node-red-contrib-mp4frag accessible in a way making it easier to work with 3rd party integration, which has made very little progress. Estimated time of completion, 2026.

One undocumented feature (not finalized) is that you can view some data in the browser by going to the correct url.

2 Likes

That's very useful indeed. I can use that to monitor that streams are alive & running :smiley: :+1:

1 Like

I was playing with your shared subflow for recordings and snapshots. If you are only interested in getting a single jpeg output from ffmpeg for your thumbnail, then you can add an extra arg -vframes 1 and ffmpeg will automatically exit after the single jpeg is produced. I do something similar to generate a jpeg snapshot once every 10 minutes.

[
    "-f",
    "mp4",
    "-i",
    "pipe:0",
    "-c",
    "mjpeg",
    "-f",
    "image2pipe",
    "-vframes",
    "1",
    "pipe:1"
]
2 Likes

Thank you, I could reduce the subflow even more, very nice!

One thing I have been trying to accomplish is to make a recording length as close as possible to a setting - like 10 seconds. But it seems I fail, I am testing with the flow below where I want the duration to be 10 seconds. But it gives me a 15 seconds recording. Is this the closest possible or are there any other params I could try?

[{"id":"ec697c6b.9451e","type":"mp4frag","z":"472fc173.1567","name":"","migrate":2e-9,"hlsPlaylistSize":"5","hlsPlaylistExtra":"1","basePath":"id","repeated":"false","timeLimit":"-1","preBuffer":"1","x":820,"y":420,"wires":[["4ed14359.c9a6fc"],["4ed14359.c9a6fc","fb66f022.61df6"]]},{"id":"ec8d42ef.8959f","type":"ffmpeg-spawn","z":"472fc173.1567","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[]","cmdOutputs":1,"killSignal":"SIGTERM","x":600,"y":540,"wires":[["ec697c6b.9451e"],["ec697c6b.9451e"]]},{"id":"e64fc1e3.d3fc9","type":"inject","z":"472fc173.1567","name":"RedBull","props":[{"p":"action","v":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"error\",\"-nostats\",\"-f\",\"hls\",\"-http_multiple\",\"1\",\"-re\",\"-i\",\"http://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_1660.m3u8\",\"-c:v\",\"copy\",\"-c:a\",\"aac\",\"-f\",\"mp4\",\"-movflags\",\"+frag_keyframe+empty_moov+default_base_moof\",\"pipe:1\",\"-progress\",\"pipe:3\"]}","vt":"json"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"5","topic":"","payload":"cam10","payloadType":"str","x":410,"y":520,"wires":[["ec8d42ef.8959f"]]},{"id":"2a8c658e.22fb7a","type":"inject","z":"472fc173.1567","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":410,"y":560,"wires":[["ec8d42ef.8959f"]]},{"id":"8b98e43f.a140a8","type":"change","z":"472fc173.1567","name":"Start recording","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"start\",\"timeLimit\":10000,\"repeated\":false,\"preBuffer\":1}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":400,"wires":[["ec697c6b.9451e"]]},{"id":"27e7ceb.b9b5b32","type":"change","z":"472fc173.1567","name":"Stop recording","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"stop\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":440,"wires":[["ec697c6b.9451e"]]},{"id":"4ed14359.c9a6fc","type":"debug","z":"472fc173.1567","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1070,"y":420,"wires":[]},{"id":"ad16e83d.a96238","type":"inject","z":"472fc173.1567","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":250,"y":400,"wires":[["8b98e43f.a140a8"]]},{"id":"2ae98497.60becc","type":"inject","z":"472fc173.1567","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":250,"y":440,"wires":[["27e7ceb.b9b5b32"]]},{"id":"d828c19f.cb374","type":"file","z":"472fc173.1567","name":"","filename":"","appendNewline":false,"createDir":false,"overwriteFile":"false","encoding":"none","x":1070,"y":540,"wires":[[]]},{"id":"fb66f022.61df6","type":"function","z":"472fc173.1567","name":"","func":"const { payload, action } = msg;\n\nif (Buffer.isBuffer(payload)) {\n    msg.filename = \"/home/pi/recording.mp4\";\n    return msg;\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1080,"y":480,"wires":[["d828c19f.cb374"]]}]

When outputting and saving the mp4 buffer directly, there will be some limitations. It has to be a multiple of the segments individual duration. Since they are consistently 3 seconds, it would have to be 3, 6, 9, 12, 15, etc. The preBuffer will be atleast 1 segment, which will be the most current segment. So, the preBuffer will give 3 seconds and then if the timeLimit is set to 10000, it will output 12 more seconds of segments, since it is designed to be generous and give >= the set amount. Should be a total of 15, but sometimes it is 12 depending on when it was started.

If you pass the mp4 segments thought another ffmpeg-spawn, then you can have more control of the duration and get closer to the desired time.

[{"id":"23160170.77800e","type":"subflow","name":"top","info":"","category":"","in":[{"x":100,"y":100,"wires":[{"id":"98a41609.d8efc8"}]}],"out":[{"x":620,"y":100,"wires":[{"id":"bc97fbbe.cb9318","port":1}]},{"x":620,"y":160,"wires":[{"id":"bc97fbbe.cb9318","port":2}]}],"env":[],"color":"#DDAA99","status":{"x":620,"y":40,"wires":[{"id":"bc97fbbe.cb9318","port":0}]}},{"id":"e4f2412d.1b609","type":"exec","z":"23160170.77800e","command":"top -b -d 5 -p","addpay":true,"append":"","useSpawn":"true","timer":"","oldrc":false,"name":"","x":329,"y":101,"wires":[["bc97fbbe.cb9318"],[],[]]},{"id":"bc97fbbe.cb9318","type":"function","z":"23160170.77800e","name":"","func":"const { payload } = msg;\n\nif (typeof payload === 'string') {\n    \n    //node.warn(payload);\n    \n    const lines = payload.toString().trim().split(/[\\n]+/);\n    \n    if (lines.length >= 2) {\n        \n        const labels = lines[lines.length - 2].trim().split(/[\\s]+/);\n\n        const values = lines[lines.length - 1].trim().split(/[\\s]+/);\n        \n        const data = {};\n        \n        for (let i = 0; i < labels.length; ++i) {\n            data[labels[i]] = values[i] || '0.0';\n        }\n        \n        const cpu = data['%CPU'];\n        \n        const mem = data['%MEM'];\n        \n        const text = `%CPU ${cpu}, %MEM ${mem}`;\n        \n        return node.send([{ payload: { fill: 'green', shape: 'dot', text } }, { payload: cpu }, { payload: mem } ]);\n\n    }\n\n}\n\nnode.send({ payload: {} });","outputs":3,"noerr":0,"initialize":"","finalize":"","x":495,"y":100,"wires":[[],[],[]],"l":false,"info":"# minimize top's output in batch mode\n\nrun top in interactive mode (dont use -b)\n\nopen column configuration menu (f) and toggle values\n\nquit (q) menu\n\nconfigure display using (l, t, m) to remove extra\n\nsave configuration (shift + w)\n\nquit (q) top\n\nrun top in batch mode (top -b) to see the changes"},{"id":"98a41609.d8efc8","type":"function","z":"23160170.77800e","name":"","func":"const { payload = {} } = msg;\n\nconst { status, pid } = payload;\n\nconst timeout = context.get('timeout');\n\nclearTimeout(timeout);\n\nif (status === 'spawn') {\n    \n    const timeout = setTimeout(() => node.send({ payload: pid }), 100);\n    \n    context.set('timeout', timeout);\n    \n} else if (status === 'close') {\n    \n    node.send({ payload: 'kill', kill: 'SIGTERM' });\n    \n}","outputs":1,"noerr":0,"initialize":"// Code added here will be run once\n// whenever the node is deployed.\n\ncontext.set('timeout', undefined);","finalize":"// Code added here will be run when the\n// node is being stopped or re-deployed.\n\nconst timeout = context.get('timeout');\n\nclearTimeout(timeout);\n\ncontext.set('timeout', undefined);","x":203,"y":101,"wires":[["e4f2412d.1b609"]],"l":false},{"id":"32d61e0f.0490b2","type":"subflow","name":"stderr","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"211d46bf.a79c7a"}]}],"out":[],"env":[],"color":"#DDAA99","status":{"x":320,"y":120,"wires":[{"id":"211d46bf.a79c7a","port":1}]}},{"id":"211d46bf.a79c7a","type":"function","z":"32d61e0f.0490b2","name":"stderr","func":"const stderr = msg.payload.toString().split('\\n');\n\nnode.send([{ stderr }, { payload: {fill: 'red', text: `${new Date().toString()}` } } ]);","outputs":2,"noerr":0,"initialize":"","finalize":"","x":190,"y":73,"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":370,"y":60,"wires":[]},{"id":"adcbab69.6989b8","type":"inject","z":"9bab7cb3.50207","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":240,"y":220,"wires":[["f5c97ce9.c297e"]]},{"id":"f5c97ce9.c297e","type":"change","z":"9bab7cb3.50207","name":"Start recording","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"start\",\"timeLimit\":20000,\"repeated\":false,\"preBuffer\":1}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":220,"wires":[["9efdfd05.fdf24"]]},{"id":"9efdfd05.fdf24","type":"mp4frag","z":"9bab7cb3.50207","name":"","hlsPlaylistSize":"5","hlsPlaylistExtra":"1","basePath":"id","repeated":"false","timeLimit":"-1","preBuffer":"1","autoStart":"false","x":810,"y":240,"wires":[["5f7995d9.b2d73c"],["5f7995d9.b2d73c","b73c092c.c6c6a8","eed0b3e5.4af53"]]},{"id":"5f7995d9.b2d73c","type":"debug","z":"9bab7cb3.50207","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1050,"y":140,"wires":[]},{"id":"b73c092c.c6c6a8","type":"function","z":"9bab7cb3.50207","name":"","func":"const { payload, action } = msg;\n\nif (Buffer.isBuffer(payload)) {\n    msg.filename = \"/home/pi/recording9-1.mp4\";\n    return msg;\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1060,"y":220,"wires":[["e508789a.412588"]]},{"id":"d5ae6fa2.1c2ac","type":"ffmpeg-spawn","z":"9bab7cb3.50207","name":"","outputs":2,"cmdPath":"ffmpeg","cmdArgs":"[]","cmdOutputs":1,"killSignal":"SIGTERM","x":590,"y":360,"wires":[["9efdfd05.fdf24"],["9efdfd05.fdf24"]]},{"id":"463998b8.7ed2a8","type":"change","z":"9bab7cb3.50207","name":"Stop recording","rules":[{"t":"set","p":"action","pt":"msg","to":"{\"subject\":\"write\",\"command\":\"stop\"}","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":430,"y":260,"wires":[["9efdfd05.fdf24"]]},{"id":"eed0b3e5.4af53","type":"ffmpeg-spawn","z":"9bab7cb3.50207","name":"","outputs":3,"cmdPath":"ffmpeg","cmdArgs":"[\"-f\",\"mp4\",\"-i\",\"pipe:0\",\"-f\",\"mp4\",\"-c\",\"copy\",\"-movflags\",\"+frag_keyframe+default_base_moof\",\"-t\",\"00:00:13\",\"pipe:1\"]","cmdOutputs":2,"killSignal":"SIGTERM","x":1080,"y":300,"wires":[["65199bc3.fd4614"],["574388cd.5a17c8"],["9b8326fc.2f1d58"]]},{"id":"e508789a.412588","type":"file","z":"9bab7cb3.50207","name":"","filename":"","appendNewline":false,"createDir":false,"overwriteFile":"false","encoding":"none","x":1270,"y":180,"wires":[[]]},{"id":"69076045.40a79","type":"inject","z":"9bab7cb3.50207","name":"RedBull","props":[{"p":"action","v":"{\"command\":\"start\",\"args\":[\"-loglevel\",\"error\",\"-nostats\",\"-f\",\"hls\",\"-http_multiple\",\"1\",\"-re\",\"-i\",\"http://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_1660.m3u8\",\"-c:v\",\"copy\",\"-c:a\",\"aac\",\"-f\",\"mp4\",\"-movflags\",\"+frag_keyframe+empty_moov+default_base_moof\",\"pipe:1\",\"-progress\",\"pipe:3\"]}","vt":"json"},{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":"5","topic":"","payload":"cam10","payloadType":"str","x":400,"y":340,"wires":[["d5ae6fa2.1c2ac"]]},{"id":"e8126ca9.8e679","type":"inject","z":"9bab7cb3.50207","name":"stop","props":[{"p":"action","v":"{\"command\":\"stop\"}","vt":"json"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":400,"y":380,"wires":[["d5ae6fa2.1c2ac"]]},{"id":"c40b6056.92fd6","type":"inject","z":"9bab7cb3.50207","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"true","payloadType":"bool","x":240,"y":260,"wires":[["463998b8.7ed2a8"]]},{"id":"574388cd.5a17c8","type":"function","z":"9bab7cb3.50207","name":"","func":"const { payload, action } = msg;\n\nif (Buffer.isBuffer(payload)) {\n    msg.filename = \"/home/pi/recording9-2.mp4\";\n    return msg;\n}\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","x":1280,"y":320,"wires":[["860a810b.3ac94"]]},{"id":"9b8326fc.2f1d58","type":"subflow:32d61e0f.0490b2","z":"9bab7cb3.50207","name":"","x":1270,"y":380,"wires":[]},{"id":"65199bc3.fd4614","type":"subflow:23160170.77800e","z":"9bab7cb3.50207","name":"","x":1270,"y":260,"wires":[[],[]]},{"id":"860a810b.3ac94","type":"file","z":"9bab7cb3.50207","name":"","filename":"","appendNewline":false,"createDir":false,"overwriteFile":"false","encoding":"none","x":1450,"y":320,"wires":[[]]}]

I will admit, at the time I made the timeLimit output, I had not yet learned how to read the individual durations of segments (previously estimated using timestamps). It might be an improvement if the timeLimit takes advantage of the calculated durations instead of estimates. Although the issue will still remain that it has to be generous because it is only capable of giving out complete segments, which may push the total amount of video slightly more than the settings.

1 Like

Thanks for the advice. It gave me an idea how to calculate it a bit better

If I in this example would like to record a 10 second clip, I do a calculation like this (assuming using 2 preBuffers, each with 3 sec length). I have the duration in a global variable "recording_duration":

{"subject":"write","command":"start","timeLimit":$abs($round(($globalContext("recording_duration")-6)/3, 0)*3000),"repeated":false,"preBuffer":2}

This gives a 12 sec clip which is good enough when using segments with 3 sec length

First reduce the required recording length with 2x preBuffer (6 sec), then divide result by preBuffer length, round this to get an integer value of number of segments that should be added to the recording length in addition to the preBuffers. Multiply by 3000 mS to get the value in milli seconds. So requesting a 10 second recording will give 2x3 seconds contributed by preBuffers plus 2x3 seconds additionally added, in total 12 seconds

I think this is fully acceptable

1 Like

Right now, you are testing with a video source that has consistent durations of 3 seconds. I have noticed that some of my ip cams, even the really good ones, will have segment durations that fluctuate a bit. Most of the time, they are 1 second as set in the cam's configuration, but sometimes 1.5 or even 4. I am not sure what the cam is doing on the inside, but when using ip cams as your source, don't expect that the segment durations will always be so consistent as the Red Bull video source.

Also, when experimenting with using the segment durations instead of date timestamps to be used in timeLimit, I found it to be less than consistent. It is easy to screw up the meta data in the mp4 giving bad durations, simple by using the wrong flags such as use_wallclocks_as_timestamps or frag_every_frame or maybe some others. I guess the current way of doing the timeLimit will stay as it is.

Yes, I am happy with how this works, it's is fully acceptable to get a clip that is "approximately" long as the request

1 Like

Kevin,
I'm seeing an odd issue with node0-red-contrib-mp4frag. When ever I add it to a flow, the label shows up as /mp4frag/longhexnumberlike this:
Screen Shot 2021-09-05 at 11.30.33 AM

How ever if I edit the node and change ID to something else, like 'foo' then that is shown in the label
Screen Shot 2021-09-05 at 11.31.47 AM

The main issue is that if I import a flow from @krambriw in a clean project on my Mac (NR v2.0.5 mp4frag v0.1.0-beta.4), when I deploy I get
Screen Shot 2021-09-05 at 12.26.13 PM
if I go into the subflow and open and close the node, the configuration error is gone.

Now when I import it on my Pi (NR v2.0.5, mp2frag v0.1.0-beta.2) I don't getthe config error but the debug shows this:
Screen Shot 2021-09-05 at 12.36.37 PM
There are four instances of the subflow where mp4frag is used.

Sooo I upgrade mp4frag to beta 4 and now the Pi experiences the same thing as I see on my Mac.

Let me know what other info you might need.

On the pi running beta.2, that is expected. We had to come up with a solution to allow node-red-contrib-mp4frag to work inside of a subflow and still have a unique basePath set. The feature added was to use the string value id and then the node will use the node's id, since that is guaranteed to be unique and is url friendly. Also, you always have the option of using the name property and that will override the label on the node because I use that first before falling back to using basePath.

Before beta.4, multiple nodes using the basePath value id will give the error about already being in use.

After reviewing my commits, it probably could have been the autoStart option I slipped in there without updating the beta version. That allows the buffer output to start automatically when the segments become available. Most likely his shared flow did not have that property and your node was expecting to see it. I should be bumping the beta version for node-red-contrib-ffmpeg-spawn and node-red-contrib-mp4frag tonight. Star the repos to keep track of the latest updates since I often don't have time to come to the forum to announce the changes.

I am currently developing on a mac running node-red 2.0.6, dashboard 3.0.2-beta, nodejs 12.11 and also a pi 4 running node-red 1.1.3, dashboard 3.0.0-beta, nodejs 12.22.2. You can share the top secret flow with me and I can try to duplicate the issue. PM is ok is it is not for the public.