mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-12 14:57:20 +00:00
[client/idd] network: implement tiled video transport
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user