Creation of the Connection Manager : this new mechanism continuously

checks for the presence of vimix programs in the network neibourhood.
The list of connections can then be used for indentifying streaming
requests and offers.
This commit is contained in:
brunoherbelin
2020-10-23 01:02:28 +02:00
parent bbeb99056a
commit 43f444f07b
18 changed files with 1125 additions and 308 deletions

View File

@@ -279,6 +279,7 @@ set(VMIX_SRCS
SystemToolkit.cpp
tinyxml2Toolkit.cpp
NetworkToolkit.cpp
Connection.cpp
ActionManager.cpp
)

235
Connection.cpp Normal file
View File

@@ -0,0 +1,235 @@
#include <thread>
#include <chrono>
#include <vector>
#include <algorithm>
#include "osc/OscOutboundPacketStream.h"
#include "defines.h"
#include "Connection.h"
#include "Log.h"
Connection::Connection()
{
receiver_ = nullptr;
}
bool Connection::init()
{
// add default info for myself
connections_.push_back(ConnectionInfo());
// try to open a socket at base handshake port
int trial = 0;
while (trial < MAX_HANDSHAKE) {
try {
// increment the port to have unique ports
connections_[0].port_handshake = HANDSHAKE_PORT + trial;
connections_[0].port_stream_send = STREAM_REQUEST_PORT + trial;
connections_[0].port_stream_receive = STREAM_RESPONSE_PORT + trial;
// try to create listenning socket
// through exception runtime if fails
receiver_ = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS,
connections_[0].port_handshake ), &listener_ );
// all good
trial = MAX_HANDSHAKE;
}
catch(const std::runtime_error& e) {
// arg, the receiver could not be initialized
// because the port was not available
receiver_ = nullptr;
}
// try again
trial++;
}
// perfect, we could initialize the receiver
if (receiver_!=nullptr) {
// listen for answers
std::thread(listen).detach();
// regularly check for available streaming hosts
std::thread(ask).detach();
}
return receiver_ != nullptr;
}
void Connection::terminate()
{
if (receiver_!=nullptr)
receiver_->AsynchronousBreak();
}
int Connection::numHosts () const
{
return connections_.size();
}
ConnectionInfo Connection::info(int index)
{
if (connections_.empty()) {
connections_.push_back(ConnectionInfo());
}
index = CLAMP(index, 0, connections_.size());
return connections_[index];
}
void Connection::print()
{
for(int i = 0; i<connections_.size(); i++) {
Log::Info(" - %s:%d", connections_[i].address_.c_str(), connections_[i].port_handshake);
}
}
int Connection::index(ConnectionInfo i) const
{
int id = -1;
std::vector<ConnectionInfo>::const_iterator p = std::find(connections_.begin(), connections_.end(), i);
if (p != connections_.end())
id = std::distance(connections_.begin(), p);
return id;
}
void Connection::listen()
{
// Log::Info("Accepting handshake on %d", Connection::manager().connections_[0].port_handshake);
Connection::manager().receiver_->Run();
}
void Connection::ask()
{
// Log::Info("Broadcasting handshakes with info %d", Connection::manager().connections_[0].port_handshake);
// prepare OSC PING message
char buffer[IP_MTU_SIZE];
osc::OutboundPacketStream p( buffer, IP_MTU_SIZE );
p.Clear();
p << osc::BeginMessage( OSC_PREFIX OSC_PING );
p << Connection::manager().connections_[0].port_handshake;
p << osc::EndMessage;
// broadcast on several ports, except myself
std::vector<int> handshake_ports;
for(int i=HANDSHAKE_PORT; i<HANDSHAKE_PORT+MAX_HANDSHAKE; i++) {
if (i != Connection::manager().connections_[0].port_handshake)
handshake_ports.push_back(i);
}
// loop infinitely
while(true)
{
// set status to pending : after pong status will be confirmed
for(auto it=Connection::manager().connections_.begin(); it!=Connection::manager().connections_.end(); it++) {
(*it).status--;
}
// broadcast the PING message on every possible ports
for(auto it=handshake_ports.begin(); it!=handshake_ports.end(); it++) {
IpEndpointName host( "255.255.255.255", (*it) );
UdpTransmitSocket socket( host );
socket.SetEnableBroadcast(true);
socket.Send( p.Data(), p.Size() );
}
// wait a bit
std::this_thread::sleep_for(std::chrono::milliseconds(500));
// check if status decreased
for(auto it=Connection::manager().connections_.begin(); it!=Connection::manager().connections_.end(); ) {
if ( it!=Connection::manager().connections_.begin() && (*it).status < 1 )
it = Connection::manager().connections_.erase(it);
else
it++;
}
// Connection::manager().print();
}
}
void ConnectionRequestListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];
remoteEndpoint.AddressAndPortAsString(sender);
// get ip of connection (without port)
std::string remote_ip(sender);
remote_ip = remote_ip.substr(0, remote_ip.find_last_of(":"));
try{
// ping request : reply with pong
if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_PING) == 0 ){
// PING message has parameter : port where to reply
osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
int remote_port = (arg++)->AsInt32();
// Log::Info("Receive PING from %s:%d", remote_ip.c_str(), remote_port);
// build message
char buffer[IP_MTU_SIZE];
osc::OutboundPacketStream p( buffer, IP_MTU_SIZE );
p.Clear();
p << osc::BeginMessage( OSC_PREFIX OSC_PONG );
p << Connection::manager().connections_[0].port_handshake;
p << Connection::manager().connections_[0].port_stream_send;
p << Connection::manager().connections_[0].port_stream_receive;
p << osc::EndMessage;
// send OSC message to port indicated by remote
IpEndpointName host( remote_ip.c_str(), remote_port );
UdpTransmitSocket socket( host );
socket.Send( p.Data(), p.Size() );
// Log::Info("reply PONG to %s:%d", remote_ip.c_str(), remote_port);
}
// pong response: add info
else if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_PONG) == 0 ){
// create info struct
ConnectionInfo info;
info.address_ = remote_ip;
// add all ports info
osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
info.port_handshake = (arg++)->AsInt32();
info.port_stream_send = (arg++)->AsInt32();
info.port_stream_receive = (arg++)->AsInt32();
int i = Connection::manager().index(info);
if ( i < 0) {
// add to list
info.status = 3;
Connection::manager().connections_.push_back(info);
// Log::Info("Received PONG from %s:%d : added", info.address_.c_str(), info.port_handshake);
// Connection::manager().print();
}
else {
// set high (== ALIVE)
Connection::manager().connections_[i].status = 3;
}
}
}
catch( osc::Exception& e ){
// any parsing errors such as unexpected argument types, or
// missing arguments get thrown as exceptions.
Log::Info("error while parsing message '%s' from %s : %s", m.AddressPattern(), sender, e.what());
}
}

