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
using namespace webrtc;
void WDCSetSessionDescriptionObserver::OnSuccess() {
#ifdef WEBRTC_DEBUG
@ -308,6 +310,13 @@ void WDCConnection::onDataChannelMessageReceived(const DataBuffer& buffer) {
_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) {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WDCConnection::sendDataMessage()";
@ -321,12 +330,13 @@ bool WDCConnection::sendDataMessage(const DataBuffer& buffer) {
}
WebRTCDataChannels::WebRTCDataChannels(NodeType_t nodeType, QObject* parent) :
_nodeType(nodeType),
_parent(parent)
WebRTCDataChannels::WebRTCDataChannels(QObject* parent, NodeType_t nodeType) :
QObject(parent),
_parent(parent),
_nodeType(nodeType)
{
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()";
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()" << nodeType << NodeType::getNodeTypeName(nodeType);
#endif
// Create a peer connection factory.
@ -354,14 +364,7 @@ WebRTCDataChannels::~WebRTCDataChannels() {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::~WebRTCDataChannels()";
#endif
QHashIterator<quint16, WDCConnection*> i(_connectionsByDataChannel);
while (i.hasNext()) {
i.next();
delete i.value();
}
_connectionsByWebSocket.clear();
_connectionsByDataChannel.clear();
reset();
_peerConnectionFactory = nullptr;
_rtcSignalingThread->Stop();
_rtcSignalingThread = nullptr;
@ -371,6 +374,16 @@ WebRTCDataChannels::~WebRTCDataChannels() {
_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() {
static const int QUINT16_LIMIT = std::numeric_limits<uint16_t>::max() + 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) {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID;
qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID << byteArray;
#endif
emit dataMessage(dataChannelID, byteArray);
}
@ -469,6 +482,14 @@ bool WebRTCDataChannels::sendDataMessage(int dataChannelID, const QByteArray& by
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(
const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver) {
#ifdef WEBRTC_DEBUG

View file

@ -23,8 +23,6 @@
#include "../NodeType.h"
using namespace webrtc;
class WebRTCDataChannels;
class WDCConnection;
@ -33,7 +31,7 @@ class WDCConnection;
/// @{
/// @brief A WebRTC session description observer.
class WDCSetSessionDescriptionObserver : public SetSessionDescriptionObserver {
class WDCSetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
public:
/// @brief The call to SetLocalDescription or SetRemoteDescription succeeded.
@ -41,22 +39,22 @@ public:
/// @brief The call to SetLocalDescription or SetRemoteDescription failed.
/// @param error Error information.
void OnFailure(RTCError error) override;
void OnFailure(webrtc::RTCError error) override;
};
/// @brief A WebRTC create session description observer.
class WDCCreateSessionDescriptionObserver : public CreateSessionDescriptionObserver {
class WDCCreateSessionDescriptionObserver : public webrtc::CreateSessionDescriptionObserver {
public:
WDCCreateSessionDescriptionObserver(WDCConnection* parent);
/// @brief The call to CreateAnswer succeeded.
/// @param The session description.
void OnSuccess(SessionDescriptionInterface* desc) override;
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
//@ @brief The call to CreateAnswer failed.
/// @param error Error information.
void OnFailure(RTCError error) override;
void OnFailure(webrtc::RTCError error) override;
private:
WDCConnection* _parent;
@ -64,32 +62,32 @@ private:
/// @brief A WebRTC peer connection observer.
class WDCPeerConnectionObserver : public PeerConnectionObserver {
class WDCPeerConnectionObserver : public webrtc::PeerConnectionObserver {
public:
WDCPeerConnectionObserver(WDCConnection* parent);
/// @brief Called when the SignalingState changes.
/// @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.
void OnRenegotiationNeeded() override;
/// @brief Called when the ICE gather state changes.
/// @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.
/// @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.
/// @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.
/// @param newState The new peer connection state.
void OnConnectionChange(PeerConnectionInterface::PeerConnectionState newState) override;
void OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState newState) override;
private:
WDCConnection* _parent;
@ -97,7 +95,7 @@ private:
/// @brief A WebRTC data channel observer.
class WDCDataChannelObserver : public DataChannelObserver {
class WDCDataChannelObserver : public webrtc::DataChannelObserver {
public:
WDCDataChannelObserver(WDCConnection* parent);
@ -106,7 +104,7 @@ public:
/// @brief A data channel message was received.
/// @param The message received.
void OnMessage(const DataBuffer& buffer) override;
void OnMessage(const webrtc::DataBuffer& buffer) override;
private:
WDCConnection* _parent;
@ -125,11 +123,11 @@ public:
/// @brief Gets the WebSocket ID.
/// @return The ID of the WebSocket.
quint16 getWebSocketID() { return _webSocketID; }
quint16 getWebSocketID() const { return _webSocketID; }
/// @brief Gets the WebRTC data channel ID.
/// @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.
@ -141,11 +139,11 @@ public:
/// @brief Sends an answer to the remote client via the signaling channel.
/// @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.
/// @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.
/// @param data The ICE candidate.
@ -153,11 +151,11 @@ public:
/// @brief Sends an ICE candidate to the remote vlient via the signaling channel.
/// @param candidate The ICE candidate.
void sendIceCandidate(const IceCandidateInterface* candidate);
void sendIceCandidate(const webrtc::IceCandidateInterface* candidate);
/// @brief Handles the WebRTC data channel being opened.
/// @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.
void onDataChannelStateChanged();
@ -165,12 +163,17 @@ public:
/// @brief Handles a message being received on the WebRTC data channel.
/// @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.
/// @param buffer The message to send.
/// @return `true` if the message was sent, otherwise `false`.
bool sendDataMessage(const DataBuffer& buffer);
bool sendDataMessage(const webrtc::DataBuffer& buffer);
private:
WebRTCDataChannels* _parent;
@ -181,10 +184,10 @@ private:
rtc::scoped_refptr<WDCCreateSessionDescriptionObserver> _createSessionDescriptionObserver { 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 };
rtc::scoped_refptr<PeerConnectionInterface> _peerConnection { nullptr };
rtc::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection { nullptr };
};
@ -206,20 +209,25 @@ class WebRTCDataChannels : public QObject {
public:
/// @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.
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.
~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.
NodeType_t getNodeType() {
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.
/// @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.
quint16 getNewDataChannelID();
@ -248,10 +256,15 @@ public:
/// @return `true` if the data message was sent, otherwise `false`.
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.
/// @param peerConnectionObserver An observer to monitor the 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);
public slots:
@ -283,9 +296,9 @@ private:
std::unique_ptr<rtc::Thread> _rtcWorkerThread { 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*> _connectionsByDataChannel;

View file

@ -19,33 +19,33 @@
const int WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS = 30000;
WebRTCSignalingServer::WebRTCSignalingServer(const QHostAddress& address, quint16 port, QObject* parent) :
WebRTCSignalingServer::WebRTCSignalingServer(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);
}
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() {
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.";
_webSocketServer->listen(_address, _port);
}
}

View file

@ -59,10 +59,14 @@ class WebRTCSignalingServer : public QObject {
public:
/// @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.
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:
@ -88,11 +92,10 @@ private slots:
private:
void checkWebSocketServerIsListening();
void bindSocket();
QWebSocketServer* _webSocketServer;
QHostAddress _address;
const quint16 _port;
quint16 _port { 0 };
QHash<quint16, QWebSocket*> _webSockets; // client WebSocket port, client WebSocket object