mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
[client/idd] network: add bidirectional audio
This commit is contained in:
@@ -6,6 +6,7 @@ if(NOT TARGET msquic::msquic)
|
||||
endif()
|
||||
|
||||
set(TRANSPORT_NETWORK_SOURCES
|
||||
audio.c
|
||||
connection.c
|
||||
control.c
|
||||
cursor.c
|
||||
|
||||
2248
client/transports/Network/audio.c
Normal file
2248
client/transports/Network/audio.c
Normal file
File diff suppressed because it is too large
Load Diff
46
client/transports/Network/audio.h
Normal file
46
client/transports/Network/audio.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "connection.h"
|
||||
|
||||
#include "interface/audio.h"
|
||||
|
||||
#include <LGProtocol/NetworkProtocol.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct NetworkAudio NetworkAudio;
|
||||
|
||||
bool networkAudioCreate(NetworkAudio ** audio,
|
||||
NetworkConnection * connection);
|
||||
void networkAudioDestroy(NetworkAudio ** audio);
|
||||
|
||||
void networkAudioConnectionState(NetworkAudio * audio, bool connected);
|
||||
void networkAudioProcess(NetworkAudio * audio);
|
||||
void networkAudioSendReady(NetworkAudio * audio, bool datagram);
|
||||
|
||||
bool networkAudioPacket(NetworkAudio * audio,
|
||||
const LGNetPacketView * packet);
|
||||
bool networkAudioDatagram(NetworkAudio * audio,
|
||||
const LGNetPacketView * packet);
|
||||
|
||||
const LG_AudioOps * networkAudioGetOps(void);
|
||||
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include "network.h"
|
||||
#include "audio.h"
|
||||
#include "connection.h"
|
||||
#include "control.h"
|
||||
#include "cursor.h"
|
||||
@@ -47,6 +48,8 @@
|
||||
#define NETWORK_MAX_PAYLOAD \
|
||||
(LG_NET_CURSOR_SHAPE_HEADER_WIRE_SIZE + LG_NET_CURSOR_MAX_SHAPE_LENGTH)
|
||||
#define NETWORK_MAX_DATAGRAM 1136U
|
||||
#define NETWORK_AUDIO_MAX_PAYLOAD \
|
||||
(LG_NET_AUDIO_DATA_HEADER_WIRE_SIZE + 65536U)
|
||||
|
||||
static const LGNetCapability NETWORK_CAPABILITIES[] =
|
||||
{
|
||||
@@ -85,6 +88,19 @@ static const LGNetCapability NETWORK_CAPABILITIES[] =
|
||||
.maxBidirectionalStreams = 0,
|
||||
.maxUnidirectionalStreams = 0,
|
||||
},
|
||||
{
|
||||
.service = LG_NET_SERVICE_AUDIO,
|
||||
.versionMin =
|
||||
LG_NET_AUDIO_OWNERSHIP_INTRODUCED_SERVICE_VERSION,
|
||||
.versionMax = LG_NET_AUDIO_VERSION_CURRENT,
|
||||
.flags = LG_NET_CAPABILITY_RELIABLE |
|
||||
LG_NET_CAPABILITY_DATAGRAM | LG_NET_CAPABILITY_MULTI_CLIENT,
|
||||
.features = 0,
|
||||
.maxPayloadLength = NETWORK_AUDIO_MAX_PAYLOAD,
|
||||
.maxDatagramPayload = NETWORK_MAX_DATAGRAM,
|
||||
.maxBidirectionalStreams = 0,
|
||||
.maxUnidirectionalStreams = 0,
|
||||
},
|
||||
{
|
||||
.service = LG_NET_SERVICE_CONTROL,
|
||||
.versionMin = LG_NET_CONTROL_VERSION_MIN,
|
||||
@@ -108,6 +124,7 @@ struct NetworkTrustPrompt
|
||||
struct LG_Transport
|
||||
{
|
||||
NetworkConnection * connection;
|
||||
NetworkAudio * audio;
|
||||
NetworkControl * control;
|
||||
NetworkCursor * cursor;
|
||||
NetworkInput * input;
|
||||
@@ -323,6 +340,7 @@ static void networkConnectionState(void * opaque, bool connected,
|
||||
LG_TRANSPORT_UNAVAILABLE : reason;
|
||||
|
||||
networkInputConnectionState(this->input, connected);
|
||||
networkAudioConnectionState(this->audio, connected);
|
||||
networkControlConnectionState(this->control, connected);
|
||||
networkRecoveryConnectionState(this->recovery, connected);
|
||||
const bool cursorAvailable =
|
||||
@@ -356,6 +374,11 @@ static void networkPacket(void * opaque, const LGNetPacketView * packet,
|
||||
case LG_NET_SERVICE_INPUT:
|
||||
valid = networkInputPacket(this->input, packet, datagram);
|
||||
break;
|
||||
case LG_NET_SERVICE_AUDIO:
|
||||
valid = datagram ?
|
||||
networkAudioDatagram(this->audio, packet) :
|
||||
networkAudioPacket(this->audio, packet);
|
||||
break;
|
||||
case LG_NET_SERVICE_CONTROL:
|
||||
valid = networkControlPacket(this->control, packet, datagram);
|
||||
break;
|
||||
@@ -377,8 +400,8 @@ static void networkSendReady(void * opaque, LGNetService service,
|
||||
bool datagram)
|
||||
{
|
||||
LG_Transport * this = opaque;
|
||||
(void)service;
|
||||
(void)datagram;
|
||||
if (service == LG_NET_SERVICE_AUDIO)
|
||||
networkAudioSendReady(this->audio, datagram);
|
||||
lgSignalEvent(this->frameEvent);
|
||||
}
|
||||
|
||||
@@ -439,7 +462,8 @@ static bool networkCreate(LG_Transport ** result)
|
||||
};
|
||||
if (!networkConnectionCreate(&this->connection, ¶ms))
|
||||
goto fail;
|
||||
if (!networkControlCreate(&this->control, this->connection) ||
|
||||
if (!networkAudioCreate(&this->audio, this->connection) ||
|
||||
!networkControlCreate(&this->control, this->connection) ||
|
||||
!networkCursorCreate(&this->cursor, this->connection) ||
|
||||
!networkInputCreate(&this->input, this->connection) ||
|
||||
!networkRecoveryCreate(&this->recovery, this->connection))
|
||||
@@ -453,6 +477,7 @@ fail:
|
||||
networkInputDestroy(&this->input);
|
||||
networkCursorDestroy(&this->cursor);
|
||||
networkControlDestroy(&this->control);
|
||||
networkAudioDestroy(&this->audio);
|
||||
networkConnectionDestroy(&this->connection);
|
||||
if (this->frameEvent)
|
||||
lgFreeEvent(this->frameEvent);
|
||||
@@ -487,6 +512,7 @@ static void networkDestroy(LG_Transport ** transport)
|
||||
networkInputDestroy(&this->input);
|
||||
networkCursorDestroy(&this->cursor);
|
||||
networkControlDestroy(&this->control);
|
||||
networkAudioDestroy(&this->audio);
|
||||
networkConnectionDestroy(&this->connection);
|
||||
lgFreeEvent(this->frameEvent);
|
||||
lgFreeEvent(this->promptIdle);
|
||||
@@ -515,6 +541,7 @@ static LG_TransportStatus networkConnect(LG_Transport * this,
|
||||
static void networkDisconnect(LG_Transport * this)
|
||||
{
|
||||
networkInputConnectionState(this->input, false);
|
||||
networkAudioConnectionState(this->audio, false);
|
||||
networkControlConnectionState(this->control, false);
|
||||
networkRecoveryConnectionState(this->recovery, false);
|
||||
networkCursorConnectionState(this->cursor, false);
|
||||
@@ -690,9 +717,12 @@ static const LG_InputOps * networkGetInputOps(LG_Transport * this,
|
||||
static const LG_AudioOps * networkGetAudioOps(LG_Transport * this,
|
||||
void ** opaque)
|
||||
{
|
||||
(void)this;
|
||||
*opaque = NULL;
|
||||
return NULL;
|
||||
if (!networkConnectionServiceAvailable(this->connection,
|
||||
LG_NET_SERVICE_AUDIO, LG_NET_CAPABILITY_RELIABLE))
|
||||
return NULL;
|
||||
*opaque = this->audio;
|
||||
return networkAudioGetOps();
|
||||
}
|
||||
|
||||
static const LG_ClipboardOps * networkGetClipboardOps(LG_Transport * this,
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
<ClCompile Include="transport\lgmp\CLGMPHost.cpp" />
|
||||
<ClCompile Include="transport\lgmp\CLGMPInputTransport.cpp" />
|
||||
<ClCompile Include="transport\lgmp\CLGMPTransport.cpp" />
|
||||
<ClCompile Include="transport\network\CNetworkAudio.cpp" />
|
||||
<ClCompile Include="transport\network\CNetworkServer.cpp" />
|
||||
<ClCompile Include="transport\network\CNetworkSession.cpp" />
|
||||
<ClCompile Include="transport\network\CNetworkTransport.cpp" />
|
||||
@@ -173,6 +174,7 @@
|
||||
<ClInclude Include="transport\lgmp\CLGMPHost.h" />
|
||||
<ClInclude Include="transport\lgmp\CLGMPInputTransport.h" />
|
||||
<ClInclude Include="transport\lgmp\CLGMPTransport.h" />
|
||||
<ClInclude Include="transport\network\CNetworkAudio.h" />
|
||||
<ClInclude Include="transport\network\CNetworkServer.h" />
|
||||
<ClInclude Include="transport\network\CNetworkSession.h" />
|
||||
<ClInclude Include="transport\network\CNetworkTransport.h" />
|
||||
|
||||
@@ -286,6 +286,9 @@
|
||||
<ClInclude Include="transport\lgmp\CLGMPTransport.h">
|
||||
<Filter>Transport\LGMP</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="transport\network\CNetworkAudio.h">
|
||||
<Filter>Transport\Network</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="transport\network\CNetworkServer.h">
|
||||
<Filter>Transport\Network</Filter>
|
||||
</ClInclude>
|
||||
@@ -477,6 +480,9 @@
|
||||
<ClCompile Include="transport\lgmp\CLGMPTransport.cpp">
|
||||
<Filter>Transport\LGMP</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="transport\network\CNetworkAudio.cpp">
|
||||
<Filter>Transport\Network</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="transport\network\CNetworkServer.cpp">
|
||||
<Filter>Transport\Network</Filter>
|
||||
</ClCompile>
|
||||
|
||||
2701
idd/LGIdd/transport/network/CNetworkAudio.cpp
Normal file
2701
idd/LGIdd/transport/network/CNetworkAudio.cpp
Normal file
File diff suppressed because it is too large
Load Diff
102
idd/LGIdd/transport/network/CNetworkAudio.h
Normal file
102
idd/LGIdd/transport/network/CNetworkAudio.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 2 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "transport/IAudioSource.h"
|
||||
|
||||
#include <LGProtocol/NetworkProtocol.h>
|
||||
|
||||
#include <memory>
|
||||
#include <stdint.h>
|
||||
|
||||
struct NetworkAudioPeer
|
||||
{
|
||||
uint32_t client = 0;
|
||||
uint32_t generation = 0;
|
||||
};
|
||||
|
||||
struct NetworkAudioPeerInfo
|
||||
{
|
||||
uint16_t serviceVersion = 0;
|
||||
LGNetCapabilityFlags capabilityFlags = 0;
|
||||
uint32_t maxReliablePayload = 0;
|
||||
uint32_t maxDatagramPayload = 0;
|
||||
};
|
||||
|
||||
enum class NetworkAudioSendResult
|
||||
{
|
||||
ACCEPTED,
|
||||
BLOCKED,
|
||||
FAILED,
|
||||
};
|
||||
|
||||
enum class NetworkAudioPacketResult
|
||||
{
|
||||
HANDLED,
|
||||
IGNORED,
|
||||
INVALID,
|
||||
};
|
||||
|
||||
class INetworkAudioSender
|
||||
{
|
||||
public:
|
||||
virtual ~INetworkAudioSender() = default;
|
||||
|
||||
virtual bool AudioPeerInfo(const NetworkAudioPeer& peer,
|
||||
NetworkAudioPeerInfo& info) = 0;
|
||||
virtual NetworkAudioSendResult SendAudio(
|
||||
const NetworkAudioPeer& peer, bool datagram, LGNetService service,
|
||||
uint16_t messageType, LGNetEnvelopeFlags flags,
|
||||
uint64_t componentEpoch, uint64_t requestID,
|
||||
const void * payload, uint32_t payloadLength) = 0;
|
||||
};
|
||||
|
||||
class CNetworkAudio final : public IAudioSource
|
||||
{
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
public:
|
||||
CNetworkAudio();
|
||||
~CNetworkAudio() override;
|
||||
|
||||
CNetworkAudio(const CNetworkAudio&) = delete;
|
||||
CNetworkAudio& operator=(const CNetworkAudio&) = delete;
|
||||
|
||||
bool AttachSender(INetworkAudioSender& sender);
|
||||
void DetachSender();
|
||||
|
||||
bool PeerConnected(const NetworkAudioPeer& peer);
|
||||
void PeerDisconnected(const NetworkAudioPeer& peer);
|
||||
NetworkAudioPacketResult HandlePacket(const NetworkAudioPeer& peer,
|
||||
const LGNetPacketView& packet, bool datagram);
|
||||
void Process(uint64_t nowMs);
|
||||
void SendReady(const NetworkAudioPeer& peer,
|
||||
LGNetService service, bool datagram);
|
||||
|
||||
bool Start(IAudioTarget& target) override;
|
||||
void Stop() override;
|
||||
void AudioState(const AudioEndpointStatus& state) override;
|
||||
AudioResult SendPlayback(const AudioPacket& packet,
|
||||
const uint8_t * data, size_t size) override;
|
||||
void CaptureReceiveReady() override;
|
||||
};
|
||||
@@ -394,8 +394,11 @@ struct CNetworkServer::Impl
|
||||
Impl& owner;
|
||||
};
|
||||
|
||||
Impl(CNetworkTransport& transport, const NetworkSettings& settings) :
|
||||
transport(transport), settings(settings)
|
||||
Impl(CNetworkTransport& transport, CNetworkAudio& audio,
|
||||
const NetworkSettings& settings) :
|
||||
transport(transport),
|
||||
audio(audio),
|
||||
settings(settings)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -413,6 +416,7 @@ struct CNetworkServer::Impl
|
||||
}
|
||||
|
||||
CNetworkTransport& transport;
|
||||
CNetworkAudio& audio;
|
||||
NetworkSettings settings;
|
||||
const QUIC_API_TABLE * api = nullptr;
|
||||
HQUIC registration = nullptr;
|
||||
@@ -468,6 +472,80 @@ struct CNetworkServer::Impl
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NetworkAudioPeer AudioPeer(const Connection& connection)
|
||||
{
|
||||
return
|
||||
{
|
||||
connection.client,
|
||||
connection.generation,
|
||||
};
|
||||
}
|
||||
|
||||
bool AudioPeerInfo(const NetworkAudioPeer& peer,
|
||||
NetworkAudioPeerInfo& info)
|
||||
{
|
||||
CSRWSharedLock guard(lock);
|
||||
for (Connection * connection : connections)
|
||||
{
|
||||
if (connection->client != peer.client ||
|
||||
connection->generation != peer.generation)
|
||||
continue;
|
||||
|
||||
CSRWSharedLock connectionGuard(connection->lock);
|
||||
const uint16_t serviceMask =
|
||||
UINT16_C(1) << LG_NET_SERVICE_AUDIO;
|
||||
if (connection->closing ||
|
||||
connection->handshake != HandshakeState::READY ||
|
||||
!(connection->selectedServices & serviceMask))
|
||||
return false;
|
||||
|
||||
const LGNetCapability& capability =
|
||||
connection->selectedCapabilities[LG_NET_SERVICE_AUDIO];
|
||||
info.serviceVersion = capability.versionMin;
|
||||
info.capabilityFlags = capability.flags;
|
||||
info.maxReliablePayload = capability.maxPayloadLength;
|
||||
info.maxDatagramPayload = capability.maxDatagramPayload;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NetworkAudioSendResult SendAudio(
|
||||
const NetworkAudioPeer& peer, bool datagram, LGNetService service,
|
||||
uint16_t messageType, LGNetEnvelopeFlags flags,
|
||||
uint64_t componentEpoch, uint64_t requestID,
|
||||
const void * payload, uint32_t payloadLength)
|
||||
{
|
||||
if (service != LG_NET_SERVICE_AUDIO)
|
||||
return NetworkAudioSendResult::FAILED;
|
||||
|
||||
CSRWSharedLock guard(lock);
|
||||
for (Connection * connection : connections)
|
||||
{
|
||||
if (connection->client != peer.client ||
|
||||
connection->generation != peer.generation)
|
||||
continue;
|
||||
|
||||
const SendResult result = datagram ?
|
||||
TrySendDatagram(*connection, service, messageType, flags,
|
||||
componentEpoch, requestID, payload, payloadLength, true) :
|
||||
TrySendPacket(*connection, service, messageType, flags,
|
||||
componentEpoch, requestID, payload, payloadLength);
|
||||
switch (result)
|
||||
{
|
||||
case SendResult::SENT:
|
||||
return NetworkAudioSendResult::ACCEPTED;
|
||||
|
||||
case SendResult::BLOCKED:
|
||||
return NetworkAudioSendResult::BLOCKED;
|
||||
|
||||
case SendResult::FAILED:
|
||||
return NetworkAudioSendResult::FAILED;
|
||||
}
|
||||
}
|
||||
return NetworkAudioSendResult::FAILED;
|
||||
}
|
||||
|
||||
void Shutdown(Connection& connection, QUIC_UINT62 error)
|
||||
{
|
||||
HQUIC handle = nullptr;
|
||||
@@ -708,6 +786,14 @@ struct CNetworkServer::Impl
|
||||
return ready;
|
||||
}
|
||||
|
||||
void NotifyAudioSendReady(Connection& connection, bool datagram)
|
||||
{
|
||||
if (ConsumeSendReady(
|
||||
connection, LG_NET_SERVICE_AUDIO, datagram))
|
||||
audio.SendReady(
|
||||
AudioPeer(connection), LG_NET_SERVICE_AUDIO, datagram);
|
||||
}
|
||||
|
||||
SendResult TrySendPacket(Connection& connection, LGNetService service,
|
||||
uint16_t messageType, LGNetEnvelopeFlags flags,
|
||||
uint64_t componentEpoch, uint64_t requestID, const void * payload,
|
||||
@@ -1081,6 +1167,11 @@ struct CNetworkServer::Impl
|
||||
minimumPayload = LG_NET_INPUT_ABSOLUTE_WIRE_SIZE;
|
||||
break;
|
||||
|
||||
case LG_NET_SERVICE_AUDIO:
|
||||
minimumPayload = LG_NET_AUDIO_DATA_HEADER_WIRE_SIZE +
|
||||
AUDIO_MAX_PCM_BYTES;
|
||||
break;
|
||||
|
||||
case LG_NET_SERVICE_CONTROL:
|
||||
minimumPayload = LG_NET_CONTROL_FRAME_SCHEDULE_WIRE_SIZE;
|
||||
break;
|
||||
@@ -1094,6 +1185,9 @@ struct CNetworkServer::Impl
|
||||
!(offered.flags & LG_NET_CAPABILITY_RELIABLE) ||
|
||||
offered.versionMin > version->current ||
|
||||
offered.versionMax < version->minimum ||
|
||||
(offered.service == LG_NET_SERVICE_AUDIO &&
|
||||
offered.versionMax <
|
||||
LG_NET_AUDIO_OWNERSHIP_INTRODUCED_SERVICE_VERSION) ||
|
||||
offered.maxPayloadLength < minimumPayload ||
|
||||
connection.maxPayload < minimumPayload)
|
||||
return false;
|
||||
@@ -1109,6 +1203,9 @@ struct CNetworkServer::Impl
|
||||
selected.versionMax = selectedVersion;
|
||||
selected.flags = LG_NET_CAPABILITY_SELECTED |
|
||||
LG_NET_CAPABILITY_RELIABLE;
|
||||
if (offered.service == LG_NET_SERVICE_AUDIO &&
|
||||
(offered.flags & LG_NET_CAPABILITY_MULTI_CLIENT))
|
||||
selected.flags |= LG_NET_CAPABILITY_MULTI_CLIENT;
|
||||
selected.features = 0;
|
||||
selected.maxPayloadLength = offered.maxPayloadLength <
|
||||
connection.maxPayload ? offered.maxPayloadLength :
|
||||
@@ -1119,7 +1216,9 @@ struct CNetworkServer::Impl
|
||||
const uint32_t minimumDatagram = offered.service ==
|
||||
LG_NET_SERVICE_INPUT ? LG_NET_INPUT_ABSOLUTE_WIRE_SIZE :
|
||||
offered.service == LG_NET_SERVICE_CURSOR ?
|
||||
LG_NET_CURSOR_POSITION_WIRE_SIZE : 0;
|
||||
LG_NET_CURSOR_POSITION_WIRE_SIZE :
|
||||
offered.service == LG_NET_SERVICE_AUDIO ?
|
||||
LG_NET_AUDIO_DATA_HEADER_WIRE_SIZE + 2U : 0;
|
||||
if (minimumDatagram &&
|
||||
(offered.flags & LG_NET_CAPABILITY_DATAGRAM) &&
|
||||
offered.maxDatagramPayload >= minimumDatagram &&
|
||||
@@ -1259,7 +1358,8 @@ struct CNetworkServer::Impl
|
||||
if (!session)
|
||||
return false;
|
||||
|
||||
bool activated;
|
||||
bool activated = false;
|
||||
bool audioSelected = false;
|
||||
{
|
||||
CSRWExclusiveLock guard(connection.lock);
|
||||
activated = !connection.closing &&
|
||||
@@ -1276,6 +1376,8 @@ struct CNetworkServer::Impl
|
||||
NetworkAuthCrypto::SecureZero(
|
||||
connection.serverNonce, sizeof(connection.serverNonce));
|
||||
connection.handshake = HandshakeState::READY;
|
||||
audioSelected = (connection.selectedServices &
|
||||
(UINT16_C(1) << LG_NET_SERVICE_AUDIO)) != 0;
|
||||
}
|
||||
}
|
||||
if (!activated)
|
||||
@@ -1283,6 +1385,8 @@ struct CNetworkServer::Impl
|
||||
transport.RemoveSession(connection.client, connection.generation);
|
||||
return false;
|
||||
}
|
||||
if (audioSelected && !audio.PeerConnected(AudioPeer(connection)))
|
||||
return false;
|
||||
DEBUG_INFO("Network client %u completed the control handshake",
|
||||
connection.client);
|
||||
return true;
|
||||
@@ -2757,18 +2861,24 @@ struct CNetworkServer::Impl
|
||||
|
||||
bool HandlePacket(Connection& connection, const LGNetPacketView& packet)
|
||||
{
|
||||
const bool cursorStatus =
|
||||
const bool cursorStatus =
|
||||
packet.envelope.service == LG_NET_SERVICE_CURSOR &&
|
||||
packet.envelope.messageType == LG_NET_CURSOR_MESSAGE_STATUS;
|
||||
const bool audioSubscribe =
|
||||
packet.envelope.service == LG_NET_SERVICE_AUDIO &&
|
||||
packet.envelope.messageType == LG_NET_AUDIO_MESSAGE_SUBSCRIBE;
|
||||
const bool audioResponse =
|
||||
packet.envelope.service == LG_NET_SERVICE_AUDIO &&
|
||||
packet.envelope.messageType == LG_NET_AUDIO_MESSAGE_STATE_ACK;
|
||||
LGNetEnvelopeFlags expectedFlags =
|
||||
LG_NET_ENVELOPE_RELIABLE | LG_NET_ENVELOPE_FINAL;
|
||||
if (audioSubscribe)
|
||||
expectedFlags |= LG_NET_ENVELOPE_ACK_REQUIRED;
|
||||
if (cursorStatus || audioResponse)
|
||||
expectedFlags |= LG_NET_ENVELOPE_RESPONSE;
|
||||
if (packet.envelope.headerSize != LG_NET_ENVELOPE_WIRE_SIZE ||
|
||||
packet.envelope.payloadLength > NETWORK_MAX_CONTROL_PAYLOAD ||
|
||||
packet.envelope.flags & (LG_NET_ENVELOPE_DATAGRAM |
|
||||
LG_NET_ENVELOPE_ERROR | LG_NET_ENVELOPE_EARLY_DATA) ||
|
||||
!!(packet.envelope.flags & LG_NET_ENVELOPE_RESPONSE) !=
|
||||
cursorStatus ||
|
||||
(packet.envelope.flags &
|
||||
(LG_NET_ENVELOPE_RELIABLE | LG_NET_ENVELOPE_FINAL)) !=
|
||||
(LG_NET_ENVELOPE_RELIABLE | LG_NET_ENVELOPE_FINAL) ||
|
||||
packet.envelope.flags != expectedFlags ||
|
||||
!lgNetEnvelopeRouteValid(&packet.envelope, LG_NET_ROLE_CLIENT,
|
||||
LG_NET_ROUTE_DELIVERY_RELIABLE))
|
||||
return false;
|
||||
@@ -2866,6 +2976,10 @@ struct CNetworkServer::Impl
|
||||
case LG_NET_SERVICE_INPUT:
|
||||
return HandleInput(connection, packet, false);
|
||||
|
||||
case LG_NET_SERVICE_AUDIO:
|
||||
return audio.HandlePacket(AudioPeer(connection), packet, false) !=
|
||||
NetworkAudioPacketResult::INVALID;
|
||||
|
||||
case LG_NET_SERVICE_CONTROL:
|
||||
return HandleControl(connection, packet);
|
||||
|
||||
@@ -2932,8 +3046,12 @@ struct CNetworkServer::Impl
|
||||
0, packet.envelope.requestID, packet.payload,
|
||||
static_cast<uint32_t>(packet.payloadLength), true);
|
||||
|
||||
return packet.envelope.service == LG_NET_SERVICE_INPUT &&
|
||||
HandleInput(connection, packet, true);
|
||||
if (packet.envelope.service == LG_NET_SERVICE_INPUT)
|
||||
return HandleInput(connection, packet, true);
|
||||
if (packet.envelope.service == LG_NET_SERVICE_AUDIO)
|
||||
return audio.HandlePacket(AudioPeer(connection), packet, true) !=
|
||||
NetworkAudioPacketResult::INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DrainReceive(Connection& connection)
|
||||
@@ -3037,6 +3155,7 @@ struct CNetworkServer::Impl
|
||||
connection.client);
|
||||
session = std::move(connection.session);
|
||||
}
|
||||
audio.PeerDisconnected(AudioPeer(connection));
|
||||
if (session)
|
||||
transport.RemoveSession(client, generation);
|
||||
session.reset();
|
||||
@@ -3171,13 +3290,16 @@ struct CNetworkServer::Impl
|
||||
|
||||
case QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED:
|
||||
{
|
||||
CSRWExclusiveLock guard(connection.lock);
|
||||
connection.datagramSendEnabled =
|
||||
event->DATAGRAM_STATE_CHANGED.SendEnabled != FALSE;
|
||||
connection.maxDatagramSendLength =
|
||||
event->DATAGRAM_STATE_CHANGED.MaxSendLength;
|
||||
connection.datagramSend.writableServices |=
|
||||
ReadyServicesLocked(connection, true);
|
||||
{
|
||||
CSRWExclusiveLock guard(connection.lock);
|
||||
connection.datagramSendEnabled =
|
||||
event->DATAGRAM_STATE_CHANGED.SendEnabled != FALSE;
|
||||
connection.maxDatagramSendLength =
|
||||
event->DATAGRAM_STATE_CHANGED.MaxSendLength;
|
||||
connection.datagramSend.writableServices |=
|
||||
ReadyServicesLocked(connection, true);
|
||||
}
|
||||
owner.NotifyAudioSendReady(connection, true);
|
||||
return QUIC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -3187,8 +3309,11 @@ struct CNetworkServer::Impl
|
||||
{
|
||||
std::unique_ptr<Send> send(static_cast<Send *>(
|
||||
event->DATAGRAM_SEND_STATE_CHANGED.ClientContext));
|
||||
CSRWExclusiveLock guard(connection.lock);
|
||||
ReleaseSendLocked(connection, *send);
|
||||
{
|
||||
CSRWExclusiveLock guard(connection.lock);
|
||||
ReleaseSendLocked(connection, *send);
|
||||
}
|
||||
owner.NotifyAudioSendReady(connection, true);
|
||||
}
|
||||
return QUIC_STATUS_SUCCESS;
|
||||
|
||||
@@ -3229,8 +3354,11 @@ struct CNetworkServer::Impl
|
||||
{
|
||||
std::unique_ptr<Send> send(
|
||||
static_cast<Send *>(event->SEND_COMPLETE.ClientContext));
|
||||
CSRWExclusiveLock guard(connection.lock);
|
||||
ReleaseSendLocked(connection, *send);
|
||||
{
|
||||
CSRWExclusiveLock guard(connection.lock);
|
||||
ReleaseSendLocked(connection, *send);
|
||||
}
|
||||
owner.NotifyAudioSendReady(connection, false);
|
||||
return QUIC_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -3269,6 +3397,7 @@ struct CNetworkServer::Impl
|
||||
void Process()
|
||||
{
|
||||
const uint64_t now = GetTickCount64();
|
||||
audio.Process(now);
|
||||
CSRWSharedLock guard(lock);
|
||||
if (stopping || !api)
|
||||
return;
|
||||
@@ -3495,8 +3624,8 @@ struct CNetworkServer::Impl
|
||||
};
|
||||
|
||||
CNetworkServer::CNetworkServer(CNetworkTransport& transport,
|
||||
const NetworkSettings& settings) :
|
||||
m_impl(new Impl(transport, settings))
|
||||
CNetworkAudio& audio, const NetworkSettings& settings) :
|
||||
m_impl(new Impl(transport, audio, settings))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3518,3 +3647,21 @@ void CNetworkServer::Process()
|
||||
if (m_impl)
|
||||
m_impl->Process();
|
||||
}
|
||||
|
||||
bool CNetworkServer::AudioPeerInfo(const NetworkAudioPeer& peer,
|
||||
NetworkAudioPeerInfo& info)
|
||||
{
|
||||
return m_impl && m_impl->AudioPeerInfo(peer, info);
|
||||
}
|
||||
|
||||
NetworkAudioSendResult CNetworkServer::SendAudio(
|
||||
const NetworkAudioPeer& peer, bool datagram, LGNetService service,
|
||||
uint16_t messageType, LGNetEnvelopeFlags flags,
|
||||
uint64_t componentEpoch, uint64_t requestID,
|
||||
const void * payload, uint32_t payloadLength)
|
||||
{
|
||||
return m_impl ?
|
||||
m_impl->SendAudio(peer, datagram, service, messageType, flags,
|
||||
componentEpoch, requestID, payload, payloadLength) :
|
||||
NetworkAudioSendResult::FAILED;
|
||||
}
|
||||
|
||||
@@ -20,19 +20,21 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "transport/network/CNetworkAudio.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class CNetworkTransport;
|
||||
struct NetworkSettings;
|
||||
|
||||
class CNetworkServer final
|
||||
class CNetworkServer final : public INetworkAudioSender
|
||||
{
|
||||
private:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
public:
|
||||
CNetworkServer(CNetworkTransport& transport,
|
||||
CNetworkServer(CNetworkTransport& transport, CNetworkAudio& audio,
|
||||
const NetworkSettings& settings);
|
||||
~CNetworkServer();
|
||||
|
||||
@@ -42,4 +44,12 @@ public:
|
||||
bool Start();
|
||||
void Stop();
|
||||
void Process();
|
||||
|
||||
bool AudioPeerInfo(const NetworkAudioPeer& peer,
|
||||
NetworkAudioPeerInfo& info) override;
|
||||
NetworkAudioSendResult SendAudio(
|
||||
const NetworkAudioPeer& peer, bool datagram, LGNetService service,
|
||||
uint16_t messageType, LGNetEnvelopeFlags flags,
|
||||
uint64_t componentEpoch, uint64_t requestID,
|
||||
const void * payload, uint32_t payloadLength) override;
|
||||
};
|
||||
|
||||
@@ -76,13 +76,19 @@ bool CNetworkTransport::Initialize()
|
||||
|
||||
try
|
||||
{
|
||||
m_server.reset(new CNetworkServer(*this, m_settings));
|
||||
m_server.reset(new CNetworkServer(*this, m_audio, m_settings));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
DEBUG_ERROR("Failed to allocate the network server");
|
||||
return false;
|
||||
}
|
||||
if (!m_audio.AttachSender(*m_server))
|
||||
{
|
||||
DEBUG_ERROR("Failed to attach the network audio sender");
|
||||
m_server.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_state = State::INITIALIZED;
|
||||
if (m_config.services & TRANSPORT_SERVICE_FRAME)
|
||||
@@ -157,6 +163,7 @@ void CNetworkTransport::Stop()
|
||||
|
||||
if (server)
|
||||
server->Stop();
|
||||
m_audio.DetachSender();
|
||||
|
||||
std::vector<SessionPtr> sessions;
|
||||
{
|
||||
@@ -167,6 +174,7 @@ void CNetworkTransport::Stop()
|
||||
session->Close();
|
||||
|
||||
m_input.Stop();
|
||||
m_audio.Stop();
|
||||
m_control.SetControlEvents(nullptr, {});
|
||||
m_events.Reset();
|
||||
}
|
||||
@@ -224,6 +232,11 @@ DirectFrameBufferMemory CNetworkTransport::GetDirectMemory() const
|
||||
return DirectFrameBufferMemory {};
|
||||
}
|
||||
|
||||
IAudioSource * CNetworkTransport::Audio()
|
||||
{
|
||||
return (m_config.services & TRANSPORT_SERVICE_AUDIO) ? &m_audio : nullptr;
|
||||
}
|
||||
|
||||
IControlSink * CNetworkTransport::Control()
|
||||
{
|
||||
return (m_config.services & TRANSPORT_SERVICE_CONTROL) ?
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "CSRWLock.h"
|
||||
#include "transport/ITransport.h"
|
||||
#include "transport/network/CNetworkAudio.h"
|
||||
#include "transport/network/CNetworkSession.h"
|
||||
#include "transport/network/NetworkSettings.h"
|
||||
|
||||
@@ -49,6 +50,7 @@ private:
|
||||
|
||||
TransportInstance m_config;
|
||||
NetworkSettings m_settings;
|
||||
CNetworkAudio m_audio;
|
||||
std::unique_ptr<CNetworkServer> m_server;
|
||||
|
||||
mutable CSRWLock m_lifecycleLock;
|
||||
@@ -89,6 +91,7 @@ public:
|
||||
|
||||
std::shared_ptr<const FrameCaps> GetFrameCaps() const override;
|
||||
DirectFrameBufferMemory GetDirectMemory() const override;
|
||||
IAudioSource * Audio() override;
|
||||
IControlSink * Control() override;
|
||||
IInputSource * Input() override;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user