mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 08:14:48 +02:00
Merge pull request #1219 from ctrlaltdavid/dev/webrtc-signaling
WebRTC Signaling Channel
This commit is contained in:
commit
229164c5b9
15 changed files with 268 additions and 21 deletions
|
@ -948,7 +948,7 @@ In an international environment English is the preferred language.
|
|||
|
||||
#### [4.3.2] Use // for all comments, including multi-line comments.
|
||||
|
||||
An exception to this rule applies for jsdoc or Doxygen comments.
|
||||
An exception to this rule applies to JSDoc and Doxygen comments.
|
||||
|
||||
```cpp
|
||||
// Comment spanning
|
||||
|
@ -1008,3 +1008,11 @@ These types of comments are explicitly not allowed. If you need to break up sect
|
|||
//--------------------------------------------------------------------------------
|
||||
```
|
||||
|
||||
#### [4.3.6] Doxygen comments should use "///"
|
||||
|
||||
Use the `///` style of [Doxygen](https://www.doxygen.nl/index.html) comments when documenting public interfaces.
|
||||
|
||||
Some editors can automatically create a Doxygen documentation stub if you type `///` in the line above the item to be
|
||||
documented.
|
||||
|
||||
**Visual Studio:** To configure Visual Studio's Doxygen commenting behavior, search for "Doxygen" in Tools > Options.
|
||||
|
|
|
@ -165,7 +165,11 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
|
|||
DomainServer::DomainServer(int argc, char* argv[]) :
|
||||
QCoreApplication(argc, argv),
|
||||
_gatekeeper(this),
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT, QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
#ifdef WEBRTC_DATA_CHANNEL
|
||||
_webrtcSignalingServer(QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT, this),
|
||||
#endif
|
||||
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT,
|
||||
QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
|
||||
{
|
||||
if (_parentPID != -1) {
|
||||
watchParentProcess(_parentPID);
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <Assignment.h>
|
||||
#include <HTTPSConnection.h>
|
||||
#include <LimitedNodeList.h>
|
||||
#include <shared/WebRTC.h>
|
||||
#include <webrtc/WebRTCSignalingServer.h>
|
||||
|
||||
#include "AssetsBackupHandler.h"
|
||||
#include "DomainGatekeeper.h"
|
||||
|
@ -311,6 +313,10 @@ private:
|
|||
std::unordered_map<int, std::unique_ptr<QTemporaryFile>> _pendingContentFiles;
|
||||
|
||||
QThread _assetClientThread;
|
||||
|
||||
#ifdef WEBRTC_DATA_CHANNEL
|
||||
WebRTCSignalingServer _webrtcSignalingServer;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 1/22/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -363,7 +364,7 @@ AudioClient::AudioClient() {
|
|||
|
||||
configureReverb();
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
configureWebrtc();
|
||||
#endif
|
||||
|
||||
|
@ -1142,7 +1143,7 @@ void AudioClient::setReverbOptions(const AudioEffectOptions* options) {
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
|
||||
static void deinterleaveToFloat(const int16_t* src, float* const* dst, int numFrames, int numChannels) {
|
||||
for (int i = 0; i < numFrames; i++) {
|
||||
|
@ -1261,7 +1262,7 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC
|
|||
}
|
||||
}
|
||||
|
||||
#endif // WEBRTC_ENABLED
|
||||
#endif // WEBRTC_AUDIO
|
||||
|
||||
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
|
||||
|
@ -1462,7 +1463,7 @@ void AudioClient::handleMicAudioInput() {
|
|||
}
|
||||
isClipping = (_timeSinceLastClip >= 0.0f) && (_timeSinceLastClip < 2.0f); // 2 second hold time
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
if (_isAECEnabled) {
|
||||
processWebrtcNearEnd(inputAudioSamples.get(), inputSamplesRequired / _inputFormat.channelCount(),
|
||||
_inputFormat.channelCount(), _inputFormat.sampleRate());
|
||||
|
@ -2420,7 +2421,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) {
|
|||
// limit the audio
|
||||
_audio->_audioLimiter.render(mixBuffer, scratchBuffer, framesPopped);
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
if (_audio->_isAECEnabled) {
|
||||
_audio->processWebrtcFarEnd(scratchBuffer, framesPopped, OUTPUT_CHANNEL_COUNT, _audio->_outputFormat.sampleRate());
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Stephen Birarda on 1/22/13.
|
||||
// Copyright 2013 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -56,6 +57,11 @@
|
|||
#include "AudioFileWav.h"
|
||||
#include "HifiAudioDeviceInfo.h"
|
||||
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
# include <modules/audio_processing/include/audio_processing.h>
|
||||
# include "modules/audio_processing/audio_processing_impl.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable : 4273 )
|
||||
|
@ -450,7 +456,7 @@ private:
|
|||
void updateReverbOptions();
|
||||
void handleLocalEchoAndReverb(QByteArray& inputByteArray);
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
#if defined(WEBRTC_AUDIO)
|
||||
static const int WEBRTC_SAMPLE_RATE_MAX = 96000;
|
||||
static const int WEBRTC_CHANNELS_MAX = 2;
|
||||
static const int WEBRTC_FRAMES_MAX = webrtc::AudioProcessing::kChunkSizeMs * WEBRTC_SAMPLE_RATE_MAX / 1000;
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
set(TARGET_NAME networking)
|
||||
setup_hifi_library(Network)
|
||||
setup_hifi_library(Network WebSockets)
|
||||
link_hifi_libraries(shared platform)
|
||||
|
||||
target_openssl()
|
||||
target_tbb()
|
||||
if (WIN32)
|
||||
# WEBRTC TODO: Add UNIX.
|
||||
target_webrtc()
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
# we need ws2_32.lib on windows, but it's static so we don't bubble it up
|
||||
|
|
|
@ -40,7 +40,15 @@ const unsigned short DEFAULT_DOMAIN_SERVER_PORT =
|
|||
? QProcessEnvironment::systemEnvironment()
|
||||
.value("HIFI_DOMAIN_SERVER_PORT")
|
||||
.toUShort()
|
||||
: 40102;
|
||||
: 40102; // UDP
|
||||
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_WS_PORT =
|
||||
QProcessEnvironment::systemEnvironment()
|
||||
.contains("VIRCADIA_DOMAIN_SERVER_WS_PORT")
|
||||
? QProcessEnvironment::systemEnvironment()
|
||||
.value("VIRCADIA_DOMAIN_SERVER_WS_PORT")
|
||||
.toUShort()
|
||||
: 40102; // TCP
|
||||
|
||||
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT =
|
||||
QProcessEnvironment::systemEnvironment()
|
||||
|
|
|
@ -16,3 +16,4 @@ Q_LOGGING_CATEGORY(networking_ice, "hifi.networking.ice")
|
|||
Q_LOGGING_CATEGORY(resourceLog, "hifi.networking.resource")
|
||||
Q_LOGGING_CATEGORY(asset_client, "hifi.networking.asset_client")
|
||||
Q_LOGGING_CATEGORY(messages_client, "hifi.networking.messages_client")
|
||||
Q_LOGGING_CATEGORY(networking_webrtc, "hifi.networking.webrtc")
|
||||
|
|
|
@ -19,5 +19,6 @@ Q_DECLARE_LOGGING_CATEGORY(networking)
|
|||
Q_DECLARE_LOGGING_CATEGORY(networking_ice)
|
||||
Q_DECLARE_LOGGING_CATEGORY(asset_client)
|
||||
Q_DECLARE_LOGGING_CATEGORY(messages_client)
|
||||
Q_DECLARE_LOGGING_CATEGORY(networking_webrtc)
|
||||
|
||||
#endif // hifi_NetworkLogging_h
|
||||
|
|
|
@ -409,7 +409,6 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
if (domainPacketType == PacketType::DomainConnectRequest) {
|
||||
|
||||
#if (PR_BUILD || DEV_BUILD)
|
||||
// #######
|
||||
if (_shouldSendNewerVersion) {
|
||||
domainPacket->setVersion(versionForPacketType(domainPacketType) + 1);
|
||||
}
|
||||
|
|
96
libraries/networking/src/webrtc/WebRTCSignalingServer.cpp
Normal file
96
libraries/networking/src/webrtc/WebRTCSignalingServer.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// WebRTCSignalingServer.cpp
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Created by David Rowe on 16 May 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#include "WebRTCSignalingServer.h"
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNEL)
|
||||
|
||||
#include <QtCore>
|
||||
#include <QWebSocket>
|
||||
|
||||
#include "../NetworkLogging.h"
|
||||
#include "../NodeType.h"
|
||||
|
||||
|
||||
const int WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS = 30000;
|
||||
|
||||
WebRTCSignalingServer::WebRTCSignalingServer(const QHostAddress& address, quint16 port, QObject* parent) :
|
||||
QObject(parent),
|
||||
_address(address),
|
||||
_port(port),
|
||||
_webSocketServer(new QWebSocketServer(QStringLiteral("WebRTC Signaling Server"), QWebSocketServer::NonSecureMode, this))
|
||||
{
|
||||
connect(_webSocketServer, &QWebSocketServer::newConnection, this, &WebRTCSignalingServer::newWebSocketConnection);
|
||||
|
||||
bindSocket();
|
||||
|
||||
// Automatically recover from network interruptions.
|
||||
_isWebSocketServerListeningTimer = new QTimer(this);
|
||||
connect(_isWebSocketServerListeningTimer, &QTimer::timeout, this, &WebRTCSignalingServer::checkWebSocketServerIsListening);
|
||||
_isWebSocketServerListeningTimer->start(WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS);
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::checkWebSocketServerIsListening() {
|
||||
if (!_webSocketServer->isListening()) {
|
||||
qCWarning(networking_webrtc) << "WebSocket on port " << QString::number(_port) << " is no longer listening";
|
||||
_webSockets.clear();
|
||||
bindSocket();
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::bindSocket() {
|
||||
if (!_webSocketServer->listen(_address, _port)) {
|
||||
qCWarning(networking_webrtc) << "Failed to open WebSocket for WebRTC signaling.";
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::webSocketTextMessageReceived(const QString& message) {
|
||||
auto source = qobject_cast<QWebSocket*>(sender());
|
||||
if (source) {
|
||||
QJsonObject json = QJsonDocument::fromJson(message.toUtf8()).object();
|
||||
// WEBRTC TODO: Move domain server echoing into domain server.
|
||||
if (json.keys().contains("echo") && json.value("to").toString() == QString(QChar(NodeType::DomainServer))) {
|
||||
// Domain server echo request - echo message back to sender.
|
||||
json.remove("to");
|
||||
json.insert("from", QString(QChar(NodeType::DomainServer)));
|
||||
QString echo = QJsonDocument(json).toJson();
|
||||
source->sendTextMessage(echo);
|
||||
} else {
|
||||
// WebRTC message or assignment client echo request. (Send both to target.)
|
||||
json.insert("from", source->peerPort());
|
||||
emit messageReceived(json);
|
||||
}
|
||||
} else {
|
||||
qCWarning(networking_webrtc) << "Failed to find WebSocket for incoming WebRTC signaling message.";
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::sendMessage(const QJsonObject& message) {
|
||||
quint16 destinationPort = message.value("to").toInt();
|
||||
if (_webSockets.contains(destinationPort)) {
|
||||
_webSockets.value(destinationPort)->sendTextMessage(QString(QJsonDocument(message).toJson()));
|
||||
} else {
|
||||
qCWarning(networking_webrtc) << "Failed to find WebSocket for outgoing WebRTC signaling message.";
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::webSocketDisconnected() {
|
||||
auto source = qobject_cast<QWebSocket*>(sender());
|
||||
if (source) {
|
||||
_webSockets.remove(source->peerPort());
|
||||
}
|
||||
}
|
||||
|
||||
void WebRTCSignalingServer::newWebSocketConnection() {
|
||||
auto webSocket = _webSocketServer->nextPendingConnection();
|
||||
connect(webSocket, &QWebSocket::textMessageReceived, this, &WebRTCSignalingServer::webSocketTextMessageReceived);
|
||||
connect(webSocket, &QWebSocket::disconnected, this, &WebRTCSignalingServer::webSocketDisconnected);
|
||||
_webSockets.insert(webSocket->peerPort(), webSocket);
|
||||
}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNEL
|
104
libraries/networking/src/webrtc/WebRTCSignalingServer.h
Normal file
104
libraries/networking/src/webrtc/WebRTCSignalingServer.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// WebRTCSignalingServer.h
|
||||
// libraries/networking/src/webrtc
|
||||
//
|
||||
// Provides a signaling channel for setting up WebRTC connections between the Web app and the domain servers and mixers.
|
||||
//
|
||||
// Created by David Rowe on 16 May 2021.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
|
||||
#ifndef vircadia_SignalingServer_h
|
||||
#define vircadia_SignalingServer_h
|
||||
|
||||
#include <shared/WebRTC.h>
|
||||
|
||||
#if defined(WEBRTC_DATA_CHANNEL)
|
||||
|
||||
#include <QObject>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QWebSocketServer>
|
||||
|
||||
#include "../HifiSockAddr.h"
|
||||
|
||||
/// @brief WebRTC signaling server that Interface clients can use to initiate WebRTC connections to the domain server and
|
||||
/// assignment clients.
|
||||
///
|
||||
/// @details The signaling server is expected to be hosted in the domain server. It provides a WebSocket for Interface clients
|
||||
/// to use in the WebRTC signaling handshake process to establish WebRTC data channel connections to each of the domain server
|
||||
/// and the assignment clients (i.e., separate WebRTC data channels for each but only a single signaling WebSocket). The
|
||||
/// assignment client signaling messages are expected to be relayed - by the domain server - via Vircadia protocol messages on
|
||||
/// the UDP connections between the domain server and assignment clients.
|
||||
///
|
||||
/// Additionally, for debugging purposes, instead of containing a WebRTC payload a signaling message may be an echo request.
|
||||
/// This is bounced back to the client from the WebRTCSignalingServer if the domain server was the target, otherwise it is
|
||||
/// expected to be bounced back upon receipt by the relevant assignment client.
|
||||
///
|
||||
/// The signaling messages are sent and received as JSON objects, with `to` and `from` fields in addition to either the WebRTC
|
||||
/// signaling `data` payload or an `echo` request:
|
||||
///
|
||||
/// | Interface -> Server ||
|
||||
/// | -------- | -----------------------|
|
||||
/// | `to` | NodeType |
|
||||
/// | `from` | WebSocket port number* |
|
||||
/// | [`data`] | WebRTC payload |
|
||||
/// | [`echo`] | Echo request |
|
||||
/// * The `from` field is filled in by the WebRTCSignalingServer.
|
||||
///
|
||||
/// | Server -> Interface ||
|
||||
/// | -------- | --------------------- |
|
||||
/// | `to` | WebSocket port number |
|
||||
/// | `from` | NodeType |
|
||||
/// | [`data`] | WebRTC payload |
|
||||
/// | [`echo`] | Echo request |
|
||||
///
|
||||
class WebRTCSignalingServer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Constructs a new WebRTCSignalingServer.
|
||||
/// @param address The IP address to use for the WebSocket.
|
||||
/// @param port The port to use for the WebSocket.
|
||||
/// @param parent Qt parent object.
|
||||
WebRTCSignalingServer(const QHostAddress& address, quint16 port, QObject* parent = nullptr);
|
||||
|
||||
public slots:
|
||||
|
||||
/// @brief Send a WebRTC signaling channel message to an Interface client.
|
||||
/// @param message The message to send to the Interface client. Includes details of the sender and the destination in
|
||||
/// addition to the WebRTC signaling channel payload.
|
||||
void sendMessage(const QJsonObject& message);
|
||||
|
||||
signals:
|
||||
|
||||
/// @brief A WebRTC signaling channel message was received from an Interface client.
|
||||
/// @param message The message received from the Interface client. Includes details of the sender and the destination in
|
||||
/// addition to the WebRTC signaling channel payload.\n
|
||||
/// Not emitted if the message was an echo request for the domain server.
|
||||
void messageReceived(const QJsonObject& message);
|
||||
|
||||
private slots:
|
||||
|
||||
void newWebSocketConnection();
|
||||
void webSocketTextMessageReceived(const QString& message);
|
||||
void webSocketDisconnected();
|
||||
|
||||
private:
|
||||
|
||||
void checkWebSocketServerIsListening();
|
||||
void bindSocket();
|
||||
|
||||
QWebSocketServer* _webSocketServer;
|
||||
QHostAddress _address;
|
||||
const quint16 _port;
|
||||
|
||||
QHash<quint16, QWebSocket*> _webSockets; // client WebSocket port, client WebSocket object
|
||||
|
||||
QTimer* _isWebSocketServerListeningTimer;
|
||||
};
|
||||
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNEL
|
||||
|
||||
#endif // vircadia_SignalingServer_h
|
|
@ -3,6 +3,7 @@
|
|||
// libraries/shared/src/shared/
|
||||
//
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
// Copyright 2021 Vircadia contributors.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
|
@ -11,26 +12,29 @@
|
|||
#ifndef hifi_WebRTC_h
|
||||
#define hifi_WebRTC_h
|
||||
|
||||
#ifndef QSYSTEMDETECTION_H
|
||||
#include <qsystemdetection.h>
|
||||
#endif
|
||||
|
||||
// WEBRTC_AUDIO: WebRTC audio features, e.g., echo canceling.
|
||||
// WEBRTC_DATA_CHANNEL: WebRTC client-server connections in parallel with UDP.
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_AUDIO 1
|
||||
# define WEBRTC_POSIX 1
|
||||
#elif defined(Q_OS_WIN)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_AUDIO 1
|
||||
# define WEBRTC_DATA_CHANNEL 1
|
||||
# define WEBRTC_WIN 1
|
||||
# define NOMINMAX 1
|
||||
# define WIN32_LEAN_AND_MEAN 1
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
// I don't yet have a working libwebrtc for android
|
||||
// # define WEBRTC_ENABLED 1
|
||||
// # define WEBRTC_AUDIO 1
|
||||
// # define WEBRTC_POSIX 1
|
||||
#elif defined(Q_OS_LINUX)
|
||||
# define WEBRTC_ENABLED 1
|
||||
# define WEBRTC_AUDIO 1
|
||||
# define WEBRTC_POSIX 1
|
||||
#endif
|
||||
|
||||
#if defined(WEBRTC_ENABLED)
|
||||
# include <modules/audio_processing/include/audio_processing.h>
|
||||
# include "modules/audio_processing/audio_processing_impl.h"
|
||||
#endif
|
||||
|
||||
#endif // hifi_WebRTC_h
|
||||
|
|
|
@ -12,6 +12,11 @@ Make a `/build/doxygen/` directory.
|
|||
If you want to run Doxygen from a command prompt, add the Doxygen install's `/bin` directory to your system PATH.
|
||||
|
||||
|
||||
## Documenting
|
||||
|
||||
See section 4.3.6 of the [*Coding Standard*](../../CODING_STANDARD.md).
|
||||
|
||||
|
||||
## Running
|
||||
|
||||
### Using the Doxywizard GUI
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit d2e383cab811018422433fe0d8f224e53e0506cf
|
||||
Subproject commit ac1f13a39c702ee54bf2cda8bc35e5d34f7f0756
|
Loading…
Reference in a new issue