Initial implementation of Control manager

Control manager will handle control actions, recorded or from OSC. Here skeleton for receiving OSC messages is in place. Cleanup of includes for NetworkToolkit. Touched a bit the BaseToolkit.
This commit is contained in:
Bruno Herbelin
2021-12-18 16:02:37 +01:00
parent bbc5e50491
commit a612154123
18 changed files with 269 additions and 92 deletions

View File

@@ -141,16 +141,26 @@ std::string BaseToolkit::bits_to_string(long b)
}
std::string BaseToolkit::trunc_string(const std::string& path, int N)
std::string BaseToolkit::truncated(const std::string& str, int N)
{
std::string trunc = path;
int l = path.size();
std::string trunc = str;
int l = str.size();
if ( l > N ) {
trunc = std::string("...") + path.substr( l - N + 3 );
trunc = std::string("...") + str.substr( l - N + 3 );
}
return trunc;
}
std::list<std::string> BaseToolkit::splitted(const std::string& str, char delim) {
std::list<std::string> strings;
size_t start;
size_t end = 0;
while ((start = str.find_first_not_of(delim, end)) != std::string::npos) {
end = str.find(delim, start);
strings.push_back(str.substr(start, end - start));
}
return strings;
}
std::string BaseToolkit::common_prefix( const std::list<std::string> & allStrings )
{

View File

@@ -22,8 +22,11 @@ std::string byte_to_string(long b);
// get a string to display bit size with unit Kbit, MBit, Gbit, Tbit
std::string bits_to_string(long b);
// Truncate a string to display the right most N characters (e.g. ./home/me/toto.mpg -> ...ome/me/toto.mpg)
std::string trunc_string(const std::string& path, int N);
// cut a string to display the right most N characters (e.g. /home/me/toto.mpg -> ...ome/me/toto.mpg)
std::string truncated(const std::string& str, int N);
// split a string into list of strings separated by delimitor (e.g. /home/me/toto.mpg -> {home, me, toto.mpg} )
std::list<std::string> splitted(const std::string& str, char delim);
// find common parts in a list of strings
std::string common_prefix(const std::list<std::string> &allStrings);

View File

@@ -337,6 +337,7 @@ set(VMIX_SRCS
ActionManager.cpp
Overlay.cpp
Metronome.cpp
ControlManager.cpp
)

View File

@@ -19,28 +19,26 @@
#include <thread>
#include <chrono>
#include <vector>
#include <algorithm>
#include "osc/OscOutboundPacketStream.h"
#include "defines.h"
#include "Connection.h"
#include "Settings.h"
#include "Streamer.h"
#include "Log.h"
#include "Connection.h"
#ifndef NDEBUG
#define CONNECTION_DEBUG
#endif
Connection::Connection()
Connection::Connection() : receiver_(nullptr)
{
receiver_ = nullptr;
}
Connection::~Connection()
{
if (receiver_!=nullptr) {
@@ -226,7 +224,7 @@ void Connection::ask()
}
void ConnectionRequestListener::ProcessMessage( const osc::ReceivedMessage& m,
void Connection::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];

View File

@@ -1,22 +1,17 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include <string>
#include <vector>
#include "osc/OscReceivedElements.h"
#include "osc/OscPacketListener.h"
#include "ip/UdpSocket.h"
#include "NetworkToolkit.h"
#define MAX_HANDSHAKE 20
#define HANDSHAKE_PORT 71310
#define STREAM_REQUEST_PORT 71510
#define OSC_DIALOG_PORT 71010
#define ALIVE 3
class ConnectionRequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
struct ConnectionInfo {
@@ -58,8 +53,6 @@ struct ConnectionInfo {
class Connection
{
friend class ConnectionRequestListener;
// Private Constructor
Connection();
Connection(Connection const& copy) = delete;
@@ -82,11 +75,19 @@ public:
int index(const std::string &name) const;
ConnectionInfo info(int index = 0); // index 0 for self
protected:
class RequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
private:
static void ask();
static void listen();
ConnectionRequestListener listener_;
RequestListener listener_;
UdpListeningReceiveSocket *receiver_;
std::vector< ConnectionInfo > connections_;

110
ControlManager.cpp Normal file
View File

@@ -0,0 +1,110 @@
/*
* This file is part of vimix - video live mixer
*
* **Copyright** (C) 2020-2021 Bruno Herbelin <bruno.herbelin@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
**/
#include <thread>
#include <mutex>
#include "osc/OscOutboundPacketStream.h"
#include "Log.h"
#include "Settings.h"
#include "BaseToolkit.h"
#include "ControlManager.h"
#ifndef NDEBUG
#define CONTROL_DEBUG
#endif
void Control::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];
remoteEndpoint.AddressAndPortAsString(sender);
try{
#ifdef CONTROL_DEBUG
Log::Info("%s sent an OSC message %s.", sender, m.AddressPattern());
#endif
std::list<std::string> address = BaseToolkit::splitted(m.AddressPattern(), '/');
if (address.size() == 3 && address.front().compare(APP_NAME) == 0 ){
address.pop_front();
Log::Info("Wellformed vimix message %s.", address.front().c_str());
}
}
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());
}
}
void Control::listen()
{
#ifdef CONTROL_DEBUG
Log::Info("Accepting OSC messages on port %d", Settings::application.control.osc_port);
#endif
if (Control::manager().receiver_)
Control::manager().receiver_->Run();
}
Control::Control() : receiver_(nullptr)
{
}
Control::~Control()
{
if (receiver_!=nullptr) {
receiver_->Break();
delete receiver_;
}
}
bool Control::init()
{
try {
// try to create listenning socket
// through exception runtime if fails
receiver_ = new UdpListeningReceiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS,
Settings::application.control.osc_port ), &listener_ );
}
catch (const std::runtime_error&) {
// arg, the receiver could not be initialized
// because the port was not available
receiver_ = nullptr;
}
// listen for answers
std::thread(listen).detach();
return receiver_ != nullptr;
}
void Control::terminate()
{
if (receiver_!=nullptr)
receiver_->AsynchronousBreak();
}

