WebRTC signaling with assignment clients via domain server

This commit is contained in:
David Rowe 2021-08-18 20:10:11 +12:00
parent 9b2c773805
commit a3c1d50478
7 changed files with 129 additions and 6 deletions

View file

@ -14,6 +14,7 @@
#include <assert.h>
#include <QJsonDocument>
#include <QProcess>
#include <QSharedMemory>
#include <QThread>
@ -125,6 +126,18 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleCreateAssignmentPacket));
packetReceiver.registerListener(PacketType::StopNode,
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(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<AssignmentClient>(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<ReceivedMessage> 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<NodeList>();
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<NodeList>();
auto domainServerAddress = nodeList->getDomainHandler().getSockAddr();
nodeList->sendPacketList(std::move(packetList), domainServerAddress);
}
#endif

View file

@ -16,6 +16,8 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QPointer>
#include <shared/WebRTC.h>
#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<ReceivedMessage> message);
void handleStopNodePacket(QSharedPointer<ReceivedMessage> message);
#if defined(WEBRTC_DATA_CHANNELS)
void handleWebRTCSignalingPacket(QSharedPointer<ReceivedMessage> message);
void sendSignalingMessageToUserClient(const QJsonObject& json);
#endif
signals:
#if defined(WEBRTC_DATA_CHANNELS)
void webrtcSignalingMessageFromUserClient(const QJsonObject& json);
#endif
private:
void setUpStatusToMonitor();

View file

@ -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<DomainServer>(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<LimitedNodeList>();
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<ReceivedMessage> message) {
auto messageString = message->readString();
auto json = QJsonDocument::fromJson(messageString.toUtf8()).object();
emit webrtcSignalingMessageForUserClient(json);
}
#endif

View file

@ -150,6 +150,10 @@ private slots:
void tokenGrantFinished();
void profileRequestFinished();
#if defined(WEBRTC_DATA_CHANNELS)
void forwardAssignmentClientSignalingMessageToUserClient(QSharedPointer<ReceivedMessage> 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);

View file

@ -46,6 +46,22 @@ static const QHash<NodeType_t, QString> TYPE_NAME_HASH {
{ NodeType::Unassigned, "Unassigned" }
};
static const QHash<NodeType_t, QString> 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) :

View file

@ -43,6 +43,7 @@ namespace NodeType {
NodeType_t downstreamType(NodeType_t primaryType);
NodeType_t fromString(QString type);
NodeType_t fromChar(QChar type);
}
typedef QSet<NodeType_t> NodeSet;

View file

@ -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;
}