/** WebRTC signaling messages. * * @author Steffen Vogel * @author Philipp Jungkamp * @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC * @copyright 2023, OPAL-RT Germany GmbH * @license Apache 2.0 *********************************************************************************/ #include #include #include #include #include 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 *server_json; size_t i; json_array_foreach(json, i, server_json) { ret = json_unpack(server_json, "{ 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(std::size(fields)) - 1) throw RuntimeError("Failed to decode signaling message"); if (rlys) { self.message.emplace(rlys); } else if (sigs) { self.message.emplace(sigs); } else if (ctrl) { self.message.emplace(ctrl); } else if (cand) { self.message.emplace(cand, mid); } else if (desc) { self.message.emplace(desc, typ); } return self; }