WebRTCSignalingServer and WebRTCDataChannels improvements

This commit is contained in:
David Rowe 2021-06-26 20:07:26 +12:00
parent c9d4fe8558
commit 5b937a1580
4 changed files with 98 additions and 61 deletions

View file

@ -24,6 +24,8 @@ const std::string ICE_SERVER_URI = "stun://ice.vircadia.com:7337";
#define WEBRTC_DEBUG #define WEBRTC_DEBUG
using namespace webrtc;
void WDCSetSessionDescriptionObserver::OnSuccess() { void WDCSetSessionDescriptionObserver::OnSuccess() {
#ifdef WEBRTC_DEBUG #ifdef WEBRTC_DEBUG
@ -308,6 +310,13 @@ void WDCConnection::onDataChannelMessageReceived(const DataBuffer& buffer) {
_parent->emitDataMessage(_dataChannelID, byteArray); _parent->emitDataMessage(_dataChannelID, byteArray);
} }
qint64 WDCConnection::getBufferedAmount() const {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WDCConnection::getBufferedAmount()";
#endif
return _dataChannel->buffered_amount();
}
bool WDCConnection::sendDataMessage(const DataBuffer& buffer) { bool WDCConnection::sendDataMessage(const DataBuffer& buffer) {
#ifdef WEBRTC_DEBUG #ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WDCConnection::sendDataMessage()"; qCDebug(networking_webrtc) << "WDCConnection::sendDataMessage()";
@ -321,12 +330,13 @@ bool WDCConnection::sendDataMessage(const DataBuffer& buffer) {
} }
WebRTCDataChannels::WebRTCDataChannels(NodeType_t nodeType, QObject* parent) : WebRTCDataChannels::WebRTCDataChannels(QObject* parent, NodeType_t nodeType) :
_nodeType(nodeType), QObject(parent),
_parent(parent) _parent(parent),
_nodeType(nodeType)
{ {
#ifdef WEBRTC_DEBUG #ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()"; qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()" << nodeType << NodeType::getNodeTypeName(nodeType);
#endif #endif
// Create a peer connection factory. // Create a peer connection factory.
@ -354,14 +364,7 @@ WebRTCDataChannels::~WebRTCDataChannels() {
#ifdef WEBRTC_DEBUG #ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::~WebRTCDataChannels()"; qCDebug(networking_webrtc) << "WebRTCDataChannels::~WebRTCDataChannels()";
#endif #endif
QHashIterator<quint16, WDCConnection*> i(_connectionsByDataChannel); reset();
while (i.hasNext()) {
i.next();
delete i.value();
}
_connectionsByWebSocket.clear();
_connectionsByDataChannel.clear();
_peerConnectionFactory = nullptr; _peerConnectionFactory = nullptr;
_rtcSignalingThread->Stop(); _rtcSignalingThread->Stop();
_rtcSignalingThread = nullptr; _rtcSignalingThread = nullptr;
@ -371,6 +374,16 @@ WebRTCDataChannels::~WebRTCDataChannels() {
_rtcNetworkThread = nullptr; _rtcNetworkThread = nullptr;
} }
void WebRTCDataChannels::reset() {
QHashIterator<quint16, WDCConnection*> i(_connectionsByDataChannel);
while (i.hasNext()) {
i.next();
delete i.value();
}
_connectionsByWebSocket.clear();
_connectionsByDataChannel.clear();
}
quint16 WebRTCDataChannels::getNewDataChannelID() { quint16 WebRTCDataChannels::getNewDataChannelID() {
static const int QUINT16_LIMIT = std::numeric_limits<uint16_t>::max() + 1; static const int QUINT16_LIMIT = std::numeric_limits<uint16_t>::max() + 1;
_lastDataChannelID = std::max((_lastDataChannelID + 1) % QUINT16_LIMIT, 1); _lastDataChannelID = std::max((_lastDataChannelID + 1) % QUINT16_LIMIT, 1);
@ -448,7 +461,7 @@ void WebRTCDataChannels::sendSignalingMessage(const QJsonObject& message) {
void WebRTCDataChannels::emitDataMessage(int dataChannelID, const QByteArray& byteArray) { void WebRTCDataChannels::emitDataMessage(int dataChannelID, const QByteArray& byteArray) {
#ifdef WEBRTC_DEBUG #ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID; qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID << byteArray;
#endif #endif
emit dataMessage(dataChannelID, byteArray); emit dataMessage(dataChannelID, byteArray);
} }
@ -469,6 +482,14 @@ bool WebRTCDataChannels::sendDataMessage(int dataChannelID, const QByteArray& by
return connection->sendDataMessage(buffer); return connection->sendDataMessage(buffer);
} }
/// @brief Gets the number of bytes waiting to be written on a data channel.
/// @param port The data channel ID.
/// @return The number of bytes waiting to be written on the data channel.
qint64 WebRTCDataChannels::getBufferedAmount(int dataChannelID) const {
auto connection = _connectionsByDataChannel.value(dataChannelID);
return connection->getBufferedAmount();
}
rtc::scoped_refptr<PeerConnectionInterface> WebRTCDataChannels::createPeerConnection( rtc::scoped_refptr<PeerConnectionInterface> WebRTCDataChannels::createPeerConnection(
const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver) { const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver) {
#ifdef WEBRTC_DEBUG #ifdef WEBRTC_DEBUG

View file

@ -23,8 +23,6 @@
#include "../NodeType.h" #include "../NodeType.h"
using namespace webrtc;
class WebRTCDataChannels; class WebRTCDataChannels;
class WDCConnection; class WDCConnection;
@ -33,7 +31,7 @@ class WDCConnection;
/// @{ /// @{
/// @brief A WebRTC session description observer. /// @brief A WebRTC session description observer.
class WDCSetSessionDescriptionObserver : public SetSessionDescriptionObserver { class WDCSetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
public: public:
/// @brief The call to SetLocalDescription or SetRemoteDescription succeeded. /// @brief The call to SetLocalDescription or SetRemoteDescription succeeded.
@ -41,22 +39,22 @@ public:
/// @brief The call to SetLocalDescription or SetRemoteDescription failed. /// @brief The call to SetLocalDescription or SetRemoteDescription failed.
/// @param error Error information. /// @param error Error information.
void OnFailure(RTCError error) override; void OnFailure(webrtc::RTCError error) override;
}; };
/// @brief A WebRTC create session description observer. /// @brief A WebRTC create session description observer.
class WDCCreateSessionDescriptionObserver : public CreateSessionDescriptionObserver { class WDCCreateSessionDescriptionObserver : public webrtc::CreateSessionDescriptionObserver {
public: public:
WDCCreateSessionDescriptionObserver(WDCConnection* parent); WDCCreateSessionDescriptionObserver(WDCConnection* parent);
/// @brief The call to CreateAnswer succeeded. /// @brief The call to CreateAnswer succeeded.
/// @param The session description. /// @param The session description.
void OnSuccess(SessionDescriptionInterface* desc) override; void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
//@ @brief The call to CreateAnswer failed. //@ @brief The call to CreateAnswer failed.
/// @param error Error information. /// @param error Error information.
void OnFailure(RTCError error) override; void OnFailure(webrtc::RTCError error) override;
private: private:
WDCConnection* _parent; WDCConnection* _parent;
@ -64,32 +62,32 @@ private:
/// @brief A WebRTC peer connection observer. /// @brief A WebRTC peer connection observer.
class WDCPeerConnectionObserver : public PeerConnectionObserver { class WDCPeerConnectionObserver : public webrtc::PeerConnectionObserver {
public: public:
WDCPeerConnectionObserver(WDCConnection* parent); WDCPeerConnectionObserver(WDCConnection* parent);
/// @brief Called when the SignalingState changes. /// @brief Called when the SignalingState changes.
/// @param newState The new signaling state. /// @param newState The new signaling state.
void OnSignalingChange(PeerConnectionInterface::SignalingState newState) override; void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState newState) override;
/// @brief Called when renegotiation is needed. For example, an ICE restart has begun. /// @brief Called when renegotiation is needed. For example, an ICE restart has begun.
void OnRenegotiationNeeded() override; void OnRenegotiationNeeded() override;
/// @brief Called when the ICE gather state changes. /// @brief Called when the ICE gather state changes.
/// @param newState The new ICE gathering state. /// @param newState The new ICE gathering state.
void OnIceGatheringChange(PeerConnectionInterface::IceGatheringState newState) override; void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState newState) override;
/// @brief Called when a new ICE candidate has been gathered. /// @brief Called when a new ICE candidate has been gathered.
/// @param candidate The new ICE candidate. /// @param candidate The new ICE candidate.
void OnIceCandidate(const IceCandidateInterface* candidate) override; void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override;
/// @brief Called when a remote peer opens a data channel. /// @brief Called when a remote peer opens a data channel.
/// @param dataChannel The data channel. /// @param dataChannel The data channel.
void OnDataChannel(rtc::scoped_refptr<DataChannelInterface> dataChannel) override; void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel) override;
/// @brief Called when the peer connection state changes. /// @brief Called when the peer connection state changes.
/// @param newState The new peer connection state. /// @param newState The new peer connection state.
void OnConnectionChange(PeerConnectionInterface::PeerConnectionState newState) override; void OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState newState) override;
private: private:
WDCConnection* _parent; WDCConnection* _parent;
@ -97,7 +95,7 @@ private:
/// @brief A WebRTC data channel observer. /// @brief A WebRTC data channel observer.
class WDCDataChannelObserver : public DataChannelObserver { class WDCDataChannelObserver : public webrtc::DataChannelObserver {
public: public:
WDCDataChannelObserver(WDCConnection* parent); WDCDataChannelObserver(WDCConnection* parent);
@ -106,7 +104,7 @@ public:
/// @brief A data channel message was received. /// @brief A data channel message was received.
/// @param The message received. /// @param The message received.
void OnMessage(const DataBuffer& buffer) override; void OnMessage(const webrtc::DataBuffer& buffer) override;
private: private:
WDCConnection* _parent; WDCConnection* _parent;
@ -125,11 +123,11 @@ public:
/// @brief Gets the WebSocket ID. /// @brief Gets the WebSocket ID.
/// @return The ID of the WebSocket. /// @return The ID of the WebSocket.
quint16 getWebSocketID() { return _webSocketID; } quint16 getWebSocketID() const { return _webSocketID; }
/// @brief Gets the WebRTC data channel ID. /// @brief Gets the WebRTC data channel ID.
/// @return The WebRTC data channel ID. `-1` if not open yet. /// @return The WebRTC data channel ID. `-1` if not open yet.
int getDataChannelID() { return _dataChannelID; } int getDataChannelID() const { return _dataChannelID; }
/// @brief Sets the remote session description received from the remote client via the signaling channel. /// @brief Sets the remote session description received from the remote client via the signaling channel.
@ -141,11 +139,11 @@ public:
/// @brief Sends an answer to the remote client via the signaling channel. /// @brief Sends an answer to the remote client via the signaling channel.
/// @param description The answer. /// @param description The answer.
void sendAnswer(SessionDescriptionInterface* description); void sendAnswer(webrtc::SessionDescriptionInterface* description);
/// @brief Sets the local session description on the WebRTC data channel being connected. /// @brief Sets the local session description on the WebRTC data channel being connected.
/// @param description The local session description. /// @param description The local session description.
void setLocalDescription(SessionDescriptionInterface* description); void setLocalDescription(webrtc::SessionDescriptionInterface* description);
/// @brief Adds an ICE candidate received from the remote client via the signaling channel. /// @brief Adds an ICE candidate received from the remote client via the signaling channel.
/// @param data The ICE candidate. /// @param data The ICE candidate.
@ -153,11 +151,11 @@ public:
/// @brief Sends an ICE candidate to the remote vlient via the signaling channel. /// @brief Sends an ICE candidate to the remote vlient via the signaling channel.
/// @param candidate The ICE candidate. /// @param candidate The ICE candidate.
void sendIceCandidate(const IceCandidateInterface* candidate); void sendIceCandidate(const webrtc::IceCandidateInterface* candidate);
/// @brief Handles the WebRTC data channel being opened. /// @brief Handles the WebRTC data channel being opened.
/// @param dataChannel The WebRTC data channel. /// @param dataChannel The WebRTC data channel.
void onDataChannelOpened(rtc::scoped_refptr<DataChannelInterface> dataChannel); void onDataChannelOpened(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel);
/// @brief Handles a change in the state of the WebRTC data channel. /// @brief Handles a change in the state of the WebRTC data channel.
void onDataChannelStateChanged(); void onDataChannelStateChanged();
@ -165,12 +163,17 @@ public:
/// @brief Handles a message being received on the WebRTC data channel. /// @brief Handles a message being received on the WebRTC data channel.
/// @param buffer The message received. /// @param buffer The message received.
void onDataChannelMessageReceived(const DataBuffer& buffer); void onDataChannelMessageReceived(const webrtc::DataBuffer& buffer);
/// @brief Gets the number of bytes waiting to be sent on the WebRTC data channel.
/// @return The number of bytes waiting to be sent on the WebRTC data channel.
qint64 getBufferedAmount() const;
/// @brief Sends a message on the WebRTC data channel. /// @brief Sends a message on the WebRTC data channel.
/// @param buffer The message to send. /// @param buffer The message to send.
/// @return `true` if the message was sent, otherwise `false`. /// @return `true` if the message was sent, otherwise `false`.
bool sendDataMessage(const DataBuffer& buffer); bool sendDataMessage(const webrtc::DataBuffer& buffer);
private: private:
WebRTCDataChannels* _parent; WebRTCDataChannels* _parent;
@ -181,10 +184,10 @@ private:
rtc::scoped_refptr<WDCCreateSessionDescriptionObserver> _createSessionDescriptionObserver { nullptr }; rtc::scoped_refptr<WDCCreateSessionDescriptionObserver> _createSessionDescriptionObserver { nullptr };
std::shared_ptr<WDCDataChannelObserver> _dataChannelObserver { nullptr }; std::shared_ptr<WDCDataChannelObserver> _dataChannelObserver { nullptr };
rtc::scoped_refptr<DataChannelInterface> _dataChannel { nullptr }; rtc::scoped_refptr<webrtc::DataChannelInterface> _dataChannel { nullptr };
std::shared_ptr<WDCPeerConnectionObserver> _peerConnectionObserver { nullptr }; std::shared_ptr<WDCPeerConnectionObserver> _peerConnectionObserver { nullptr };
rtc::scoped_refptr<PeerConnectionInterface> _peerConnection { nullptr }; rtc::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection { nullptr };
}; };
@ -206,20 +209,25 @@ class WebRTCDataChannels : public QObject {
public: public:
/// @brief Constructs a new WebRTCDataChannels object. /// @brief Constructs a new WebRTCDataChannels object.
/// @param nodeType The type of node that the WebRTCDataChannels object is being used in.
/// @param parent The parent Qt object. /// @param parent The parent Qt object.
WebRTCDataChannels(NodeType_t nodeType, QObject* parent); /// @param nodeType The type of node that the WebRTCDataChannels object is being used in.
WebRTCDataChannels(QObject* parent, NodeType_t nodeType);
/// @brief Destroys a WebRTCDataChannels object. /// @brief Destroys a WebRTCDataChannels object.
~WebRTCDataChannels(); ~WebRTCDataChannels();
/// @brief Returns the type of node that the WebRTCDataChannels object is being used in. /// @brief Gets the type of node that the WebRTCDataChannels object is being used in.
/// @return The type of node. /// @return The type of node.
NodeType_t getNodeType() { NodeType_t getNodeType() {
return _nodeType; return _nodeType;
} }
/// @brief Immediately closes all connections and resets the socket.
void reset();
/// @brief Get a new data channel ID to uniquely identify a WDCConnection. /// @brief Get a new data channel ID to uniquely identify a WDCConnection.
/// @details This ID is assigned by WebRTCDataChannels; it is <em>not</em> the WebRTC data channel ID because that is only
/// unique within a peer connection.
/// @return A new data channel ID. /// @return A new data channel ID.
quint16 getNewDataChannelID(); quint16 getNewDataChannelID();
@ -248,10 +256,15 @@ public:
/// @return `true` if the data message was sent, otherwise `false`. /// @return `true` if the data message was sent, otherwise `false`.
bool sendDataMessage(int dataChannelID, const QByteArray& message); bool sendDataMessage(int dataChannelID, const QByteArray& message);
/// @brief Gets the number of bytes waiting to be sent on a data channel.
/// @param dataChannelID The data channel ID.
/// @return The number of bytes waiting to be sent on the data channel.
qint64 getBufferedAmount(int dataChannelID) const;
/// @brief Creates a new WebRTC peer connection for connecting to an Interface client. /// @brief Creates a new WebRTC peer connection for connecting to an Interface client.
/// @param peerConnectionObserver An observer to monitor the WebRTC peer connection. /// @param peerConnectionObserver An observer to monitor the WebRTC peer connection.
/// @return The new WebRTC peer connection. /// @return The new WebRTC peer connection.
rtc::scoped_refptr<PeerConnectionInterface> createPeerConnection( rtc::scoped_refptr<webrtc::PeerConnectionInterface> createPeerConnection(
const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver); const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver);
public slots: public slots:
@ -283,9 +296,9 @@ private:
std::unique_ptr<rtc::Thread> _rtcWorkerThread { nullptr }; std::unique_ptr<rtc::Thread> _rtcWorkerThread { nullptr };
std::unique_ptr<rtc::Thread> _rtcSignalingThread { nullptr }; std::unique_ptr<rtc::Thread> _rtcSignalingThread { nullptr };
rtc::scoped_refptr<PeerConnectionFactoryInterface> _peerConnectionFactory { nullptr }; rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> _peerConnectionFactory { nullptr };
quint16 _lastDataChannelID { 0 }; quint16 _lastDataChannelID { 0 }; // First data channel ID is 1.
QHash<quint16, WDCConnection*> _connectionsByWebSocket; QHash<quint16, WDCConnection*> _connectionsByWebSocket;
QHash<quint16, WDCConnection*> _connectionsByDataChannel; QHash<quint16, WDCConnection*> _connectionsByDataChannel;

View file

@ -19,33 +19,33 @@
const int WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS = 30000; const int WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS = 30000;
WebRTCSignalingServer::WebRTCSignalingServer(const QHostAddress& address, quint16 port, QObject* parent) : WebRTCSignalingServer::WebRTCSignalingServer(QObject* parent) :
QObject(parent), QObject(parent),
_address(address),
_port(port),
_webSocketServer(new QWebSocketServer(QStringLiteral("WebRTC Signaling Server"), QWebSocketServer::NonSecureMode, this)) _webSocketServer(new QWebSocketServer(QStringLiteral("WebRTC Signaling Server"), QWebSocketServer::NonSecureMode, this))
{ {
connect(_webSocketServer, &QWebSocketServer::newConnection, this, &WebRTCSignalingServer::newWebSocketConnection); connect(_webSocketServer, &QWebSocketServer::newConnection, this, &WebRTCSignalingServer::newWebSocketConnection);
bindSocket();
// Automatically recover from network interruptions. // Automatically recover from network interruptions.
_isWebSocketServerListeningTimer = new QTimer(this); _isWebSocketServerListeningTimer = new QTimer(this);
connect(_isWebSocketServerListeningTimer, &QTimer::timeout, this, &WebRTCSignalingServer::checkWebSocketServerIsListening); connect(_isWebSocketServerListeningTimer, &QTimer::timeout, this, &WebRTCSignalingServer::checkWebSocketServerIsListening);
_isWebSocketServerListeningTimer->start(WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS); _isWebSocketServerListeningTimer->start(WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS);
} }
bool WebRTCSignalingServer::bind(const QHostAddress& address, quint16 port) {
_address = address;
_port = port;
auto success = _webSocketServer->listen(_address, _port);
if (!success) {
qCWarning(networking_webrtc) << "Failed to open WebSocket for WebRTC signaling.";
}
return success;
}
void WebRTCSignalingServer::checkWebSocketServerIsListening() { void WebRTCSignalingServer::checkWebSocketServerIsListening() {
if (!_webSocketServer->isListening()) { if (!_webSocketServer->isListening()) {
qCWarning(networking_webrtc) << "WebSocket on port " << QString::number(_port) << " is no longer listening"; qCWarning(networking_webrtc) << "WebSocket on port " << QString::number(_port) << " is no longer listening";
_webSockets.clear(); _webSockets.clear();
bindSocket(); _webSocketServer->listen(_address, _port);
}
}
void WebRTCSignalingServer::bindSocket() {
if (!_webSocketServer->listen(_address, _port)) {
qCWarning(networking_webrtc) << "Failed to open WebSocket for WebRTC signaling.";
} }
} }

View file

@ -59,10 +59,14 @@ class WebRTCSignalingServer : public QObject {
public: public:
/// @brief Constructs a new WebRTCSignalingServer object. /// @brief Constructs a new WebRTCSignalingServer object.
/// @param address The IP address to use for the WebSocket.
/// @param port The port to use for the WebSocket.
/// @param parent Qt parent object. /// @param parent Qt parent object.
WebRTCSignalingServer(const QHostAddress& address, quint16 port, QObject* parent = nullptr); WebRTCSignalingServer(QObject* parent);
/// @brief Binds the WebRTC signaling server's WebSocket to an address and port.
/// @param address The address to use for the WebSocket.
/// @param port The port to use for the WebSocket.
/// @return <code>true</code> if the WebSocket was successfully bound, <code>false</code> if it wasn't.
bool bind(const QHostAddress& address, quint16 port);
public slots: public slots:
@ -88,11 +92,10 @@ private slots:
private: private:
void checkWebSocketServerIsListening(); void checkWebSocketServerIsListening();
void bindSocket();
QWebSocketServer* _webSocketServer; QWebSocketServer* _webSocketServer;
QHostAddress _address; QHostAddress _address;
const quint16 _port; quint16 _port { 0 };
QHash<quint16, QWebSocket*> _webSockets; // client WebSocket port, client WebSocket object QHash<quint16, QWebSocket*> _webSockets; // client WebSocket port, client WebSocket object