mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-16 00:00:02 +01:00
193 lines
6 KiB
C++
193 lines
6 KiB
C++
/* WebRTC signaling messages.
|
|
*
|
|
* Author: Steffen Vogel <post@steffenvogel.de>
|
|
* Author: Philipp Jungkamp <Philipp.Jungkamp@opal-rt.com>
|
|
* SPDX-FileCopyrightText: 2014-2023 Institute for Automation of Complex Power Systems, RWTH Aachen University
|
|
* SPDX-FileCopyrightText: 2023 OPAL-RT Germany GmbH
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <algorithm>
|
|
#include <fmt/format.h>
|
|
#include <villas/exceptions.hpp>
|
|
#include <villas/nodes/webrtc/signaling_message.hpp>
|
|
#include <villas/utils.hpp>
|
|
|
|
using namespace villas;
|
|
using namespace villas::node;
|
|
using namespace villas::node::webrtc;
|
|
|
|
json_t *Peer::toJson() const {
|
|
return json_pack("{ s: i, s: s*, s: s*, s: s* }", "id", id, "name",
|
|
name.empty() ? nullptr : name.c_str(), "remote",
|
|
remote.empty() ? nullptr : remote.c_str(), "user_agent",
|
|
userAgent.empty() ? nullptr : userAgent.c_str()
|
|
// TODO: created, connected
|
|
);
|
|
}
|
|
|
|
Peer::Peer(json_t *json) {
|
|
const char *nme = nullptr, *rem = nullptr, *ua = nullptr, *tscreat, *tsconn;
|
|
|
|
int ret = json_unpack(json, "{ s: i, s?: s, s?: s, s?: s, s?: s, s?: s }",
|
|
"id", &id, "name", &nme, "remote", &rem, "user_agent",
|
|
&ua, "created", &tscreat, "connected", &tsconn);
|
|
if (ret)
|
|
throw RuntimeError("Failed to decode signaling message");
|
|
|
|
if (nme)
|
|
name = nme;
|
|
|
|
if (rem)
|
|
remote = rem;
|
|
|
|
if (ua)
|
|
userAgent = ua;
|
|
|
|
// TODO: created, connected
|
|
}
|
|
|
|
RelayMessage::RelayMessage(json_t *json) {
|
|
|
|
if (!json_is_array(json))
|
|
throw RuntimeError("Failed to decode signaling message");
|
|
|
|
int ret;
|
|
char *url;
|
|
char *user;
|
|
char *pass;
|
|
char *realm;
|
|
char *expires;
|
|
json_t *json_server;
|
|
size_t i;
|
|
json_array_foreach(json, i, json_server) {
|
|
ret = json_unpack(json_server, "{ s: s, s: s, s: s, s: s, s: s }", "url",
|
|
&url, "user", &user, "pass", &pass, "realm", &realm,
|
|
"expires", &expires);
|
|
if (ret)
|
|
throw RuntimeError("Failed to decode signaling message");
|
|
|
|
auto &server = servers.emplace_back(url);
|
|
server.username = user;
|
|
server.password = pass;
|
|
|
|
// TODO: warn about unsupported realm
|
|
// TODO: log info about expires time
|
|
}
|
|
}
|
|
|
|
json_t *ControlMessage::toJson() const {
|
|
json_t *json_peers = json_array();
|
|
|
|
for (auto &p : peers) {
|
|
json_t *json_peer = p.toJson();
|
|
|
|
json_array_append_new(json_peers, json_peer);
|
|
}
|
|
|
|
return json_pack("{ s: i, s: o }", "peer_id", peerID, "peers", json_peers);
|
|
}
|
|
|
|
ControlMessage::ControlMessage(json_t *j) {
|
|
int ret;
|
|
|
|
json_t *json_peers;
|
|
|
|
ret = json_unpack(j, "{ s: i, s: o }", "peer_id", &peerID, "peers",
|
|
&json_peers);
|
|
if (ret)
|
|
throw RuntimeError("Failed to decode signaling message");
|
|
|
|
if (!json_is_array(json_peers))
|
|
throw RuntimeError("Failed to decode signaling message");
|
|
|
|
json_t *json_peer;
|
|
size_t i;
|
|
// cppcheck-suppress unknownMacro
|
|
json_array_foreach(json_peers, i, json_peer) peers.emplace_back(json_peer);
|
|
}
|
|
|
|
json_t *SignalingMessage::toJson() const {
|
|
return std::visit(
|
|
villas::utils::overloaded{
|
|
[](ControlMessage const &c) {
|
|
return json_pack("{ s: o }", "control", c.toJson());
|
|
},
|
|
[](SignalList const &s) {
|
|
return json_pack("{ s: o }", "signals", s.toJson());
|
|
},
|
|
[](rtc::Description const &d) {
|
|
return json_pack("{ s: { s: s, s: s } }", "description", "spd",
|
|
d.generateSdp().c_str(), "type",
|
|
d.typeString().c_str());
|
|
},
|
|
[](rtc::Candidate const &c) {
|
|
return json_pack("{ s: { s: s, s: s } }", "candidate", "spd",
|
|
c.candidate().c_str(), "mid", c.mid().c_str());
|
|
},
|
|
[](auto &other) { return (json_t *){nullptr}; }},
|
|
message);
|
|
}
|
|
|
|
std::string SignalingMessage::toString() const {
|
|
return std::visit(
|
|
villas::utils::overloaded{
|
|
[](RelayMessage const &r) { return fmt::format("type=relay"); },
|
|
[](ControlMessage const &c) {
|
|
return fmt::format("type=control, control={}",
|
|
json_dumps(c.toJson(), 0));
|
|
},
|
|
[](SignalList const &s) { return fmt::format("type=signal"); },
|
|
[](rtc::Description const &d) {
|
|
return fmt::format("type=description, type={}, spd=\n{}",
|
|
d.typeString(), d.generateSdp());
|
|
},
|
|
[](rtc::Candidate const &c) {
|
|
return fmt::format("type=candidate, mid={}, spd=\n{}",
|
|
c.candidate(), c.mid());
|
|
},
|
|
[](auto other) { return fmt::format("invalid signaling message"); }},
|
|
message);
|
|
}
|
|
|
|
SignalingMessage SignalingMessage::fromJson(json_t *json) {
|
|
auto self = SignalingMessage{std::monostate()};
|
|
|
|
// Relay message
|
|
json_t *rlys = nullptr;
|
|
// Signal message
|
|
json_t *sigs = nullptr;
|
|
// Control message
|
|
json_t *ctrl = nullptr;
|
|
// Candidate message
|
|
const char *cand = nullptr;
|
|
const char *mid = nullptr;
|
|
// Description message
|
|
const char *desc = nullptr;
|
|
const char *typ = nullptr;
|
|
|
|
int ret = json_unpack(
|
|
json, "{ s?: o, s?: o, s?: o, s?: { s: s, s: s }, s?: { s: s, s: s } }",
|
|
"servers", &rlys, "signals", &sigs, "control", &ctrl, "candidate", "spd",
|
|
&cand, "mid", &mid, "description", "spd", &desc, "type", &typ);
|
|
|
|
// Exactly 1 field may be specified
|
|
const void *fields[] = {ctrl, cand, desc};
|
|
if (ret || std::count(std::begin(fields), std::end(fields), nullptr) <
|
|
std::make_signed_t<size_t>(std::size(fields)) - 1)
|
|
throw RuntimeError("Failed to decode signaling message");
|
|
|
|
if (rlys) {
|
|
self.message.emplace<RelayMessage>(rlys);
|
|
} else if (sigs) {
|
|
self.message.emplace<SignalList>(sigs);
|
|
} else if (ctrl) {
|
|
self.message.emplace<ControlMessage>(ctrl);
|
|
} else if (cand) {
|
|
self.message.emplace<rtc::Candidate>(cand, mid);
|
|
} else if (desc) {
|
|
self.message.emplace<rtc::Description>(desc, typ);
|
|
}
|
|
|
|
return self;
|
|
}
|