Skip to content

Instantly share code, notes, and snippets.

@antimodular
Created October 18, 2019 17:05
Show Gist options
  • Save antimodular/efb651df214bd733558f4e698eea51c4 to your computer and use it in GitHub Desktop.
Save antimodular/efb651df214bd733558f4e698eea51c4 to your computer and use it in GitHub Desktop.
//
// ofxNDISoundObject.cpp
//
// Created by Roy Macdonald on 9/14/19.
//
//
#include "ofxNDISoundObject.h"
#include "ofLog.h"
//--------------------------------------------------------------------------
//---------- NDI SENDER
//--------------------------------------------------------------------------
ofxNDISenderSoundObject::ofxNDISenderSoundObject():ofxSoundObject(OFX_SOUND_OBJECT_PROCESSOR){
bMute = true;
numChannels = 0;
metadata = "";
}
//--------------------------------------------------------------------------
void ofxNDISenderSoundObject::setup(const std::string& name, const std::string & group){
if(sender_.setup(name, group)) {
audio_.setup(sender_);
}
else {
ofLogError("NDISenderObject::setup") << "NDI setup failed.";
}
}
//--------------------------------------------------------------------------
void ofxNDISenderSoundObject::setMuteOutput(bool bMute){
this->bMute = bMute;
}
//--------------------------------------------------------------------------
bool ofxNDISenderSoundObject::isMuteOutput(){
return bMute;
}
//--------------------------------------------------------------------------
size_t ofxNDISenderSoundObject::getNumChannels(){
return numChannels;
}
//--------------------------------------------------------------------------
void ofxNDISenderSoundObject::setNumChannels(const size_t& channels){
numChannels = channels;
}
//--------------------------------------------------------------------------
void ofxNDISenderSoundObject::audioOut(ofSoundBuffer &output) {
if(numChannels == 0) numChannels = output.getNumChannels();
if(numChannels != workingBuffer.getNumChannels() ||
workingBuffer.getNumFrames() != output.getNumFrames()||
workingBuffer.getSampleRate() != output.getSampleRate()){
workingBuffer.allocate(output.getNumFrames(), numChannels);
workingBuffer.setSampleRate(output.getSampleRate());
}
workingBuffer.setTickCount(output.getTickCount());
workingBuffer.setDeviceID(output.getDeviceID());
if(inputObject!=nullptr) {
inputObject->audioOut(workingBuffer);
}
if(sender_.isSetup()){
audio_.send(workingBuffer, metadata );
metadata = "";
}
if(bMute){
output.set(0);
}else{
workingBuffer.copyTo(output);
}
}
//--------------------------------------------------------------------------
void ofxNDISenderSoundObject::setMetadata(const std::string& metadata){
std::lock_guard<std::mutex> mtx(metadataMutex);
this->metadata = metadata;
}
//--------------------------------------------------------------------------
//---------- UTILS
//--------------------------------------------------------------------------
std::pair<std::string, int> getIpAndPort(const std::string&url){
auto split = ofSplitString(url, ":");
std::string ip = "";
int port = -1;
if(split.size()){
ip = split[0];
if(split.size() > 1){
port = ofToInt(split[1]);
}
}
return {ip, port};
}
//--------------------------------------------------------------------------
//---------- NDI RECEIVER SETTINGS
//--------------------------------------------------------------------------
ofxNDIReceiverSettings::ofxNDIReceiverSettings(const std::string& _sourceName,
const std::string& _sourceUrl,
const std::string& _receiverName,
const std::string& _group,
uint32_t _waittime_ms,
ofxNDI::Location _location):
sourceName(_sourceName),
sourceUrl(_sourceUrl),
receiverName(_receiverName),
group(_group),
waittime_ms(_waittime_ms),
location(_location)
{
}
//--------------------------------------------------------------------------
std::pair<std::string, int> ofxNDIReceiverSettings::getSourceIpAndPort(){
ofLog()<<"ofxNDIReceiverSettings() sourceUrl "<<sourceUrl;
return getIpAndPort(sourceUrl);
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSettings::ofxNDIReceiverSettings::isSourceEmpty() const{
return sourceName.empty() && sourceUrl.empty();
}
//--------------------------------------------------------------------------
ofxNDIReceiverSettings::operator ofxNDI::Recv::Receiver::Settings() const {
ofxNDI::Recv::Receiver::Settings s;
s.bandwidth = ( quality == ofxNDISoundQuality::HIGH )? NDIlib_recv_bandwidth_audio_only : NDIlib_recv_bandwidth_lowest ;
s.name = receiverName;
return s;
}
//--------------------------------------------------------------------------
ofxNDIReceiverSettings::operator ofxNDI::Source() const {
ofxNDI::Source s;
s.p_ndi_name = sourceName;
s.p_url_address = sourceUrl;
return s;
}
//--------------------------------------------------------------------------
//---------- NDI RECEIVER
//--------------------------------------------------------------------------
ofxNDIReceiverSoundObject::ofxNDIReceiverSoundObject():ofxSoundObject(OFX_SOUND_OBJECT_SOURCE){
numChannels = 0;
metadata ="";
bAudioNeedsSetup = false;
}
//--------------------------------------------------------------------------
bool findSource( std::function<bool(const ofxNDI::Source & s)> func, ofxNDIReceiverSettings& settings, const std::vector<std::string>& extra_ips, const std::string& notFoundMsg){
// const std::string &group,uint32_t waittime_ms,ofxNDI::Location location, const std::vector<std::string>& extra_ips){
auto sources = ofxNDI::listSources(settings.waittime_ms, settings.location, settings.group, extra_ips);
auto found = find_if(begin(sources), end(sources), func);
if(found == end(sources)) {
ofLogWarning("ofxNDI") << "no NDI source " << notFoundMsg;
}else{
settings.sourceName = found->p_ndi_name;
settings.sourceUrl = found->p_url_address;
return true;
}
return false;
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::findSourceByName(ofxNDIReceiverSettings& settings, const std::vector<std::string>& extra_ips){
// std::string& name, const std::string &group,uint32_t waittime_ms,ofxNDI::Location location, const std::vector<std::string>& extra_ips){
if(settings.sourceName != "") {
return findSource([&settings](const ofxNDI::Source &s) {
return ofIsStringInString(s.p_ndi_name, settings.sourceName);
},
settings, extra_ips, "with name: " + settings.sourceName);
}
ofLogWarning("ofxNDIReceiverSoundObject::findSourceByName") << "can not find a source by name if name is empty";
return false;
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::findSourceByIpAndPort(ofxNDIReceiverSettings& settings, const std::vector<std::string>& extra_ips){
if(settings.sourceUrl != "") {
auto ipAndPort = settings.getSourceIpAndPort();
// ofLog()<<"ipAndPort.first "<<ipAndPort.first<<" ipAndPort.second "<<ipAndPort.second;
return findSource([&ipAndPort](const ofxNDI::Source &s) {
// ofLog()<<"s.p_url_address "<<s.p_url_address;
auto sip = getIpAndPort(s.p_url_address);
// ofLog()<<"ipAndPort.first "<<ipAndPort.first<<" ipAndPort.second "<<ipAndPort.second;
// ofLog()<<"sip.first "<<sip.first<<" sip.second "<<sip.second;
return (ipAndPort.first == sip.first) && (ipAndPort.second == -1 || ipAndPort.second == sip.second);
},settings, extra_ips, "with IP: " + ipAndPort.first + " and port: " + ((ipAndPort.second == -1)?" ANY": ofToString(ipAndPort.second)));
}
ofLogWarning("ofxNDIReceiverSoundObject::findSourceByIpAndPort") << "can not find a source by IP if IP is empty";
return false;
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::findSourceByUrl(ofxNDIReceiverSettings& settings, const std::vector<std::string>& extra_ips){
if(settings.sourceUrl != "") {
return findSource([&settings](const ofxNDI::Source &s) {
std::vector<std::string> split_url = ofSplitString(s.p_url_address, ":");
std::vector<std::string> split_sourceUrl = ofSplitString(settings.sourceUrl, ":");
return settings.sourceUrl == s.p_url_address;
// return settings.sourceUrl == split_url[0];
// ofLog()<<"split_sourceUrl[0] "<<split_sourceUrl[0]<<" | "<<split_url[0];
// return split_sourceUrl[0] == split_url[0];
},settings, extra_ips, "at URL: " + settings.sourceUrl );
}
ofLogWarning("ofxNDIReceiverSoundObject::findSourceByUrl") << "can not find a source by URL if URL is empty";
return false;
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::setupBySourceIpAndPort(const std::string& ip, const int & port, const ofxNDIReceiverSettings& settings ){
this->settings = settings;
this->settings.sourceUrl = ip + ":" + ofToString(port);
return setup(this->settings);
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::setupBySourceUrl(const std::string & sourceUrl, const ofxNDIReceiverSettings& settings ){
this->settings = settings;
this->settings.sourceUrl = sourceUrl;
return setup(this->settings);
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::setupBySourceName(const std::string &sourceName, const ofxNDIReceiverSettings& settings){
this->settings = settings;
this->settings.sourceName = sourceName;
return setup(this->settings);
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::setup(const ofxNDIReceiverSettings& settings){
this->settings = settings;
if(!this->settings.isSourceEmpty()){
bAudioNeedsSetup =false;
// bool bFound = findSourceByName(this->settings);
bool bFound = findSourceByIpAndPort(this->settings);
// if(!bFound){
// bFound = findSourceByIpAndPort(this->settings);
// }
// if(!bFound){
// bFound = findSourceByUrl(this->settings);
// }
if(bFound && receiver_.setup(this->settings, this->settings)){
bAudioNeedsSetup = true;
std::cout << settings << std::endl;
return true;
}else{
ofLogWarning("ofxNDIReceiverSoundObject::setup") << "Unable to setup. Source seems to be unavailable. try calling reconnect() later.";
return false;
}
}
ofLogVerbose("ofxNDIReceiverSoundObject::setup") << "Unable to setup. source name and url are empty.";
return false;
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::reconnect(){
if(isConnected()){
ofLogWarning("ofxNDIReceiverSoundObject::reconnect") << "already connected!";
return true;
}
// ofLogVerbose("ofxNDIReceiverSoundObject::reconnect") << "attempting reconnect..";
bool reconnected = setup(this->settings);
ofLogVerbose("ofxNDIReceiverSoundObject::reconnect") << (reconnected?"Success.":"not possible.") << " Receiver Name: " << this->settings.receiverName << " source name: " << this->settings.sourceName << " source url: " << this->settings.sourceUrl;
return reconnected;
}
//--------------------------------------------------------------------------
void ofxNDIReceiverSoundObject::audioOut(ofSoundBuffer &output) {
if(isConnected()){
if(numChannels == 0) {
numChannels = output.getNumChannels();
}
if(bAudioNeedsSetup){
bAudioNeedsSetup = false;
audio_.setup(receiver_);
audio_.setSampleRate(output.getSampleRate());
audio_.setNumChannels(numChannels);
audio_.setNumSamples(output.getNumFrames());
}
if(numChannels != workingBuffer.getNumChannels() ||
workingBuffer.getNumFrames() != output.getNumFrames()||
workingBuffer.getSampleRate() != output.getSampleRate()){
workingBuffer.allocate(output.getNumFrames(), numChannels);
workingBuffer.setSampleRate(output.getSampleRate());
}
workingBuffer.setTickCount(output.getTickCount());
workingBuffer.setDeviceID(output.getDeviceID());
audio_.update();
if(audio_.isFrameNew()) {
audio_.decodeTo(workingBuffer);
if(audio_.getFrame().p_metadata != NULL){
metadata = std::string(audio_.getFrame().p_metadata);
}else{
metadata = "";
}
workingBuffer.copyTo(output);
}else{
output.set(0);
}
}else{
// if the receiver_ object is not setup just place silence in the out buffer
output.set(0);
}
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::isConnected(){
return receiver_.isConnected();
}
//--------------------------------------------------------------------------
const std::string& ofxNDIReceiverSoundObject::getSourceName(){
return settings.sourceName;
}
//--------------------------------------------------------------------------
const std::string& ofxNDIReceiverSoundObject::getSourceUrl(){
return settings.sourceUrl;
}
//--------------------------------------------------------------------------
size_t ofxNDIReceiverSoundObject::getNumChannels(){
return numChannels;
}
//--------------------------------------------------------------------------
void ofxNDIReceiverSoundObject::setNumChannels(const size_t& channels){
numChannels = channels;
}
//--------------------------------------------------------------------------
ofxNDIReceiver& ofxNDIReceiverSoundObject::getOfxNDIReceiver(){
return receiver_;
}
//--------------------------------------------------------------------------
const ofxNDIReceiver& ofxNDIReceiverSoundObject::getOfxNDIReceiver() const{
return receiver_;
}
//--------------------------------------------------------------------------
ofxNDIRecvAudioFrameSync& ofxNDIReceiverSoundObject::getOfxNDIAudioFrame(){
return audio_;
}
//--------------------------------------------------------------------------
const ofxNDIRecvAudioFrameSync& ofxNDIReceiverSoundObject::getOfxNDIAudioFrame() const{
return audio_;
}
//--------------------------------------------------------------------------
bool ofxNDIReceiverSoundObject::hasMetadata(){
std::lock_guard<std::mutex> mtx(metadataMutex);
return metadata != "";
}
//--------------------------------------------------------------------------
const std::string& ofxNDIReceiverSoundObject::getMetadata(){
std::lock_guard<std::mutex> mtx(metadataMutex);
return metadata;
}
//--------------------------------------------------------------------------
ofxNDIReceiverSettings& ofxNDIReceiverSoundObject::getSettings(){
return settings;
}
//--------------------------------------------------------------------------
const ofxNDIReceiverSettings& ofxNDIReceiverSoundObject::getSettings() const{
return settings;
}
//--------------------------------------------------------------------------
std::ostream& operator << (std::ostream& os, const ofxNDIReceiverSettings& ss) {
os << "receiverName " << ss.receiverName
<< " sourceName " << ss.sourceName
<< " sourceUrl " << ss.sourceUrl
<< " group " << ss.group
<< " waittime_ms " << ss.waittime_ms
<< " location ";
switch (ss.location) {
case ofxNDI::Location::BOTH: os << "BOTH" ; break;
case ofxNDI::Location::LOCAL: os<< "LOCAL"; break;
case ofxNDI::Location::REMOTE: os<< "REMOTE"; break;
}
os << " quality " << ((ss.quality == ofxNDISoundQuality::HIGH )?"HIGH":"LOW");
return os;
}
//
// NDISenderObject.h
// example-ofxNDI
//
// Created by Roy Macdonald on 9/14/19.
//
//
#pragma once
#include "ofxSoundObject.h"
#include <mutex>
#include "ofxNDISender.h"
#include "ofxNDISendStream.h"
#include "ofxNDIReceiver.h"
#include "ofxNDIRecvStream.h"
//--------------------------------------------------------------------------
class ofxNDISenderSoundObject : public ofxSoundObject{
public:
ofxNDISenderSoundObject();
void setup(const std::string& name, const std::string &group="");
virtual size_t getNumChannels() override;
void setNumChannels(const size_t& channels);
void setMuteOutput(bool bMute);
bool isMuteOutput();
virtual void audioOut(ofSoundBuffer &output) override;
void setMetadata(const std::string& metadata);
private:
ofxNDISender sender_;
ofxNDISendAudio audio_;
std::atomic<bool> bMute;
std::atomic<size_t> numChannels;
ofSoundBuffer workingBuffer;
std::string metadata;
std::mutex metadataMutex;
};
//--------------------------------------------------------------------------
enum ofxNDISoundQuality{
HIGH = 0,
LOW
};
//--------------------------------------------------------------------------
class ofxNDIReceiverSettings{
public:
ofxNDIReceiverSettings(){}
ofxNDIReceiverSettings(const std::string& _sourceName,
const std::string& _sourceUrl,
const std::string& _receiverName,
const std::string& _group="",
uint32_t _waittime_ms = 1000,
ofxNDI::Location _location = ofxNDI::Location::BOTH);
std::string receiverName, sourceName, sourceUrl;
std::string group="";
uint32_t waittime_ms = 1000;
ofxNDI::Location location = ofxNDI::Location::BOTH;
ofxNDISoundQuality quality = ofxNDISoundQuality::HIGH;
bool isSourceEmpty() const;
std::pair<std::string, int> getSourceIpAndPort();
operator ofxNDI::Recv::Receiver::Settings() const;
operator ofxNDI::Source() const;
friend std::ostream& operator << (std::ostream& os, const ofxNDIReceiverSettings& ss) ;
};
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
class ofxNDIReceiverSoundObject : public ofxSoundObject{
public:
ofxNDIReceiverSoundObject();
static bool findSourceByName(ofxNDIReceiverSettings& settings, const std::vector<std::string>& extra_ips={});
static bool findSourceByIpAndPort(ofxNDIReceiverSettings& settings, const std::vector<std::string>& extra_ips={});
static bool findSourceByUrl(ofxNDIReceiverSettings& settings, const std::vector<std::string>& extra_ips={});
bool setup(const ofxNDIReceiverSettings& settings);
bool setupBySourceIpAndPort(const std::string& ip, const int & port = -1, const ofxNDIReceiverSettings& settings = ofxNDIReceiverSettings());
bool setupBySourceUrl(const std::string & sourceUrl, const ofxNDIReceiverSettings& settings = ofxNDIReceiverSettings());
bool setupBySourceName(const std::string &sourceName, const ofxNDIReceiverSettings& settings = ofxNDIReceiverSettings());
bool reconnect();
const std::string& getSourceName();
const std::string& getSourceUrl();
bool isConnected();
virtual size_t getNumChannels() override;
void setNumChannels(const size_t& channels);
virtual void audioOut(ofSoundBuffer &output) override;
ofxNDIReceiver& getOfxNDIReceiver();
const ofxNDIReceiver& getOfxNDIReceiver() const;
ofxNDIRecvAudioFrameSync& getOfxNDIAudioFrame();
const ofxNDIRecvAudioFrameSync& getOfxNDIAudioFrame() const;
ofxNDIReceiverSettings& getSettings();
const ofxNDIReceiverSettings& getSettings() const;
bool hasMetadata();
const std::string& getMetadata();
private:
ofxNDIReceiverSettings settings;
ofxNDIReceiver receiver_;
ofxNDIRecvAudioFrameSync audio_;
std::atomic<bool> bAudioNeedsSetup;
std::atomic<size_t> numChannels;
ofSoundBuffer workingBuffer;
std::string metadata = "";
std::mutex metadataMutex;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment