For those of you interested in re-streaming your ip cams or other videos sources as HLS and playable in the browser or app, I have some nodes that are available to test.
npm install kevinGodell/node-red-contrib-mp4frag
npm install kevinGodell/node-red-contrib-ui-mp4frag
node-red-contrib-mp4frag
is a wrapper around an existing lib, mp4frag
. It goes a little further and sets up http routes for serving the files to any player capable of consuming HLS video, such as VLC, HLS.js in most modern browsers, or native HLS on iPhone using Safari.
node-red-contrib-ui-mp4frag
is an implemention of HLS.js, but can fall back to native HLS on devices that support it. It needs much work, especially for handling the many errors that can occur with video streaming.
The 2 nodes seem tightly coupled, but are not intended to be. Just as you can view the HLS stream created by mp4frag from outside of nodered, you can also directly give an HLS playlist for ui_mp4frag to play without needing mp4frag to create it.
As for myself, I currently have 14 ip cams setup and I can play them all live to any of my laptops, desktop, or iPhones. Of course, I am using the sub stream of my cams so that I don't overload my browser when asking it to decode so many large videos simultaneously.
Future plans: I would like to setup sockets for feeding the HLS video files from server to client. HLS.js does not seem like it was created for live streaming and tends to be far behind realtime. My old video player using socket.io could keep up near realtime, delayed only by the duration of a single segment. The tradeoff for keeping up near realtime was video and audio much less smooth than HLS.js.
Edit: forgot to include the flow example code.
For trying out just the ui_mp4frag:
[{"id":"80969b76.764fc8","type":"tab","label":"Video Test 2","disabled":false,"info":""},{"id":"42030e.9afbb4f4","type":"inject","z":"80969b76.764fc8","name":"Sintel","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/hls/Sintel.m3u8","payloadType":"str","x":136,"y":157,"wires":[["c3f14086.b7964"]]},{"id":"c3f14086.b7964","type":"ui_mp4frag","z":"80969b76.764fc8","name":"","group":"c1a8b6f8.c0022","order":3,"width":"6","height":4,"readyPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_ready.png","errorPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_error.png","hlsJsConfig":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":0,\"maxBufferLength\":5,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","restart":"true","autoplay":"true","x":481,"y":156,"wires":[[]]},{"id":"2a1133d2.816f2c","type":"inject","z":"80969b76.764fc8","name":"Elephants Dream","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/hls/ElephantsDream.m3u8","payloadType":"str","x":165,"y":216,"wires":[["c3f14086.b7964"]]},{"id":"bd240e19.706d48","type":"inject","z":"80969b76.764fc8","name":"Big Buck Bunny","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/hls/BigBuckBunny.m3u8","payloadType":"str","x":166,"y":97,"wires":[["c3f14086.b7964"]]},{"id":"c1a8b6f8.c0022","type":"ui_group","z":"","name":"Video Test 2","tab":"2418e319.003404","order":1,"disp":true,"width":"6","collapse":false},{"id":"2418e319.003404","type":"ui_tab","z":"","name":"Video Test 2","icon":"dashboard","disabled":false,"hidden":false}]
For trying out the mp4frag and ui_mp4frag:
[{"id":"198dbe2b.3d8d9a","type":"tab","label":"Video Test 1","disabled":false,"info":""},{"id":"5c1f54b6.454f7c","type":"inject","z":"198dbe2b.3d8d9a","name":"Start stream","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"true","payloadType":"bool","x":190,"y":100,"wires":[["372f3933.65a7a6"]]},{"id":"d52ddf1.b4a51a","type":"inject","z":"198dbe2b.3d8d9a","name":"Stop stream","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":190,"y":146,"wires":[["372f3933.65a7a6"]]},{"id":"372f3933.65a7a6","type":"switch","z":"198dbe2b.3d8d9a","name":"","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":341,"y":100,"wires":[["6f7b48af.2862f"],["3f385b73.54d284"]]},{"id":"3f385b73.54d284","type":"function","z":"198dbe2b.3d8d9a","name":"stop","func":"msg = {\n kill:'SIGHUP',\n payload : 'SIGHUP' \n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":361,"y":149,"wires":[["6f7b48af.2862f"]]},{"id":"6f7b48af.2862f","type":"exec","z":"198dbe2b.3d8d9a","command":"ffmpeg -loglevel quiet -f mp4 -re -i https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ElephantsDream.mp4 -c:a copy -c:v copy -f mp4 -movflags +frag_keyframe+empty_moov+default_base_moof pipe:1","addpay":false,"append":"","useSpawn":"true","timer":"","oldrc":false,"name":"elephants dream ffmpeg","x":572,"y":120,"wires":[["609e41bb.4ac748"],[],["609e41bb.4ac748"]]},{"id":"5aedd6d7.d727d","type":"ui_mp4frag","z":"198dbe2b.3d8d9a","name":"elephants dream ui_mp4frag","group":"28171ec9.c62efa","order":0,"width":"5","height":"4","readyPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_ready.png","errorPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_error.png","hlsJsConfig":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":0,\"maxBufferLength\":5,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","restart":"true","autoplay":"true","x":1103,"y":117,"wires":[[]]},{"id":"609e41bb.4ac748","type":"mp4frag","z":"198dbe2b.3d8d9a","name":"elephants dream mp4frag","hlsPlaylistSize":"10","hlsPlaylistExtra":"5","hlsPlaylistUrl":"609e41bb.4ac748","x":832,"y":117,"wires":[["5aedd6d7.d727d"]]},{"id":"5c018bd6.6c9e54","type":"inject","z":"198dbe2b.3d8d9a","name":"Start stream","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"true","payloadType":"bool","x":190,"y":220,"wires":[["72de9e54.1c0068"]]},{"id":"2a3b4b00.fa1354","type":"inject","z":"198dbe2b.3d8d9a","name":"Stop stream","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":190,"y":266,"wires":[["72de9e54.1c0068"]]},{"id":"72de9e54.1c0068","type":"switch","z":"198dbe2b.3d8d9a","name":"","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":341,"y":220,"wires":[["9f93c481.f530c8"],["c9f285e2.5f9bb"]]},{"id":"c9f285e2.5f9bb","type":"function","z":"198dbe2b.3d8d9a","name":"stop","func":"msg = {\n kill:'SIGHUP',\n payload : 'SIGHUP' \n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":361,"y":269,"wires":[["9f93c481.f530c8"]]},{"id":"9f93c481.f530c8","type":"exec","z":"198dbe2b.3d8d9a","command":"ffmpeg -loglevel quiet -f mp4 -re -i https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4 -c:a copy -c:v copy -f mp4 -movflags +frag_keyframe+empty_moov+default_base_moof pipe:1","addpay":false,"append":"","useSpawn":"true","timer":"","oldrc":false,"name":"sintel ffmpeg","x":569,"y":240,"wires":[["47dbee0c.608cc8"],[],["47dbee0c.608cc8"]]},{"id":"a5e6df5b.fe7c28","type":"ui_mp4frag","z":"198dbe2b.3d8d9a","name":"sintel ui_mp4frag","group":"28171ec9.c62efa","order":0,"width":"5","height":"4","readyPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_ready.png","errorPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_error.png","hlsJsConfig":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":0,\"maxBufferLength\":5,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","restart":"true","autoplay":"true","x":1130,"y":237,"wires":[[]]},{"id":"47dbee0c.608cc8","type":"mp4frag","z":"198dbe2b.3d8d9a","name":"sintel mp4frag","hlsPlaylistSize":"10","hlsPlaylistExtra":"5","hlsPlaylistUrl":"47dbee0c.608cc8","x":828,"y":237,"wires":[["a5e6df5b.fe7c28"]]},{"id":"d22a455c.0e5c48","type":"inject","z":"198dbe2b.3d8d9a","name":"Start stream","props":[{"p":"payload"}],"repeat":"","crontab":"","once":true,"onceDelay":"1","topic":"","payload":"true","payloadType":"bool","x":190,"y":340,"wires":[["ff047477.259928"]]},{"id":"7f59ae27.f6ab3","type":"inject","z":"198dbe2b.3d8d9a","name":"Stop stream","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"false","payloadType":"bool","x":190,"y":386,"wires":[["ff047477.259928"]]},{"id":"ff047477.259928","type":"switch","z":"198dbe2b.3d8d9a","name":"","property":"payload","propertyType":"msg","rules":[{"t":"true"},{"t":"false"}],"checkall":"true","repair":false,"outputs":2,"x":341,"y":340,"wires":[["27f81ab5.717a9e"],["6af91471.f4237c"]]},{"id":"6af91471.f4237c","type":"function","z":"198dbe2b.3d8d9a","name":"stop","func":"msg = {\n kill:'SIGHUP',\n payload : 'SIGHUP' \n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":361,"y":389,"wires":[["27f81ab5.717a9e"]]},{"id":"27f81ab5.717a9e","type":"exec","z":"198dbe2b.3d8d9a","command":"ffmpeg -loglevel quiet -f mp4 -re -i https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -c:a copy -c:v copy -f mp4 -movflags +frag_keyframe+empty_moov+default_base_moof pipe:1","addpay":false,"append":"","useSpawn":"true","timer":"","oldrc":false,"name":"big buck bunny ffmpeg","x":576,"y":360,"wires":[["7c65c57a.0c4c2c"],[],["7c65c57a.0c4c2c"]]},{"id":"22fedad.e9cdaa6","type":"ui_mp4frag","z":"198dbe2b.3d8d9a","name":"big buck bunny ui_mp4frag","group":"28171ec9.c62efa","order":0,"width":"5","height":"4","readyPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_ready.png","errorPoster":"https://raw.githubusercontent.com/kevinGodell/node-red-contrib-ui-mp4frag/master/video_playback_error.png","hlsJsConfig":"{\"liveDurationInfinity\":true,\"liveBackBufferLength\":0,\"maxBufferLength\":5,\"manifestLoadingTimeOut\":1000,\"manifestLoadingMaxRetry\":10,\"manifestLoadingRetryDelay\":500}","restart":"true","autoplay":"true","x":1101,"y":357,"wires":[[]]},{"id":"7c65c57a.0c4c2c","type":"mp4frag","z":"198dbe2b.3d8d9a","name":"big buck bunny mp4frag","hlsPlaylistSize":"10","hlsPlaylistExtra":"5","hlsPlaylistUrl":"7c65c57a.0c4c2c","x":832,"y":357,"wires":[["22fedad.e9cdaa6"]]},{"id":"28171ec9.c62efa","type":"ui_group","z":"","name":"Video Test 1","tab":"4e54a2ca.371c7c","order":15,"disp":true,"width":"15","collapse":true},{"id":"4e54a2ca.371c7c","type":"ui_tab","z":"","name":"Video Test 1","icon":"dashboard","disabled":false,"hidden":false}]
And some screenshots: