diff --git a/CMakeLists.txt b/CMakeLists.txt index f9686e0..05d2465 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -279,6 +279,7 @@ set(VMIX_SRCS SystemToolkit.cpp tinyxml2Toolkit.cpp NetworkToolkit.cpp + Connection.cpp ActionManager.cpp ) diff --git a/Connection.cpp b/Connection.cpp new file mode 100644 index 0000000..4f46eaf --- /dev/null +++ b/Connection.cpp @@ -0,0 +1,235 @@ + +#include +#include +#include +#include + +#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::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 handshake_ports; + for(int i=HANDSHAKE_PORT; iAsInt32(); + +// 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()); + } +} + diff --git a/Connection.h b/Connection.h new file mode 100644 index 0000000..fc49e4f --- /dev/null +++ b/Connection.h @@ -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 diff --git a/DeviceSource.h b/DeviceSource.h index 7fc0e78..c582543 100644 --- a/DeviceSource.h +++ b/DeviceSource.h @@ -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; diff --git a/Mixer.cpp b/Mixer.cpp index a0b339f..711717f 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -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()); } diff --git a/Mixer.h b/Mixer.h index 1228468..4585946 100644 --- a/Mixer.h +++ b/Mixer.h @@ -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); diff --git a/NetworkSource.cpp b/NetworkSource.cpp index e237319..0677087 100644 --- a/NetworkSource.cpp +++ b/NetworkSource.cpp @@ -1,5 +1,8 @@ #include #include +#include +#include + #include #include @@ -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 +{ + 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(stream_); -} - diff --git a/NetworkSource.h b/NetworkSource.h index 95a31cd..6833fd6 100644 --- a/NetworkSource.h +++ b/NetworkSource.h @@ -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 diff --git a/NetworkToolkit.cpp b/NetworkToolkit.cpp index 2caf9ed..8377ef8 100644 --- a/NetworkToolkit.cpp +++ b/NetworkToolkit.cpp @@ -1,10 +1,15 @@ #include +#include #include +#include // std::count #include #include #include #include +//#include + +#include "ip/NetworkingUtils.h" #include "NetworkToolkit.h" @@ -15,7 +20,7 @@ const char* NetworkToolkit::protocol_name[NetworkToolkit::DEFAULT] = { "Shared Memory" }; -const std::vector NetworkToolkit::protocol_broadcast_pipeline { +const std::vector 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 NetworkToolkit::protocol_receive_pipeline { * */ +std::vector ipstrings; +std::vector iplongs; std::vector NetworkToolkit::host_ips() { - std::vector 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; +} diff --git a/NetworkToolkit.h b/NetworkToolkit.h index efc3cc9..00850fb 100644 --- a/NetworkToolkit.h +++ b/NetworkToolkit.h @@ -4,6 +4,22 @@ #include #include +#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 protocol_broadcast_pipeline; +extern const std::vector protocol_send_pipeline; extern const std::vector protocol_receive_pipeline; std::vector host_ips(); +bool is_host_ip(const std::string &ip); +std::string closest_host_ip(const std::string &ip); } diff --git a/Settings.cpp b/Settings.cpp index 79a940b..01443d7 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -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"); diff --git a/Settings.h b/Settings.h index ab2e652..8cbc28e 100644 --- a/Settings.h +++ b/Settings.h @@ -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; diff --git a/Streamer.cpp b/Streamer.cpp index e217e39..43a788a 100644 --- a/Streamer.cpp +++ b/Streamer.cpp @@ -11,25 +11,168 @@ #include #include +//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 +#include + +// 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"; } diff --git a/Streamer.h b/Streamer.h index c84df4d..dc58c69 100644 --- a/Streamer.h +++ b/Streamer.h @@ -4,9 +4,68 @@ #include #include +#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 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 streaming_; std::atomic 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; diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index d76dfa7..edbf68f 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -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 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 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); } } diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index 429f023..27ec8a5 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -117,7 +117,7 @@ class UserInterface // frame grabbers uint64_t video_recorder_; - uint64_t video_streamer_; +// uint64_t video_streamer_; // Private Constructor UserInterface(); diff --git a/ext/OSCPack/ip/UdpSocket.h b/ext/OSCPack/ip/UdpSocket.h index 8bebb27..7a25216 100644 --- a/ext/OSCPack/ip/UdpSocket.h +++ b/ext/OSCPack/ip/UdpSocket.h @@ -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* diff --git a/main.cpp b/main.cpp index 792dd3f..5a0cad2 100644 --- a/main.cpp +++ b/main.cpp @@ -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 ///