90
Connection.h Normal file
View File

@@ -0,0 +1,90 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include "osc/OscReceivedElements.h"
#include "osc/OscPacketListener.h"
#include "ip/UdpSocket.h"
#include "NetworkToolkit.h"
class ConnectionRequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
struct ConnectionInfo {
std::string address_;
int port_handshake;
int port_stream_send;
int port_stream_receive;
int status;
ConnectionInfo () {
address_ = "localhost";
port_handshake = HANDSHAKE_PORT;
port_stream_send = STREAM_REQUEST_PORT;
port_stream_receive = STREAM_RESPONSE_PORT;
status = 0;
}
inline ConnectionInfo& operator = (const ConnectionInfo& o)
{
if (this != &o) {
this->address_ = o.address_;
this->port_handshake = o.port_handshake;
this->port_stream_send = o.port_stream_send;
this->port_stream_receive = o.port_stream_receive;
}
return *this;
}
inline bool operator == (const ConnectionInfo& o) const
{
return this->address_.compare(o.address_) == 0
&& this->port_handshake == o.port_handshake;
}
};
class Connection
{
friend class ConnectionRequestListener;
// Private Constructor
Connection();
Connection(Connection const& copy); // Not Implemented
Connection& operator=(Connection const& copy); // Not Implemented
public:
static Connection& manager()
{
// The only instance
static Connection _instance;
return _instance;
}
bool init();
void terminate();
int numHosts () const;
int index(ConnectionInfo i) const;
ConnectionInfo info(int index = 0); // index 0 for self
private:
std::string hostname_;
static void ask();
static void listen();
ConnectionRequestListener listener_;
UdpListeningReceiveSocket *receiver_;
std::vector< ConnectionInfo > connections_;
void print();
};
#endif // CONNECTION_H

View File

@@ -87,8 +87,8 @@ class Device
friend class DeviceSource;
Device();
Device(Rendering const& copy); // Not Implemented
Device& operator=(Rendering const& copy); // Not Implemented
Device(Device const& copy); // Not Implemented
Device& operator=(Device const& copy); // Not Implemented
public:
@@ -104,7 +104,7 @@ public:
std::string description (int index) const;
DeviceConfigSet config (int index) const;
int index (const std::string &device) const;
int index (const std::string &device) const;
bool exists (const std::string &device) const;
bool unplugged (const std::string &device) const;

View File

@@ -27,6 +27,7 @@ using namespace tinyxml2;
#include "StreamSource.h"
#include "NetworkSource.h"
#include "ActionManager.h"
#include "Streamer.h"
#include "Mixer.h"
@@ -307,11 +308,11 @@ Source * Mixer::createSourceDevice(const std::string &namedevice)
}
Source * Mixer::createSourceNetwork(uint protocol, const std::string &address)
Source * Mixer::createSourceNetwork(const std::string &address)
{
// ready to create a source
NetworkSource *s = new NetworkSource;
s->connect((NetworkToolkit::Protocol) protocol, address);
s->setAddress(address);
// propose a new name based on address
s->setName(address);
@@ -848,6 +849,9 @@ void Mixer::swap()
// reset History manager
Action::manager().clear();
// inform streaming manager
Streaming::manager().setSession(session_);
// notification
Log::Notify("Session %s loaded. %d source(s) created.", session_->filename().c_str(), session_->numSource());
}

View File

@@ -42,7 +42,7 @@ public:
Source * createSourceStream (const std::string &gstreamerpipeline);
Source * createSourcePattern(uint pattern, glm::ivec2 res);
Source * createSourceDevice (const std::string &namedevice);
Source * createSourceNetwork(uint protocol, const std::string &address);
Source * createSourceNetwork(const std::string &address);
// operations on sources
void addSource (Source *s);

View File

@@ -1,5 +1,8 @@
#include <algorithm>
#include <sstream>
#include <thread>
#include <chrono>
#include <glm/gtc/matrix_transform.hpp>
#include <gst/pbutils/pbutils.h>
@@ -18,6 +21,259 @@
#define NETWORK_DEBUG
#endif
NetworkSource::NetworkSource() : StreamSource()
{
// create stream
stream_ = (Stream *) new NetworkStream;
// set icons
overlays_[View::MIXING]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) );
overlays_[View::LAYER]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) );
}
void NetworkSource::setAddress(const std::string &address)
{
address_ = address;
Log::Notify("Creating Network Source '%s'", address_.c_str());
// // extract host and port from "host:port"
// std::string host = address.substr(0, address.find_last_of(":"));
// std::string port_s = address.substr(address.find_last_of(":") + 1);
// uint port = std::stoi(port_s);
// // validate protocol
// if (protocol < NetworkToolkit::TCP_JPEG || protocol >= NetworkToolkit::DEFAULT)
// protocol = NetworkToolkit::TCP_JPEG;
// // open network stream
// networkstream()->open( protocol, host, port );
// stream_->play(true);
}
std::string NetworkSource::address() const
{
return address_;
}
void NetworkSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
void NetworkHosts::listen()
{
Log::Info("Accepting Streaming responses on port %d", STREAM_RESPONSE_PORT);
NetworkHosts::manager().receiver_->Run();
Log::Info("Refusing Streaming responses");
}
void NetworkHosts::ask()
{
Log::Info("Broadcasting Streaming requests on port %d", STREAM_REQUEST_PORT);
IpEndpointName host( "255.255.255.255", STREAM_REQUEST_PORT );
UdpTransmitSocket socket( host );
socket.SetEnableBroadcast(true);
char buffer[IP_MTU_SIZE];
osc::OutboundPacketStream p( buffer, IP_MTU_SIZE );
p.Clear();
p << osc::BeginMessage( OSC_PREFIX OSC_STREAM_REQUEST ) << osc::EndMessage;
while(true)
{
socket.Send( p.Data(), p.Size() );
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
// this is called when receiving an answer for streaming request
void HostsResponsesListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];
remoteEndpoint.AddressAndPortAsString(sender);
std::string se(sender);
std::string senderip = se.substr(0, se.find_last_of(":"));
try{
if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_STREAM_OFFER ) == 0 ){
// someone is offering a stream
osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
const char *address = (arg++)->AsString();
int p = (arg++)->AsInt32();
int w = (arg++)->AsInt32();
int h = (arg++)->AsInt32();
Log::Info("Able to receive %s %d x %d stream from %s ", address, w, h, senderip.c_str());
NetworkHosts::manager().addHost(address, p, w, h);
}
else if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_STREAM_RETRACT ) == 0 ){
// someone is retracting a stream
osc::ReceivedMessage::const_iterator arg = m.ArgumentsBegin();
const char *address = (arg++)->AsString();
NetworkHosts::manager().removeHost(address);
}
}
catch( osc::Exception& e ){
// any parsing errors such as unexpected argument types, or
// missing arguments get thrown as exceptions.
Log::Info("error while parsing message '%s' from %s : %s", m.AddressPattern(), sender, e.what());
}
}
NetworkHosts::NetworkHosts()
{
// // listen for answers
// receiver_ = new UdpListeningReceiveSocket(IpEndpointName( IpEndpointName::ANY_ADDRESS, STREAM_RESPONSE_PORT ), &listener_ );
// std::thread(listen).detach();
// // regularly check for available streaming hosts
// std::thread(ask).detach();
}
void NetworkHosts::addHost(const std::string &address, int protocol, int width, int height)
{
// add only new
if (disconnected(address)) {
src_address_.push_back(address);
// TODO : fill description
src_description_.push_back(address);
list_uptodate_ = false;
}
}
void NetworkHosts::removeHost(const std::string &address)
{
// remove existing only
if (connected(address)) {
std::vector< std::string >::iterator addrit = src_address_.begin();
std::vector< std::string >::iterator descit = src_description_.begin();
while (addrit != src_address_.end()){
if ( (*addrit).compare(address) == 0 )
{
src_address_.erase(addrit);
src_description_.erase(descit);
break;
}
addrit++;
descit++;
}
list_uptodate_ = false;
}
}
int NetworkHosts::numHosts() const
{
return src_address_.size();
}
std::string NetworkHosts::name(int index) const
{
if (index > -1 && index < (int) src_address_.size())
return src_address_[index];
else
return "";
}
std::string NetworkHosts::description(int index) const
{
if (index > -1 && index < (int) src_description_.size())
return src_description_[index];
else
return "";
}
int NetworkHosts::index(const std::string &address) const
{
int i = -1;
std::vector< std::string >::const_iterator p = std::find(src_address_.begin(), src_address_.end(), address);
if (p != src_address_.end())
i = std::distance(src_address_.begin(), p);
return i;
}
bool NetworkHosts::connected(const std::string &address) const
{
std::vector< std::string >::const_iterator d = std::find(src_address_.begin(), src_address_.end(), address);
return d != src_address_.end();
}
bool NetworkHosts::disconnected(const std::string &address) const
{
if (list_uptodate_)
return false;
return !connected(address);
}
struct hasAddress: public std::unary_function<NetworkSource*, bool>
{
inline bool operator()(const NetworkSource* elem) const {
return (elem && elem->address() == _a);
}
hasAddress(std::string a) : _a(a) { }
private:
std::string _a;
};
Source *NetworkHosts::createSource(const std::string &address) const
{
Source *s = nullptr;
// find if a DeviceSource with this device is already registered
std::list< NetworkSource *>::const_iterator d = std::find_if(network_sources_.begin(), network_sources_.end(), hasAddress(address));
// if already registered, clone the device source
if ( d != network_sources_.end()) {
CloneSource *cs = (*d)->clone();
s = cs;
}
// otherwise, we are free to create a new Network source
else {
NetworkSource *ns = new NetworkSource();
ns->setAddress(address);
s = ns;
}
return s;
}
NetworkStream::NetworkStream(): Stream(), protocol_(NetworkToolkit::DEFAULT), host_("127.0.0.1"), port_(5000)
{
@@ -65,138 +321,3 @@ void NetworkStream::open(NetworkToolkit::Protocol protocol, const std::string &h
}
bool NetworkStream::ping(int *w, int *h)
{
bool ret = false;
std::ostringstream pipeline_desc;
if (protocol_ == NetworkToolkit::TCP_JPEG || protocol_ == NetworkToolkit::TCP_H264) {
pipeline_desc << "tcpclientsrc is-live=true host=" << host_ << " port=" << port_;
}
else if (protocol_ == NetworkToolkit::SHM_RAW) {
std::string path = SystemToolkit::full_filename(SystemToolkit::settings_path(), "shm_socket");
pipeline_desc << "shmsrc is-live=true socket-path=" << path;
}
pipeline_desc << " ! appsink name=sink";
GError *error = NULL;
GstElement *pipeline = gst_parse_launch (pipeline_desc.str().c_str(), &error);
if (error != NULL) return false;
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
if (sink) {
GstAppSinkCallbacks callbacks;
callbacks.new_preroll = callback_sample;
callbacks.new_sample = callback_sample;
callbacks.eos = NULL;
gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL);
gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false);
GstStateChangeReturn status = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (status == GST_STATE_CHANGE_FAILURE) {
ret = false;
}
GstState state;
gst_element_get_state (pipeline, &state, NULL, GST_CLOCK_TIME_NONE);
gst_element_set_state (pipeline, GST_STATE_PAUSED);
// ret = true;
// unref sink
gst_object_unref (sink);
}
// free pipeline
gst_object_unref (pipeline);
*w = 1920;
*h = 1080;
return ret;
}
GstFlowReturn NetworkStream::callback_sample (GstAppSink *sink, gpointer p)
{
GstFlowReturn ret = GST_FLOW_OK;
Log::Info("callback_sample PING");
// non-blocking read new sample
GstSample *sample = gst_app_sink_pull_sample(sink);
// if got a valid sample
if (sample != NULL && !gst_app_sink_is_eos (sink)) {
const GstStructure *truc = gst_sample_get_info(sample);
GstCaps *cap = gst_sample_get_caps(sample);
gchar *c = gst_caps_to_string(cap);
// get buffer from sample (valid until sample is released)
// GstBuffer *buf = gst_sample_get_buffer (sample) ;
// // send frames to media player only if ready
// Stream *m = (Stream *)p;
// if (m && m->ready_) {
// // fill frame with buffer
// if ( !m->fill_frame(buf, Stream::SAMPLE) )
// ret = GST_FLOW_ERROR;
// }
}
else
ret = GST_FLOW_FLUSHING;
// release sample
gst_sample_unref (sample);
return ret;
}
NetworkSource::NetworkSource() : StreamSource()
{
// create stream
stream_ = (Stream *) new NetworkStream;
// set icons
overlays_[View::MIXING]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) );
overlays_[View::LAYER]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) );
}
void NetworkSource::connect(NetworkToolkit::Protocol protocol, const std::string &address)
{
Log::Notify("Creating Network Source '%s'", address.c_str());
// extract host and port from "host:port"
std::string host = address.substr(0, address.find_last_of(":"));
std::string port_s = address.substr(address.find_last_of(":") + 1);
uint port = std::stoi(port_s);
// validate protocol
if (protocol < NetworkToolkit::TCP_JPEG || protocol >= NetworkToolkit::DEFAULT)
protocol = NetworkToolkit::TCP_JPEG;
// open network stream
networkstream()->open( protocol, host, port );
stream_->play(true);
}
void NetworkSource::accept(Visitor& v)
{
Source::accept(v);
if (!failed())
v.visit(*this);
}
NetworkStream *NetworkSource::networkstream() const
{
return dynamic_cast<NetworkStream *>(stream_);
}

