2022-07-19 19:05:13 +00:00
|
|
|
/** Node-type: webrtc
|
2022-07-14 10:43:09 +00:00
|
|
|
*
|
|
|
|
* @author Steffen Vogel <svogel2@eonerc.rwth-aachen.de>
|
2023-06-19 12:12:33 +02:00
|
|
|
* @author Philipp Jungkamp <Philipp.Jungkamp@opal-rt.com>
|
2022-07-14 10:43:09 +00:00
|
|
|
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
|
2023-06-19 12:12:33 +02:00
|
|
|
* @copyright 2023, OPAL-RT Germany GmbH
|
2022-07-14 10:43:09 +00:00
|
|
|
* @license Apache 2.0
|
|
|
|
*********************************************************************************/
|
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2022-07-14 10:43:09 +00:00
|
|
|
#include <villas/node_compat.hpp>
|
|
|
|
#include <villas/nodes/webrtc.hpp>
|
|
|
|
#include <villas/utils.hpp>
|
|
|
|
#include <villas/sample.hpp>
|
|
|
|
#include <villas/exceptions.hpp>
|
|
|
|
#include <villas/super_node.hpp>
|
|
|
|
#include <villas/exceptions.hpp>
|
|
|
|
|
|
|
|
using namespace villas;
|
|
|
|
using namespace villas::node;
|
|
|
|
using namespace villas::utils;
|
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
static villas::node::Web *web;
|
|
|
|
|
2022-07-14 10:43:09 +00:00
|
|
|
WebRTCNode::WebRTCNode(const std::string &name) :
|
2022-07-19 19:05:13 +00:00
|
|
|
Node(name),
|
2023-06-21 15:02:08 +02:00
|
|
|
server("https://villas.k8s.eonerc.rwth-aachen.de/ws/signaling"),
|
2023-06-13 17:48:04 +02:00
|
|
|
wait_seconds(0),
|
2023-06-15 11:24:18 +02:00
|
|
|
format(nullptr),
|
|
|
|
queue({}),
|
|
|
|
pool({}),
|
2023-06-13 17:48:04 +02:00
|
|
|
dci({})
|
2022-07-19 19:05:13 +00:00
|
|
|
{
|
2023-06-13 17:48:04 +02:00
|
|
|
dci.reliability.type = rtc::Reliability::Type::Rexmit;
|
2022-07-19 19:05:13 +00:00
|
|
|
}
|
2022-07-14 10:43:09 +00:00
|
|
|
|
|
|
|
WebRTCNode::~WebRTCNode()
|
2023-06-13 17:48:04 +02:00
|
|
|
{
|
|
|
|
int ret = pool_destroy(&pool);
|
|
|
|
if (ret) // TODO log
|
|
|
|
;
|
|
|
|
}
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
int WebRTCNode::parse(json_t *json, const uuid_t sn_uuid)
|
2022-07-14 10:43:09 +00:00
|
|
|
{
|
2022-07-19 19:05:13 +00:00
|
|
|
int ret = Node::parse(json, sn_uuid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
const char *sess;
|
|
|
|
const char *svr = nullptr;
|
2023-06-29 05:53:04 +00:00
|
|
|
const char *pr = nullptr;
|
2023-06-13 17:48:04 +02:00
|
|
|
int ord = -1;
|
|
|
|
int &rexmit = dci.reliability.rexmit.emplace<int>(0);
|
|
|
|
json_t *ice_json = nullptr;
|
|
|
|
json_t *fmt_json = nullptr;
|
2022-07-19 19:05:13 +00:00
|
|
|
|
|
|
|
json_error_t err;
|
2023-06-29 05:53:04 +00:00
|
|
|
ret = json_unpack_ex(json, &err, 0, "{ s:s, s?:s, s?s, s?i, s?i, s?b, s?o }",
|
2022-07-19 19:05:13 +00:00
|
|
|
"session", &sess,
|
2023-06-29 05:53:04 +00:00
|
|
|
"peer", &pr,
|
2022-07-19 19:05:13 +00:00
|
|
|
"server", &svr,
|
2023-06-13 17:48:04 +02:00
|
|
|
"wait_seconds", &wait_seconds,
|
|
|
|
"max_retransmits", &rexmit,
|
2022-07-19 19:05:13 +00:00
|
|
|
"ordered", &ord,
|
2023-06-13 17:48:04 +02:00
|
|
|
"ice", &ice_json,
|
|
|
|
"format", &fmt_json
|
2022-07-19 19:05:13 +00:00
|
|
|
);
|
|
|
|
if (ret)
|
|
|
|
throw ConfigError(json, err, "node-config-node-webrtc");
|
|
|
|
|
|
|
|
session = sess;
|
|
|
|
|
|
|
|
if (svr)
|
|
|
|
server = svr;
|
|
|
|
|
2023-06-29 05:53:04 +00:00
|
|
|
if (pr)
|
|
|
|
peer = pr;
|
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
if (ord)
|
|
|
|
dci.reliability.unordered = !ord;
|
2022-07-19 19:05:13 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
if (ice_json) {
|
2022-07-19 19:05:13 +00:00
|
|
|
json_t *json_servers = nullptr;
|
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
ret = json_unpack_ex(ice_json, &err, 0, "{ s?: o }",
|
2022-07-19 19:05:13 +00:00
|
|
|
"servers", &json_servers
|
|
|
|
);
|
|
|
|
if (ret)
|
|
|
|
throw ConfigError(json, err, "node-config-node-webrtc-ice");
|
|
|
|
|
|
|
|
if (json_servers) {
|
2023-06-15 11:24:18 +02:00
|
|
|
rtcConf.iceServers.clear();
|
2022-07-19 19:05:13 +00:00
|
|
|
|
|
|
|
if (!json_is_array(json_servers))
|
|
|
|
throw ConfigError(json_servers, "node-config-node-webrtc-ice-servers", "ICE Servers must be a an array of server configurations.");
|
|
|
|
|
|
|
|
size_t i;
|
|
|
|
json_t *json_server;
|
|
|
|
json_array_foreach(json_servers, i, json_server) {
|
|
|
|
if (!json_is_string(json_server))
|
|
|
|
throw ConfigError(json_server, "node-config-node-webrtc-ice-server", "ICE servers must be provided as STUN/TURN url.");
|
|
|
|
|
|
|
|
std::string uri = json_string_value(json_server);
|
|
|
|
|
2023-06-15 11:24:18 +02:00
|
|
|
rtcConf.iceServers.emplace_back(uri);
|
2022-07-19 19:05:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
format = fmt_json
|
|
|
|
? FormatFactory::make(fmt_json)
|
|
|
|
: FormatFactory::make("villas.binary");
|
|
|
|
|
|
|
|
assert(format);
|
|
|
|
|
2022-07-14 10:43:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
int WebRTCNode::check()
|
2022-07-14 10:43:09 +00:00
|
|
|
{
|
2023-06-13 17:48:04 +02:00
|
|
|
return Node::check();
|
2022-07-14 10:43:09 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
int WebRTCNode::prepare()
|
2022-07-14 10:43:09 +00:00
|
|
|
{
|
2023-06-13 17:48:04 +02:00
|
|
|
int ret = Node::prepare();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
format->start(getInputSignals(false), ~(int) SampleFlags::HAS_OFFSET);
|
|
|
|
|
2023-06-15 11:24:18 +02:00
|
|
|
conn = std::make_shared<webrtc::PeerConnection>(server, session, rtcConf, web, dci);
|
2023-06-13 17:48:04 +02:00
|
|
|
|
|
|
|
ret = pool_init(&pool, 1024, SAMPLE_LENGTH(getInputSignals(false)->size()));
|
|
|
|
if (ret) // TODO log
|
|
|
|
return ret;
|
2022-07-19 19:05:13 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
ret = queue_signalled_init(&queue, 1024);
|
|
|
|
if (ret) // TODO log
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
conn->onMessage([this](rtc::binary msg){
|
|
|
|
int ret;
|
|
|
|
std::vector<Sample *> smps;
|
|
|
|
smps.resize(this->in.vectorize);
|
|
|
|
|
|
|
|
ret = sample_alloc_many(&this->pool, smps.data(), smps.size());
|
|
|
|
if (ret < 0) // TODO log
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = format->sscan((const char *)msg.data(), msg.size(), nullptr, smps.data(), ret);
|
|
|
|
if (ret < 0) // TODO log
|
|
|
|
return;
|
|
|
|
|
|
|
|
ret = queue_signalled_push_many(&this->queue, (void **) smps.data(), ret);
|
|
|
|
if (ret < 0) // TODO log
|
|
|
|
return;
|
|
|
|
|
2023-06-21 13:52:05 +02:00
|
|
|
this->logger->trace("onMessage(rtc::binary) callback finished pushing {} samples", ret);
|
2023-06-13 17:48:04 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
return 0;
|
2022-07-14 10:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int WebRTCNode::start()
|
|
|
|
{
|
2023-06-13 17:48:04 +02:00
|
|
|
int ret = Node::start();
|
|
|
|
if (!ret)
|
|
|
|
state = State::STARTED;
|
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
conn->connect();
|
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
if (wait_seconds > 0) {
|
|
|
|
logger->info("Waiting for datachannel...");
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
if (!conn->waitForDataChannel(std::chrono::seconds { wait_seconds })) {
|
|
|
|
throw RuntimeError { "Waiting for datachannel timed out after {} seconds", wait_seconds };
|
|
|
|
}
|
2022-07-19 19:05:13 +00:00
|
|
|
}
|
|
|
|
|
2022-07-14 10:43:09 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
int WebRTCNode::stop()
|
|
|
|
{
|
|
|
|
conn->disconnect();
|
|
|
|
return Node::stop();
|
|
|
|
}
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
std::vector<int> WebRTCNode::getPollFDs()
|
|
|
|
{
|
|
|
|
return { queue_signalled_fd(&queue) };
|
|
|
|
}
|
2022-07-14 10:43:09 +00:00
|
|
|
|
|
|
|
const std::string & WebRTCNode::getDetails()
|
|
|
|
{
|
|
|
|
details = fmt::format("");
|
|
|
|
return details;
|
|
|
|
}
|
|
|
|
|
|
|
|
int WebRTCNode::_read(struct Sample *smps[], unsigned cnt)
|
|
|
|
{
|
2023-06-13 17:48:04 +02:00
|
|
|
std::vector<Sample *> smpt;
|
|
|
|
smpt.resize(cnt);
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
int pulled = queue_signalled_pull_many(&queue, (void **) smpt.data(), smpt.size());
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
sample_copy_many(smps, smpt.data(), pulled);
|
|
|
|
sample_decref_many(smpt.data(), pulled);
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
return pulled;
|
2022-07-14 10:43:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int WebRTCNode::_write(struct Sample *smps[], unsigned cnt)
|
|
|
|
{
|
2023-06-13 17:48:04 +02:00
|
|
|
rtc::binary buf;
|
|
|
|
size_t wbytes;
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
buf.resize(4 * 1024);
|
|
|
|
int ret = format->sprint((char *) buf.data(), buf.size(), &wbytes, smps, cnt);
|
|
|
|
if (ret < 0) // TODO log
|
|
|
|
return ret;
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
buf.resize(wbytes);
|
|
|
|
conn->sendMessage(buf);
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2023-06-13 17:48:04 +02:00
|
|
|
return ret;
|
2022-07-14 10:43:09 +00:00
|
|
|
}
|
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
int WebRTCNodeFactory::start(SuperNode *sn)
|
|
|
|
{
|
|
|
|
web = sn->getWeb();
|
|
|
|
if (!web->isEnabled())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-14 10:43:09 +00:00
|
|
|
|
2022-07-19 19:05:13 +00:00
|
|
|
static WebRTCNodeFactory p;
|