/*
OpenFrameworks NDI receiver
using the NewTek NDI SDK to receive frames via network
Advanced example using a shader for BGRA-RGBA conversion and sender selection dialog
http://NDI.NewTek.com
Copyright (C) 2016-2017 Lynn Jarvis.
http://www.spout.zeal.co
With help from Harvey Buchan
https://github.com/Harvey3141
=========================================================================
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
=========================================================================
13.10.16 - Addon receiver example created
14.10.16 - Included received frame rate
03.11.16 - Receive into image pixels directly
- Add a sender selection dialog
05.11.16 - Note - dialog is Windows only
the ofxNDIdialog class is used separately by the application
and can be omitted along with resources
09.02.17 - Updated to ofxNDI with Version 2 NDI SDK
- Added changes by Harvey Buchan to optionally
specify preferred pixel format in CreateReceiver
*/
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup() {
ofBackground(0);
ofSetColor(255);
// Set the window title to show that it is a receiver
ofSetWindowTitle("Openframeworks NDI receiver");
cout << "NDI SDK copyright NewTek (http:\\NDI.NewTek.com)" << endl;
cout << "Press 'SPACE' to list NDI senders or RH click to open sender dialog" << endl;
senderName[0] = 0; // The sender name used for display
nSenders = 0; // Total number of NDI senders
senderWidth = 0; // Sender width
senderHeight = 0; // Sender height
bNDIreceiver = false; // Receiver creation
// Create an intial receiving image
ndiImage.allocate(ofGetWidth(), ofGetHeight(), OF_IMAGE_COLOR_ALPHA);
// For received frame fps calculations - independent of the rendering rate
startTime = lastTime = frameTime = 0;
fps = frameRate = 1; // starting value
} // end setup
//--------------------------------------------------------------
void ofApp::update() {
}
//--------------------------------------------------------------
void ofApp::draw() {
char str[256];
unsigned int width = 0;
unsigned int height = 0;
bool bResult = false;
// Update the NDI sender list to find new senders
// There is no delay if no new senders are found
nSenders = ndiReceiver.FindSenders();
if(nSenders > 0) {
// Has the user changed the sender index ?
if(ndiReceiver.SenderSelected()) {
// Release the current receiver.
// A new one is then created from the selected sender index.
ndiReceiver.ReleaseReceiver();
bNDIreceiver = false;
}
// Create a new receiver if one does not exist.
// We don't know the sender dimensions until a frame is received.
if(!bNDIreceiver) {
// The receiver will detect which format a sender is using and convert
// the pixel buffer to BGRA for return. However, we can specify here
// that RGBA is the preferred format.
bNDIreceiver = ndiReceiver.CreateReceiver(NDIlib_recv_color_format_e_RGBX_RGBA);
// bNDIreceiver = ndiReceiver.CreateReceiver(); // default is BRRA
//
// A receiver is created from an index into a list of sender names.
// The current user selected index is saved in the NDIreceiver class
// and is used to create the receiver unless you specify a particular index.
//
// The name of the sender can also be retrieved if you need it.
// If you specified a particular sender index to create the receiver
// use that index to retrieve it's name.
//
// In this application we use it to display the sender name.
//
ndiReceiver.GetSenderName(senderName);
if(bNDIreceiver)
cout << "Created NDI receiver for " << senderName << endl;
// Reset the starting values for frame rate calulations
fps = frameRate = 1;
}
}
// Receive an image from the NDI sender
if(bNDIreceiver) {
// If the NDI sender uses BGRA format, the received buffer is converted to rgba by ReceiveImage.
// Optionally you can flip the image if necessary.
// !CHECKME MACOS
// ReceiveImage needs an unsigned char* so changed
// ndiImage.getPixels() to ndiImage.getPixels().getData()
// to avoid type error
if(ndiReceiver.ReceiveImage(ndiImage.getPixels().getData(), width, height, false)) { // receives as rgba
ndiImage.update();
// ----------------------------
// Calculate received frame fps
lastTime = startTime;
startTime = ofGetElapsedTimeMicros();
frameTime = (startTime - lastTime)/1000000; // seconds
if( frameTime > 0.01) {
frameRate = floor(1.0/frameTime + 0.5);
// damping from a starting fps value
fps *= 0.95;
fps += 0.05*frameRate;
}
// ----------------------------
// Have the NDI sender dimensions changed ?
if(senderWidth != width || senderHeight != height) {
// Update the sender dimensions
senderWidth = width;
senderHeight = height;
// Update the receiving image size
ndiImage.allocate(senderWidth, senderHeight, OF_IMAGE_COLOR_ALPHA);
}
}
//
// Draw the current image.
//
// If receiveimage fails, the connection could be down so keep waiting for it to come back up.
// Or the frame rate of the NDI sender can be less than the receiver draw cycle.
//
if(bNDIreceiver) ndiImage.draw(0, 0, ofGetWidth(), ofGetHeight());
// Show fps etc.
if(nSenders > 0) {
if(bNDIreceiver) {
#ifdef _MSC_VER
sprintf_s(str, 256, "[%s] (%dx%d) - fps %2.0f", senderName, senderWidth, senderHeight, fps);
#else
// !CHECK MACOS
snprintf(str, 256, "[%s] (%dx%d) - fps %2.0f", senderName, senderWidth, senderHeight, fps);
str[255] = 0;
#endif
ofDrawBitmapString(str, 20, 30);
}
if(nSenders == 1) {
ofDrawBitmapString("1 network source", 20, ofGetHeight()-20);
}
else {
#ifdef _MSC_VER
sprintf_s(str, 256, "%d network sources", nSenders);
#else
// !CHECK MACOS
snprintf(str, 256, "%d network sources", nSenders);
str[255] = 0;
#endif
ofDrawBitmapString(str, 20, ofGetHeight()-40);
ofDrawBitmapString("'SPACE' to list senders or RH click to open sender dialog", 20, ofGetHeight()-20);
}
}
}
else {
ofDrawBitmapString("Connecting . . .", 20, 30);
}
}
//--------------------------------------------------------------
void ofApp::keyPressed(int key) {
char name[256];
int index = key-48;
int nSenders = ndiReceiver.GetSenderCount();
if(key == ' ') {
// List all the senders
if(nSenders > 0) {
cout << "Number of NDI senders found: " << nSenders << endl;
for (int i = 0; i < nSenders; i++) {
ndiReceiver.GetSenderName(name, i);
cout << " Sender " << i << " [" << name << "]" << endl;
}
if(nSenders > 1)
cout << "Press key [0] to [" << nSenders-1 << "] to select a sender" << endl;
}
else
cout << "No NDI senders found" << endl;
}
else if(index >= 0 && index < nSenders) {
// Update the receiver with the returned index
// "SenderSelected" will then return true in Draw() to update the receiver
ndiReceiver.SetSenderIndex(index);
}
}
//--------------------------------------------------------------
void ofApp::keyReleased(int key){
}
//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button) {
char name[256];
int index = 0;
std::vector senderlist;
if(button == 2) { // RH button
// Get the senders into a list for the dialog
int nSenders = ndiReceiver.GetSenderCount();
if(nSenders > 0) {
cout << "Number of NDI senders found: " << nSenders << endl;
for (int i = 0; i < nSenders; i++) {
ndiReceiver.GetSenderName(name, i);
senderlist.push_back(name);
cout << " Sender " << i << " [" << name << "]" << endl;
}
// Open the sender list dialog
// Returns true for OK, false for Cancel and the selected index
if(ndiDialog.SelectNDIPanel(senderlist, index)) {
// Update the receiver with the returned index
// "SenderSelected" will then return true in Draw() to update the receiver
ndiReceiver.SetSenderIndex(index);
// Show which sender was selected
ndiReceiver.GetSenderName(name, index);
cout << " Selected sender " << index << " [" << name << "]" << endl;
}
}
else
cout << "No NDI senders found" << endl;
}
}
//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){
}
//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){
}
//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){
}
//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){
}
//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){
}
//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){
}
//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){
}