View File

@@ -1,16 +1,98 @@
#ifndef NETWORKSOURCE_H
#define NETWORKSOURCE_H
#include "osc/OscReceivedElements.h"
#include "osc/OscPacketListener.h"
#include "osc/OscOutboundPacketStream.h"
#include "ip/UdpSocket.h"
#include "NetworkToolkit.h"
#include "StreamSource.h"
class NetworkSource : public StreamSource
{
std::string address_;
public:
NetworkSource();
// Source interface
void accept (Visitor& v) override;
// StreamSource interface
Stream *stream() const override { return stream_; }
// specific interface
void setAddress(const std::string &address);
std::string address() const;
glm::ivec2 icon() const override { return glm::ivec2(11, 8); }
};
class HostsResponsesListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
class NetworkHosts
{
friend class NetworkSource;
friend class HostsResponsesListener;
NetworkHosts();
NetworkHosts(NetworkHosts const& copy); // Not Implemented
NetworkHosts& operator=(NetworkHosts const& copy); // Not Implemented
public:
static NetworkHosts& manager()
{
// The only instance
static NetworkHosts _instance;
return _instance;
}
int numHosts () const;
std::string name (int index) const;
std::string description (int index) const;
int index (const std::string &address) const;
bool connected (const std::string &address) const;
bool disconnected (const std::string &address) const;
Source *createSource(const std::string &address) const;
private:
std::vector< std::string > src_address_;
std::vector< std::string > src_description_;
bool list_uptodate_;
std::list< NetworkSource * > network_sources_;
static void ask();
static void listen();
HostsResponsesListener listener_;
UdpListeningReceiveSocket *receiver_;
void addHost(const std::string &address, int protocol, int width, int height);
void removeHost(const std::string &address);
};
class NetworkStream : public Stream
{
public:
NetworkStream();
void open(NetworkToolkit::Protocol protocol, const std::string &host, uint port );
bool ping(int *w, int *h);
glm::ivec2 resolution();
@@ -23,26 +105,7 @@ private:
std::string host_;
uint port_;
static GstFlowReturn callback_sample (GstAppSink *, gpointer );
};
class NetworkSource : public StreamSource
{
public:
NetworkSource();
// Source interface
void accept (Visitor& v) override;
// StreamSource interface
Stream *stream() const override { return stream_; }
// specific interface
NetworkStream *networkstream() const;
void connect(NetworkToolkit::Protocol protocol, const std::string &address);
glm::ivec2 icon() const override { return glm::ivec2(11, 8); }
};
#endif // NETWORKSOURCE_H

View File

