mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-04 19:24:32 +02:00
Merge pull request #1333 from ctrlaltdavid/dev/webrtc-assignment-clients
Enable assignment client protocol communications over WebRTC.
This commit is contained in:
commit
6bc83b5ea2
23 changed files with 212 additions and 179 deletions
|
@ -17,7 +17,7 @@
|
|||
NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, const SockAddr& senderSockAddr,
|
||||
bool isConnectRequest) {
|
||||
NodeConnectionData newHeader;
|
||||
|
||||
|
||||
if (isConnectRequest) {
|
||||
dataStream >> newHeader.connectUUID;
|
||||
|
||||
|
@ -51,23 +51,28 @@ NodeConnectionData NodeConnectionData::fromDataStream(QDataStream& dataStream, c
|
|||
|
||||
dataStream >> newHeader.lastPingTimestamp;
|
||||
|
||||
SocketType publicSocketType, localSocketType;
|
||||
dataStream >> newHeader.nodeType
|
||||
>> newHeader.publicSockAddr >> newHeader.localSockAddr
|
||||
>> publicSocketType >> newHeader.publicSockAddr >> localSocketType >> newHeader.localSockAddr
|
||||
>> newHeader.interestList >> newHeader.placeName;
|
||||
newHeader.publicSockAddr.setType(publicSocketType);
|
||||
newHeader.localSockAddr.setType(localSocketType);
|
||||
|
||||
// A WebRTC web client doesn't necessarily know it's public Internet or local network addresses, and for WebRTC they aren't
|
||||
// needed: for WebRTC, the data channel ID is the important thing. The client's public Internet IP address still needs to
|
||||
// be known for domain access permissions, though, and this can be obtained from the WebSocket signaling connection.
|
||||
if (senderSockAddr.getSocketType() == SocketType::WebRTC) {
|
||||
// WEBRTC TODO: Rather than setting the SocketType here, serialize and deserialize the SocketType in the leading byte of
|
||||
// the 5 bytes used to encode the IP address.
|
||||
newHeader.publicSockAddr.setSocketType(SocketType::WebRTC);
|
||||
newHeader.localSockAddr.setSocketType(SocketType::WebRTC);
|
||||
// For WebRTC connections, the user client's signaling channel WebSocket address is used instead of the actual data
|
||||
// channel's address.
|
||||
if (senderSockAddr.getType() == SocketType::WebRTC) {
|
||||
if (newHeader.publicSockAddr.getType() != SocketType::WebRTC
|
||||
|| newHeader.localSockAddr.getType() != SocketType::WebRTC) {
|
||||
qDebug() << "Inconsistent WebRTC socket types!";
|
||||
}
|
||||
|
||||
// WEBRTC TODO: Set the public Internet address obtained from the WebSocket used in WebRTC signaling.
|
||||
|
||||
newHeader.publicSockAddr.setPort(senderSockAddr.getPort()); // We don't know whether it's a public or local connection
|
||||
newHeader.localSockAddr.setPort(senderSockAddr.getPort()); // so set both ports.
|
||||
// We don't know whether it's a public or local connection so set both the same.
|
||||
auto address = senderSockAddr.getAddress();
|
||||
auto port = senderSockAddr.getPort();
|
||||
newHeader.publicSockAddr.setAddress(address);
|
||||
newHeader.publicSockAddr.setPort(port);
|
||||
newHeader.localSockAddr.setAddress(address);
|
||||
newHeader.localSockAddr.setPort(port);
|
||||
}
|
||||
|
||||
newHeader.senderSockAddr = senderSockAddr;
|
||||
|
|
|
@ -942,7 +942,7 @@ void AudioClient::Gate::flush() {
|
|||
|
||||
|
||||
void AudioClient::handleNoisyMutePacket(QSharedPointer<ReceivedMessage> message) {
|
||||
if (!_muted) {
|
||||
if (!_isMuted) {
|
||||
setMuted(true);
|
||||
|
||||
// have the audio scripting interface emit a signal to say we were muted by the mixer
|
||||
|
@ -989,7 +989,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
|
||||
_selectedCodecName = selectedCodecName;
|
||||
|
||||
qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput;
|
||||
qCDebug(audioclient) << "Selected codec:" << _selectedCodecName << "; Is stereo input:" << _isStereoInput;
|
||||
|
||||
// release any old codec encoder/decoder first...
|
||||
if (_codec && _encoder) {
|
||||
|
@ -1005,7 +1005,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) {
|
|||
_codec = plugin;
|
||||
_receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO);
|
||||
_encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get();
|
||||
qCDebug(audioclient) << "Selected codec plugin:" << _codec.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1269,7 +1269,7 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC
|
|||
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.
|
||||
bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
|
||||
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) {
|
||||
if ((_isMuted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1357,7 +1357,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
|||
|
||||
bool audioGateOpen = false;
|
||||
|
||||
if (!_muted) {
|
||||
if (!_isMuted) {
|
||||
int16_t* samples = reinterpret_cast<int16_t*>(audioBuffer.data());
|
||||
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
|
||||
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
|
||||
|
@ -1378,7 +1378,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
|
|||
}
|
||||
|
||||
// loudness after mute/gate
|
||||
_lastInputLoudness = (_muted || !audioGateOpen) ? 0.0f : _lastRawInputLoudness;
|
||||
_lastInputLoudness = (_isMuted || !audioGateOpen) ? 0.0f : _lastRawInputLoudness;
|
||||
|
||||
// detect gate opening and closing
|
||||
bool openedInLastBlock = !_audioGateOpen && audioGateOpen; // the gate just opened
|
||||
|
@ -1482,7 +1482,7 @@ void AudioClient::handleMicAudioInput() {
|
|||
|
||||
emit inputLoudnessChanged(_lastSmoothedRawInputLoudness, isClipping);
|
||||
|
||||
if (!_muted) {
|
||||
if (!_isMuted) {
|
||||
possibleResampling(_inputToNetworkResampler,
|
||||
inputAudioSamples.get(), networkAudioSamples,
|
||||
inputSamplesRequired, numNetworkSamples,
|
||||
|
@ -1748,10 +1748,10 @@ void AudioClient::sendMuteEnvironmentPacket() {
|
|||
}
|
||||
|
||||
void AudioClient::setMuted(bool muted, bool emitSignal) {
|
||||
if (_muted != muted) {
|
||||
_muted = muted;
|
||||
if (_isMuted != muted) {
|
||||
_isMuted = muted;
|
||||
if (emitSignal) {
|
||||
emit muteToggled(_muted);
|
||||
emit muteToggled(_isMuted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1896,7 +1896,6 @@ bool AudioClient::switchInputToAudioDevice(const HifiAudioDeviceInfo inputDevice
|
|||
|
||||
if (_dummyAudioInput) {
|
||||
_dummyAudioInput->stop();
|
||||
|
||||
_dummyAudioInput->deleteLater();
|
||||
_dummyAudioInput = NULL;
|
||||
}
|
||||
|
|
|
@ -217,7 +217,7 @@ public slots:
|
|||
void audioMixerKilled();
|
||||
|
||||
void setMuted(bool muted, bool emitSignal = true);
|
||||
bool isMuted() { return _muted; }
|
||||
bool isMuted() { return _isMuted; }
|
||||
|
||||
virtual bool setIsStereoInput(bool stereo) override;
|
||||
virtual bool isStereoInput() override { return _isStereoInput; }
|
||||
|
@ -410,7 +410,7 @@ private:
|
|||
float _timeSinceLastClip{ -1.0f };
|
||||
int _totalInputAudioSamples;
|
||||
|
||||
bool _muted{ false };
|
||||
bool _isMuted{ false };
|
||||
bool _shouldEchoLocally{ false };
|
||||
bool _shouldEchoToServer{ false };
|
||||
bool _isNoiseGateEnabled{ true };
|
||||
|
|
|
@ -167,7 +167,7 @@ int InboundAudioStream::parseData(ReceivedMessage& message) {
|
|||
bool packetPCM = codecInPacket == "pcm" || codecInPacket == "";
|
||||
if (codecInPacket == _selectedCodecName || (packetPCM && selectedPCM)) {
|
||||
auto afterProperties = message.readWithoutCopy(message.getBytesLeftToRead());
|
||||
parseAudioData(message.getType(), afterProperties);
|
||||
parseAudioData(afterProperties);
|
||||
_mismatchedAudioCodecCount = 0;
|
||||
|
||||
} else {
|
||||
|
@ -267,7 +267,7 @@ int InboundAudioStream::lostAudioData(int numPackets) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int InboundAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) {
|
||||
int InboundAudioStream::parseAudioData(const QByteArray& packetAfterStreamProperties) {
|
||||
QByteArray decodedBuffer;
|
||||
|
||||
// may block on the real-time thread, which is acceptible as
|
||||
|
|
|
@ -132,7 +132,7 @@ protected:
|
|||
|
||||
/// parses the audio data in the network packet.
|
||||
/// default implementation assumes packet contains raw audio samples after stream properties
|
||||
virtual int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties);
|
||||
virtual int parseAudioData(const QByteArray& packetAfterStreamProperties);
|
||||
|
||||
/// produces audio data for lost network packets.
|
||||
virtual int lostAudioData(int numPackets);
|
||||
|
|
|
@ -61,7 +61,7 @@ int MixedProcessedAudioStream::lostAudioData(int numPackets) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int MixedProcessedAudioStream::parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) {
|
||||
int MixedProcessedAudioStream::parseAudioData(const QByteArray& packetAfterStreamProperties) {
|
||||
QByteArray decodedBuffer;
|
||||
|
||||
// may block on the real-time thread, which is acceptible as
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
|
||||
protected:
|
||||
int writeDroppableSilentFrames(int silentFrames) override;
|
||||
int parseAudioData(PacketType type, const QByteArray& packetAfterStreamProperties) override;
|
||||
int parseAudioData(const QByteArray& packetAfterStreamProperties) override;
|
||||
int lostAudioData(int numPackets) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -196,7 +196,9 @@ bool Node::isIgnoringNodeWithID(const QUuid& nodeID) const {
|
|||
QDataStream& operator<<(QDataStream& out, const Node& node) {
|
||||
out << node._type;
|
||||
out << node._uuid;
|
||||
out << node._publicSocket.getType();
|
||||
out << node._publicSocket;
|
||||
out << node._localSocket.getType();
|
||||
out << node._localSocket;
|
||||
out << node._permissions;
|
||||
out << node._isReplicated;
|
||||
|
@ -205,10 +207,15 @@ QDataStream& operator<<(QDataStream& out, const Node& node) {
|
|||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& in, Node& node) {
|
||||
SocketType publicSocketType, localSocketType;
|
||||
in >> node._type;
|
||||
in >> node._uuid;
|
||||
in >> publicSocketType;
|
||||
in >> node._publicSocket;
|
||||
node._publicSocket.setType(publicSocketType);
|
||||
in >> localSocketType;
|
||||
in >> node._localSocket;
|
||||
node._localSocket.setType(localSocketType);
|
||||
in >> node._permissions;
|
||||
in >> node._isReplicated;
|
||||
in >> node._localID;
|
||||
|
|
|
@ -470,10 +470,12 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
QByteArray compressedSystemInfo = qCompress(systemInfo);
|
||||
|
||||
if (compressedSystemInfo.size() > MAX_SYSTEM_INFO_SIZE) {
|
||||
// FIXME
|
||||
// Highly unlikely, as not even unreasonable machines will
|
||||
// overflow the max size, but prevent MTU overflow anyway.
|
||||
// We could do something sophisticated like clearing specific
|
||||
// values if they're too big, but we'll save that for later.
|
||||
// Alternative solution would be to write system info at the end of the packet, only if there is space.
|
||||
compressedSystemInfo.clear();
|
||||
}
|
||||
|
||||
|
@ -494,7 +496,8 @@ void NodeList::sendDomainServerCheckIn() {
|
|||
|
||||
// pack our data to send to the domain-server including
|
||||
// the hostname information (so the domain-server can see which place name we came in on)
|
||||
packetStream << _ownerType.load() << publicSockAddr << localSockAddr << _nodeTypesOfInterest.toList();
|
||||
packetStream << _ownerType.load() << publicSockAddr.getType() << publicSockAddr << localSockAddr.getType()
|
||||
<< localSockAddr << _nodeTypesOfInterest.toList();
|
||||
packetStream << DependencyManager::get<AddressManager>()->getPlaceName();
|
||||
|
||||
if (!domainIsConnected) {
|
||||
|
@ -879,14 +882,19 @@ void NodeList::processDomainServerRemovedNode(QSharedPointer<ReceivedMessage> me
|
|||
void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
||||
NewNodeInfo info;
|
||||
|
||||
SocketType publicSocketType, localSocketType;
|
||||
packetStream >> info.type
|
||||
>> info.uuid
|
||||
>> publicSocketType
|
||||
>> info.publicSocket
|
||||
>> localSocketType
|
||||
>> info.localSocket
|
||||
>> info.permissions
|
||||
>> info.isReplicated
|
||||
>> info.sessionLocalID
|
||||
>> info.connectionSecretUUID;
|
||||
info.publicSocket.setType(publicSocketType);
|
||||
info.localSocket.setType(localSocketType);
|
||||
|
||||
// if the public socket address is 0 then it's reachable at the same IP
|
||||
// as the domain server
|
||||
|
@ -894,10 +902,6 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
|
|||
info.publicSocket.setAddress(_domainHandler.getIP());
|
||||
}
|
||||
|
||||
// WEBRTC TODO: Handle WebRTC-connected nodes. Probably need to include SocketType in SockAddr << and >>
|
||||
info.publicSocket.setSocketType(SocketType::UDP);
|
||||
info.localSocket.setSocketType(SocketType::UDP);
|
||||
|
||||
addNewNode(info);
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,10 @@ QString SockAddr::toString() const {
|
|||
return socketTypeToString(_socketType) + " " + _address.toString() + ":" + QString::number(_port);
|
||||
}
|
||||
|
||||
QString SockAddr::toShortString() const {
|
||||
return _address.toString() + ":" + QString::number(_port);
|
||||
}
|
||||
|
||||
bool SockAddr::hasPrivateAddress() const {
|
||||
// an address is private if it is loopback or falls in any of the RFC1918 address spaces
|
||||
const QPair<QHostAddress, int> TWENTY_FOUR_BIT_BLOCK = { QHostAddress("10.0.0.0"), 8 };
|
||||
|
@ -129,27 +133,22 @@ bool SockAddr::hasPrivateAddress() const {
|
|||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const SockAddr& sockAddr) {
|
||||
debug.nospace() << socketTypeToString(sockAddr._socketType).toLocal8Bit().constData() << " "
|
||||
debug.nospace()
|
||||
<< (sockAddr._socketType != SocketType::Unknown
|
||||
? (socketTypeToString(sockAddr._socketType) + " ").toLocal8Bit().constData() : "")
|
||||
<< sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
|
||||
return debug.space();
|
||||
}
|
||||
|
||||
QDataStream& operator<<(QDataStream& dataStream, const SockAddr& sockAddr) {
|
||||
// Don't include socketType because it can be implied from the type of connection used.
|
||||
// WEBRTC TODO: Reconsider this.
|
||||
// Don't include socket type because ICE packets must not have it.
|
||||
dataStream << sockAddr._address << sockAddr._port;
|
||||
return dataStream;
|
||||
}
|
||||
|
||||
QDataStream& operator>>(QDataStream& dataStream, SockAddr& sockAddr) {
|
||||
// Don't include socketType because it can be implied from the type of connection used.
|
||||
// WEBRTC TODO: Reconsider this.
|
||||
// Don't include socket type because ICE packets must not have it.
|
||||
dataStream >> sockAddr._address >> sockAddr._port;
|
||||
|
||||
// Set default for non-WebRTC code.
|
||||
// WEBRTC TODO: Reconsider this.
|
||||
sockAddr.setSocketType(SocketType::UDP);
|
||||
|
||||
return dataStream;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,9 +40,9 @@ public:
|
|||
bool operator==(const SockAddr& rhsSockAddr) const;
|
||||
bool operator!=(const SockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); }
|
||||
|
||||
SocketType getSocketType() const { return _socketType; }
|
||||
SocketType getType() const { return _socketType; }
|
||||
SocketType* getSocketTypePointer() { return &_socketType; }
|
||||
void setSocketType(const SocketType socketType) { _socketType = socketType; }
|
||||
void setType(const SocketType socketType) { _socketType = socketType; }
|
||||
|
||||
const QHostAddress& getAddress() const { return _address; }
|
||||
QHostAddress* getAddressPointer() { return &_address; }
|
||||
|
@ -56,6 +56,7 @@ public:
|
|||
static int unpackSockAddr(const unsigned char* packetData, SockAddr& unpackDestSockAddr);
|
||||
|
||||
QString toString() const;
|
||||
QString toShortString() const;
|
||||
|
||||
bool hasPrivateAddress() const; // checks if the address behind this sock addr is private per RFC 1918
|
||||
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
|
||||
/// @brief The types of network socket.
|
||||
enum class SocketType {
|
||||
Unknown, ///< Unknown socket type.
|
||||
enum class SocketType : uint8_t {
|
||||
Unknown, ///< Socket type unknown or not set.
|
||||
UDP, ///< UDP socket.
|
||||
WebRTC ///< WebRTC socket. A WebRTC data channel presented as a UDP-style socket.
|
||||
};
|
||||
|
|
|
@ -126,14 +126,14 @@ qintptr NetworkSocket::socketDescriptor(SocketType socketType) const {
|
|||
|
||||
|
||||
qint64 NetworkSocket::writeDatagram(const QByteArray& datagram, const SockAddr& sockAddr) {
|
||||
switch (sockAddr.getSocketType()) {
|
||||
switch (sockAddr.getType()) {
|
||||
case SocketType::UDP:
|
||||
// WEBRTC TODO: The Qt documentation says that the following call shouldn't be used if the UDP socket is connected!!!
|
||||
// https://doc.qt.io/qt-5/qudpsocket.html#writeDatagram
|
||||
return _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.writeDatagram(datagram, sockAddr.getPort());
|
||||
return _webrtcSocket.writeDatagram(datagram, sockAddr);
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in writeDatagram() address";
|
||||
|
@ -141,13 +141,13 @@ qint64 NetworkSocket::writeDatagram(const QByteArray& datagram, const SockAddr&
|
|||
}
|
||||
}
|
||||
|
||||
qint64 NetworkSocket::bytesToWrite(SocketType socketType, quint16 port) const {
|
||||
qint64 NetworkSocket::bytesToWrite(SocketType socketType, const SockAddr& address) const {
|
||||
switch (socketType) {
|
||||
case SocketType::UDP:
|
||||
return _udpSocket.bytesToWrite();
|
||||
#if defined(WEBRTC_DATA_CHANNELS)
|
||||
case SocketType::WebRTC:
|
||||
return _webrtcSocket.bytesToWrite(port);
|
||||
return _webrtcSocket.bytesToWrite(address);
|
||||
#endif
|
||||
default:
|
||||
qCCritical(networking) << "Socket type not specified in bytesToWrite()";
|
||||
|
@ -197,7 +197,7 @@ qint64 NetworkSocket::readDatagram(char* data, qint64 maxSize, SockAddr* sockAdd
|
|||
_lastSocketTypeRead = SocketType::UDP;
|
||||
_pendingDatagramSizeSocketType = SocketType::Unknown;
|
||||
if (sockAddr) {
|
||||
sockAddr->setSocketType(SocketType::UDP);
|
||||
sockAddr->setType(SocketType::UDP);
|
||||
return _udpSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
|
||||
} else {
|
||||
return _udpSocket.readDatagram(data, maxSize);
|
||||
|
@ -206,7 +206,7 @@ qint64 NetworkSocket::readDatagram(char* data, qint64 maxSize, SockAddr* sockAdd
|
|||
_lastSocketTypeRead = SocketType::WebRTC;
|
||||
_pendingDatagramSizeSocketType = SocketType::Unknown;
|
||||
if (sockAddr) {
|
||||
sockAddr->setSocketType(SocketType::WebRTC);
|
||||
sockAddr->setType(SocketType::WebRTC);
|
||||
return _webrtcSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
|
||||
} else {
|
||||
return _webrtcSocket.readDatagram(data, maxSize);
|
||||
|
@ -214,7 +214,7 @@ qint64 NetworkSocket::readDatagram(char* data, qint64 maxSize, SockAddr* sockAdd
|
|||
}
|
||||
#else
|
||||
if (sockAddr) {
|
||||
sockAddr->setSocketType(SocketType::UDP);
|
||||
sockAddr->setType(SocketType::UDP);
|
||||
return _udpSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
|
||||
} else {
|
||||
return _udpSocket.readDatagram(data, maxSize);
|
||||
|
|
|
@ -80,9 +80,10 @@ public:
|
|||
/// @brief Gets the number of bytes waiting to be written.
|
||||
/// @details For UDP, there's a single buffer used for all destinations. For WebRTC, each destination has its own buffer.
|
||||
/// @param socketType The type of socket for which to get the number of bytes waiting to be written.
|
||||
/// @param port If a WebRTC socket, the data channel for which to get the number of bytes waiting.
|
||||
/// @param address If a WebRTCSocket, the destination address for which to get the number of bytes waiting.
|
||||
/// @param port If a WebRTC socket, the destination port for which to get the number of bytes waiting.
|
||||
/// @return The number of bytes waiting to be written.
|
||||
qint64 bytesToWrite(SocketType socketType, quint16 port = 0) const;
|
||||
qint64 bytesToWrite(SocketType socketType, const SockAddr& address = SockAddr()) const;
|
||||
|
||||
|
||||
/// @brief Gets whether there is a pending datagram waiting to be read.
|
||||
|
|
|
@ -27,7 +27,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
case PacketType::DomainConnectRequestPending: // keeping the old version to maintain the protocol hash
|
||||
return 17;
|
||||
case PacketType::DomainList:
|
||||
return static_cast<PacketVersion>(DomainListVersion::HasConnectReason);
|
||||
return static_cast<PacketVersion>(DomainListVersion::SocketTypes);
|
||||
case PacketType::EntityAdd:
|
||||
case PacketType::EntityClone:
|
||||
case PacketType::EntityEdit:
|
||||
|
@ -72,10 +72,12 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
|||
return static_cast<PacketVersion>(DomainConnectionDeniedVersion::IncludesExtraInfo);
|
||||
|
||||
case PacketType::DomainConnectRequest:
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::HasCompressedSystemInfo);
|
||||
return static_cast<PacketVersion>(DomainConnectRequestVersion::SocketTypes);
|
||||
case PacketType::DomainListRequest:
|
||||
return static_cast<PacketVersion>(DomainListRequestVersion::SocketTypes);
|
||||
|
||||
case PacketType::DomainServerAddedNode:
|
||||
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::PermissionsGrid);
|
||||
return static_cast<PacketVersion>(DomainServerAddedNodeVersion::SocketTypes);
|
||||
|
||||
case PacketType::EntityScriptCallMethod:
|
||||
return static_cast<PacketVersion>(EntityScriptCallMethodVersion::ClientCallable);
|
||||
|
|
|
@ -368,7 +368,13 @@ enum class DomainConnectRequestVersion : PacketVersion {
|
|||
HasTimestamp,
|
||||
HasReason,
|
||||
HasSystemInfo,
|
||||
HasCompressedSystemInfo
|
||||
HasCompressedSystemInfo,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class DomainListRequestVersion : PacketVersion {
|
||||
PreSocketTypes = 22,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class DomainConnectionDeniedVersion : PacketVersion {
|
||||
|
@ -379,7 +385,8 @@ enum class DomainConnectionDeniedVersion : PacketVersion {
|
|||
|
||||
enum class DomainServerAddedNodeVersion : PacketVersion {
|
||||
PrePermissionsGrid = 17,
|
||||
PermissionsGrid
|
||||
PermissionsGrid,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class DomainListVersion : PacketVersion {
|
||||
|
@ -389,7 +396,8 @@ enum class DomainListVersion : PacketVersion {
|
|||
GetMachineFingerprintFromUUIDSupport,
|
||||
AuthenticationOptional,
|
||||
HasTimestamp,
|
||||
HasConnectReason
|
||||
HasConnectReason,
|
||||
SocketTypes
|
||||
};
|
||||
|
||||
enum class AudioVersion : PacketVersion {
|
||||
|
|
|
@ -245,7 +245,7 @@ qint64 Socket::writeDatagram(const char* data, qint64 size, const SockAddr& sock
|
|||
}
|
||||
|
||||
qint64 Socket::writeDatagram(const QByteArray& datagram, const SockAddr& sockAddr) {
|
||||
auto socketType = sockAddr.getSocketType();
|
||||
auto socketType = sockAddr.getType();
|
||||
|
||||
// don't attempt to write the datagram if we're unbound. Just drop it.
|
||||
// _networkSocket.writeDatagram will return an error anyway, but there are
|
||||
|
@ -256,7 +256,7 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const SockAddr& sockAdd
|
|||
}
|
||||
qint64 bytesWritten = _networkSocket.writeDatagram(datagram, sockAddr);
|
||||
|
||||
int pending = _networkSocket.bytesToWrite(socketType, sockAddr.getPort());
|
||||
int pending = _networkSocket.bytesToWrite(socketType, sockAddr);
|
||||
if (bytesWritten < 0 || pending) {
|
||||
int wsaError = 0;
|
||||
static std::atomic<int> previousWsaError (0);
|
||||
|
|
|
@ -131,12 +131,12 @@ void WDCDataChannelObserver::OnMessage(const DataBuffer& buffer) {
|
|||
}
|
||||
|
||||
|
||||
WDCConnection::WDCConnection(WebRTCDataChannels* parent, quint16 webSocketID) :
|
||||
WDCConnection::WDCConnection(WebRTCDataChannels* parent, const QString& dataChannelID) :
|
||||
_parent(parent),
|
||||
_webSocketID(webSocketID)
|
||||
_dataChannelID(dataChannelID)
|
||||
{
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WDCConnection::WDCConnection() :" << webSocketID;
|
||||
qCDebug(networking_webrtc) << "WDCConnection::WDCConnection() :" << dataChannelID;
|
||||
#endif
|
||||
|
||||
// Create observers.
|
||||
|
@ -196,7 +196,7 @@ void WDCConnection::sendAnswer(SessionDescriptionInterface* description) {
|
|||
|
||||
QJsonObject jsonObject;
|
||||
jsonObject.insert("from", QString(_parent->getNodeType()));
|
||||
jsonObject.insert("to", _webSocketID);
|
||||
jsonObject.insert("to", _dataChannelID);
|
||||
jsonObject.insert("data", jsonWebRTCPayload);
|
||||
|
||||
_parent->sendSignalingMessage(jsonObject);
|
||||
|
@ -250,7 +250,7 @@ void WDCConnection::sendIceCandidate(const IceCandidateInterface* candidate) {
|
|||
|
||||
QJsonObject jsonObject;
|
||||
jsonObject.insert("from", QString(_parent->getNodeType()));
|
||||
jsonObject.insert("to", _webSocketID);
|
||||
jsonObject.insert("to", _dataChannelID);
|
||||
jsonObject.insert("data", jsonWebRTCData);
|
||||
QJsonDocument jsonDocument = QJsonDocument(jsonObject);
|
||||
|
||||
|
@ -288,7 +288,6 @@ void WDCConnection::onDataChannelOpened(rtc::scoped_refptr<DataChannelInterface>
|
|||
#endif
|
||||
|
||||
_dataChannel = dataChannel;
|
||||
_dataChannelID = _parent->getNewDataChannelID(); // Not dataChannel->id() because it's only unique per peer connection.
|
||||
_dataChannel->RegisterObserver(_dataChannelObserver.get());
|
||||
|
||||
#ifdef WEBRTC_DEBUG
|
||||
|
@ -328,7 +327,13 @@ void WDCConnection::onDataChannelMessageReceived(const DataBuffer& buffer) {
|
|||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Echo message back";
|
||||
#endif
|
||||
_parent->sendDataMessage(_dataChannelID, byteArray); // Use parent method to exercise the code stack.
|
||||
auto addressParts = _dataChannelID.split(":");
|
||||
if (addressParts.length() != 2) {
|
||||
qCWarning(networking_webrtc) << "Invalid dataChannelID:" << _dataChannelID;
|
||||
return;
|
||||
}
|
||||
auto address = SockAddr(SocketType::WebRTC, QHostAddress(addressParts[0]), addressParts[1].toInt());
|
||||
_parent->sendDataMessage(address, byteArray); // Use parent method to exercise the code stack.
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -420,26 +425,19 @@ WebRTCDataChannels::~WebRTCDataChannels() {
|
|||
}
|
||||
|
||||
void WebRTCDataChannels::reset() {
|
||||
QHashIterator<quint16, WDCConnection*> i(_connectionsByDataChannel);
|
||||
QHashIterator<QString, WDCConnection*> i(_connectionsByID);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
delete i.value();
|
||||
}
|
||||
_connectionsByWebSocket.clear();
|
||||
_connectionsByDataChannel.clear();
|
||||
_connectionsByID.clear();
|
||||
}
|
||||
|
||||
quint16 WebRTCDataChannels::getNewDataChannelID() {
|
||||
static const int QUINT16_LIMIT = std::numeric_limits<uint16_t>::max() + 1;
|
||||
_lastDataChannelID = std::max((_lastDataChannelID + 1) % QUINT16_LIMIT, 1);
|
||||
return _lastDataChannelID;
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::onDataChannelOpened(WDCConnection* connection, quint16 dataChannelID) {
|
||||
void WebRTCDataChannels::onDataChannelOpened(WDCConnection* connection, const QString& dataChannelID) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::onDataChannelOpened() :" << dataChannelID;
|
||||
#endif
|
||||
_connectionsByDataChannel.insert(dataChannelID, connection);
|
||||
_connectionsByID.insert(dataChannelID, connection);
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::onSignalingMessage(const QJsonObject& message) {
|
||||
|
@ -449,13 +447,13 @@ void WebRTCDataChannels::onSignalingMessage(const QJsonObject& message) {
|
|||
|
||||
// Validate message.
|
||||
const int MAX_DEBUG_DETAIL_LENGTH = 64;
|
||||
const QRegularExpression DATA_CHANNEL_ID_REGEX{ "^[1-9]\\d*\\.\\d+\\.\\d+\\.\\d+:\\d+$" };
|
||||
auto data = message.value("data").isObject() ? message.value("data").toObject() : QJsonObject();
|
||||
int from = message.value("from").isDouble() ? (quint16)(message.value("from").toInt()) : 0;
|
||||
auto from = message.value("from").toString();
|
||||
auto to = NodeType::fromChar(message.value("to").toString().at(0));
|
||||
|
||||
if (from <= 0 || from > MAXUINT16 || to == NodeType::Unassigned
|
||||
if (!DATA_CHANNEL_ID_REGEX.match(from).hasMatch() || to == NodeType::Unassigned
|
||||
|| !data.contains("description") && !data.contains("candidate")) {
|
||||
qCWarning(networking_webrtc) << "Unexpected signaling message:"
|
||||
qCWarning(networking_webrtc) << "Invalid or unexpected signaling message:"
|
||||
<< QJsonDocument(message).toJson(QJsonDocument::Compact).left(MAX_DEBUG_DETAIL_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
@ -465,11 +463,11 @@ void WebRTCDataChannels::onSignalingMessage(const QJsonObject& message) {
|
|||
|
||||
// Find or create a connection.
|
||||
WDCConnection* connection;
|
||||
if (_connectionsByWebSocket.contains(from)) {
|
||||
connection = _connectionsByWebSocket.value(from);
|
||||
if (_connectionsByID.contains(from)) {
|
||||
connection = _connectionsByID.value(from);
|
||||
} else {
|
||||
connection = new WDCConnection(this, from);
|
||||
_connectionsByWebSocket.insert(from, connection);
|
||||
_connectionsByID.insert(from, connection);
|
||||
}
|
||||
|
||||
// Set the remote description and reply with an answer.
|
||||
|
@ -498,35 +496,45 @@ void WebRTCDataChannels::sendSignalingMessage(const QJsonObject& message) {
|
|||
emit signalingMessage(message);
|
||||
}
|
||||
|
||||
void WebRTCDataChannels::emitDataMessage(int dataChannelID, const QByteArray& byteArray) {
|
||||
void WebRTCDataChannels::emitDataMessage(const QString& dataChannelID, const QByteArray& byteArray) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID << byteArray.toHex()
|
||||
<< byteArray.length();
|
||||
#endif
|
||||
emit dataMessage(dataChannelID, byteArray);
|
||||
auto addressParts = dataChannelID.split(":");
|
||||
if (addressParts.length() != 2) {
|
||||
qCWarning(networking_webrtc) << "Invalid dataChannelID:" << dataChannelID;
|
||||
return;
|
||||
}
|
||||
auto address = SockAddr(SocketType::WebRTC, QHostAddress(addressParts[0]), addressParts[1].toInt());
|
||||
emit dataMessage(address, byteArray);
|
||||
}
|
||||
|
||||
bool WebRTCDataChannels::sendDataMessage(int dataChannelID, const QByteArray& byteArray) {
|
||||
bool WebRTCDataChannels::sendDataMessage(const SockAddr& destination, const QByteArray& byteArray) {
|
||||
auto dataChannelID = destination.toShortString();
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::sendDataMessage() :" << dataChannelID;
|
||||
#endif
|
||||
|
||||
// Find connection.
|
||||
if (!_connectionsByDataChannel.contains(dataChannelID)) {
|
||||
qCWarning(networking_webrtc) << "Could not find data channel to send message on!";
|
||||
if (!_connectionsByID.contains(dataChannelID)) {
|
||||
qCWarning(networking_webrtc) << "Could not find WebRTC data channel to send message on!";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto connection = _connectionsByDataChannel.value(dataChannelID);
|
||||
auto connection = _connectionsByID.value(dataChannelID);
|
||||
DataBuffer buffer(byteArray.toStdString(), true);
|
||||
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);
|
||||
qint64 WebRTCDataChannels::getBufferedAmount(const SockAddr& address) const {
|
||||
auto dataChannelID = address.toShortString();
|
||||
if (!_connectionsByID.contains(dataChannelID)) {
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "WebRTCDataChannels::getBufferedAmount() : Channel doesn't exist:" << dataChannelID;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
auto connection = _connectionsByID.value(dataChannelID);
|
||||
return connection->getBufferedAmount();
|
||||
}
|
||||
|
||||
|
@ -573,10 +581,9 @@ void WebRTCDataChannels::closePeerConnectionNow(WDCConnection* connection) {
|
|||
|
||||
// Delete the WDCConnection.
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Dispose of connection for channel ID:" << connection->getDataChannelID();
|
||||
qCDebug(networking_webrtc) << "Dispose of connection for channel:" << connection->getDataChannelID();
|
||||
#endif
|
||||
_connectionsByWebSocket.remove(connection->getWebSocketID());
|
||||
_connectionsByDataChannel.remove(connection->getDataChannelID());
|
||||
_connectionsByID.remove(connection->getDataChannelID());
|
||||
delete connection;
|
||||
#ifdef WEBRTC_DEBUG
|
||||
qCDebug(networking_webrtc) << "Disposed of connection";
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define emit
|
||||
|
||||
#include "../NodeType.h"
|
||||
#include "../SockAddr.h"
|
||||
|
||||
class WebRTCDataChannels;
|
||||
class WDCConnection;
|
||||
|
@ -128,16 +129,12 @@ public:
|
|||
|
||||
/// @brief Constructs a new WDCConnection and opens a WebRTC data connection.
|
||||
/// @param parent The parent WebRTCDataChannels object.
|
||||
/// @param webSocketID The signaling channel that initiated the opening of the WebRTC data channel.
|
||||
WDCConnection(WebRTCDataChannels* parent, quint16 webSocketID);
|
||||
/// @param dataChannelID The data channel ID.
|
||||
WDCConnection(WebRTCDataChannels* parent, const QString& dataChannelID);
|
||||
|
||||
/// @brief Gets the WebSocket ID.
|
||||
/// @return The ID of the WebSocket.
|
||||
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() const { return _dataChannelID; }
|
||||
/// @brief Gets the data channel ID.
|
||||
/// @return The data channel ID.
|
||||
QString getDataChannelID() const { return _dataChannelID; }
|
||||
|
||||
|
||||
/// @brief Sets the remote session description received from the remote client via the signaling channel.
|
||||
|
@ -159,7 +156,7 @@ public:
|
|||
/// @param data The ICE candidate.
|
||||
void addIceCandidate(QJsonObject& data);
|
||||
|
||||
/// @brief Sends an ICE candidate to the remote vlient via the signaling channel.
|
||||
/// @brief Sends an ICE candidate to the remote client via the signaling channel.
|
||||
/// @param candidate The ICE candidate.
|
||||
void sendIceCandidate(const webrtc::IceCandidateInterface* candidate);
|
||||
|
||||
|
@ -194,8 +191,7 @@ public:
|
|||
|
||||
private:
|
||||
WebRTCDataChannels* _parent;
|
||||
quint16 _webSocketID { 0 };
|
||||
int _dataChannelID { -1 };
|
||||
QString _dataChannelID;
|
||||
|
||||
rtc::scoped_refptr<WDCSetSessionDescriptionObserver> _setSessionDescriptionObserver { nullptr };
|
||||
rtc::scoped_refptr<WDCCreateSessionDescriptionObserver> _createSessionDescriptionObserver { nullptr };
|
||||
|
@ -220,6 +216,9 @@ private:
|
|||
/// Additionally, for debugging purposes, instead of containing a Vircadia protocol payload, a WebRTC message may be an echo
|
||||
/// request. This is bounced back to the client.
|
||||
///
|
||||
/// A WebRTC data channel is identified by the IP address and port of the client WebSocket that was used when opening the data
|
||||
/// channel - this is considered to be the WebRTC data channel's address. The IP address and port of the actual WebRTC
|
||||
/// connection is not used.
|
||||
class WebRTCDataChannels : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -241,36 +240,30 @@ public:
|
|||
/// @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();
|
||||
|
||||
/// @brief Handles a WebRTC data channel opening.
|
||||
/// @param connection The WebRTC data channel connection.
|
||||
/// @param dataChannelID The WebRTC data channel ID.
|
||||
void onDataChannelOpened(WDCConnection* connection, quint16 dataChannelID);
|
||||
/// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`.
|
||||
void onDataChannelOpened(WDCConnection* connection, const QString& dataChannelID);
|
||||
|
||||
/// @brief Emits a signalingMessage to be sent to the Interface client.
|
||||
/// @param message The WebRTC signaling message to send.
|
||||
void sendSignalingMessage(const QJsonObject& message);
|
||||
|
||||
/// @brief Emits a dataMessage received from the Interface client.
|
||||
/// @param dataChannelID The WebRTC data channel the message was received on.
|
||||
/// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`.
|
||||
/// @param byteArray The data message received.
|
||||
void emitDataMessage(int dataChannelID, const QByteArray& byteArray);
|
||||
void emitDataMessage(const QString& dataChannelID, const QByteArray& byteArray);
|
||||
|
||||
/// @brief Sends a data message to an Interface client.
|
||||
/// @param dataChannelID The WebRTC channel ID of the Interface client.
|
||||
/// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`.
|
||||
/// @param message The data message to send.
|
||||
/// @return `true` if the data message was sent, otherwise `false`.
|
||||
bool sendDataMessage(int dataChannelID, const QByteArray& message);
|
||||
bool sendDataMessage(const SockAddr& destination, const QByteArray& message);
|
||||
|
||||
/// @brief Gets the number of bytes waiting to be sent on a data channel.
|
||||
/// @param dataChannelID The data channel ID.
|
||||
/// @param address The address of the signaling WebSocket that the client used to connect.
|
||||
/// @return The number of bytes waiting to be sent on the data channel.
|
||||
qint64 getBufferedAmount(int dataChannelID) const;
|
||||
qint64 getBufferedAmount(const SockAddr& address) const;
|
||||
|
||||
/// @brief Creates a new WebRTC peer connection for connecting to an Interface client.
|
||||
/// @param peerConnectionObserver An observer to monitor the WebRTC peer connection.
|
||||
|
@ -305,9 +298,9 @@ signals:
|
|||
|
||||
/// @brief A WebRTC data message received from the Interface client.
|
||||
/// @details This message is for handling at a higher level in the Vircadia protocol.
|
||||
/// @param dataChannelID The WebRTC data channel ID.
|
||||
/// @param address The address of the signaling WebSocket that the client used to connect.
|
||||
/// @param byteArray The Vircadia protocol message.
|
||||
void dataMessage(int dataChannelID, const QByteArray& byteArray);
|
||||
void dataMessage(const SockAddr& address, const QByteArray& byteArray);
|
||||
|
||||
/// @brief Signals that the peer connection for a WebRTC data channel should be closed.
|
||||
/// @details Used by {@link WebRTCDataChannels.closePeerConnection}.
|
||||
|
@ -326,10 +319,9 @@ private:
|
|||
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> _peerConnectionFactory { nullptr };
|
||||
|
||||
quint16 _lastDataChannelID { 0 }; // First data channel ID is 1.
|
||||
|
||||
QHash<quint16, WDCConnection*> _connectionsByWebSocket;
|
||||
QHash<quint16, WDCConnection*> _connectionsByDataChannel;
|
||||
QHash<QString, WDCConnection*> _connectionsByID; // <client data channel ID, WDCConnection>
|
||||
// The client's WebSocket IP and port is used as the data channel ID to uniquely identify each.
|
||||
// The WebSocket IP address and port is formatted as "n.n.n.n:n", the same as used in WebRTCSignalingServer.
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -62,7 +62,8 @@ void WebRTCSignalingServer::webSocketTextMessageReceived(const QString& message)
|
|||
source->sendTextMessage(echo);
|
||||
} else {
|
||||
// WebRTC message or assignment client echo request. (Send both to target.)
|
||||
json.insert("from", source->peerPort());
|
||||
auto from = source->peerAddress().toString() + ":" + QString::number(source->peerPort());
|
||||
json.insert("from", from);
|
||||
emit messageReceived(json);
|
||||
}
|
||||
} else {
|
||||
|
@ -71,9 +72,9 @@ void WebRTCSignalingServer::webSocketTextMessageReceived(const QString& 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()));
|
||||
auto destinationAddress = message.value("to").toString();
|
||||
if (_webSockets.contains(destinationAddress)) {
|
||||
_webSockets.value(destinationAddress)->sendTextMessage(QString(QJsonDocument(message).toJson()));
|
||||
} else {
|
||||
qCWarning(networking_webrtc) << "Failed to find WebSocket for outgoing WebRTC signaling message.";
|
||||
}
|
||||
|
@ -82,7 +83,9 @@ void WebRTCSignalingServer::sendMessage(const QJsonObject& message) {
|
|||
void WebRTCSignalingServer::webSocketDisconnected() {
|
||||
auto source = qobject_cast<QWebSocket*>(sender());
|
||||
if (source) {
|
||||
_webSockets.remove(source->peerPort());
|
||||
auto address = source->peerAddress().toString() + ":" + QString::number(source->peerPort());
|
||||
delete _webSockets.value(address);
|
||||
_webSockets.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +93,8 @@ 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);
|
||||
auto webSocketAddress = webSocket->peerAddress().toString() + ":" + QString::number(webSocket->peerPort());
|
||||
_webSockets.insert(webSocketAddress, webSocket);
|
||||
}
|
||||
|
||||
#endif // WEBRTC_DATA_CHANNELS
|
||||
|
|
|
@ -39,19 +39,20 @@
|
|||
/// 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.
|
||||
/// | -------- | ---------------------------------------- |
|
||||
/// | `to` | NodeType |
|
||||
/// | `from` | WebSocket IP address & port, "n.n.n.n:n" |
|
||||
/// | [`data`] | WebRTC signaling payload |
|
||||
/// | [`echo`] | Echo request |
|
||||
///
|
||||
/// `*` The `from` field is filled in upon receipt by the WebRTCSignalingServer.
|
||||
///
|
||||
/// | Server -> Interface ||
|
||||
/// | -------- | --------------------- |
|
||||
/// | `to` | WebSocket port number |
|
||||
/// | `from` | NodeType |
|
||||
/// | [`data`] | WebRTC payload |
|
||||
/// | [`echo`] | Echo response |
|
||||
/// | -------- | ---------------------------------------- |
|
||||
/// | `to` | WebSocket IP address & port, "n.n.n.n:n" |
|
||||
/// | `from` | NodeType |
|
||||
/// | [`data`] | WebRTC signaling payload |
|
||||
/// | [`echo`] | Echo response |
|
||||
///
|
||||
class WebRTCSignalingServer : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -97,7 +98,9 @@ private:
|
|||
QHostAddress _address;
|
||||
quint16 _port { 0 };
|
||||
|
||||
QHash<quint16, QWebSocket*> _webSockets; // client WebSocket port, client WebSocket object
|
||||
QHash<QString, QWebSocket*> _webSockets; // <client WebSocket IP address and port, client connection WebSocket object>
|
||||
// The WebSocket IP address and port is formatted as "n.n.n.n:n".
|
||||
// A QString is used rather than a SockAddr, to make signaling easier.
|
||||
|
||||
QTimer* _isWebSocketServerListeningTimer;
|
||||
};
|
||||
|
|
|
@ -78,17 +78,17 @@ void WebRTCSocket::abort() {
|
|||
}
|
||||
|
||||
|
||||
qint64 WebRTCSocket::writeDatagram(const QByteArray& datagram, quint16 port) {
|
||||
qint64 WebRTCSocket::writeDatagram(const QByteArray& datagram, const SockAddr& destination) {
|
||||
clearError();
|
||||
if (_dataChannels.sendDataMessage(port, datagram)) {
|
||||
if (_dataChannels.sendDataMessage(destination, datagram)) {
|
||||
return datagram.length();
|
||||
}
|
||||
setError(QAbstractSocket::SocketError::UnknownSocketError, "Failed to write datagram");
|
||||
return -1;
|
||||
}
|
||||
|
||||
qint64 WebRTCSocket::bytesToWrite(quint16 port) const {
|
||||
return _dataChannels.getBufferedAmount(port);
|
||||
qint64 WebRTCSocket::bytesToWrite(const SockAddr& destination) const {
|
||||
return _dataChannels.getBufferedAmount(destination);
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,12 +114,11 @@ qint64 WebRTCSocket::readDatagram(char* data, qint64 maxSize, QHostAddress* addr
|
|||
}
|
||||
|
||||
if (address) {
|
||||
// WEBRTC TODO: Use signaling channel's remote WebSocket address? Or remote data channel address?
|
||||
*address = QHostAddress::AnyIPv4;
|
||||
*address = datagram.first.getAddress();
|
||||
}
|
||||
|
||||
if (port) {
|
||||
*port = datagram.first;
|
||||
*port = datagram.first.getPort();
|
||||
}
|
||||
|
||||
return length;
|
||||
|
@ -148,8 +147,8 @@ void WebRTCSocket::clearError() {
|
|||
}
|
||||
|
||||
|
||||
void WebRTCSocket::onDataChannelReceivedMessage(int dataChannelID, const QByteArray& message) {
|
||||
_receivedQueue.enqueue(QPair<int, QByteArray>(dataChannelID, message));
|
||||
void WebRTCSocket::onDataChannelReceivedMessage(const SockAddr& source, const QByteArray& message) {
|
||||
_receivedQueue.enqueue(QPair<SockAddr, QByteArray>(source, message));
|
||||
emit readyRead();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
|
||||
|
||||
/// @brief Provides a QUdpSocket-style interface for using WebRTCDataChannels.
|
||||
///
|
||||
/// @details A WebRTC data channel is identified by the IP address and port of the client WebSocket that was used when opening
|
||||
/// the data channel - this is considered to be the WebRTC data channel's address. The IP address and port of the actual WebRTC
|
||||
/// connection is not used.
|
||||
class WebRTCSocket : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -71,28 +75,26 @@ public:
|
|||
void abort();
|
||||
|
||||
/// @brief Nominally gets the host port number.
|
||||
/// @details
|
||||
/// Included for compatibility with the QUdpSocket interface.
|
||||
/// @return <code>0</code>
|
||||
quint16 localPort() const { return 0; }
|
||||
|
||||
/// @brief Nominally gets the socket descriptor.
|
||||
/// @details
|
||||
/// Included for compatibility with the QUdpSocket interface.
|
||||
/// @return <code>-1</code>
|
||||
qintptr socketDescriptor() const { return -1; }
|
||||
|
||||
|
||||
/// @brief Sends a datagram to the host on a data channel.
|
||||
/// @brief Sends a datagram.
|
||||
/// @param datagram The datagram to send.
|
||||
/// @param port The data channel ID.
|
||||
/// @param destination The destination WebRTC data channel address.
|
||||
/// @return The number of bytes if successfully sent, otherwise <code>-1</code>.
|
||||
qint64 writeDatagram(const QByteArray& datagram, quint16 port);
|
||||
qint64 writeDatagram(const QByteArray& datagram, const SockAddr& destination);
|
||||
|
||||
/// @brief Gets the number of bytes waiting to be written.
|
||||
/// @param port The data channel ID.
|
||||
/// @param destination The destination WebRTC data channel address.
|
||||
/// @return The number of bytes waiting to be written.
|
||||
qint64 bytesToWrite(quint16 port) const;
|
||||
qint64 bytesToWrite(const SockAddr& destination) const;
|
||||
|
||||
/// @brief Gets whether there's a datagram waiting to be read.
|
||||
/// @return <code>true</code> if there's a datagram waiting to be read, <code>false</code> if there isn't.
|
||||
|
@ -106,8 +108,8 @@ public:
|
|||
/// @details Any remaining data in the datagram is lost.
|
||||
/// @param data The destination to read the datagram into.
|
||||
/// @param maxSize The maximum number of bytes to read.
|
||||
/// @param address The destination to put the IP address that the datagram was read from. (Not currently set.)
|
||||
/// @param port The destination to put the data channel ID that the datagram was read from.
|
||||
/// @param address The destination to put the WebRTC data channel's IP address.
|
||||
/// @param port The destination to put the WebRTC data channel's port.
|
||||
/// @return The number of bytes read on success; <code>-1</code> if reading unsuccessful.
|
||||
qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address = nullptr, quint16* port = nullptr);
|
||||
|
||||
|
@ -124,9 +126,9 @@ public slots:
|
|||
|
||||
/// @brief Handles the WebRTC data channel receiving a message.
|
||||
/// @details Queues the message to be read via readDatagram.
|
||||
/// @param dataChannelID The data channel that the message was received on.
|
||||
/// @param source The WebRTC data channel that the message was received on.
|
||||
/// @param message The message that was received.
|
||||
void onDataChannelReceivedMessage(int dataChannelID, const QByteArray& message);
|
||||
void onDataChannelReceivedMessage(const SockAddr& source, const QByteArray& message);
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -155,7 +157,7 @@ private:
|
|||
|
||||
bool _isBound { false };
|
||||
|
||||
QQueue<QPair<int, QByteArray>> _receivedQueue; // Messages received are queued for reading from the "socket".
|
||||
QQueue<QPair<SockAddr, QByteArray>> _receivedQueue; // Messages received are queued for reading from the "socket".
|
||||
|
||||
QAbstractSocket::SocketError _lastErrorType { QAbstractSocket::UnknownSocketError };
|
||||
QString _lastErrorString;
|
||||
|
|
Loading…
Reference in a new issue