Messages from uibuilder page (not index.html) to nodered flow (not the flow with uibuilder node in it)

I am sure this is one of those "of course that's how you do it moments".... I've tried experimantation, thinking and searching (here).

I have a number of uibuilder webpages for example index.html and cctv.html. I have a flow which initiates the uibuilder websocket (lets say index flow) and a flow (lets say cctv flow) which uses mp4frag to convert 4 rtsp camera feeds to hls. These are fed to cctv.html and dispalyed using video.js.

For simpicity all my pages use index.js and any page specific code (e.g. the window.onload function) is placed in the respective html page.

At present the streams are started and stopped (on the cctv flow) manually. I want to automate this task using (for example) "window.onload" function on my cctv html page. I can get the cctv.html page to send a suitable message e.g. "start_stream" using "uibuilder.send" function when the page loads and it is received by the uibuilder node on "index flow" . So uibuilder node and cctv.html page work as they should.

I can't work out how to receive "start_stream" on the cctv flow page. I tried using a "UIB sender node" in the cctv flow page with "allow return messages" enabled but I don't see any resulting messages. Is it possible to confgure a uibuilder node which simply listens for messages from uibuilder webpages ?

Appolgies in advance for my lack of brain power.

Ian

Hi, struggling to visualise your flows - can you share a picture?

Of Course

Lets call this one "index flow".... it is part of a flow page which sets up various (unrelated) services, including uilbuilder / websocket

Lets call this one "cctv flow" it converts rstp and delivers hls to a uibuidler webpage cctv.html

I need to be able to use a message from cctv.html to start the streams in "cctv flow" The 'auto' node on cctv flow was my vain atempt to use the send node to receive meassges from cctv.html.

I hope thats clear

Ian

OK, so you have a single uibuilder node servicing 2 pages which is fine. But of course, any returned messages from those pages all come out of the same outputs - the 2 on the actual uibuilder node.

However, if you are using v6.0, you can turn on the option to have a msg._uib property added to the standard output messages (control messages always get the same data anyway). Inside that property, you will find the page name that the message came from.

As always, the msg._socketId property is also important, if you want to send to a specific client, you need to include that socket id in the msg you send back into the uibuilder node. That is the easiest way right now to send a message to a particular page - if only 1 instance of that page is in use. If you might have multiple instances of the page, the easiest way right now is to include an extra property in the messages you send that identifies the target page and then in the uibuilder.onChange function in ALL pages, simply filter out and discard the messages you don't want for that page.

I'll be adding features that makes this easier in the future but that's about it for the current release.

Sorry Julian I am not explaining myself very well. Yes I have a single uibuilder node servicing all my pages via index.js I don't have a huge amount of traffic so this works and I can use topic / payload to make sure only the intended receipient (web page or flow) consumes the messsage.

My issue is .... Getting messages from 'window.onload' finction on cctv.html back to the cctv flow page. What node do I use on the cctv flow page? and how do I configure it ?

I hope that's clearer and I appologise for any confusion.
Ian

So are you loading the uibuilder client on the cctv.html page? If so. you can use uibuilder.send. All pages under the the same uibuilder node can all use the client library and will send back to that node.

If you have a single index.js that you are loading both into index.html and cctv.html, don't forget that it is a separate instance in each case. But still, a uibuilder.send works in both cases because the client library is loaded.

Does that make sense?

Thank you so much, Julian. If I was at home I would have drawn you a picture !!! It would have saved you so much time.

Your diagam is correct on the left hand side.

