leadedge / ofxNDI

An Openframeworks addon to allow sending and receiving images over a network using the NewTek Network Device Protocol.
GNU General Public License v3.0
134 stars 48 forks source link

Proper way to handle multiple receivers #21

Closed MadlyFX closed 2 years ago

MadlyFX commented 3 years ago

Hi, I'm wondering what the best way to handle having multiple NDI receivers is. Should I create a new object per receiver, or call CreateReceiver?

MadlyFX commented 3 years ago

To expand on this a little bit, I've been running into issues creating them by adding to vectors. Here's a simplified example of what I'm doing:

ofApp.h


#include "ofMain.h"
#include "ofxNDI.h" // NDI classes

class ofApp : public ofBaseApp{

    struct NDIStruct {
        ofxNDIreceiver ndiReceiver; // NDI receiver
        ofFbo ndiFbo; // Fbo to receive
    };

    public:
        void setup();
        void draw();

        vector<NDIStruct> NDIRecs;
};

ofApp.cpp:


//--------------------------------------------------------------
void ofApp::setup(){
    ofBackground(0);
    ofSetColor(255);

    for (int i = 0; i < 3; i++) {//Create an arbitrary amount of NDI receivers
        NDIStruct n;
        n.ndiFbo.allocate(ofGetWidth(), ofGetHeight(), GL_RGBA);
        n.ndiFbo.begin();
        ofClear(0, 0, 0, 0);
        n.ndiFbo.end();
        n.ndiReceiver.SetSenderIndex(i);

        NDIRecs.push_back(n);
    }

}

//--------------------------------------------------------------
void ofApp::draw(){

    for (auto rn : NDIRecs) {
        rn.ndiReceiver.ReceiveImage(rn.ndiFbo);
        rn.ndiFbo.draw(0, 0, ofGetWidth(), ofGetHeight());
    }
}

This gives me a read access violation from this->p_NDILib in the ofxNDIdynloader::~ofxNDIdynloader() function.

leadedge commented 3 years ago

For some reason the destructor of ofxNDIdynloader is being called. I am not sure why, but I found that it works OK if you use use a pointer in NDIStruct.

struct NDIStruct {
    ofxNDIreceiver * ndiReceiver; // NDI receiver
    ofFbo ndiFbo; // Fbo to receive
};

n.ndiReceiver = new ofxNDIreceiver;
n.ndiReceiver->SetSenderIndex(i);

But then SetSenderIndex would return false if set up in advance like that because there are no received senders yet. Each receiver would need to find all the senders running before SetSenderIndex could be used.

Perhaps you try using FindSenders() for each receiver and discover whether all senders have been found for each one. Finding the senders is not immediate and would change as senders started or ended, so you would need to test whether SetSenderIndex succeeded and whether the sender of the index for each receiver existed.

leadedge commented 2 years ago

I had another look at this and confirm that setting the index requires that there is an existing sender list. It's purpose is for the user to select from a list.

There is another function "SetSenderName" that can be used without a sender list. The purpose is to establish the sender that you want to receive from. I tested it and have made a slight correction.

If you specify a different sender name for every receiver, each will wait for that particular sender to open. You can use it in advance if you know the sender name. It's the whole NDI name, including the system. For example, "DESKTOP-P706Q09 (Test Pattern)".

Setting an index without a sender list could be done but it becomes complicated quickly and could introduce problems. I regret that I don't have time to investigate that right now.