[client/idd] network: implement tiled video transport

This commit is contained in:
Geoffrey McRae
2026-10-01 14:47:29 +10:00
parent e14d5dd3c9
commit bdf5fed38d
12 changed files with 3913 additions and 1207 deletions

View File

@@ -25,6 +25,7 @@
#include "common/debug.h"
#include "common/event.h"
#include "common/locking.h"
#include "common/thread.h"
#include <LGProtocol/NetworkAuth.h>
#include <LGProtocol/NetworkPacket.h>
@@ -50,10 +51,14 @@
#define NETWORK_KEEPALIVE_INTERVAL_MS 1000U
#define NETWORK_INITIAL_RTT_MS 1U
#define NETWORK_MAX_PEER_STREAMS 16U
#define NETWORK_STREAM_RECV_BUFFER (1U * 1024U * 1024U)
#define NETWORK_STREAM_RECV_WINDOW (128U * 1024U * 1024U)
#define NETWORK_CONN_RECV_WINDOW (256U * 1024U * 1024U)
#define NETWORK_MAX_CONTROL_PAYLOAD \
(LG_NET_CURSOR_SHAPE_HEADER_WIRE_SIZE + LG_NET_CURSOR_MAX_SHAPE_LENGTH)
#define NETWORK_MAX_CONTROL_BUFFER \
(2U * (LG_NET_ENVELOPE_WIRE_SIZE + NETWORK_MAX_CONTROL_PAYLOAD))
#define NETWORK_MAX_NEGOTIATED_PAYLOAD LG_NET_MAX_PAYLOAD_LENGTH
#define NETWORK_MAX_DATAGRAM_PAYLOAD 1136U
#define NETWORK_HELLO_REQUEST_ID UINT64_C(1)
#define NETWORK_SEQUENCE_CONNECTION_EPOCH UINT64_C(1)
@@ -125,6 +130,9 @@ struct NetworkBulkStream
bool bound;
bool openingReturned;
bool shutdownComplete;
bool receivePending;
bool receiveFailed;
uint32_t receiveReferences;
uint8_t bindingData[LG_NET_STREAM_BINDING_WIRE_SIZE];
size_t bindingLength;
LGNetStreamParser receiveParser;
@@ -133,6 +141,16 @@ struct NetworkBulkStream
size_t receivePayloadSize;
};
struct NetworkBulkReceive
{
struct NetworkBulkReceive * next;
struct NetworkBulkStream * stream;
HQUIC handle;
uint64_t length;
uint32_t bufferCount;
QUIC_BUFFER buffers[];
};
struct NetworkConnection
{
const QUIC_API_TABLE * api;
@@ -141,10 +159,13 @@ struct NetworkConnection
HQUIC connection;
HQUIC controlStream;
LGEvent * connectEvent;
LGEvent * closeEvent;
LG_Lock lock;
LG_Lock stateCallbackLock;
LGEvent * connectEvent;
LGEvent * closeEvent;
LGEvent * bulkReceiveEvent;
LGEvent * bulkReceiveIdle;
LGThread * bulkReceiveThread;
LG_Lock lock;
LG_Lock stateCallbackLock;
atomic_bool stopping;
atomic_bool sessionValid;
@@ -221,6 +242,11 @@ struct NetworkConnection
[LG_NET_SERVICE_CONTROL + 1U];
struct NetworkBulkStream * inboundBulk;
struct NetworkBulkReceive * bulkReceiveHead;
struct NetworkBulkReceive * bulkReceiveTail;
uint32_t bulkReceiveReferences;
bool bulkReceiveStopping;
uint8_t clientHelloNonce[LG_NET_HELLO_NONCE_LENGTH];
uint8_t serverHelloNonce[LG_NET_HELLO_NONCE_LENGTH];
LGNetPasswordChallenge authChallenge;
@@ -948,7 +974,7 @@ static bool networkConnectionSendHello(NetworkConnection * this)
{
LGNetHello hello;
lgNetHelloInit(&hello, LG_NET_ROLE_CLIENT);
hello.maxPayloadLength = NETWORK_MAX_CONTROL_PAYLOAD;
hello.maxPayloadLength = NETWORK_MAX_NEGOTIATED_PAYLOAD;
hello.maxDatagramPayload = NETWORK_MAX_DATAGRAM_PAYLOAD;
hello.capabilityCount = this->offeredCapabilityCount;
hello.authModes = this->password && *this->password ?
@@ -1443,7 +1469,8 @@ static bool networkConnectionHandleCore(NetworkConnection * this,
acknowledgement.flags ||
acknowledgement.capabilityCount >
this->offeredCapabilityCount ||
acknowledgement.maxPayloadLength > NETWORK_MAX_CONTROL_PAYLOAD ||
acknowledgement.maxPayloadLength >
NETWORK_MAX_NEGOTIATED_PAYLOAD ||
acknowledgement.maxDatagramPayload >
NETWORK_MAX_DATAGRAM_PAYLOAD ||
!networkBytesNonzero(
@@ -1649,6 +1676,7 @@ static bool networkConnectionValidateControl(NetworkConnection * this,
(packet->envelope.service == LG_NET_SERVICE_CONTROL &&
packet->envelope.messageType == LG_NET_CONTROL_MESSAGE_STATUS);
return packet->envelope.headerSize == LG_NET_ENVELOPE_WIRE_SIZE &&
packet->payloadLength <= NETWORK_MAX_CONTROL_PAYLOAD &&
packet->payloadLength <= maxPayloadLength &&
packet->envelope.messageVersion == LG_NET_MESSAGE_VERSION_CURRENT &&
(packet->envelope.flags & LG_NET_ENVELOPE_RELIABLE) &&
@@ -2004,6 +2032,76 @@ static bool networkBulkStreamReceive(struct NetworkBulkStream * stream,
return !stream->bound || networkBulkStreamDrainReceive(stream);
}
static bool networkBulkStreamQueueReceive(struct NetworkBulkStream * stream,
const QUIC_BUFFER * buffers, uint32_t bufferCount, uint64_t length)
{
const size_t bufferBytes = (size_t)bufferCount * sizeof(*buffers);
if ((bufferCount && !buffers) ||
(bufferCount && bufferBytes / sizeof(*buffers) != bufferCount) ||
bufferBytes > SIZE_MAX - sizeof(struct NetworkBulkReceive))
return false;
uint64_t bufferLength = 0;
for (uint32_t index = 0; index < bufferCount; ++index)
{
if ((buffers[index].Length && !buffers[index].Buffer) ||
UINT64_MAX - bufferLength < buffers[index].Length)
return false;
bufferLength += buffers[index].Length;
}
if (bufferLength != length)
return false;
const size_t receiveSize =
sizeof(struct NetworkBulkReceive) + bufferBytes;
struct NetworkBulkReceive * receive = malloc(receiveSize);
if (!receive)
return false;
receive->next = NULL;
receive->stream = stream;
receive->handle = stream->handle;
receive->length = length;
receive->bufferCount = bufferCount;
/* MsQuic retains the payload while this receive is pending, but the buffer
* descriptor array itself is only valid for the callback duration. */
if (bufferCount)
memcpy(receive->buffers, buffers, bufferBytes);
NetworkConnection * this = stream->connection;
LG_LOCK(this->lock);
/* Multi-receive is intentionally disabled for this connection. */
const bool accepted =
!this->bulkReceiveStopping &&
!atomic_load_explicit(&this->stopping, memory_order_acquire) &&
!stream->shutdownComplete &&
!stream->receivePending &&
!stream->receiveFailed;
if (accepted)
{
if (!this->bulkReceiveReferences)
lgResetEvent(this->bulkReceiveIdle);
++this->bulkReceiveReferences;
++stream->receiveReferences;
stream->receivePending = true;
if (this->bulkReceiveTail)
this->bulkReceiveTail->next = receive;
else
this->bulkReceiveHead = receive;
this->bulkReceiveTail = receive;
}
LG_UNLOCK(this->lock);
if (!accepted)
{
free(receive);
return false;
}
lgSignalEvent(this->bulkReceiveEvent);
return true;
}
static LG_TransportStatus networkConnectionOpenBulkStream(
NetworkConnection * this, LGNetService service, uint64_t packetSize)
{
@@ -2106,6 +2204,76 @@ static void networkConnectionProtocolFailure(NetworkConnection * this)
QUIC_CONNECTION_SHUTDOWN_FLAG_SILENT, 0);
}
static int networkBulkReceiveThread(void * opaque)
{
NetworkConnection * this = opaque;
for (;;)
{
lgWaitEvent(this->bulkReceiveEvent, TIMEOUT_INFINITE);
for (;;)
{
LG_LOCK(this->lock);
struct NetworkBulkReceive * receive = this->bulkReceiveHead;
if (receive)
{
this->bulkReceiveHead = receive->next;
if (!this->bulkReceiveHead)
this->bulkReceiveTail = NULL;
}
const bool stopping = this->bulkReceiveStopping;
LG_UNLOCK(this->lock);
if (!receive)
{
if (stopping)
return 0;
break;
}
struct NetworkBulkStream * stream = receive->stream;
const bool received = networkBulkStreamReceive(stream,
receive->buffers, receive->bufferCount);
LG_LOCK(this->lock);
stream->receivePending = false;
if (!received)
stream->receiveFailed = true;
LG_UNLOCK(this->lock);
this->api->StreamReceiveComplete(receive->handle, receive->length);
if (!received && !atomic_load_explicit(
&this->stopping, memory_order_acquire))
networkConnectionProtocolFailure(this);
bool closeStream = false;
bool freeStream = false;
LG_LOCK(this->lock);
if (stream->receiveReferences)
--stream->receiveReferences;
if (this->bulkReceiveReferences)
{
--this->bulkReceiveReferences;
if (!this->bulkReceiveReferences)
lgSignalEvent(this->bulkReceiveIdle);
}
if (!stream->receiveReferences && stream->shutdownComplete)
{
closeStream = true;
freeStream = !stream->outbound || stream->openingReturned;
}
LG_UNLOCK(this->lock);
if (closeStream)
this->api->StreamClose(receive->handle);
if (freeStream)
networkBulkStreamFree(stream);
free(receive);
}
}
}
static bool networkConnectionAcceptBulkStream(NetworkConnection * this,
HQUIC handle, QUIC_STREAM_OPEN_FLAGS flags)
{
@@ -2560,10 +2728,16 @@ static QUIC_STATUS QUIC_API networkBulkStreamCallback(HQUIC handle,
}
case QUIC_STREAM_EVENT_RECEIVE:
if (stream->outbound || !networkBulkStreamReceive(stream,
event->RECEIVE.Buffers, event->RECEIVE.BufferCount))
networkConnectionProtocolFailure(this);
return QUIC_STATUS_SUCCESS;
if (stream->outbound || !networkBulkStreamQueueReceive(stream,
event->RECEIVE.Buffers, event->RECEIVE.BufferCount,
event->RECEIVE.TotalBufferLength))
{
if (!atomic_load_explicit(
&this->stopping, memory_order_acquire))
networkConnectionProtocolFailure(this);
return QUIC_STATUS_SUCCESS;
}
return QUIC_STATUS_PENDING;
case QUIC_STREAM_EVENT_SEND_COMPLETE:
{
@@ -2587,11 +2761,13 @@ static QUIC_STATUS QUIC_API networkBulkStreamCallback(HQUIC handle,
{
LG_LOCK(this->lock);
networkBulkStreamDetachNL(stream);
const bool deferFree =
stream->outbound && !stream->openingReturned;
const bool deferClose = stream->receiveReferences != 0;
const bool deferFree = deferClose ||
(stream->outbound && !stream->openingReturned);
stream->shutdownComplete = deferFree;
LG_UNLOCK(this->lock);
this->api->StreamClose(handle);
if (!deferClose)
this->api->StreamClose(handle);
if (!deferFree)
networkBulkStreamFree(stream);
return QUIC_STATUS_SUCCESS;
@@ -2663,9 +2839,13 @@ bool networkConnectionCreate(NetworkConnection ** result,
this->sendReady = params->sendReady;
this->opaque = params->opaque;
this->connectEvent = lgCreateEvent(false, 0);
this->closeEvent = lgCreateEvent(false, 0);
if (!this->connectEvent || !this->closeEvent)
this->connectEvent = lgCreateEvent(false, 0);
this->closeEvent = lgCreateEvent(false, 0);
this->bulkReceiveEvent = lgCreateEvent(true, 0);
this->bulkReceiveIdle = lgCreateEvent(false, 0);
if (!this->connectEvent || !this->closeEvent ||
!this->bulkReceiveEvent || !this->bulkReceiveIdle ||
!lgSignalEvent(this->bulkReceiveIdle))
goto fail;
this->receiveData = malloc(NETWORK_MAX_CONTROL_BUFFER);
@@ -2696,21 +2876,31 @@ bool networkConnectionCreate(NetworkConnection ** result,
{
.IsSet =
{
.HandshakeIdleTimeoutMs = true,
.IdleTimeoutMs = true,
.InitialRttMs = true,
.KeepAliveIntervalMs = true,
.PeerBidiStreamCount = true,
.PeerUnidiStreamCount = true,
.DatagramReceiveEnabled = true,
.HandshakeIdleTimeoutMs = true,
.IdleTimeoutMs = true,
.StreamRecvBufferDefault = true,
.ConnFlowControlWindow = true,
.InitialRttMs = true,
.MaxAckDelayMs = true,
.KeepAliveIntervalMs = true,
.PeerBidiStreamCount = true,
.PeerUnidiStreamCount = true,
.SendBufferingEnabled = true,
.DatagramReceiveEnabled = true,
.StreamRecvWindowUnidiDefault = true,
},
.HandshakeIdleTimeoutMs = NETWORK_HANDSHAKE_TIMEOUT_MS,
.IdleTimeoutMs = NETWORK_IDLE_TIMEOUT_MS,
.InitialRttMs = NETWORK_INITIAL_RTT_MS,
.KeepAliveIntervalMs = NETWORK_KEEPALIVE_INTERVAL_MS,
.PeerBidiStreamCount = NETWORK_MAX_PEER_STREAMS,
.PeerUnidiStreamCount = NETWORK_MAX_PEER_STREAMS,
.DatagramReceiveEnabled = true,
.HandshakeIdleTimeoutMs = NETWORK_HANDSHAKE_TIMEOUT_MS,
.IdleTimeoutMs = NETWORK_IDLE_TIMEOUT_MS,
.StreamRecvBufferDefault = NETWORK_STREAM_RECV_BUFFER,
.ConnFlowControlWindow = NETWORK_CONN_RECV_WINDOW,
.InitialRttMs = NETWORK_INITIAL_RTT_MS,
.MaxAckDelayMs = 1,
.KeepAliveIntervalMs = NETWORK_KEEPALIVE_INTERVAL_MS,
.PeerBidiStreamCount = NETWORK_MAX_PEER_STREAMS,
.PeerUnidiStreamCount = NETWORK_MAX_PEER_STREAMS,
.SendBufferingEnabled = false,
.DatagramReceiveEnabled = true,
.StreamRecvWindowUnidiDefault = NETWORK_STREAM_RECV_WINDOW,
};
if (QUIC_FAILED(this->api->ConfigurationOpen(this->registration,
&alpn, 1, &settings, sizeof(settings), NULL,
@@ -2731,6 +2921,10 @@ bool networkConnectionCreate(NetworkConnection ** result,
this->configuration, &credential)))
goto fail;
if (!lgCreateThread("networkReceive", networkBulkReceiveThread,
this, &this->bulkReceiveThread))
goto fail;
*result = this;
return true;
@@ -2745,13 +2939,34 @@ void networkConnectionDestroy(NetworkConnection ** connection)
return;
NetworkConnection * this = *connection;
LGThread * bulkReceiveThread;
*connection = NULL;
networkConnectionDisconnect(this);
LG_LOCK(this->lock);
this->bulkReceiveStopping = true;
bulkReceiveThread = this->bulkReceiveThread;
this->bulkReceiveThread = NULL;
LG_UNLOCK(this->lock);
if (this->bulkReceiveEvent)
lgSignalEvent(this->bulkReceiveEvent);
if (bulkReceiveThread && !lgJoinThread(bulkReceiveThread, NULL))
{
DEBUG_ERROR("Failed to stop the network receive worker; "
"preserving state");
return;
}
if (this->configuration)
this->api->ConfigurationClose(this->configuration);
if (this->registration)
this->api->RegistrationClose(this->registration);
if (this->api)
MsQuicClose(this->api);
if (this->bulkReceiveIdle)
lgFreeEvent(this->bulkReceiveIdle);
if (this->bulkReceiveEvent)
lgFreeEvent(this->bulkReceiveEvent);
if (this->closeEvent)
lgFreeEvent(this->closeEvent);
if (this->connectEvent)
@@ -2766,7 +2981,6 @@ void networkConnectionDestroy(NetworkConnection ** connection)
free(this->password);
free(this->address);
free(this);
*connection = NULL;
}
LG_TransportStatus networkConnectionConnect(NetworkConnection * this,
@@ -2805,7 +3019,7 @@ LG_TransportStatus networkConnectionConnect(NetworkConnection * this,
this->bulkServices = 0;
this->maxPeerBulkStreams = 0;
this->peerBulkStreams = 0;
this->maxPayloadLength = NETWORK_MAX_CONTROL_PAYLOAD;
this->maxPayloadLength = NETWORK_MAX_NEGOTIATED_PAYLOAD;
this->maxDatagramPayload = 0;
this->maxDatagramSendLength = 0;
this->datagramSendEnabled = false;
@@ -2921,6 +3135,7 @@ void networkConnectionDisconnect(NetworkConnection * this)
return;
lgWaitEvent(this->closeEvent, TIMEOUT_INFINITE);
lgWaitEvent(this->bulkReceiveIdle, TIMEOUT_INFINITE);
LG_LOCK(this->stateCallbackLock);
LG_LOCK(this->lock);
const bool ownsConnection = this->connection == connection;

View File

@@ -57,9 +57,7 @@
(LG_NET_CLIPBOARD_CHUNK_HEADER_WIRE_SIZE + NETWORK_TRANSFER_CHUNK)
#define NETWORK_FILE_MAX_PAYLOAD \
(LG_NET_FILE_CHUNK_HEADER_WIRE_SIZE + NETWORK_TRANSFER_CHUNK)
#define NETWORK_VIDEO_MAX_PAYLOAD \
(LG_NET_VIDEO_FRAGMENT_HEADER_WIRE_SIZE + \
LG_NET_VIDEO_MAX_FRAGMENT_LENGTH)
#define NETWORK_VIDEO_MAX_PAYLOAD LG_NET_MAX_PAYLOAD_LENGTH
static const LGNetCapability NETWORK_CAPABILITIES[] =
{

File diff suppressed because it is too large Load Diff

View File

@@ -62,6 +62,7 @@ NetworkVideoDecodeResult networkVideoDecoderConfigure(
NetworkVideoDecoder * decoder, const LGNetVideoStreamConfig * config);
NetworkVideoDecodeResult networkVideoDecoderDecode(
NetworkVideoDecoder * decoder, const LGNetVideoStreamConfig * config,
const void * data, size_t size, NetworkVideoDecodedFrame * frame);
const void * data, size_t size, uint32_t blockCount,
NetworkVideoDecodedFrame * frame);
void networkVideoDecoderRelease(NetworkVideoDecodedFrame * frame);

View File

@@ -61,11 +61,16 @@
#include <stdlib.h>
#include <string.h>
#define NETWORK_VIDEO_LEVELS 5U
#define NETWORK_VIDEO_COMPONENTS 3U
#define NETWORK_VIDEO_BANDS 4U
#define NETWORK_VIDEO_ALIGNMENT (1U << NETWORK_VIDEO_LEVELS)
#define NETWORK_VIDEO_MIN_SIZE (4U << NETWORK_VIDEO_LEVELS)
#define NETWORK_VIDEO_LEVELS 5U
#define NETWORK_VIDEO_COMPONENTS 3U
#define NETWORK_VIDEO_BANDS 4U
#define NETWORK_VIDEO_ALIGNMENT (1U << NETWORK_VIDEO_LEVELS)
#define NETWORK_VIDEO_MIN_SIZE (4U << NETWORK_VIDEO_LEVELS)
#define NETWORK_VIDEO_UPLOADS 3U
#define NETWORK_VIDEO_POOL_SIZE 3U
#define NETWORK_VIDEO_POOL_BYTE_LIMIT \
(UINT64_C(192) * 1024U * 1024U)
#define NETWORK_VIDEO_PIXEL_BYTES 8U
struct NetworkVideoBlockMeta
{
@@ -83,6 +88,72 @@ struct NetworkVideoCurrent
EGLenum api;
};
struct NetworkVideoUpload
{
GLuint offsetBuffer;
GLuint payloadBuffer;
GLsync readySync;
size_t payloadCapacity;
};
struct NetworkVideoTexture
{
GLuint texture;
GLsync readySync;
};
struct NetworkVideoTexturePool
{
LG_Lock lock;
atomic_uint references;
EGLDisplay display;
EGLConfig eglConfig;
EGLContext shareContext;
struct NetworkVideoTexture textures[NETWORK_VIDEO_POOL_SIZE];
unsigned textureCount;
unsigned textureLimit;
uint32_t width;
uint32_t height;
};
struct NetworkVideoUniforms
{
struct
{
GLint resolution;
GLint outputLayer;
GLint blockOffset;
GLint blockStride;
}
dequant;
struct
{
GLint coefficients;
GLint resolution;
GLint invResolution;
GLint baseLayer;
GLint dcShift;
}
idwt;
struct
{
GLint lumaPlane;
GLint cbPlane;
GLint crPlane;
GLint outputSize;
GLint bt2020;
GLint chromaLeft;
GLint lumaOffset;
GLint lumaScale;
GLint chromaMidpoint;
GLint chromaScale;
}
yuv;
};
struct NetworkVideoDecoder
{
LG_Lock lock;
@@ -92,13 +163,15 @@ struct NetworkVideoDecoder
EGLContext shareContext;
EGLContext context;
GLuint dequantProgram;
GLuint idwtProgram;
GLuint yuvProgram;
GLuint coefficientTextures[NETWORK_VIDEO_LEVELS];
GLuint planeTextures [NETWORK_VIDEO_COMPONENTS];
GLuint offsetBuffer;
GLuint payloadBuffer;
GLuint dequantProgram;
GLuint idwtProgram;
GLuint yuvProgram;
GLuint coefficientTextures[NETWORK_VIDEO_LEVELS];
GLuint planeTextures [NETWORK_VIDEO_COMPONENTS];
struct NetworkVideoUpload uploads [NETWORK_VIDEO_UPLOADS];
struct NetworkVideoTexturePool * texturePool;
struct NetworkVideoUniforms uniforms;
uint32_t * offsets;
LGNetVideoStreamConfig config;
struct NetworkVideoBlockMeta
@@ -108,6 +181,7 @@ struct NetworkVideoDecoder
uint32_t alignedWidth;
uint32_t alignedHeight;
uint32_t blockCapacity;
unsigned uploadIndex;
GLint maxTextureSize;
GLint maxArrayLayers;
GLint maxWorkGroupCount[2];
@@ -118,23 +192,13 @@ struct NetworkVideoDecoder
struct NetworkVideoExternalFrame
{
LG_ExternalFrame base;
atomic_uint references;
EGLDisplay display;
EGLConfig eglConfig;
EGLContext shareContext;
GLuint texture;
GLsync readySync;
LG_ExternalFrame base;
atomic_uint references;
struct NetworkVideoTexturePool * pool;
GLuint texture;
GLsync readySync;
};
static uint32_t networkVideoReadU32(const uint8_t * data)
{
return (uint32_t)data[0] |
(uint32_t)data[1] << 8 |
(uint32_t)data[2] << 16 |
(uint32_t)data[3] << 24;
}
static uint32_t networkVideoAlign(uint32_t value, uint32_t alignment)
{
return (value + alignment - 1U) & ~(alignment - 1U);
@@ -179,6 +243,159 @@ static void networkVideoRestoreCurrent(NetworkVideoDecoder * decoder,
eglBindAPI(previous->api);
}
static bool networkVideoTexturePoolMakeCurrent(
const struct NetworkVideoTexturePool * pool,
struct NetworkVideoCurrent * previous, EGLContext * temporary)
{
networkVideoSaveCurrent(previous);
*temporary = EGL_NO_CONTEXT;
if (previous->context != EGL_NO_CONTEXT &&
previous->display == pool->display &&
previous->context == pool->shareContext)
return true;
const EGLint attributes[] =
{
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE,
};
if (eglBindAPI(EGL_OPENGL_ES_API))
*temporary = eglCreateContext(pool->display, pool->eglConfig,
pool->shareContext, attributes);
if (*temporary != EGL_NO_CONTEXT &&
eglMakeCurrent(pool->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
*temporary))
return true;
if (*temporary != EGL_NO_CONTEXT)
{
eglDestroyContext(pool->display, *temporary);
*temporary = EGL_NO_CONTEXT;
}
eglBindAPI(previous->api);
return false;
}
static void networkVideoTexturePoolRestoreCurrent(
const struct NetworkVideoTexturePool * pool,
const struct NetworkVideoCurrent * previous, EGLContext temporary)
{
if (temporary == EGL_NO_CONTEXT)
return;
eglMakeCurrent(pool->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglDestroyContext(pool->display, temporary);
if (previous->context != EGL_NO_CONTEXT &&
previous->display != EGL_NO_DISPLAY)
{
eglBindAPI(previous->api);
eglMakeCurrent(previous->display, previous->draw, previous->read,
previous->context);
}
else
eglBindAPI(previous->api);
}
static struct NetworkVideoTexturePool * networkVideoTexturePoolCreate(
const NetworkVideoDecoder * decoder)
{
struct NetworkVideoTexturePool * pool = calloc(1, sizeof(*pool));
if (!pool)
return NULL;
const uint64_t width = decoder->config.width;
const uint64_t height = decoder->config.height;
const uint64_t textureBytes = width * height * NETWORK_VIDEO_PIXEL_BYTES;
const uint64_t textureLimit = textureBytes ?
NETWORK_VIDEO_POOL_BYTE_LIMIT / textureBytes : 0U;
LG_LOCK_INIT(pool->lock);
atomic_init(&pool->references, 1U);
pool->display = decoder->display;
pool->eglConfig = decoder->eglConfig;
pool->shareContext = decoder->shareContext;
pool->width = decoder->config.width;
pool->height = decoder->config.height;
pool->textureLimit = textureLimit < NETWORK_VIDEO_POOL_SIZE ?
(unsigned)textureLimit : NETWORK_VIDEO_POOL_SIZE;
return pool;
}
static void networkVideoTexturePoolRetain(
struct NetworkVideoTexturePool * pool)
{
atomic_fetch_add_explicit(&pool->references, 1U, memory_order_relaxed);
}
static void networkVideoTexturePoolRelease(
struct NetworkVideoTexturePool * pool, bool current)
{
if (!pool || atomic_fetch_sub_explicit(
&pool->references, 1U, memory_order_acq_rel) != 1U)
return;
if (pool->textureCount)
{
struct NetworkVideoCurrent previous;
EGLContext temporary = EGL_NO_CONTEXT;
bool usable = current;
if (!usable)
usable = networkVideoTexturePoolMakeCurrent(
pool, &previous, &temporary);
if (usable)
{
for (unsigned index = 0; index < pool->textureCount; ++index)
{
struct NetworkVideoTexture * texture = &pool->textures[index];
if (texture->readySync)
glDeleteSync(texture->readySync);
if (texture->texture)
glDeleteTextures(1, &texture->texture);
}
}
if (!current && usable)
networkVideoTexturePoolRestoreCurrent(pool, &previous, temporary);
}
LG_LOCK_FREE(pool->lock);
free(pool);
}
static bool networkVideoTexturePoolReturn(
struct NetworkVideoTexturePool * pool, GLuint texture, GLsync readySync)
{
if (!pool || !texture)
return false;
bool retained = false;
LG_LOCK(pool->lock);
if (pool->textureCount < pool->textureLimit)
{
struct NetworkVideoTexture * pooled =
&pool->textures[pool->textureCount++];
pooled->texture = texture;
pooled->readySync = readySync;
retained = true;
}
LG_UNLOCK(pool->lock);
return retained;
}
static void networkVideoTexturePoolRecycleCurrent(
struct NetworkVideoTexturePool * pool, GLuint texture, GLsync readySync)
{
if (networkVideoTexturePoolReturn(pool, texture, readySync))
return;
if (readySync)
glDeleteSync(readySync);
if (texture)
glDeleteTextures(1, &texture);
}
static GLuint networkVideoCompileShader(
const char * const * sources, GLsizei count)
{
@@ -237,16 +454,26 @@ static void networkVideoDeleteConfigurationNL(NetworkVideoDecoder * decoder)
{
glDeleteTextures(NETWORK_VIDEO_LEVELS, decoder->coefficientTextures);
glDeleteTextures(NETWORK_VIDEO_COMPONENTS, decoder->planeTextures);
if (decoder->offsetBuffer)
glDeleteBuffers(1, &decoder->offsetBuffer);
if (decoder->payloadBuffer)
glDeleteBuffers(1, &decoder->payloadBuffer);
for (unsigned index = 0; index < NETWORK_VIDEO_UPLOADS; ++index)
{
struct NetworkVideoUpload * upload = &decoder->uploads[index];
if (upload->readySync)
glDeleteSync(upload->readySync);
if (upload->offsetBuffer)
glDeleteBuffers(1, &upload->offsetBuffer);
if (upload->payloadBuffer)
glDeleteBuffers(1, &upload->payloadBuffer);
}
networkVideoTexturePoolRelease(decoder->texturePool, true);
free(decoder->offsets);
decoder->offsetBuffer = 0;
decoder->payloadBuffer = 0;
memset(decoder->uploads, 0, sizeof(decoder->uploads));
decoder->texturePool = NULL;
decoder->offsets = NULL;
decoder->alignedWidth = 0;
decoder->alignedHeight = 0;
decoder->blockCapacity = 0;
decoder->uploadIndex = 0;
decoder->configured = false;
memset(decoder->coefficientTextures, 0,
sizeof(decoder->coefficientTextures));
@@ -266,6 +493,78 @@ static void networkVideoDeleteProgramsNL(NetworkVideoDecoder * decoder)
decoder->dequantProgram = 0;
decoder->idwtProgram = 0;
decoder->yuvProgram = 0;
memset(&decoder->uniforms, 0, sizeof(decoder->uniforms));
}
static bool networkVideoCacheUniformsNL(NetworkVideoDecoder * decoder)
{
decoder->uniforms.dequant.resolution = glGetUniformLocation(
decoder->dequantProgram, "resolution");
decoder->uniforms.dequant.outputLayer = glGetUniformLocation(
decoder->dequantProgram, "outputLayer");
decoder->uniforms.dequant.blockOffset = glGetUniformLocation(
decoder->dequantProgram, "blockOffset");
decoder->uniforms.dequant.blockStride = glGetUniformLocation(
decoder->dequantProgram, "blockStride");
decoder->uniforms.idwt.coefficients = glGetUniformLocation(
decoder->idwtProgram, "coefficients");
decoder->uniforms.idwt.resolution = glGetUniformLocation(
decoder->idwtProgram, "resolution");
decoder->uniforms.idwt.invResolution = glGetUniformLocation(
decoder->idwtProgram, "invResolution");
decoder->uniforms.idwt.baseLayer = glGetUniformLocation(
decoder->idwtProgram, "baseLayer");
decoder->uniforms.idwt.dcShift = glGetUniformLocation(
decoder->idwtProgram, "dcShift");
decoder->uniforms.yuv.lumaPlane = glGetUniformLocation(
decoder->yuvProgram, "lumaPlane");
decoder->uniforms.yuv.cbPlane = glGetUniformLocation(
decoder->yuvProgram, "cbPlane");
decoder->uniforms.yuv.crPlane = glGetUniformLocation(
decoder->yuvProgram, "crPlane");
decoder->uniforms.yuv.outputSize = glGetUniformLocation(
decoder->yuvProgram, "outputSize");
decoder->uniforms.yuv.bt2020 = glGetUniformLocation(
decoder->yuvProgram, "bt2020");
decoder->uniforms.yuv.chromaLeft = glGetUniformLocation(
decoder->yuvProgram, "chromaLeft");
decoder->uniforms.yuv.lumaOffset = glGetUniformLocation(
decoder->yuvProgram, "lumaOffset");
decoder->uniforms.yuv.lumaScale = glGetUniformLocation(
decoder->yuvProgram, "lumaScale");
decoder->uniforms.yuv.chromaMidpoint = glGetUniformLocation(
decoder->yuvProgram, "chromaMidpoint");
decoder->uniforms.yuv.chromaScale = glGetUniformLocation(
decoder->yuvProgram, "chromaScale");
const struct NetworkVideoUniforms * uniforms = &decoder->uniforms;
if (uniforms->dequant.resolution < 0 ||
uniforms->dequant.outputLayer < 0 ||
uniforms->dequant.blockOffset < 0 ||
uniforms->dequant.blockStride < 0 ||
uniforms->idwt.coefficients < 0 ||
uniforms->idwt.resolution < 0 ||
uniforms->idwt.invResolution < 0 ||
uniforms->idwt.baseLayer < 0 ||
uniforms->idwt.dcShift < 0 ||
uniforms->yuv.lumaPlane < 0 ||
uniforms->yuv.cbPlane < 0 || uniforms->yuv.crPlane < 0 ||
uniforms->yuv.outputSize < 0 || uniforms->yuv.bt2020 < 0 ||
uniforms->yuv.chromaLeft < 0 || uniforms->yuv.lumaOffset < 0 ||
uniforms->yuv.lumaScale < 0 ||
uniforms->yuv.chromaMidpoint < 0 ||
uniforms->yuv.chromaScale < 0)
return false;
glUseProgram(decoder->idwtProgram);
glUniform1i(uniforms->idwt.coefficients, 0);
glUseProgram(decoder->yuvProgram);
glUniform1i(uniforms->yuv.lumaPlane, 0);
glUniform1i(uniforms->yuv.cbPlane, 1);
glUniform1i(uniforms->yuv.crPlane, 2);
return glGetError() == GL_NO_ERROR;
}
static bool networkVideoBuildLayoutNL(NetworkVideoDecoder * decoder,
@@ -365,6 +664,30 @@ static bool networkVideoCreateTexture2D(GLuint * texture, GLenum format,
return false;
}
static GLuint networkVideoTexturePoolAcquire(
struct NetworkVideoTexturePool * pool)
{
struct NetworkVideoTexture pooled = { 0 };
LG_LOCK(pool->lock);
if (pool->textureCount)
{
pooled = pool->textures[--pool->textureCount];
memset(&pool->textures[pool->textureCount], 0,
sizeof(pool->textures[pool->textureCount]));
}
LG_UNLOCK(pool->lock);
if (pooled.readySync)
glDeleteSync(pooled.readySync);
if (pooled.texture)
return pooled.texture;
GLuint texture = 0;
if (!networkVideoCreateTexture2D(&texture, GL_RGBA16F,
pool->width, pool->height, GL_LINEAR))
return 0;
return texture;
}
static bool networkVideoAllocateNL(NetworkVideoDecoder * decoder)
{
while (glGetError() != GL_NO_ERROR)
@@ -405,14 +728,26 @@ static bool networkVideoAllocateNL(NetworkVideoDecoder * decoder)
return false;
}
glGenBuffers(1, &decoder->offsetBuffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, decoder->offsetBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER,
(GLsizeiptr)decoder->blockCapacity * sizeof(uint32_t),
NULL, GL_STREAM_DRAW);
glGenBuffers(1, &decoder->payloadBuffer);
return decoder->offsetBuffer && decoder->payloadBuffer &&
glGetError() == GL_NO_ERROR;
decoder->offsets = malloc(
(size_t)decoder->blockCapacity * sizeof(*decoder->offsets));
decoder->texturePool = networkVideoTexturePoolCreate(decoder);
if (!decoder->offsets || !decoder->texturePool)
return false;
for (unsigned index = 0; index < NETWORK_VIDEO_UPLOADS; ++index)
{
struct NetworkVideoUpload * upload = &decoder->uploads[index];
glGenBuffers(1, &upload->offsetBuffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, upload->offsetBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER,
(GLsizeiptr)decoder->blockCapacity * sizeof(*decoder->offsets),
NULL, GL_STREAM_DRAW);
glGenBuffers(1, &upload->payloadBuffer);
if (!upload->offsetBuffer || !upload->payloadBuffer)
return false;
}
return glGetError() == GL_NO_ERROR;
}
static void networkVideoExternalRetain(LG_ExternalFrame * base)
@@ -426,56 +761,25 @@ static void networkVideoExternalRetain(LG_ExternalFrame * base)
static void networkVideoExternalDelete(
struct NetworkVideoExternalFrame * frame)
{
struct NetworkVideoCurrent previous;
networkVideoSaveCurrent(&previous);
EGLContext temporary = EGL_NO_CONTEXT;
bool changed = false;
bool usable = previous.context != EGL_NO_CONTEXT &&
previous.context == frame->shareContext &&
previous.display == frame->display;
if (!usable)
struct NetworkVideoTexturePool * pool = frame->pool;
const bool retained = networkVideoTexturePoolReturn(
pool, frame->texture, frame->readySync);
if (!retained)
{
const EGLint attributes[] =
struct NetworkVideoCurrent previous;
EGLContext temporary = EGL_NO_CONTEXT;
const bool usable = networkVideoTexturePoolMakeCurrent(
pool, &previous, &temporary);
if (usable)
{
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE,
};
if (eglBindAPI(EGL_OPENGL_ES_API))
temporary = eglCreateContext(frame->display, frame->eglConfig,
frame->shareContext, attributes);
if (temporary != EGL_NO_CONTEXT &&
eglMakeCurrent(frame->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
temporary))
{
changed = true;
usable = true;
if (frame->readySync)
glDeleteSync(frame->readySync);
if (frame->texture)
glDeleteTextures(1, &frame->texture);
networkVideoTexturePoolRestoreCurrent(pool, &previous, temporary);
}
}
if (usable)
{
if (frame->readySync)
glDeleteSync(frame->readySync);
if (frame->texture)
glDeleteTextures(1, &frame->texture);
}
if (temporary != EGL_NO_CONTEXT)
{
if (changed)
eglMakeCurrent(frame->display, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT);
eglDestroyContext(frame->display, temporary);
}
if (previous.context != EGL_NO_CONTEXT &&
previous.display != EGL_NO_DISPLAY)
{
eglBindAPI(previous.api);
eglMakeCurrent(previous.display, previous.draw, previous.read,
previous.context);
}
else
eglBindAPI(previous.api);
networkVideoTexturePoolRelease(pool, false);
}
static void networkVideoExternalRelease(LG_ExternalFrame * base)
@@ -657,9 +961,11 @@ bool networkVideoDecoderAttach(NetworkVideoDecoder * decoder,
NETWORK_VIDEO_YUV_SOURCES,
sizeof(NETWORK_VIDEO_YUV_SOURCES) /
sizeof(NETWORK_VIDEO_YUV_SOURCES[0]));
const bool programsReady = decoder->dequantProgram &&
decoder->idwtProgram && decoder->yuvProgram &&
networkVideoCacheUniformsNL(decoder);
networkVideoRestoreCurrent(decoder, &previous);
if (!decoder->dequantProgram || !decoder->idwtProgram ||
!decoder->yuvProgram)
if (!programsReady)
goto fail;
decoder->attached = true;
@@ -743,50 +1049,59 @@ static bool networkVideoConfigMatches(
left->bitDepth == right->bitDepth && left->flags == right->flags;
}
static bool networkVideoPayloadOffsetsNL(NetworkVideoDecoder * decoder,
const uint8_t * data, size_t size, uint32_t * offsets,
bool * chromaLeft)
static size_t networkVideoPayloadCapacity(size_t size)
{
for (uint32_t index = 0; index < decoder->blockCapacity; ++index)
offsets[index] = UINT32_MAX;
size_t capacity = 64U * 1024U;
while (capacity < size)
capacity *= 2U;
return capacity;
}
size_t cursor = 0;
bool header = false;
while (cursor + 8U <= size)
static struct NetworkVideoUpload * networkVideoUploadNL(
NetworkVideoDecoder * decoder, const void * data, size_t size)
{
static const uint64_t padding = 0;
struct NetworkVideoUpload * upload =
&decoder->uploads[decoder->uploadIndex++ % NETWORK_VIDEO_UPLOADS];
if (upload->readySync)
{
const uint32_t first = networkVideoReadU32(data + cursor);
const uint32_t second = networkVideoReadU32(data + cursor + 4U);
if (first >> 31)
{
*chromaLeft = (second >> 31) != 0;
header = true;
cursor += 8U;
continue;
}
const uint32_t words = (first >> 16) & UINT32_C(0x00000FFF);
const uint32_t block = second >> 8;
const size_t bytes = (size_t)words * sizeof(uint32_t);
if (!words || block >= decoder->blockCapacity || bytes > size - cursor)
return false;
if (offsets[block] == UINT32_MAX)
offsets[block] = (uint32_t)(cursor / sizeof(uint32_t));
cursor += bytes;
const GLenum wait = glClientWaitSync(upload->readySync,
GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED);
if (wait != GL_ALREADY_SIGNALED && wait != GL_CONDITION_SATISFIED)
return NULL;
glDeleteSync(upload->readySync);
upload->readySync = 0;
}
return header && cursor == size;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, upload->offsetBuffer);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0,
(GLsizeiptr)decoder->blockCapacity * sizeof(*decoder->offsets),
decoder->offsets);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, upload->payloadBuffer);
const size_t required = size + sizeof(padding);
if (upload->payloadCapacity < required)
{
upload->payloadCapacity = networkVideoPayloadCapacity(required);
glBufferData(GL_SHADER_STORAGE_BUFFER,
(GLsizeiptr)upload->payloadCapacity,
NULL, GL_STREAM_DRAW);
}
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)size, data);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, (GLintptr)size,
sizeof(padding), &padding);
if (glGetError() != GL_NO_ERROR)
return NULL;
return upload;
}
static void networkVideoSetUniform2i(
GLuint program, const char * name, GLint x, GLint y)
{
glUniform2i(glGetUniformLocation(program, name), x, y);
}
static void networkVideoDequantNL(NetworkVideoDecoder * decoder)
static void networkVideoDequantNL(NetworkVideoDecoder * decoder,
const struct NetworkVideoUpload * upload)
{
glUseProgram(decoder->dequantProgram);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, decoder->offsetBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, decoder->payloadBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, upload->offsetBuffer);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, upload->payloadBuffer);
for (unsigned level = 0; level < NETWORK_VIDEO_LEVELS; ++level)
{
@@ -794,8 +1109,7 @@ static void networkVideoDequantNL(NetworkVideoDecoder * decoder)
const uint32_t height = decoder->alignedHeight >> (level + 1U);
glBindImageTexture(0, decoder->coefficientTextures[level], 0,
GL_TRUE, 0, GL_WRITE_ONLY, GL_R32F);
networkVideoSetUniform2i(decoder->dequantProgram,
"resolution", width, height);
glUniform2i(decoder->uniforms.dequant.resolution, width, height);
for (unsigned component = 0;
component < NETWORK_VIDEO_COMPONENTS; ++component)
@@ -809,12 +1123,10 @@ static void networkVideoDequantNL(NetworkVideoDecoder * decoder)
{
const struct NetworkVideoBlockMeta * meta =
&decoder->blocks[component][level][band];
glUniform1i(glGetUniformLocation(decoder->dequantProgram,
"outputLayer"), component * NETWORK_VIDEO_BANDS + band);
glUniform1i(glGetUniformLocation(decoder->dequantProgram,
"blockOffset"), meta->offset);
glUniform1i(glGetUniformLocation(decoder->dequantProgram,
"blockStride"), meta->stride);
glUniform1i(decoder->uniforms.dequant.outputLayer,
component * NETWORK_VIDEO_BANDS + band);
glUniform1i(decoder->uniforms.dequant.blockOffset, meta->offset);
glUniform1i(decoder->uniforms.dequant.blockStride, meta->stride);
glDispatchCompute((width + 31U) / 32U, (height + 31U) / 32U, 1);
}
}
@@ -827,8 +1139,6 @@ static void networkVideoIDWTNL(NetworkVideoDecoder * decoder)
{
glUseProgram(decoder->idwtProgram);
glActiveTexture(GL_TEXTURE0);
glUniform1i(glGetUniformLocation(decoder->idwtProgram,
"coefficients"), 0);
for (unsigned component = 0;
component < NETWORK_VIDEO_COMPONENTS; ++component)
@@ -842,17 +1152,15 @@ static void networkVideoIDWTNL(NetworkVideoDecoder * decoder)
decoder->coefficientTextures[level]);
const uint32_t inputWidth = decoder->alignedWidth >> (level + 1);
const uint32_t inputHeight = decoder->alignedHeight >> (level + 1);
networkVideoSetUniform2i(decoder->idwtProgram,
"resolution", inputHeight, inputWidth);
glUniform2f(glGetUniformLocation(decoder->idwtProgram,
"invResolution"), 1.0f / inputHeight, 1.0f / inputWidth);
glUniform1i(glGetUniformLocation(decoder->idwtProgram,
"inputLevel"), level);
glUniform1i(glGetUniformLocation(decoder->idwtProgram,
"baseLayer"), component * NETWORK_VIDEO_BANDS);
glUniform2i(decoder->uniforms.idwt.resolution,
inputHeight, inputWidth);
glUniform2f(decoder->uniforms.idwt.invResolution,
1.0f / inputHeight, 1.0f / inputWidth);
glUniform1i(decoder->uniforms.idwt.baseLayer,
component * NETWORK_VIDEO_BANDS);
const bool final = level == (int)finalLevel;
glUniform1i(glGetUniformLocation(decoder->idwtProgram,
"dcShift"), final ? GL_TRUE : GL_FALSE);
glUniform1i(decoder->uniforms.idwt.dcShift,
final ? GL_TRUE : GL_FALSE);
if (final)
{
@@ -910,9 +1218,8 @@ static void networkVideoColorRange(
static struct NetworkVideoExternalFrame * networkVideoConvertNL(
NetworkVideoDecoder * decoder, bool chromaLeft)
{
GLuint texture = 0;
if (!networkVideoCreateTexture2D(&texture, GL_RGBA16F,
decoder->config.width, decoder->config.height, GL_LINEAR))
GLuint texture = networkVideoTexturePoolAcquire(decoder->texturePool);
if (!texture)
return NULL;
glUseProgram(decoder->yuvProgram);
@@ -922,29 +1229,21 @@ static struct NetworkVideoExternalFrame * networkVideoConvertNL(
glActiveTexture(GL_TEXTURE0 + component);
glBindTexture(GL_TEXTURE_2D, decoder->planeTextures[component]);
}
glUniform1i(glGetUniformLocation(decoder->yuvProgram, "lumaPlane"), 0);
glUniform1i(glGetUniformLocation(decoder->yuvProgram, "cbPlane"), 1);
glUniform1i(glGetUniformLocation(decoder->yuvProgram, "crPlane"), 2);
networkVideoSetUniform2i(decoder->yuvProgram, "outputSize",
glUniform2i(decoder->uniforms.yuv.outputSize,
decoder->config.width, decoder->config.height);
glUniform1i(glGetUniformLocation(decoder->yuvProgram, "bt2020"),
glUniform1i(decoder->uniforms.yuv.bt2020,
decoder->config.colorMatrix == LG_NET_COLOR_MATRIX_BT2020);
glUniform1i(glGetUniformLocation(decoder->yuvProgram, "chromaLeft"),
chromaLeft);
glUniform1i(decoder->uniforms.yuv.chromaLeft, chromaLeft);
float lumaOffset;
float lumaScale;
float chromaMidpoint;
float chromaScale;
networkVideoColorRange(&decoder->config,
&lumaOffset, &lumaScale, &chromaMidpoint, &chromaScale);
glUniform1f(glGetUniformLocation(decoder->yuvProgram, "lumaOffset"),
lumaOffset);
glUniform1f(glGetUniformLocation(decoder->yuvProgram, "lumaScale"),
lumaScale);
glUniform1f(glGetUniformLocation(decoder->yuvProgram, "chromaMidpoint"),
chromaMidpoint);
glUniform1f(glGetUniformLocation(decoder->yuvProgram, "chromaScale"),
chromaScale);
glUniform1f(decoder->uniforms.yuv.lumaOffset, lumaOffset);
glUniform1f(decoder->uniforms.yuv.lumaScale, lumaScale);
glUniform1f(decoder->uniforms.yuv.chromaMidpoint, chromaMidpoint);
glUniform1f(decoder->uniforms.yuv.chromaScale, chromaScale);
glBindImageTexture(0, texture, 0, GL_FALSE, 0,
GL_WRITE_ONLY, GL_RGBA16F);
glDispatchCompute((decoder->config.width + 7U) / 8U,
@@ -956,32 +1255,31 @@ static struct NetworkVideoExternalFrame * networkVideoConvertNL(
glFlush();
if (!readySync || glGetError() != GL_NO_ERROR)
{
if (readySync)
glDeleteSync(readySync);
glDeleteTextures(1, &texture);
networkVideoTexturePoolRecycleCurrent(
decoder->texturePool, texture, readySync);
return NULL;
}
struct NetworkVideoExternalFrame * frame = calloc(1, sizeof(*frame));
if (!frame)
{
glDeleteSync(readySync);
glDeleteTextures(1, &texture);
networkVideoTexturePoolRecycleCurrent(
decoder->texturePool, texture, readySync);
return NULL;
}
frame->base.ops = &NETWORK_VIDEO_EXTERNAL_OPS;
frame->display = decoder->display;
frame->eglConfig = decoder->eglConfig;
frame->shareContext = decoder->shareContext;
frame->pool = decoder->texturePool;
frame->texture = texture;
frame->readySync = readySync;
networkVideoTexturePoolRetain(frame->pool);
atomic_init(&frame->references, 1U);
return frame;
}
NetworkVideoDecodeResult networkVideoDecoderDecode(
NetworkVideoDecoder * decoder, const LGNetVideoStreamConfig * config,
const void * data, size_t size, NetworkVideoDecodedFrame * result)
const void * data, size_t size, uint32_t blockCount,
NetworkVideoDecodedFrame * result)
{
if (!decoder || !config || !data || !size || !result)
return NETWORK_VIDEO_DECODE_CORRUPT;
@@ -995,21 +1293,20 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
return NETWORK_VIDEO_DECODE_UNSUPPORTED;
}
uint32_t * offsets = malloc(
(size_t)decoder->blockCapacity * sizeof(*offsets));
bool chromaLeft = false;
if (!offsets || !networkVideoPayloadOffsetsNL(decoder,
data, size, offsets, &chromaLeft))
NetworkPyroWaveLayout layout;
const NetworkPyroWaveResult parse = networkPyroWaveParse(
config, data, size, blockCount, decoder->offsets,
decoder->blockCapacity, &layout);
if (parse != NETWORK_PYROWAVE_OK)
{
free(offsets);
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_CORRUPT;
return parse == NETWORK_PYROWAVE_UNSUPPORTED ?
NETWORK_VIDEO_DECODE_UNSUPPORTED : NETWORK_VIDEO_DECODE_CORRUPT;
}
struct NetworkVideoCurrent previous;
if (!networkVideoMakeCurrent(decoder, &previous))
{
free(offsets);
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_ERROR;
}
@@ -1017,20 +1314,21 @@ NetworkVideoDecodeResult networkVideoDecoderDecode(
while (glGetError() != GL_NO_ERROR)
{
}
glBindBuffer(GL_SHADER_STORAGE_BUFFER, decoder->offsetBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER,
(GLsizeiptr)decoder->blockCapacity * sizeof(*offsets),
offsets, GL_STREAM_DRAW);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, decoder->payloadBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, size + 8U, NULL, GL_STREAM_DRAW);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, size, data);
free(offsets);
struct NetworkVideoUpload * upload = networkVideoUploadNL(
decoder, data, size);
if (!upload)
{
networkVideoRestoreCurrent(decoder, &previous);
LG_UNLOCK(decoder->lock);
return NETWORK_VIDEO_DECODE_ERROR;
}
const uint64_t decodeStart = nanotime();
networkVideoDequantNL(decoder);
networkVideoDequantNL(decoder, upload);
upload->readySync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
networkVideoIDWTNL(decoder);
struct NetworkVideoExternalFrame * external =
networkVideoConvertNL(decoder, chromaLeft);
networkVideoConvertNL(decoder, layout.chromaLeft);
const uint64_t decodeTime = nanotime() - decodeStart;
const bool success = external && glGetError() == GL_NO_ERROR;
networkVideoRestoreCurrent(decoder, &previous);

View File

@@ -48,7 +48,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define NETWORK_PYROWAVE_HEADER_SIZE 8U
#define NETWORK_PYROWAVE_DECOMPOSITION_LEVELS 5U
@@ -224,38 +224,41 @@ static bool networkPyroWaveSequenceProfile(
range == wireRange;
}
NetworkPyroWaveResult networkPyroWaveValidate(
NetworkPyroWaveResult networkPyroWaveParse(
const LGNetVideoStreamConfig * config, const void * data, size_t size,
uint32_t expectedBlocks, uint8_t * sequence)
uint32_t expectedBlocks, uint32_t * offsets, uint32_t offsetCount,
NetworkPyroWaveLayout * layout)
{
if (!networkPyroWaveProfileValid(config))
return NETWORK_PYROWAVE_UNSUPPORTED;
if (!data || size < NETWORK_PYROWAVE_HEADER_SIZE || !expectedBlocks)
if (!data || size < NETWORK_PYROWAVE_HEADER_SIZE ||
size > config->maxFrameLength || !expectedBlocks || !offsets || !layout)
return NETWORK_PYROWAVE_INVALID;
uint32_t capacity;
if (!networkPyroWaveBlockCapacity(config, &capacity) ||
expectedBlocks > capacity)
expectedBlocks > capacity || offsetCount != capacity)
return NETWORK_PYROWAVE_INVALID;
const size_t seenSize = ((size_t)capacity + 7U) / 8U;
uint8_t * seen = calloc(1, seenSize);
if (!seen)
return NETWORK_PYROWAVE_NO_MEMORY;
memset(offsets, 0xff, (size_t)capacity * sizeof(*offsets));
*layout = (NetworkPyroWaveLayout){0};
const uint8_t * bytes = data;
size_t remaining = size;
size_t cursor = 0;
uint32_t uniqueBlocks = 0;
uint32_t declaredBlocks = 0;
uint32_t headerFirst = 0;
uint32_t headerSecond = 0;
uint8_t frameSequence = 0;
bool haveSequence = false;
bool haveHeader = false;
bool valid = true;
while (valid && remaining >= NETWORK_PYROWAVE_HEADER_SIZE)
while (valid && size - cursor >= NETWORK_PYROWAVE_HEADER_SIZE)
{
const uint32_t first = networkPyroWaveReadU32(bytes);
const uint32_t second = networkPyroWaveReadU32(bytes + 4U);
const uint32_t first = networkPyroWaveReadU32(bytes + cursor);
const uint32_t second = networkPyroWaveReadU32(
bytes + cursor + sizeof(uint32_t));
const uint8_t packetSequence =
(uint8_t)((first >> 28) & NETWORK_PYROWAVE_SEQUENCE_MASK);
const bool extended = (first >> 31) != 0;
@@ -277,16 +280,18 @@ NetworkPyroWaveResult networkPyroWaveValidate(
if (code || width != config->width || height != config->height ||
!blocks || blocks > capacity ||
!networkPyroWaveSequenceProfile(config, second) ||
(haveHeader && blocks != declaredBlocks))
(haveHeader &&
(first != headerFirst || second != headerSecond)))
{
valid = false;
break;
}
declaredBlocks = blocks;
headerFirst = first;
headerSecond = second;
haveHeader = true;
bytes += NETWORK_PYROWAVE_HEADER_SIZE;
remaining -= NETWORK_PYROWAVE_HEADER_SIZE;
cursor += NETWORK_PYROWAVE_HEADER_SIZE;
continue;
}
@@ -294,31 +299,26 @@ NetworkPyroWaveResult networkPyroWaveValidate(
const uint32_t payloadWords = (first >> 16) & UINT32_C(0x00000FFF);
const uint32_t blockIndex = second >> 8;
const size_t packetSize = (size_t)payloadWords * sizeof(uint32_t);
if (!ballot || payloadWords < 2U || packetSize > remaining ||
blockIndex >= capacity ||
!networkPyroWavePacketValid(bytes, packetSize, (uint16_t)ballot))
if (!ballot || payloadWords < 2U || packetSize > size - cursor ||
blockIndex >= capacity || offsets[blockIndex] != UINT32_MAX ||
!networkPyroWavePacketValid(
bytes + cursor, packetSize, (uint16_t)ballot))
{
valid = false;
break;
}
const uint8_t mask = (uint8_t)(1U << (blockIndex & 7U));
if (!(seen[blockIndex >> 3] & mask))
{
seen[blockIndex >> 3] |= mask;
++uniqueBlocks;
}
bytes += packetSize;
remaining -= packetSize;
offsets[blockIndex] = (uint32_t)(cursor / sizeof(uint32_t));
++uniqueBlocks;
cursor += packetSize;
}
valid = valid && !remaining && haveHeader && haveSequence &&
valid = valid && cursor == size && haveHeader && haveSequence &&
declaredBlocks == uniqueBlocks && expectedBlocks == uniqueBlocks;
free(seen);
if (!valid)
return NETWORK_PYROWAVE_INVALID;
if (sequence)
*sequence = frameSequence;
layout->sequence = frameSequence;
layout->chromaLeft = (headerSecond >> 31) != 0;
return NETWORK_PYROWAVE_OK;
}

View File

@@ -31,12 +31,19 @@ typedef enum NetworkPyroWaveResult
NETWORK_PYROWAVE_OK,
NETWORK_PYROWAVE_INVALID,
NETWORK_PYROWAVE_UNSUPPORTED,
NETWORK_PYROWAVE_NO_MEMORY,
}
NetworkPyroWaveResult;
typedef struct NetworkPyroWaveLayout
{
uint8_t sequence;
bool chromaLeft;
}
NetworkPyroWaveLayout;
bool networkPyroWaveProfileValid(const LGNetVideoStreamConfig * config);
NetworkPyroWaveResult networkPyroWaveValidate(
NetworkPyroWaveResult networkPyroWaveParse(
const LGNetVideoStreamConfig * config, const void * data, size_t size,
uint32_t expectedBlocks, uint8_t * sequence);
uint32_t expectedBlocks, uint32_t * offsets, uint32_t offsetCount,
NetworkPyroWaveLayout * layout);

View File

@@ -190,7 +190,6 @@ static const char NETWORK_VIDEO_IDWT_SHADER_0[] =
"layout(r32f,binding=1) writeonly uniform highp image2D outputImage;\n"
"uniform ivec2 resolution;\n"
"uniform vec2 invResolution;\n"
"uniform int inputLevel;\n"
"uniform int baseLayer;\n"
"uniform bool dcShift;\n"
"const int APRON=4, HALF_APRON=2, BLOCK=32, HALF_BLOCK=16;\n"

View File

@@ -59,11 +59,19 @@ namespace
static constexpr uint16_t NETWORK_PEER_BIDI_STREAMS = 16;
static constexpr uint16_t NETWORK_PEER_UNIDI_STREAMS = 16;
static constexpr uint16_t NETWORK_BULK_STREAMS_PER_SERVICE = 1;
static constexpr uint32_t NETWORK_STREAM_RECV_BUFFER =
1U * 1024U * 1024U;
static constexpr uint32_t NETWORK_STREAM_RECV_WINDOW =
128U * 1024U * 1024U;
static constexpr uint32_t NETWORK_CONN_RECV_WINDOW =
256U * 1024U * 1024U;
static constexpr uint32_t NETWORK_MAX_CONTROL_PAYLOAD =
LG_NET_CURSOR_SHAPE_HEADER_WIRE_SIZE +
LG_NET_CURSOR_MAX_SHAPE_LENGTH;
static constexpr size_t NETWORK_MAX_CONTROL_BUFFER =
LG_NET_ENVELOPE_WIRE_SIZE + NETWORK_MAX_CONTROL_PAYLOAD;
static constexpr uint32_t NETWORK_MAX_NEGOTIATED_PAYLOAD =
LG_NET_MAX_PAYLOAD_LENGTH;
static constexpr uint32_t NETWORK_MAX_DATAGRAM_PAYLOAD = 1136;
static constexpr uint32_t NETWORK_INPUT_LEASE_MS = 500;
static constexpr uint8_t NETWORK_KEYBOARD_ROLLOVER = 0x01;
@@ -95,6 +103,10 @@ namespace
static constexpr uint32_t NETWORK_SEND_BULK_COUNT_MAX = 256;
static constexpr uint64_t NETWORK_SEND_BULK_BYTES_MAX =
UINT64_C(256) * 1024U * 1024U;
static constexpr uint32_t NETWORK_VIDEO_BULK_COUNT_MAX = 3;
static constexpr uint64_t NETWORK_VIDEO_BULK_BYTES_MAX =
static_cast<uint64_t>(NETWORK_VIDEO_BULK_COUNT_MAX) *
(LG_NET_ENVELOPE_WIRE_SIZE + LG_NET_MAX_PAYLOAD_LENGTH);
static constexpr uint32_t NETWORK_SEND_RESUME_NUMERATOR = 3;
static constexpr uint32_t NETWORK_SEND_RESUME_DENOMINATOR = 4;
@@ -352,12 +364,18 @@ struct CNetworkServer::Impl
struct Send
{
QUIC_BUFFER buffer = {};
using SharedData = std::shared_ptr<const std::vector<uint8_t>>;
QUIC_BUFFER buffers [2] = {};
uint8_t envelope[LG_NET_ENVELOPE_WIRE_SIZE] = {};
std::vector<uint8_t> data;
LGNetService service = LG_NET_SERVICE_CORE;
uint32_t length = 0;
Delivery delivery = Delivery::RELIABLE;
bool accounted = true;
SharedData sharedData;
uint32_t bufferCount = 1;
LGNetService service = LG_NET_SERVICE_CORE;
uint32_t length = 0;
Delivery delivery = Delivery::RELIABLE;
bool accounted = true;
};
struct SendLimits
@@ -826,7 +844,8 @@ struct CNetworkServer::Impl
connection->selectedCapabilities[LG_NET_SERVICE_VIDEO];
info.serviceVersion = capability.versionMin;
info.capabilityFlags = capability.flags;
info.maxReliablePayload = capability.maxPayloadLength;
info.maxReliablePayload = (std::min)(
capability.maxPayloadLength, NETWORK_MAX_CONTROL_PAYLOAD);
info.maxDatagramPayload = capability.maxDatagramPayload;
info.maxBulkPayload =
capability.flags & LG_NET_CAPABILITY_BULK ?
@@ -840,8 +859,16 @@ struct CNetworkServer::Impl
const NetworkVideoPeer& peer, NetworkVideoDelivery delivery,
uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength)
std::shared_ptr<const std::vector<uint8_t>> payload)
{
if (payload && payload->size() > UINT32_MAX)
return NetworkVideoSendResult::FAILED;
const auto payloadLength = static_cast<uint32_t>(
payload ? payload->size() : 0U);
const auto payloadData = payloadLength ?
static_cast<const void *>(payload->data()) : nullptr;
CSRWSharedLock guard(lock);
for (Connection * connection : connections)
{
@@ -855,19 +882,19 @@ struct CNetworkServer::Impl
case NetworkVideoDelivery::RELIABLE:
result = TrySendPacket(*connection, LG_NET_SERVICE_VIDEO,
messageType, flags, componentEpoch, requestID,
payload, payloadLength);
payloadData, payloadLength);
break;
case NetworkVideoDelivery::DATAGRAM:
result = TrySendDatagram(*connection, LG_NET_SERVICE_VIDEO,
messageType, flags, componentEpoch, requestID,
payload, payloadLength, true);
payloadData, payloadLength, true, std::move(payload));
break;
case NetworkVideoDelivery::BULK:
result = TrySendBulk(*connection, LG_NET_SERVICE_VIDEO,
messageType, flags, componentEpoch, requestID,
payload, payloadLength);
payloadData, payloadLength, std::move(payload));
break;
}
@@ -926,7 +953,11 @@ struct CNetworkServer::Impl
switch (service)
{
case LG_NET_SERVICE_VIDEO:
return { 128U, UINT64_C(192) * 1024U * 1024U };
return
{
NETWORK_VIDEO_BULK_COUNT_MAX,
NETWORK_VIDEO_BULK_BYTES_MAX,
};
case LG_NET_SERVICE_CLIPBOARD:
case LG_NET_SERVICE_FILE:
@@ -1390,10 +1421,11 @@ struct CNetworkServer::Impl
return SendResult::FAILED;
}
send->buffer.Buffer = send->data.data();
send->buffer.Length = static_cast<uint32_t>(wireSize);
status = api->StreamSend(connection.controlStream, &send->buffer, 1,
QUIC_SEND_FLAG_NONE, send.get());
send->buffers[0].Buffer = send->data.data();
send->buffers[0].Length = static_cast<uint32_t>(wireSize);
status = api->StreamSend(
connection.controlStream, send->buffers,
send->bufferCount, QUIC_SEND_FLAG_NONE, send.get());
if (QUIC_FAILED(status))
{
--connection.sendSequence;
@@ -1530,8 +1562,17 @@ struct CNetworkServer::Impl
SendResult TrySendBulk(Connection& connection, LGNetService service,
uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID, const void * payload,
uint32_t payloadLength)
uint32_t payloadLength,
std::shared_ptr<const std::vector<uint8_t>> sharedPayload = {})
{
if (sharedPayload)
{
if (service != LG_NET_SERVICE_VIDEO ||
sharedPayload->size() != static_cast<size_t>(payloadLength))
return SendResult::FAILED;
payload = payloadLength ? sharedPayload->data() : nullptr;
}
const LGNetServiceVersionInfo * version =
lgNetServiceVersionInfo(service);
const LGNetMessageRoute * route =
@@ -1545,10 +1586,11 @@ struct CNetworkServer::Impl
LG_NET_ENVELOPE_EARLY_DATA))
return SendResult::FAILED;
const size_t packetSize =
const size_t packetSize =
LG_NET_ENVELOPE_WIRE_SIZE + payloadLength;
const SendLimits limits =
const SendLimits limits =
GetSendLimits(service, Delivery::BULK);
const bool scatterPayload = static_cast<bool>(sharedPayload);
if (packetSize > limits.bytes ||
packetSize > NETWORK_SEND_BULK_BYTES_MAX)
return SendResult::FAILED;
@@ -1609,17 +1651,20 @@ retry:
ReleaseSendLocked(connection, rollback);
return SendResult::FAILED;
}
try
if (!scatterPayload)
{
send->data.resize(packetSize);
}
catch (...)
{
send->service = service;
send->length = static_cast<uint32_t>(packetSize);
send->delivery = Delivery::BULK;
ReleaseSendLocked(connection, *send);
return SendResult::FAILED;
try
{
send->data.resize(packetSize);
}
catch (...)
{
send->service = service;
send->length = static_cast<uint32_t>(packetSize);
send->delivery = Delivery::BULK;
ReleaseSendLocked(connection, *send);
return SendResult::FAILED;
}
}
send->service = service;
send->length = static_cast<uint32_t>(packetSize);
@@ -1634,20 +1679,54 @@ retry:
parameters.sequence = ++stream->sendSequence;
parameters.requestID = requestID;
size_t wireSize;
if (!lgNetPacketConstruct(send->data.data(), send->data.size(),
&wireSize, &parameters, payload, payloadLength) ||
wireSize != packetSize)
size_t wireSize = 0;
if (scatterPayload)
{
LGNetEnvelope envelope;
lgNetEnvelopeInit(&envelope, parameters.service,
parameters.messageType, parameters.serviceVersion,
parameters.messageVersion);
envelope.flags = parameters.flags;
envelope.sessionEpoch = parameters.sessionEpoch;
envelope.componentEpoch = parameters.componentEpoch;
envelope.sequence = parameters.sequence;
envelope.requestID = parameters.requestID;
envelope.payloadLength = payloadLength;
if (lgNetEnvelopeEncode(send->envelope, sizeof(send->envelope),
&envelope))
wireSize = packetSize;
}
else if (!lgNetPacketConstruct(send->data.data(), send->data.size(),
&wireSize, &parameters, payload, payloadLength))
wireSize = 0;
if (wireSize != packetSize)
{
--stream->sendSequence;
ReleaseSendLocked(connection, *send);
return SendResult::FAILED;
}
send->buffer.Buffer = send->data.data();
send->buffer.Length = static_cast<uint32_t>(wireSize);
status = api->StreamSend(stream->handle, &send->buffer, 1,
QUIC_SEND_FLAG_NONE, send.get());
if (scatterPayload)
{
send->buffers[0].Buffer = send->envelope;
send->buffers[0].Length = LG_NET_ENVELOPE_WIRE_SIZE;
send->sharedData = std::move(sharedPayload);
if (payloadLength)
{
send->buffers[1].Buffer = const_cast<uint8_t *>(
send->sharedData->data());
send->buffers[1].Length = payloadLength;
send->bufferCount = 2;
}
}
else
{
send->buffers[0].Buffer = send->data.data();
send->buffers[0].Length = static_cast<uint32_t>(wireSize);
}
status = api->StreamSend(stream->handle, send->buffers,
send->bufferCount, QUIC_SEND_FLAG_NONE, send.get());
if (QUIC_FAILED(status))
{
--stream->sendSequence;
@@ -1663,8 +1742,17 @@ retry:
SendResult TrySendDatagram(Connection& connection, LGNetService service,
uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID, const void * payload,
uint32_t payloadLength, bool discardIfBlocked)
uint32_t payloadLength, bool discardIfBlocked,
std::shared_ptr<const std::vector<uint8_t>> sharedPayload = {})
{
if (sharedPayload)
{
if (service != LG_NET_SERVICE_VIDEO ||
sharedPayload->size() != static_cast<size_t>(payloadLength))
return SendResult::FAILED;
payload = payloadLength ? sharedPayload->data() : nullptr;
}
const LGNetServiceVersionInfo * version =
lgNetServiceVersionInfo(service);
const LGNetMessageRoute * route =
@@ -1678,8 +1766,9 @@ retry:
LG_NET_ENVELOPE_EARLY_DATA))
return SendResult::FAILED;
const size_t packetSize =
const size_t packetSize =
LG_NET_ENVELOPE_WIRE_SIZE + payloadLength;
const bool scatterPayload = static_cast<bool>(sharedPayload);
std::unique_ptr<Send> send;
QUIC_STATUS status;
{
@@ -1730,17 +1819,20 @@ retry:
ReleaseSendLocked(connection, rollback);
return SendResult::FAILED;
}
try
if (!scatterPayload)
{
send->data.resize(packetSize);
}
catch (...)
{
send->service = service;
send->length = static_cast<uint32_t>(packetSize);
send->delivery = Delivery::DATAGRAM;
ReleaseSendLocked(connection, *send);
return SendResult::FAILED;
try
{
send->data.resize(packetSize);
}
catch (...)
{
send->service = service;
send->length = static_cast<uint32_t>(packetSize);
send->delivery = Delivery::DATAGRAM;
ReleaseSendLocked(connection, *send);
return SendResult::FAILED;
}
}
send->service = service;
send->length = static_cast<uint32_t>(packetSize);
@@ -1756,23 +1848,57 @@ retry:
parameters.sequence = ++connection.datagramSendSequence;
parameters.requestID = requestID;
size_t wireSize;
if (!lgNetPacketConstruct(send->data.data(), send->data.size(),
&wireSize, &parameters, payload, payloadLength) ||
wireSize > UINT32_MAX)
size_t wireSize = 0;
if (scatterPayload)
{
LGNetEnvelope envelope;
lgNetEnvelopeInit(&envelope, parameters.service,
parameters.messageType, parameters.serviceVersion,
parameters.messageVersion);
envelope.flags = parameters.flags;
envelope.sessionEpoch = parameters.sessionEpoch;
envelope.componentEpoch = parameters.componentEpoch;
envelope.sequence = parameters.sequence;
envelope.requestID = parameters.requestID;
envelope.payloadLength = payloadLength;
if (lgNetEnvelopeEncode(send->envelope, sizeof(send->envelope),
&envelope))
wireSize = packetSize;
}
else if (!lgNetPacketConstruct(send->data.data(), send->data.size(),
&wireSize, &parameters, payload, payloadLength))
wireSize = 0;
if (wireSize != packetSize)
{
--connection.datagramSendSequence;
ReleaseSendLocked(connection, *send);
return SendResult::FAILED;
}
send->buffer.Buffer = send->data.data();
send->buffer.Length = static_cast<uint32_t>(wireSize);
if (scatterPayload)
{
send->buffers[0].Buffer = send->envelope;
send->buffers[0].Length = LG_NET_ENVELOPE_WIRE_SIZE;
send->sharedData = std::move(sharedPayload);
if (payloadLength)
{
send->buffers[1].Buffer = const_cast<uint8_t *>(
send->sharedData->data());
send->buffers[1].Length = payloadLength;
send->bufferCount = 2;
}
}
else
{
send->buffers[0].Buffer = send->data.data();
send->buffers[0].Length = static_cast<uint32_t>(wireSize);
}
QUIC_SEND_FLAGS sendFlags = QUIC_SEND_FLAG_PRIORITY_WORK;
if (discardIfBlocked)
sendFlags |= QUIC_SEND_FLAG_CANCEL_ON_BLOCKED;
status = api->DatagramSend(connection.handle, &send->buffer, 1,
sendFlags, send.get());
status = api->DatagramSend(connection.handle, send->buffers,
send->bufferCount, sendFlags, send.get());
if (QUIC_FAILED(status))
{
--connection.datagramSendSequence;
@@ -1838,8 +1964,8 @@ retry:
connection.receivedCapabilities = 0;
connection.selectedCapabilityCount = 0;
connection.maxPayload = hello.maxPayloadLength <
NETWORK_MAX_CONTROL_PAYLOAD ? hello.maxPayloadLength :
static_cast<uint32_t>(NETWORK_MAX_CONTROL_PAYLOAD);
NETWORK_MAX_NEGOTIATED_PAYLOAD ? hello.maxPayloadLength :
NETWORK_MAX_NEGOTIATED_PAYLOAD;
connection.maxDatagramPayload = hello.maxDatagramPayload <
NETWORK_MAX_DATAGRAM_PAYLOAD ? hello.maxDatagramPayload :
NETWORK_MAX_DATAGRAM_PAYLOAD;
@@ -4694,12 +4820,12 @@ retry:
return QUIC_STATUS_SUCCESS;
}
send->buffer.Buffer = send->data.data();
send->buffer.Length = LG_NET_STREAM_BINDING_WIRE_SIZE;
send->service = stream.service;
send->length = LG_NET_STREAM_BINDING_WIRE_SIZE;
send->delivery = Delivery::BULK;
send->accounted = false;
send->buffers[0].Buffer = send->data.data();
send->buffers[0].Length = LG_NET_STREAM_BINDING_WIRE_SIZE;
send->service = stream.service;
send->length = LG_NET_STREAM_BINDING_WIRE_SIZE;
send->delivery = Delivery::BULK;
send->accounted = false;
uint16_t ready;
QUIC_STATUS status;
@@ -4712,8 +4838,9 @@ retry:
connection.outboundBulk[stream.service] == &stream &&
(connection.selectedServices & serviceMask) &&
(connection.bulkServices & serviceMask);
status = valid ? owner.api->StreamSend(handle, &send->buffer, 1,
QUIC_SEND_FLAG_NONE, send.get()) : QUIC_STATUS_ABORTED;
status = valid ? owner.api->StreamSend(handle, send->buffers,
send->bufferCount, QUIC_SEND_FLAG_NONE, send.get()) :
QUIC_STATUS_ABORTED;
ready = 0;
if (QUIC_SUCCEEDED(status))
{
@@ -4905,25 +5032,36 @@ retry:
}
QUIC_SETTINGS quicSettings = {};
quicSettings.IsSet.HandshakeIdleTimeoutMs = TRUE;
quicSettings.IsSet.IdleTimeoutMs = TRUE;
quicSettings.IsSet.InitialRttMs = TRUE;
quicSettings.IsSet.MaxAckDelayMs = TRUE;
quicSettings.IsSet.KeepAliveIntervalMs = TRUE;
quicSettings.IsSet.PeerBidiStreamCount = TRUE;
quicSettings.IsSet.PeerUnidiStreamCount = TRUE;
quicSettings.IsSet.DatagramReceiveEnabled = TRUE;
quicSettings.IsSet.ServerResumptionLevel = TRUE;
quicSettings.IsSet.HandshakeIdleTimeoutMs = TRUE;
quicSettings.IsSet.IdleTimeoutMs = TRUE;
quicSettings.IsSet.StreamRecvBufferDefault = TRUE;
quicSettings.IsSet.ConnFlowControlWindow = TRUE;
quicSettings.IsSet.InitialRttMs = TRUE;
quicSettings.IsSet.MaxAckDelayMs = TRUE;
quicSettings.IsSet.KeepAliveIntervalMs = TRUE;
quicSettings.IsSet.PeerBidiStreamCount = TRUE;
quicSettings.IsSet.PeerUnidiStreamCount = TRUE;
quicSettings.IsSet.SendBufferingEnabled = TRUE;
quicSettings.IsSet.DatagramReceiveEnabled = TRUE;
quicSettings.IsSet.ServerResumptionLevel = TRUE;
quicSettings.IsSet.StreamRecvWindowUnidiDefault = TRUE;
quicSettings.HandshakeIdleTimeoutMs = NETWORK_HANDSHAKE_TIMEOUT_MS;
quicSettings.IdleTimeoutMs = NETWORK_IDLE_TIMEOUT_MS;
quicSettings.StreamRecvBufferDefault =
NETWORK_STREAM_RECV_BUFFER;
quicSettings.ConnFlowControlWindow =
NETWORK_CONN_RECV_WINDOW;
quicSettings.InitialRttMs = NETWORK_INITIAL_RTT_MS;
quicSettings.MaxAckDelayMs = 1;
quicSettings.KeepAliveIntervalMs =
NETWORK_KEEPALIVE_INTERVAL_MS;
quicSettings.PeerBidiStreamCount = NETWORK_PEER_BIDI_STREAMS;
quicSettings.PeerUnidiStreamCount = NETWORK_PEER_UNIDI_STREAMS;
quicSettings.SendBufferingEnabled = FALSE;
quicSettings.DatagramReceiveEnabled = TRUE;
quicSettings.ServerResumptionLevel = QUIC_SERVER_NO_RESUME;
quicSettings.StreamRecvWindowUnidiDefault =
NETWORK_STREAM_RECV_WINDOW;
QUIC_BUFFER alpn = {};
alpn.Buffer = reinterpret_cast<uint8_t *>(
@@ -5121,10 +5259,10 @@ NetworkVideoSendResult CNetworkServer::SendVideo(
const NetworkVideoPeer& peer, NetworkVideoDelivery delivery,
uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength)
std::shared_ptr<const std::vector<uint8_t>> payload)
{
return m_impl ?
m_impl->SendVideo(peer, delivery, messageType, flags,
componentEpoch, requestID, payload, payloadLength) :
componentEpoch, requestID, std::move(payload)) :
NetworkVideoSendResult::FAILED;
}

View File

@@ -73,5 +73,5 @@ public:
const NetworkVideoPeer& peer, NetworkVideoDelivery delivery,
uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength) override;
std::shared_ptr<const std::vector<uint8_t>> payload) override;
};

File diff suppressed because it is too large Load Diff

View File

@@ -28,6 +28,7 @@
#include <memory>
#include <stdint.h>
#include <vector>
struct NetworkVideoPeer
{
@@ -76,7 +77,7 @@ public:
const NetworkVideoPeer& peer, NetworkVideoDelivery delivery,
uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength) = 0;
std::shared_ptr<const std::vector<uint8_t>> payload) = 0;
};
class CNetworkVideo final : public ITexSink