mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-05 15:30:00 +01:00
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:
@@ -279,6 +279,7 @@ set(VMIX_SRCS
|
||||
SystemToolkit.cpp
|
||||
tinyxml2Toolkit.cpp
|
||||
NetworkToolkit.cpp
|
||||
Connection.cpp
|
||||
ActionManager.cpp
|
||||
)
|
||||
|
||||
|
||||
235
Connection.cpp
Normal file
235
Connection.cpp
Normal 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
90
Connection.h
Normal 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
|
||||
@@ -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:
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
2
Mixer.h
2
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);
|
||||
|
||||
@@ -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_);
|
||||
}
|
||||
|
||||
|
||||
103
NetworkSource.h
103
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
|
||||
|
||||
@@ -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,10 +79,13 @@ 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) {
|
||||
@@ -96,14 +104,55 @@ std::vector<std::string> NetworkToolkit::host_ips()
|
||||
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
|
||||
ipstrings.push_back( std::string(ip) );
|
||||
iplongs.push_back( GetHostByName(ip) );
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
38
Settings.cpp
38
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");
|
||||
|
||||
24
Settings.h
24
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;
|
||||
|
||||
174
Streamer.cpp
174
Streamer.cpp
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
65
Streamer.h
65
Streamer.h
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
if ( ImGui::MenuItem( ICON_FA_PODCAST " Accept connections", NULL, &Settings::application.accept_connections) ) {
|
||||
Streaming::manager().enable(Settings::application.accept_connections);
|
||||
}
|
||||
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);
|
||||
// }
|
||||
}
|
||||
}
|
||||
// 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::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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class UserInterface
|
||||
|
||||
// frame grabbers
|
||||
uint64_t video_recorder_;
|
||||
uint64_t video_streamer_;
|
||||
// uint64_t video_streamer_;
|
||||
|
||||
// Private Constructor
|
||||
UserInterface();
|
||||
|
||||
7
main.cpp
7
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
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user