43
ControlManager.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef CONTROL_H
#define CONTROL_H
#include "NetworkToolkit.h"
class Control
{
// Private Constructor
Control();
Control(Control const& copy) = delete;
Control& operator=(Control const& copy) = delete;
public:
static Control& manager ()
{
// The only instance
static Control _instance;
return _instance;
}
~Control();
bool init();
void terminate();
// void setOscPort(int P);
protected:
class RequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
private:
static void listen();
RequestListener listener_;
UdpListeningReceiveSocket *receiver_;
};
#endif // CONTROL_H

View File

@@ -582,7 +582,7 @@ void ImGuiVisitor::visit (MediaSource& s)
// folder
std::string path = SystemToolkit::path_filename(s.path());
std::string label = BaseToolkit::trunc_string(path, 25);
std::string label = BaseToolkit::truncated(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
@@ -637,7 +637,7 @@ void ImGuiVisitor::visit (SessionFileSource& s)
ImGui::Text("File");
std::string path = SystemToolkit::path_filename(s.path());
std::string label = BaseToolkit::trunc_string(path, 25);
std::string label = BaseToolkit::truncated(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
@@ -864,7 +864,7 @@ void ImGuiVisitor::visit (MultiFileSource& s)
// offer to open file browser at location
std::string path = SystemToolkit::path_filename(s.sequence().location);
std::string label = BaseToolkit::trunc_string(path, 25);
std::string label = BaseToolkit::truncated(path, 25);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();

View File

@@ -27,13 +27,14 @@
#include <gst/pbutils/pbutils.h>
#include <gst/gst.h>
#include "osc/OscOutboundPacketStream.h"
#include "SystemToolkit.h"
#include "defines.h"
#include "Stream.h"
#include "Decorations.h"
#include "Visitor.h"
#include "Log.h"
#include "Connection.h"
#include "NetworkSource.h"
@@ -43,7 +44,7 @@
// this is called when receiving an answer for streaming request
void StreamerResponseListener::ProcessMessage( const osc::ReceivedMessage& m,
void NetworkStream::ResponseListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];

View File

@@ -1,33 +1,13 @@
#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 "Connection.h"
#include "StreamSource.h"
class NetworkStream;
class StreamerResponseListener : public osc::OscPacketListener
{
protected:
class NetworkStream *parent_;
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
public:
inline void setParent(NetworkStream *s) { parent_ = s; }
StreamerResponseListener() : parent_(nullptr) {}
};
class NetworkStream : public Stream
{
friend class StreamerResponseListener;
public:
NetworkStream();
@@ -43,10 +23,22 @@ public:
std::string clientAddress() const;
std::string serverAddress() const;
protected:
class ResponseListener : public osc::OscPacketListener
{
protected:
class NetworkStream *parent_;
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
public:
inline void setParent(NetworkStream *s) { parent_ = s; }
ResponseListener() : parent_(nullptr) {}
};
private:
// connection information
ConnectionInfo streamer_;
StreamerResponseListener listener_;
ResponseListener listener_;
UdpListeningReceiveSocket *receiver_;
std::atomic<bool> received_config_;
std::atomic<bool> connected_;

View File

@@ -4,6 +4,10 @@
#include <string>
#include <vector>
#include "osc/OscReceivedElements.h"
#include "osc/OscPacketListener.h"
#include "ip/UdpSocket.h"
#define OSC_PREFIX "/vimix"
#define OSC_PING "/ping"
#define OSC_PONG "/pong"
@@ -12,11 +16,6 @@
#define OSC_STREAM_REJECT "/reject"
#define OSC_STREAM_DISCONNECT "/disconnect"
#define STREAMING_FPS 30
#define MAX_HANDSHAKE 20
#define HANDSHAKE_PORT 71310
#define STREAM_REQUEST_PORT 71510
#define OSC_DIALOG_PORT 71010
#define IP_MTU_SIZE 1536
namespace NetworkToolkit

View File

@@ -189,6 +189,15 @@ struct TimerConfig
}
};
struct ControllerConfig
{
int osc_port;
ControllerConfig() {
osc_port = OSC_DEFAULT_PORT;
}
};
struct Application
{
// instance check
@@ -237,6 +246,9 @@ struct Application
// settings transition
TransitionConfig transition;
// settings controller
ControllerConfig control;
// multiple windows handling
std::vector<WindowConfig> windows;

View File

@@ -32,6 +32,8 @@
// gstreamer
#include <gst/gstformat.h>
#include <gst/video/video.h>
#include <gst/app/gstappsrc.h>
#include <gst/pbutils/pbutils.h>
//osc
#include "osc/OscOutboundPacketStream.h"
@@ -52,7 +54,7 @@
#define STREAMER_DEBUG
#endif
void StreamingRequestListener::ProcessMessage( const osc::ReceivedMessage& m,
void Streaming::RequestListener::ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint )
{
char sender[IpEndpointName::ADDRESS_AND_PORT_STRING_LENGTH];

View File

@@ -3,31 +3,15 @@
#include <mutex>
#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"
#define STREAMING_FPS 30
class Session;
class VideoStreamer;
class StreamingRequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
class Streaming
{
friend class StreamingRequestListener;
// Private Constructor
Streaming();
Streaming(Streaming const& copy) = delete;
@@ -53,13 +37,20 @@ public:
std::vector<std::string> listStreams();
protected:
class RequestListener : public osc::OscPacketListener {
protected:
virtual void ProcessMessage( const osc::ReceivedMessage& m,
const IpEndpointName& remoteEndpoint );
};
void addStream(const std::string &sender, int reply_to, const std::string &clientname);
void refuseStream(const std::string &sender, int reply_to);
private:
bool enabled_;
StreamingRequestListener listener_;
RequestListener listener_;
UdpListeningReceiveSocket *receiver_;
std::vector<VideoStreamer *> streamers_;

View File

@@ -286,7 +286,7 @@ bool SystemToolkit::file_exists(const string& path)
// tests if dir is a directory and return its path, empty string otherwise
std::string SystemToolkit::path_directory(const std::string& path)
string SystemToolkit::path_directory(const string& path)
{
string directorypath = "";
@@ -359,13 +359,13 @@ void SystemToolkit::execute(const string& command)
vector<string> split(const string& str, char delim) {
vector<string> split_path(const string& path) {
vector<string> strings;
size_t start;
size_t end = 0;
while ((start = str.find_first_not_of(delim, end)) != string::npos) {
end = str.find(delim, start);
strings.push_back(str.substr(start, end - start));
while ((start = path.find_first_not_of(PATH_SEP, end)) != string::npos) {
end = path.find(PATH_SEP, start);
strings.push_back(path.substr(start, end - start));
}
return strings;
}
@@ -376,8 +376,8 @@ vector<string> split(const string& str, char delim) {
string SystemToolkit::path_relative_to_path( const string& absolutePath, const string& relativeTo )
{
string relativePath = "";
vector<string> absoluteDirectories = split(absolutePath, PATH_SEP);
vector<string> relativeToDirectories = split(relativeTo, PATH_SEP);
vector<string> absoluteDirectories = split_path(absolutePath);
vector<string> relativeToDirectories = split_path(relativeTo);
// Get the shortest of the two paths
size_t length = MINI( absoluteDirectories.size(), relativeToDirectories.size() );
@@ -415,11 +415,11 @@ string SystemToolkit::path_relative_to_path( const string& absolutePath, const s
return relativePath;
}
std::string SystemToolkit::path_absolute_from_path(const std::string& relativePath, const std::string& relativeTo)
string SystemToolkit::path_absolute_from_path(const string& relativePath, const string& relativeTo)
{
string absolutePath = string(1, PATH_SEP);
vector<string> relativeDirectories = split(relativePath, PATH_SEP);
vector<string> relativeToDirectories = split(relativeTo, PATH_SEP);
vector<string> relativeDirectories = split_path(relativePath);
vector<string> relativeToDirectories = split_path(relativeTo);
// how many ".."
size_t count_relative = 0;

View File

@@ -4349,7 +4349,7 @@ void Navigator::RenderNewPannel()
// combo to offer lists
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##SelectionNewMedia", BaseToolkit::trunc_string(Settings::application.recentImportFolders.path, 25).c_str() ))
if (ImGui::BeginCombo("##SelectionNewMedia", BaseToolkit::truncated(Settings::application.recentImportFolders.path, 25).c_str() ))
{
// Mode MEDIA_RECENT : recent files
if (ImGui::Selectable( ICON_FA_LIST_OL IMGUI_LABEL_RECENT_FILES) ) {
@@ -4362,7 +4362,7 @@ void Navigator::RenderNewPannel()
// Mode MEDIA_FOLDER : known folders
for(auto foldername = Settings::application.recentImportFolders.filenames.begin();
foldername != Settings::application.recentImportFolders.filenames.end(); foldername++) {
std::string f = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::trunc_string( *foldername, 40);
std::string f = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::truncated( *foldername, 40);
if (ImGui::Selectable( f.c_str() )) {
setNewMedia(MEDIA_FOLDER, *foldername);
}
@@ -4458,7 +4458,7 @@ void Navigator::RenderNewPannel()
for(auto it = sourceMediaFiles.begin(); it != sourceMediaFiles.end(); ++it) {
// build displayed file name
std::string filename = BaseToolkit::transliterate(*it);
std::string label = BaseToolkit::trunc_string(SystemToolkit::filename(filename), 25);
std::string label = BaseToolkit::truncated(SystemToolkit::filename(filename), 25);
// add selectable item to ListBox; open if clickec
if (ImGui::Selectable( label.c_str(), sourceMediaFileCurrent.compare(*it) == 0 )) {
// set new source preview
@@ -4721,7 +4721,7 @@ void Navigator::RenderMainPannelVimix()
// Show combo box of quick selection modes
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("##SelectionSession", BaseToolkit::trunc_string(Settings::application.recentFolders.path, 25).c_str() )) {
if (ImGui::BeginCombo("##SelectionSession", BaseToolkit::truncated(Settings::application.recentFolders.path, 25).c_str() )) {
// Mode 0 : recent files
if (ImGui::Selectable( ICON_FA_LIST_OL IMGUI_LABEL_RECENT_FILES) ) {
@@ -4732,7 +4732,7 @@ void Navigator::RenderMainPannelVimix()
// Mode 1 : known folders
for(auto foldername = Settings::application.recentFolders.filenames.begin();
foldername != Settings::application.recentFolders.filenames.end(); foldername++) {
std::string f = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::trunc_string( *foldername, 40);
std::string f = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::truncated( *foldername, 40);
if (ImGui::Selectable( f.c_str() )) {
// remember which path was selected
Settings::application.recentFolders.path.assign(*foldername);
@@ -5017,7 +5017,7 @@ void Navigator::RenderMainPannelVimix()
{
// Folder
std::string path = SystemToolkit::path_filename(sessionfilename);
std::string label = BaseToolkit::trunc_string(path, 23);
std::string label = BaseToolkit::truncated(path, 23);
label = BaseToolkit::transliterate(label);
ImGuiToolkit::ButtonOpenUrl( label.c_str(), path.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine();
@@ -5607,7 +5607,7 @@ void SourcePreview::setSource(Source *s, const string &label)
delete source_;
source_ = s;
label_ = BaseToolkit::trunc_string(label, 35);
label_ = BaseToolkit::truncated(label, 35);
reset_ = true;
}

View File

@@ -105,4 +105,6 @@
#define USE_GST_APPSINK_CALLBACKS
#define OSC_DEFAULT_PORT 7000
#endif // VMIX_DEFINES_H

View File

@@ -27,6 +27,7 @@
#include "Mixer.h"
#include "RenderingManager.h"
#include "UserInterfaceManager.h"
#include "ControlManager.h"
#include "Connection.h"
#include "Metronome.h"
@@ -91,6 +92,12 @@ int main(int argc, char *argv[])
if ( !Connection::manager().init() )
return 1;
///
/// CONTROLLER INIT
///
if ( !Control::manager().init() )
return 1;
///
/// METRONOME INIT
///
@@ -151,6 +158,11 @@ int main(int argc, char *argv[])
///
Metronome::manager().terminate();
///
/// CONTROLLER TERMINATE
///
Control::manager().terminate();
///
/// CONNECTION TERMINATE
///