@@ -1,10 +1,15 @@
#include <stdio.h>
#include <cstring>
#include <unistd.h>
#include <algorithm> // std::count
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/netdevice.h>
#include <arpa/inet.h>
//#include <netdb.h>
#include "ip/NetworkingUtils.h"
#include "NetworkToolkit.h"
@@ -15,7 +20,7 @@ const char* NetworkToolkit::protocol_name[NetworkToolkit::DEFAULT] = {
"Shared Memory"
};
const std::vector<std::string> NetworkToolkit::protocol_broadcast_pipeline {
const std::vector<std::string> NetworkToolkit::protocol_send_pipeline {
"video/x-raw, format=I420 ! queue max-size-buffers=3 ! jpegenc ! rtpjpegpay ! rtpstreampay ! tcpserversink name=sink",
"video/x-raw, format=I420 ! queue max-size-buffers=3 ! x264enc tune=\"zerolatency\" threads=2 ! rtph264pay ! rtpstreampay ! tcpserversink name=sink",
@@ -74,36 +79,80 @@ const std::vector<std::string> NetworkToolkit::protocol_receive_pipeline {
* */
std::vector<std::string> ipstrings;
std::vector<unsigned long> iplongs;
std::vector<std::string> NetworkToolkit::host_ips()
{
std::vector<std::string> ipstrings;
// fill the list of IPs only once
if (ipstrings.empty()) {
int s = socket(AF_INET, SOCK_STREAM, 0);
if (s > -1) {
struct ifconf ifconf;
struct ifreq ifr[50];
int ifs;
int i;
int s = socket(AF_INET, SOCK_STREAM, 0);
if (s > -1) {
struct ifconf ifconf;
struct ifreq ifr[50];
int ifs;
int i;
ifconf.ifc_buf = (char *) ifr;
ifconf.ifc_len = sizeof ifr;
ifconf.ifc_buf = (char *) ifr;
ifconf.ifc_len = sizeof ifr;
if (ioctl(s, SIOCGIFCONF, &ifconf) > -1) {
ifs = ifconf.ifc_len / sizeof(ifr[0]);
for (i = 0; i < ifs; i++) {
char ip[INET_ADDRSTRLEN];
struct sockaddr_in *s_in = (struct sockaddr_in *) &ifr[i].ifr_addr;
if (ioctl(s, SIOCGIFCONF, &ifconf) > -1) {
ifs = ifconf.ifc_len / sizeof(ifr[0]);
for (i = 0; i < ifs; i++) {
char ip[INET_ADDRSTRLEN];
struct sockaddr_in *s_in = (struct sockaddr_in *) &ifr[i].ifr_addr;
if (inet_ntop(AF_INET, &s_in->sin_addr, ip, sizeof(ip))) {
if ( std::string(ip).compare("127.0.0.1") == 0 )
ipstrings.push_back( "localhost" );
else
if (inet_ntop(AF_INET, &s_in->sin_addr, ip, sizeof(ip))) {
ipstrings.push_back( std::string(ip) );
iplongs.push_back( GetHostByName(ip) );
}
}
close(s);
}
close(s);
}
}
return ipstrings;
}
bool NetworkToolkit::is_host_ip(const std::string &ip)
{
if ( ip.compare("localhost") == 0)
return true;
if (ipstrings.empty())
host_ips();
return std::find(ipstrings.begin(), ipstrings.end(), ip) != ipstrings.end();
}
std::string NetworkToolkit::closest_host_ip(const std::string &ip)
{
std::string address = "localhost";
if (iplongs.empty())
host_ips();
// discard trivial case
if ( ip.compare("localhost") != 0)
{
int index_mini = -1;
unsigned long host = GetHostByName( ip.c_str() );
unsigned long mini = host;
for (int i=0; i < iplongs.size(); i++){
unsigned long diff = host > iplongs[i] ? host-iplongs[i] : iplongs[i]-host;
if (diff < mini) {
mini = diff;
index_mini = i;
}
}
if (index_mini>0)
address = ipstrings[index_mini];
}
return address;
}

View File

@@ -4,6 +4,22 @@
#include <string>
#include <vector>
#define OSC_PREFIX "/vimix"
#define OSC_PING "/ping"
#define OSC_PONG "/pong"
#define OSC_STREAM_REQUEST "/request"
#define OSC_STREAM_OFFER "/offer"
#define OSC_STREAM_RETRACT "/retract"
#define OSC_STREAM_CONNECT "/connect"
#define OSC_STREAM_DISCONNECT "/disconnect"
#define MAX_HANDSHAKE 10
#define HANDSHAKE_PORT 71310
#define STREAM_REQUEST_PORT 71510
#define STREAM_RESPONSE_PORT 71610
#define IP_MTU_SIZE 1536
namespace NetworkToolkit
{
@@ -15,10 +31,12 @@ typedef enum {
} Protocol;
extern const char* protocol_name[DEFAULT];
extern const std::vector<std::string> protocol_broadcast_pipeline;
extern const std::vector<std::string> protocol_send_pipeline;
extern const std::vector<std::string> protocol_receive_pipeline;
std::vector<std::string> host_ips();
bool is_host_ip(const std::string &ip);
std::string closest_host_ip(const std::string &ip);
}

View File

@@ -58,6 +58,8 @@ void Settings::Save()
applicationNode->SetAttribute("pannel_stick", application.pannel_stick);
applicationNode->SetAttribute("smooth_transition", application.smooth_transition);
applicationNode->SetAttribute("smooth_cursor", application.smooth_cursor);
applicationNode->SetAttribute("action_history_follow_view", application.action_history_follow_view);
applicationNode->SetAttribute("accept_connections", application.accept_connections);
pRoot->InsertEndChild(applicationNode);
// Widgets
@@ -88,12 +90,12 @@ void Settings::Save()
RecordNode->SetAttribute("timeout", application.record.timeout);
pRoot->InsertEndChild(RecordNode);
// Record
XMLElement *StreamNode = xmlDoc.NewElement( "Stream" );
StreamNode->SetAttribute("profile", application.stream.profile);
StreamNode->SetAttribute("ip", application.stream.ip.c_str());
StreamNode->SetAttribute("port", application.stream.port);
pRoot->InsertEndChild(StreamNode);
// // Record
// XMLElement *StreamNode = xmlDoc.NewElement( "Stream" );
// StreamNode->SetAttribute("profile", application.stream.profile);
// StreamNode->SetAttribute("ip", application.stream.ip.c_str());
// StreamNode->SetAttribute("port", application.stream.port);
// pRoot->InsertEndChild(StreamNode);
// Transition
XMLElement *TransitionNode = xmlDoc.NewElement( "Transition" );
@@ -220,6 +222,8 @@ void Settings::Load()
applicationNode->QueryBoolAttribute("pannel_stick", &application.pannel_stick);
applicationNode->QueryBoolAttribute("smooth_transition", &application.smooth_transition);
applicationNode->QueryBoolAttribute("smooth_cursor", &application.smooth_cursor);
applicationNode->QueryBoolAttribute("action_history_follow_view", &application.action_history_follow_view);
applicationNode->QueryBoolAttribute("accept_connections", &application.accept_connections);
}
// Widgets
@@ -258,18 +262,18 @@ void Settings::Load()
application.record.path = SystemToolkit::home_path();
}
// Stream
XMLElement * streamnode = pRoot->FirstChildElement("Stream");
if (streamnode != nullptr) {
streamnode->QueryIntAttribute("profile", &application.stream.profile);
streamnode->QueryIntAttribute("port", &application.stream.port);
// // Stream
// XMLElement * streamnode = pRoot->FirstChildElement("Stream");
// if (streamnode != nullptr) {
// streamnode->QueryIntAttribute("profile", &application.stream.profile);
// streamnode->QueryIntAttribute("port", &application.stream.port);
const char *ip_ = streamnode->Attribute("ip");
if (ip_)
application.stream.ip = std::string(ip_);
else
application.stream.ip = "localhost";
}
// const char *ip_ = streamnode->Attribute("ip");
// if (ip_)
// application.stream.ip = std::string(ip_);
// else
// application.stream.ip = "localhost";
// }
// Source
XMLElement * sourceconfnode = pRoot->FirstChildElement("Source");

View File

@@ -75,18 +75,19 @@ struct RecordConfig
};
struct StreamingConfig
{
std::string ip;
int port;
int profile;
//struct StreamingConfig
//{
// std::string ip;
// int port;
// int profile;
StreamingConfig() : ip("localhost") {
profile = 0;
port = 5400;
}
// StreamingConfig() : ip("localhost") {
// profile = 0;
// port = 5400;
// }
//};
};
struct History
{
std::string path;
@@ -187,6 +188,7 @@ struct Application
bool smooth_transition;
bool smooth_cursor;
bool action_history_follow_view;
bool accept_connections;
// Settings of widgets
WidgetsConfig widget;
@@ -200,7 +202,7 @@ struct Application
// settings exporters
RecordConfig record;
StreamingConfig stream;
// StreamingConfig stream;
// settings new source
SourceConfig source;

View File

@@ -11,25 +11,168 @@
#include <gst/gstformat.h>
#include <gst/video/video.h>
//osc
#include "osc/OscOutboundPacketStream.h"
#include "Settings.h"
#include "GstToolkit.h"
#include "defines.h"
#include "SystemToolkit.h"
#include "Session.h"
#include "FrameBuffer.h"
#include "Log.h"
#include "NetworkToolkit.h"
#include "Streamer.h"
#include <iostream>
#include <cstring>
// this is called when a U
void StreamingRequestListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];
remoteEndpoint.AddressAndPortAsString(sender);
try{
if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_STREAM_REQUEST) == 0 ){
Log::Info("%s wants to know if i can stream", sender);
Streaming::manager().makeOffer(sender);
}
else if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_STREAM_CONNECT) == 0 ){
Log::Info("%s wants me stream", sender);
}
else if( std::strcmp( m.AddressPattern(), OSC_PREFIX OSC_STREAM_DISCONNECT) == 0 ){
Log::Info("%s ended reception", sender);
}
}
catch( osc::Exception& e ){
// any parsing errors such as unexpected argument types, or
// missing arguments get thrown as exceptions.
Log::Info("error while parsing message '%s' from %s : %s", m.AddressPattern(), sender, e.what());
}
}
VideoStreamer::VideoStreamer(): FrameGrabber(), frame_buffer_(nullptr), width_(0), height_(0),
streaming_(false), accept_buffer_(false), pipeline_(nullptr), src_(nullptr), timestamp_(0)
Streaming::Streaming() : session_(nullptr), width_(0), height_(0)
{
// receiver_ = new UdpListeningReceiveSocket(IpEndpointName( IpEndpointName::ANY_ADDRESS, STREAM_REQUEST_PORT ), &listener_ );
//// receiver_ = new UdpListeningReceiveSocket(IpEndpointName( IpEndpointName::ANY_ADDRESS, STREAM_REQUEST_PORT ), &listener_ );
}
void Streaming::listen()
{
Log::Info("Accepting Streaming requests on port %d", STREAM_REQUEST_PORT);
Streaming::manager().receiver_->Run();
Log::Info("Refusing Streaming requests");
}
void Streaming::enable(bool on)
{
// if (on){
// std::thread(listen).detach();
// }
// else {
// // end streaming requests
// receiver_->AsynchronousBreak();
// // end all streaming TODO
// }
}
void Streaming::setSession(Session *se)
{
if (se != nullptr && session_ != se) {
session_ = se;
FrameBuffer *f = session_->frame();
width_ = f->width();
height_ = f->height();
}
else {
session_ = nullptr;
width_ = 0;
height_ = 0;
}
}
void Streaming::makeOffer(const std::string &client)
{
// do not reply if no session can be streamed (should not happen)
if (session_ == nullptr)
return;
// get ip of client
std::string clientip = client.substr(0, client.find_last_of(":"));
// get port used to request
std::string clientport_s = client.substr(client.find_last_of(":") + 1);
// prepare to reply to client
IpEndpointName host( clientip.c_str(), STREAM_RESPONSE_PORT );
UdpTransmitSocket socket( host );
// prepare an offer
offer o;
o.client = clientip;
o.width = width_;
o.height = height_;
// offer SHM if same IP that our host IP (i.e. on the same machine)
if( NetworkToolkit::is_host_ip(clientip) ){
// this port seems free, so re-use it!
o.address = "localhost:" + clientport_s;
o.protocol = NetworkToolkit::SHM_RAW;
}
// any other IP : offer UDP streaming
else {
o.address = NetworkToolkit::closest_host_ip(clientip) + ":" + clientport_s;
o.protocol = NetworkToolkit::TCP_JPEG;
}
// build message
char buffer[IP_MTU_SIZE];
osc::OutboundPacketStream p( buffer, IP_MTU_SIZE );
p.Clear();
p << osc::BeginMessage( OSC_PREFIX OSC_STREAM_OFFER );
p << o.address.c_str();
p << (int) o.protocol;
p << o.width << o.height;
p << osc::EndMessage;
// send OSC message
socket.Send( p.Data(), p.Size() );
}
void Streaming::addStream(const std::string &address)
{
}
void Streaming::removeStream(const std::string &address)
{
}
VideoStreamer::VideoStreamer(Streaming::offer config): FrameGrabber(), frame_buffer_(nullptr), width_(0), height_(0),
streaming_(false), accept_buffer_(false), pipeline_(nullptr), src_(nullptr), timestamp_(0)
{
// configure fix parameter
frame_duration_ = gst_util_uint64_scale_int (1, GST_SECOND, 30); // 30 FPS
timeframe_ = 2 * frame_duration_;
protocol_ = config.protocol;
address_ = config.address;
width_ = config.width;
height_ = config.height;
}
VideoStreamer::~VideoStreamer()
@@ -54,7 +197,6 @@ void VideoStreamer::addFrame (FrameBuffer *frame_buffer, float dt)
if (frame_buffer == nullptr)
return;
// first frame for initialization
if (frame_buffer_ == nullptr) {
@@ -76,9 +218,9 @@ void VideoStreamer::addFrame (FrameBuffer *frame_buffer, float dt)
// create a gstreamer pipeline
std::string description = "appsrc name=src ! videoconvert ! ";
if (Settings::application.stream.profile < 0 || Settings::application.stream.profile >= NetworkToolkit::DEFAULT)
Settings::application.stream.profile = NetworkToolkit::TCP_JPEG;
description += NetworkToolkit::protocol_broadcast_pipeline[Settings::application.stream.profile];
if (protocol_ < 0 || protocol_ >= NetworkToolkit::DEFAULT)
protocol_ = NetworkToolkit::TCP_JPEG;
description += NetworkToolkit::protocol_send_pipeline[protocol_];
// parse pipeline descriptor
GError *error = NULL;
@@ -91,12 +233,16 @@ void VideoStreamer::addFrame (FrameBuffer *frame_buffer, float dt)
}
// setup streaming sink
if (Settings::application.stream.profile == NetworkToolkit::TCP_JPEG || Settings::application.stream.profile == NetworkToolkit::TCP_H264) {
if (protocol_ == NetworkToolkit::TCP_JPEG || protocol_ == NetworkToolkit::TCP_H264) {
// extract host and port from "host:port"
std::string ip = address_.substr(0, address_.find_last_of(":"));
std::string port_s = address_.substr(address_.find_last_of(":") + 1);
uint port = std::stoi(port_s);
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
"host", Settings::application.stream.ip.c_str(),
"port", Settings::application.stream.port, NULL);
"host", ip.c_str(),
"port", port, NULL);
}
else if (Settings::application.stream.profile == NetworkToolkit::SHM_RAW) {
else if (protocol_ == NetworkToolkit::SHM_RAW) {
std::string path = SystemToolkit::full_filename(SystemToolkit::settings_path(), "shm_socket");
SystemToolkit::remove_file(path);
g_object_set (G_OBJECT (gst_bin_get_by_name (GST_BIN (pipeline_), "sink")),
@@ -150,7 +296,7 @@ void VideoStreamer::addFrame (FrameBuffer *frame_buffer, float dt)
}
// all good
Log::Info("VideoStreamer start (%s %d x %d)", NetworkToolkit::protocol_name[Settings::application.stream.profile], width_, height_);
Log::Info("VideoStreamer start (%s %d x %d)", NetworkToolkit::protocol_name[protocol_], width_, height_);
Log::Info("%s", description.c_str());
@@ -267,7 +413,7 @@ Log::Info("%s", description.c_str());
}
// make sure the shared memory socket is deleted
if (Settings::application.stream.profile == NetworkToolkit::SHM_RAW) {
if (protocol_ == NetworkToolkit::SHM_RAW) {
std::string path = SystemToolkit::full_filename(SystemToolkit::settings_path(), "shm_socket");
SystemToolkit::remove_file(path);
}
@@ -291,13 +437,13 @@ std::string VideoStreamer::info()
std::string ret = "Streaming terminated.";
if (streaming_) {
if (Settings::application.stream.profile == NetworkToolkit::TCP_JPEG || Settings::application.stream.profile == NetworkToolkit::TCP_H264) {
if (protocol_ == NetworkToolkit::TCP_JPEG || protocol_ == NetworkToolkit::TCP_H264) {
ret = "TCP";
}
else if (Settings::application.stream.profile == NetworkToolkit::SHM_RAW) {
else if (protocol_ == NetworkToolkit::SHM_RAW) {
ret = "Shared Memory";
}

View File

@@ -4,9 +4,68 @@
#include <gst/pbutils/pbutils.h>
#include <gst/app/gstappsrc.h>
#include "osc/OscReceivedElements.h"
#include "osc/OscPacketListener.h"
#include "ip/UdpSocket.h"
#include "NetworkToolkit.h"
#include "FrameGrabber.h"
class Session;
class StreamingRequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
class Streaming
{
// Private Constructor
Streaming();
Streaming(Streaming const& copy); // Not Implemented
Streaming& operator=(Streaming const& copy); // Not Implemented
public:
static Streaming& manager()
{
// The only instance
static Streaming _instance;
return _instance;
}
void enable(bool on);
void setSession(Session *se);
typedef struct {
std::string client;
std::string address;
NetworkToolkit::Protocol protocol;
int width;
int height;
} offer;
void makeOffer(const std::string &client);
void retractOffer(offer o);
void addStream(const std::string &address);
void removeStream(const std::string &address);
private:
std::string hostname_;
static void listen();
StreamingRequestListener listener_;
UdpListeningReceiveSocket *receiver_;
Session *session_;
int width_;
int height_;
std::vector<offer> offers_;
};
class VideoStreamer : public FrameGrabber
{
// Frame buffer information
@@ -14,6 +73,10 @@ class VideoStreamer : public FrameGrabber
uint width_;
uint height_;
//
NetworkToolkit::Protocol protocol_;
std::string address_;
// operation
std::atomic<bool> streaming_;
std::atomic<bool> accept_buffer_;
@@ -30,7 +93,7 @@ class VideoStreamer : public FrameGrabber
public:
VideoStreamer();
VideoStreamer(Streaming::offer config);
~VideoStreamer();
void addFrame(FrameBuffer *frame_buffer, float dt) override;

View File

@@ -57,6 +57,7 @@ using namespace std;
#include "MediaSource.h"
#include "PatternSource.h"
#include "DeviceSource.h"
#include "NetworkSource.h"
#include "StreamSource.h"
#include "PickingVisitor.h"
#include "ImageShader.h"
@@ -163,7 +164,7 @@ UserInterface::UserInterface()
screenshot_step = 0;
video_recorder_ = 0;
video_streamer_ = 0;
// video_streamer_ = 0;
}
bool UserInterface::Init()
@@ -1103,7 +1104,7 @@ void UserInterface::RenderPreview()
}
FrameGrabber *rec = Mixer::manager().session()->getFrameGrabber(video_recorder_);
FrameGrabber *str = Mixer::manager().session()->getFrameGrabber(video_streamer_);
// FrameGrabber *str = Mixer::manager().session()->getFrameGrabber(video_streamer_);
// return from thread for folder openning
if ( !recordFolderFileDialogs.empty() ) {
@@ -1203,72 +1204,77 @@ void UserInterface::RenderPreview()
}
if (ImGui::BeginMenu("Stream"))
{
// Stop recording menu if main recorder already exists
if (str) {
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.8f));
if ( ImGui::MenuItem( ICON_FA_SQUARE " Stop Streaming") ) {
str->stop();
video_streamer_ = 0;
}
ImGui::PopStyleColor(1);
if (video_streamer_ > 0) {
if (Settings::application.stream.profile == NetworkToolkit::TCP_JPEG || Settings::application.stream.profile == NetworkToolkit::TCP_H264) {
// Options menu
ImGui::Separator();
ImGui::MenuItem("Connection parameters", nullptr, false, false);
static char dummy_str[512];
sprintf(dummy_str, "%s", Settings::application.stream.ip.c_str());
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::InputText("Host", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
sprintf(dummy_str, "%d", Settings::application.stream.port);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::InputText("Port", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
}
// else if (Settings::application.stream.profile == NetworkToolkit::SHM_RAW)
// {
// ImGui::Separator();
// ImGui::MenuItem("Shared Memory active", nullptr, false, false);
// }
}
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.8f));
if ( ImGui::MenuItem( ICON_FA_PODCAST " Accept connections", NULL, &Settings::application.accept_connections) ) {
Streaming::manager().enable(Settings::application.accept_connections);
}
// start recording
else {
// detecting the absence of video streamer but the variable is still not 0: fix this!
if (video_streamer_ > 0)
video_streamer_ = 0;
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.8f));
if ( ImGui::MenuItem( ICON_FA_PODCAST " Stream") ) {
FrameGrabber *fg = new VideoStreamer;
video_streamer_ = fg->id();
Mixer::manager().session()->addFrameGrabber(fg);
}
ImGui::PopStyleColor(1);
// select profile
ImGui::SetNextItemWidth(300);
ImGui::Combo("##StreamProfile", &Settings::application.stream.profile, NetworkToolkit::protocol_name, IM_ARRAYSIZE(NetworkToolkit::protocol_name) );
if (Settings::application.stream.profile == NetworkToolkit::TCP_JPEG || Settings::application.stream.profile == NetworkToolkit::TCP_H264) {
// Options menu
ImGui::Separator();
ImGui::MenuItem("TCP Options", nullptr, false, false);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("Host", Settings::application.stream.ip.c_str()))
{
static std::vector<std::string> ips = NetworkToolkit::host_ips();
for (int i=0; i < ips.size(); i++) {
if (ImGui::Selectable( ips[i].c_str() ))
Settings::application.stream.ip = ips[i];
}
ImGui::EndCombo();
}
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::InputInt("Port", &Settings::application.stream.port, 100, 1000);
Settings::application.stream.port = CLAMP(Settings::application.stream.port, 1000, 9000);
}
}
ImGui::PopStyleColor(1);
ImGui::EndMenu();
}
// if (ImGui::BeginMenu("Stream"))
// {
// // Stop recording menu if main recorder already exists
// if (str) {
// ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.8f));
// if ( ImGui::MenuItem( ICON_FA_SQUARE " Stop Streaming") ) {
// str->stop();
// video_streamer_ = 0;
// }
// ImGui::PopStyleColor(1);
// if (video_streamer_ > 0) {
// if (Settings::application.stream.profile == NetworkToolkit::TCP_JPEG || Settings::application.stream.profile == NetworkToolkit::TCP_H264) {
// // Options menu
// ImGui::Separator();
// ImGui::MenuItem("Connection parameters", nullptr, false, false);
// static char dummy_str[512];
// sprintf(dummy_str, "%s", Settings::application.stream.ip.c_str());
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
// ImGui::InputText("Host", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
// sprintf(dummy_str, "%d", Settings::application.stream.port);
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
// ImGui::InputText("Port", dummy_str, IM_ARRAYSIZE(dummy_str), ImGuiInputTextFlags_ReadOnly);
// }
// }
// }
// // start recording
// else {
// // detecting the absence of video streamer but the variable is still not 0: fix this!
// if (video_streamer_ > 0)
// video_streamer_ = 0;
// ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.8f));
// if ( ImGui::MenuItem( ICON_FA_PODCAST " Stream") ) {
// FrameGrabber *fg = new VideoStreamer;
// video_streamer_ = fg->id();
// Mixer::manager().session()->addFrameGrabber(fg);
// Streaming::manager().enable(true);
// }
// ImGui::PopStyleColor(1);
// // select profile
// ImGui::SetNextItemWidth(300);
// ImGui::Combo("##StreamProfile", &Settings::application.stream.profile, NetworkToolkit::protocol_name, IM_ARRAYSIZE(NetworkToolkit::protocol_name) );
// if (Settings::application.stream.profile == NetworkToolkit::TCP_JPEG || Settings::application.stream.profile == NetworkToolkit::TCP_H264) {
// // Options menu
// ImGui::Separator();
// ImGui::MenuItem("TCP Options", nullptr, false, false);
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
// if (ImGui::BeginCombo("Host", Settings::application.stream.ip.c_str()))
// {
// static std::vector<std::string> ips = NetworkToolkit::host_ips();
// for (int i=0; i < ips.size(); i++) {
// if (ImGui::Selectable( ips[i].c_str() ))
// Settings::application.stream.ip = ips[i];
// }
// ImGui::EndCombo();
// }
// ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
// ImGui::InputInt("Port", &Settings::application.stream.port, 100, 1000);
// Settings::application.stream.port = CLAMP(Settings::application.stream.port, 1000, 9000);
// }
// }
// ImGui::EndMenu();
// }
ImGui::EndMenuBar();
}
@@ -1300,19 +1306,19 @@ void UserInterface::RenderPreview()
ImGui::PopFont();
}
// streaming indicator overlay
if (str)
{
float r = ImGui::GetTextLineHeightWithSpacing();
ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + width - 2.f * r, draw_pos.y + r));
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
if (str->busy())
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.8f));
else
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.2f));
ImGui::Text(ICON_FA_PODCAST);
ImGui::PopStyleColor(1);
ImGui::PopFont();
}
// if (str)
// {
// float r = ImGui::GetTextLineHeightWithSpacing();
// ImGui::SetCursorScreenPos(ImVec2(draw_pos.x + width - 2.f * r, draw_pos.y + r));
// ImGuiToolkit::PushFont(ImGuiToolkit::FONT_LARGE);
// if (str->busy())
// ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.8f));
// else
// ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05, 1.0, 0.05, 0.2f));
// ImGui::Text(ICON_FA_PODCAST);
// ImGui::PopStyleColor(1);
// ImGui::PopFont();
// }
ImGui::End();
}
@@ -2306,9 +2312,17 @@ void Navigator::RenderNewPannel()
}
}
for (uint n = 0; n < NetworkToolkit::DEFAULT; ++n){
if (ImGui::Selectable( NetworkToolkit::protocol_name[n] )) {
new_source_preview_.setSource( Mixer::manager().createSourceNetwork(n, "192.168.0.30:5400") );
// for (uint n = 0; n < NetworkToolkit::DEFAULT; ++n){
// if (ImGui::Selectable( NetworkToolkit::protocol_name[n] )) {
// new_source_preview_.setSource( Mixer::manager().createSourceNetwork("192.168.0.30") );
// }
// }
for (int d = 0; d < NetworkHosts::manager().numHosts(); ++d){
std::string namehost = NetworkHosts::manager().name(d);
if (ImGui::Selectable( namehost.c_str() )) {
new_source_preview_.setSource( Mixer::manager().createSourceNetwork(namehost), namehost);
}
}

View File

@@ -117,7 +117,7 @@ class UserInterface
// frame grabbers
uint64_t video_recorder_;
uint64_t video_streamer_;
// uint64_t video_streamer_;
// Private Constructor
UserInterface();

View File

@@ -91,7 +91,7 @@ public:
// Enable broadcast addresses (e.g. x.x.x.255)
// Sets SO_BROADCAST socket option.
void SetEnableBroadcast( bool enableBroadcast );
void SetEnableBroadcast( bool enableBroadcast );
// Enable multiple listeners for a single port on same
// network interface*

View File

@@ -28,6 +28,7 @@
#include "Mixer.h"
#include "RenderingManager.h"
#include "UserInterfaceManager.h"
#include "Connection.h"
void drawScene()
@@ -64,6 +65,10 @@ int main(int argc, char *argv[])
if ( !UserInterface::manager().Init() )
return 1;
if ( !Connection::manager().init() )
return 1;
///
/// GStreamer
///
@@ -94,6 +99,8 @@ int main(int argc, char *argv[])
Rendering::manager().draw();
}
Connection::manager().terminate();
///
/// UI TERMINATE
///