From a3c1d504784f1b5fbda1b32a8ab2afb1bfd84940 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 18 Aug 2021 20:10:11 +1200 Subject: [PATCH] WebRTC signaling with assignment clients via domain server --- assignment-client/src/AssignmentClient.cpp | 54 ++++++++++++++++++++ assignment-client/src/AssignmentClient.h | 19 +++++-- domain-server/src/DomainServer.cpp | 33 ++++++++++++ domain-server/src/DomainServer.h | 6 +++ libraries/networking/src/Node.cpp | 19 +++++++ libraries/networking/src/NodeType.h | 1 + libraries/networking/src/udt/PacketHeaders.h | 3 +- 7 files changed, 129 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index d1c3efc475..cfe940133f 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -14,6 +14,7 @@ #include +#include #include #include #include @@ -125,6 +126,18 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri PacketReceiver::makeUnsourcedListenerReference(this, &AssignmentClient::handleCreateAssignmentPacket)); packetReceiver.registerListener(PacketType::StopNode, PacketReceiver::makeUnsourcedListenerReference(this, &AssignmentClient::handleStopNodePacket)); + +#if defined(WEBRTC_DATA_CHANNELS) + auto webrtcSocket = nodeList->getWebRTCSocket(); + + // Route inbound WebRTC signaling messages from the Domain Server. + packetReceiver.registerListener(PacketType::WebRTCSignaling, + PacketReceiver::makeUnsourcedListenerReference(this, &AssignmentClient::handleWebRTCSignalingPacket)); + connect(this, &AssignmentClient::webrtcSignalingMessageFromUserClient, webrtcSocket, &WebRTCSocket::onSignalingMessage); + + // Route outbound WebRTC signaling messages via the Domain Server to the user client. + connect(webrtcSocket, &WebRTCSocket::sendSignalingMessage, this, &AssignmentClient::sendSignalingMessageToUserClient); +#endif } void AssignmentClient::stopAssignmentClient() { @@ -333,3 +346,44 @@ void AssignmentClient::assignmentCompleted() { _isAssigned = false; } + +#if defined(WEBRTC_DATA_CHANNELS) + +void AssignmentClient::handleWebRTCSignalingPacket(QSharedPointer message) { + auto messageString = message->readString(); + auto json = QJsonDocument::fromJson(messageString.toUtf8()).object(); + if (json.keys().contains("echo")) { + // Echo message back to sender. + + if (!json.keys().contains("to") || !json.keys().contains("from")) { + return; + } + + // Swap to/from. + auto to = json.value("to"); + json.insert("to", json.value("from")); + json.insert("from", to); + + // Send back to sender via the Domain Server. + auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true); + packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact)); + auto nodeList = DependencyManager::get(); + auto domainServerAddress = nodeList->getDomainHandler().getSockAddr(); + nodeList->sendPacketList(std::move(packetList), domainServerAddress); + + } else { + // WebRTC signaling message. + emit webrtcSignalingMessageFromUserClient(json); + } +} + +// Sends a signaling message from the assignment client to the user client via the Domain Server. +void AssignmentClient::sendSignalingMessageToUserClient(const QJsonObject& json) { + auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true); + packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact)); + auto nodeList = DependencyManager::get(); + auto domainServerAddress = nodeList->getDomainHandler().getSockAddr(); + nodeList->sendPacketList(std::move(packetList), domainServerAddress); +} + +#endif diff --git a/assignment-client/src/AssignmentClient.h b/assignment-client/src/AssignmentClient.h index c70baf11fd..58e0e8cda1 100644 --- a/assignment-client/src/AssignmentClient.h +++ b/assignment-client/src/AssignmentClient.h @@ -16,6 +16,8 @@ #include #include +#include + #include "ThreadedAssignment.h" class QSharedMemory; @@ -29,19 +31,26 @@ public: quint16 assignmentMonitorPort); ~AssignmentClient(); +public slots: + void aboutToQuit(); + private slots: void sendAssignmentRequest(); void assignmentCompleted(); void handleAuthenticationRequest(); void sendStatusPacketToACM(); void stopAssignmentClient(); - -public slots: - void aboutToQuit(); - -private slots: void handleCreateAssignmentPacket(QSharedPointer message); void handleStopNodePacket(QSharedPointer message); +#if defined(WEBRTC_DATA_CHANNELS) + void handleWebRTCSignalingPacket(QSharedPointer message); + void sendSignalingMessageToUserClient(const QJsonObject& json); +#endif + +signals: +#if defined(WEBRTC_DATA_CHANNELS) + void webrtcSignalingMessageFromUserClient(const QJsonObject& json); +#endif private: void setUpStatusToMonitor(); diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 341c658ce7..185250e809 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -857,6 +857,7 @@ void DomainServer::setupNodeListAndAssignments() { #if defined(WEBRTC_DATA_CHANNELS) +// Sets up the WebRTC signaling server that's hosted by the domain server. void DomainServer::setUpWebRTCSignalingServer() { // Bind the WebRTC signaling server's WebSocket to its port. bool isBound = _webrtcSignalingServer.bind(QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT); @@ -875,14 +876,46 @@ void DomainServer::setUpWebRTCSignalingServer() { auto webrtcSocket = limitedNodeList->getWebRTCSocket(); connect(this, &DomainServer::webrtcSignalingMessageForDomainServer, webrtcSocket, &WebRTCSocket::onSignalingMessage); connect(webrtcSocket, &WebRTCSocket::sendSignalingMessage, &_webrtcSignalingServer, &WebRTCSignalingServer::sendMessage); + + // Forward signaling messages received from assignment clients to user client. + PacketReceiver& packetReceiver = limitedNodeList->getPacketReceiver(); + packetReceiver.registerListener(PacketType::WebRTCSignaling, + PacketReceiver::makeUnsourcedListenerReference(this, + &DomainServer::forwardAssignmentClientSignalingMessageToUserClient)); + connect(this, &DomainServer::webrtcSignalingMessageForUserClient, + &_webrtcSignalingServer, &WebRTCSignalingServer::sendMessage); } +// Routes an inbound WebRTC signaling message received from a client app to the appropriate recipient. void DomainServer::routeWebRTCSignalingMessage(const QJsonObject& json) { if (json.value("to").toString() == NodeType::DomainServer) { emit webrtcSignalingMessageForDomainServer(json); + } else { + sendWebRTCSignalingMessageToAssignmentClient(json); } } +// Sends a WebRTC signaling message to the target AC contained in the message. +void DomainServer::sendWebRTCSignalingMessageToAssignmentClient(const QJsonObject& json) { + NodeType_t destinationNodeType = NodeType::fromChar(json.value("to").toString().at(0)); + auto limitedNodeList = DependencyManager::get(); + auto destinationNode = limitedNodeList->soloNodeOfType(destinationNodeType); + if (!destinationNode) { + return; + } + // Use an NLPacketList because the signaling message is not necessarily small. + auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true); + packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact)); + limitedNodeList->sendPacketList(std::move(packetList), *destinationNode); +} + +// Forwards a WebRTC signaling message received from an assignment client to the relevant user client. +void DomainServer::forwardAssignmentClientSignalingMessageToUserClient(QSharedPointer message) { + auto messageString = message->readString(); + auto json = QJsonDocument::fromJson(messageString.toUtf8()).object(); + emit webrtcSignalingMessageForUserClient(json); +} + #endif diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 3f509b232d..53f2aec85b 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -150,6 +150,10 @@ private slots: void tokenGrantFinished(); void profileRequestFinished(); +#if defined(WEBRTC_DATA_CHANNELS) + void forwardAssignmentClientSignalingMessageToUserClient(QSharedPointer message); +#endif + void aboutToQuit(); signals: @@ -159,6 +163,7 @@ signals: #if defined(WEBRTC_DATA_CHANNELS) void webrtcSignalingMessageForDomainServer(const QJsonObject& json); + void webrtcSignalingMessageForUserClient(const QJsonObject& json); #endif @@ -245,6 +250,7 @@ private: #if defined(WEBRTC_DATA_CHANNELS) void setUpWebRTCSignalingServer(); void routeWebRTCSignalingMessage(const QJsonObject& json); + void sendWebRTCSignalingMessageToAssignmentClient(const QJsonObject& json); #endif QString operationToString(const QNetworkAccessManager::Operation &op); diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index aa13dd6565..a90da4a929 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -46,6 +46,22 @@ static const QHash TYPE_NAME_HASH { { NodeType::Unassigned, "Unassigned" } }; +static const QHash TYPE_CHAR_HASH { + { NodeType::DomainServer, "D" }, + { NodeType::EntityServer, "o" }, + { NodeType::Agent, "I" }, + { NodeType::AudioMixer, "M" }, + { NodeType::AvatarMixer, "W" }, + { NodeType::AssetServer, "A" }, + { NodeType::MessagesMixer, "m" }, + { NodeType::EntityScriptServer, "S" }, + { NodeType::UpstreamAudioMixer, "B" }, + { NodeType::UpstreamAvatarMixer, "C" }, + { NodeType::DownstreamAudioMixer, "a" }, + { NodeType::DownstreamAvatarMixer, "w" }, + { NodeType::Unassigned, QChar(1) } +}; + const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { const auto matchedTypeName = TYPE_NAME_HASH.find(nodeType); return matchedTypeName != TYPE_NAME_HASH.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME; @@ -85,6 +101,9 @@ NodeType_t NodeType::fromString(QString type) { return TYPE_NAME_HASH.key(type, NodeType::Unassigned); } +NodeType_t NodeType::fromChar(QChar type) { + return TYPE_CHAR_HASH.key(type, NodeType::Unassigned); +} Node::Node(const QUuid& uuid, NodeType_t type, const SockAddr& publicSocket, const SockAddr& localSocket, QObject* parent) : diff --git a/libraries/networking/src/NodeType.h b/libraries/networking/src/NodeType.h index 8539ce8fb3..55754888c4 100644 --- a/libraries/networking/src/NodeType.h +++ b/libraries/networking/src/NodeType.h @@ -43,6 +43,7 @@ namespace NodeType { NodeType_t downstreamType(NodeType_t primaryType); NodeType_t fromString(QString type); + NodeType_t fromChar(QChar type); } typedef QSet NodeSet; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index d57fa9f663..6fc1b6c157 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -139,6 +139,7 @@ public: BulkAvatarTraitsAck, StopInjector, AvatarZonePresence, + WebRTCSignaling, NUM_PACKET_TYPE }; @@ -190,7 +191,7 @@ public: << PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho << PacketTypeEnum::Value::ReplicatedInjectAudio << PacketTypeEnum::Value::ReplicatedSilentAudioFrame << PacketTypeEnum::Value::ReplicatedAvatarIdentity << PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData - << PacketTypeEnum::Value::AvatarZonePresence; + << PacketTypeEnum::Value::AvatarZonePresence << PacketTypeEnum::Value::WebRTCSignaling; return NON_SOURCED_PACKETS; }