On the right the uibuilder node is on "index.flow" page. but I also have a completely seperate page "cctv flow". How do I also receive messages from the browesr on this flow page (given it doesn't have a uibuilder node) ?

Ian

OK, that's easy. On the uibuilder output port, add a switch node and use the msg._uib.pageName property to send the cctv.html page output to a link-out node, on the cctv flow tab, have the corresponding link-in to accept the messages you need to process.

image

To send messages back, again, simply use link nodes to do the job.

Oh FFS......... I said at the begining......... "I am sure this is one of those , of course that's how you do it moments"

I am so grateful for you patience, Julian. I can only applogise again for my lack of knowledge.

Thank you

Ian

:grin: no problem. I'm always using these interactions to think about how to better document and explain how to use uibuilder.

Just glad people find it useful.

I am trying to use "client connect" and "client discconect" (filtered on cctv.html) messages from uibuilder node to autostart and stop mp4frag nodes (which convert rtsp to hls and serve to the cctv.html webpage.)

This is the function code (5 outlets for future expansion).

var msgIn = msg
var msgOut1 = {}
var msgOut2 = {}
var msgOut3 = {}
var msgOut4 = {}

if (msgIn.pageName == "cctv.html") {
    if (msgIn.uibuilderCtrl == "client connect"){
        msgOut1 = {"topic": "stream_on", "payload": true}
    } else if (msgIn.uibuilderCtrl == "client disconnect"){
        msgOut1 = { "topic": "stream_off", "payload": false}
    } else {
        msgOut1=null
        }
    }


return [msgOut1, msgOut2, msgOut3, msgOut4];

I am getting this error
error

This is the mp4frag flow. If I disconnect the 'auto ln' and start the flow manually before opening the webpage the video appears as planned.

I am guessing it is a matter of timing. I have tried using window.onload on cctv.html but this gives the same video error.

I don't want 4 cctv conversion flows (I have 4 cameras) working in the background. I want them to start and stop on the webpage and don't mind a few seconds delay while the stream fires up.

I hope someone is able to point me in the right direction

Ian.

What is producing that error? In the browser?

The client (dis)connect messages are actually generated in Node-RED in response to the socket.io client library making/loosing connection with the socket.io server in node-red. So the connect message should mean that the client is already ready to receive data.

But without seeing your front-end code, hard to say what is happening. Is there also an error in the browser console?

To retiteate. The code works if I start the conversion flows before opening the webpage. The front end code....

                        <div>
                            <video id="gatecctv" class="video-js" controls autoplay="false" preload="auto" width="640"
                                height="360" data-setup="{}">
                                <source src="../mp4frag/gate/hls.m3u8" type="application/x-mpegURL">
                            </video>
                            <hr>
                            <video-player :options="videoOptions" />
                        </div>

This front end code works when I start the rtsp -> conversion -> hls flow manaually.

The browser errors are

Specified "type" attribute of "application/x-mpegURL" is not supported. Load of media resource ../mp4frag/gate/hls.m3u8 failed.

followed by

Object { code: 3, message: "Playback cannot continue. No available working or supported playlists." }"

Right, so I got it working.

[{"id":"f29ba9d80a04a8a5","type":"uibuilder","z":"c51f51da7ee02d68","name":"","topic":"","url":"streaming-video-test","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"blank","extTemplate":"","showfolder":false,"reload":true,"sourceFolder":"src","deployedVersion":"6.1.0-beta","showMsgUib":false,"x":700,"y":2180,"wires":[["6ba9b3108645ae90"],["3cddafd5b4417b8a","f13e273c7d57514a"]]},{"id":"116114e2e4efff5b","type":"inject","z":"c51f51da7ee02d68","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"play","x":390,"y":2180,"wires":[["f29ba9d80a04a8a5"]]},{"id":"6ba9b3108645ae90","type":"debug","z":"c51f51da7ee02d68","name":"debug 101","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"","statusType":"counter","x":875,"y":2140,"wires":[],"l":false},{"id":"3cddafd5b4417b8a","type":"debug","z":"c51f51da7ee02d68","name":"debug 102","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"","statusType":"counter","x":875,"y":2200,"wires":[],"l":false},{"id":"aec8461659368b17","type":"inject","z":"c51f51da7ee02d68","name":"","props":[{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"pause","x":390,"y":2220,"wires":[["f29ba9d80a04a8a5"]]},{"id":"f13e273c7d57514a","type":"switch","z":"c51f51da7ee02d68","name":"","property":"uibuilderCtrl","propertyType":"msg","rules":[{"t":"eq","v":"client connect","vt":"str"},{"t":"else"}],"checkall":"false","repair":false,"outputs":2,"x":910,"y":2260,"wires":[["84147b5c165db7db"],[]]},{"id":"84147b5c165db7db","type":"change","z":"c51f51da7ee02d68","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"play","tot":"str"},{"t":"delete","p":"uibuilderCtrl","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1060,"y":2240,"wires":[["ef09cbfcfdab3a16"]]},{"id":"0963e15e41ebacda","type":"link in","z":"c51f51da7ee02d68","name":"link in 8","links":["ef09cbfcfdab3a16"],"x":385,"y":2140,"wires":[["f29ba9d80a04a8a5"]]},{"id":"ef09cbfcfdab3a16","type":"link out","z":"c51f51da7ee02d68","name":"link out 28","mode":"link","links":["0963e15e41ebacda"],"x":1185,"y":2240,"wires":[]}]

index.html

<!doctype html>
<html lang="en"><head>

    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Blank template - Node-RED uibuilder</title>
    <meta name="description" content="Node-RED uibuilder - Blank template">
    <link rel="icon" href="../uibuilder/images/node-blue.ico">

    <!-- Your own CSS (defaults to loading uibuilders css)-->
    <link type="text/css" rel="stylesheet" href="./index.css" media="all">
    <link href="https://unpkg.com/video.js@latest/dist/video-js.css" rel="stylesheet">
    

    <!-- #region Supporting Scripts. These MUST be in the right order. Note no leading / -->
    <script defer src="https://unpkg.com/video.js@latest/dist/video.js"></script>
    <script defer src="../uibuilder/uibuilder.iife.min.js"></script>
    <script defer src="./index.js">/* OPTIONAL: Put your custom code in that */</script>
    <!-- #endregion -->

</head><body class="uib">
    
    <h1>Video.js Example Embed</h1>
    
    <video-js id="my_video_1" class="vjs-default-skin" 
            controls preload="auto" width="640" height="268" muted="muted">
        <source src="https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8"
            type="application/x-mpegURL">
    </video-js>

    <div id="more"><!-- '#more' is used as a parent for dynamic HTML content in examples --></div>

</body></html>

index.js

// @ts-nocheck

/** The simplest use of uibuilder client library
 * Note that uibuilder.start() should no longer be needed.
 * See the docs if the client doesn't start on its own.
 */
'use strict'

// logLevel 2+ shows more built-in logging. 0=error,1=warn,2=info,3=log,4=debug,5=trace.
// uibuilder.set('logLevel', 2)
// uibuilder.log('info', 'a prefix', 'some info', {any:'data',life:42})

var player = videojs('my_video_1')

// myPlayer.ready(function () {
//     player.play()
// })

// Listen for incoming messages from Node-RED
uibuilder.onChange('msg', function(msg) {
    if (msg.topic === 'pause') {
        player.pause()
    }
    if (msg.topic === 'play') {
        player.play()
    }
})

However, at least in Chrome, you CANNOT auto-start a video unless one of two things is true. Either the user must have already interacted with the web page. Or you have to set the video player to muted="muted" which is what I've done here. So the video starts but you get no sound.

As expected, no delay is needed after the connected message is output.

Thank you. I'm not suprised you got that to work. As I said ......my code works if the m3u8 stream has already started when I load the page. It doesn't work when I rely upon the html page (either window.load or uibuilder client connect) to start the conversion flow. Your test video stream is already running when the page is loaded.

Ian

Unfortunately, I don't have anything I can use to reproduce that case I don't think. It is possible that a simple delay in the client processing of the play option might be enough? Or try a delay node after the change node in my flow? Sounds like it just takes a while for the source to wind up the stream so the client looses interest before it gets any data.

Thanks, sounds logical to me.

I am going to try to put all of the js player code in a windows.onload function and see if I can delay actually defining the players as late as possble.

Ian

PARTIALLY SOLVED

set videojs controls

preload = 'none'

So now cctv.html page loads without error and starts the rtsp->hls conversion process / stream. If I wait a few moments before 'playing' each cctv stream it all works.

Maybe I will see if it is possible to code a windows.onload delay followed by a videojs auotplay

Ian

1 Like

Glad it wasn't a uibuilder issue anyway :grin:

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.