Compare commits
10 commits
2d984f4b0d
...
611a2e7f8f
| Author | SHA1 | Date | |
|---|---|---|---|
| 611a2e7f8f | |||
|
|
6250fa29ee | ||
|
|
07c32c80f9 | ||
|
|
305993b013 |
||
|
|
2a5a1f3e8a |
||
|
|
435bc6a5a4 | ||
|
|
3a377e7d7b | ||
|
|
0586f3d65f | ||
|
|
b126e481a1 | ||
|
|
20c05eda6a |
14 changed files with 281 additions and 84 deletions
2
enet
2
enet
|
|
@ -1 +1 @@
|
||||||
Subproject commit dea6fb5414b180908b58c0293c831105b5d124dd
|
Subproject commit 78cc9b4b03bdd4f95c277391cac6e0951407072b
|
||||||
|
|
@ -377,6 +377,19 @@ int LiStartConnection(PSERVER_INFORMATION serverInfo, PSTREAM_CONFIGURATION stre
|
||||||
ListenerCallbacks.stageFailed(STAGE_NAME_RESOLUTION, err);
|
ListenerCallbacks.stageFailed(STAGE_NAME_RESOLUTION, err);
|
||||||
goto Cleanup;
|
goto Cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve LocalAddr by RemoteAddr.
|
||||||
|
{
|
||||||
|
SOCKADDR_LEN localAddrLen;
|
||||||
|
err = getLocalAddressByUdpConnect(&RemoteAddr, AddrLen, RtspPortNumber, &LocalAddr, &localAddrLen);
|
||||||
|
if (err != 0) {
|
||||||
|
Limelog("failed to resolve local addr: %d\n", err);
|
||||||
|
ListenerCallbacks.stageFailed(STAGE_NAME_RESOLUTION, err);
|
||||||
|
goto Cleanup;
|
||||||
|
}
|
||||||
|
LC_ASSERT(localAddrLen == AddrLen);
|
||||||
|
}
|
||||||
|
|
||||||
stage++;
|
stage++;
|
||||||
LC_ASSERT(stage == STAGE_NAME_RESOLUTION);
|
LC_ASSERT(stage == STAGE_NAME_RESOLUTION);
|
||||||
ListenerCallbacks.stageComplete(STAGE_NAME_RESOLUTION);
|
ListenerCallbacks.stageComplete(STAGE_NAME_RESOLUTION);
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,12 @@ typedef struct _NVCTL_ENCRYPTED_PACKET_HEADER {
|
||||||
// encrypted NVCTL_ENET_PACKET_HEADER_V2 and payload data follow
|
// encrypted NVCTL_ENET_PACKET_HEADER_V2 and payload data follow
|
||||||
} NVCTL_ENCRYPTED_PACKET_HEADER, *PNVCTL_ENCRYPTED_PACKET_HEADER;
|
} NVCTL_ENCRYPTED_PACKET_HEADER, *PNVCTL_ENCRYPTED_PACKET_HEADER;
|
||||||
|
|
||||||
typedef struct _QUEUED_FRAME_INVALIDATION_TUPLE {
|
typedef struct _QUEUED_REFERENCE_FRAME_CONTROL {
|
||||||
uint32_t startFrame;
|
uint32_t startFrame;
|
||||||
uint32_t endFrame;
|
uint32_t endFrame;
|
||||||
|
bool invalidate; // true: RFI(startFrame, endFrame); false: LTR_ACK(startFrame)
|
||||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||||
} QUEUED_FRAME_INVALIDATION_TUPLE, *PQUEUED_FRAME_INVALIDATION_TUPLE;
|
} QUEUED_REFERENCE_FRAME_CONTROL, *PQUEUED_REFERENCE_FRAME_CONTROL;
|
||||||
|
|
||||||
typedef struct _QUEUED_FRAME_FEC_STATUS {
|
typedef struct _QUEUED_FRAME_FEC_STATUS {
|
||||||
SS_FRAME_FEC_STATUS fecStatus;
|
SS_FRAME_FEC_STATUS fecStatus;
|
||||||
|
|
@ -82,6 +83,12 @@ typedef struct _QUEUED_ASYNC_CALLBACK {
|
||||||
uint8_t left[DS_EFFECT_PAYLOAD_SIZE];
|
uint8_t left[DS_EFFECT_PAYLOAD_SIZE];
|
||||||
uint8_t right[DS_EFFECT_PAYLOAD_SIZE];
|
uint8_t right[DS_EFFECT_PAYLOAD_SIZE];
|
||||||
} dsAdaptiveTrigger;
|
} dsAdaptiveTrigger;
|
||||||
|
struct {
|
||||||
|
uint8_t allowKeyboard;
|
||||||
|
uint8_t allowMouse;
|
||||||
|
uint8_t allowGamepad;
|
||||||
|
uint8_t reason;
|
||||||
|
} setInputPolicy;
|
||||||
} data;
|
} data;
|
||||||
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
LINKED_BLOCKING_QUEUE_ENTRY entry;
|
||||||
} QUEUED_ASYNC_CALLBACK, *PQUEUED_ASYNC_CALLBACK;
|
} QUEUED_ASYNC_CALLBACK, *PQUEUED_ASYNC_CALLBACK;
|
||||||
|
|
@ -113,7 +120,7 @@ static int lastConnectionStatusUpdate;
|
||||||
static uint32_t currentEnetSequenceNumber;
|
static uint32_t currentEnetSequenceNumber;
|
||||||
static uint64_t firstFrameTimeMs;
|
static uint64_t firstFrameTimeMs;
|
||||||
|
|
||||||
static LINKED_BLOCKING_QUEUE invalidReferenceFrameTuples;
|
static LINKED_BLOCKING_QUEUE referenceFrameControlQueue;
|
||||||
static LINKED_BLOCKING_QUEUE frameFecStatusQueue;
|
static LINKED_BLOCKING_QUEUE frameFecStatusQueue;
|
||||||
static LINKED_BLOCKING_QUEUE asyncCallbackQueue;
|
static LINKED_BLOCKING_QUEUE asyncCallbackQueue;
|
||||||
static PLT_EVENT idrFrameRequiredEvent;
|
static PLT_EVENT idrFrameRequiredEvent;
|
||||||
|
|
@ -139,6 +146,7 @@ static PPLT_CRYPTO_CONTEXT decryptionCtx;
|
||||||
#define IDX_SET_MOTION_EVENT 10
|
#define IDX_SET_MOTION_EVENT 10
|
||||||
#define IDX_SET_RGB_LED 11
|
#define IDX_SET_RGB_LED 11
|
||||||
#define IDX_DS_ADAPTIVE_TRIGGERS 12
|
#define IDX_DS_ADAPTIVE_TRIGGERS 12
|
||||||
|
#define IDX_SET_INPUT_POLICY 13
|
||||||
|
|
||||||
#define CONTROL_STREAM_TIMEOUT_SEC 10
|
#define CONTROL_STREAM_TIMEOUT_SEC 10
|
||||||
#define CONTROL_STREAM_LINGER_TIMEOUT_SEC 2
|
#define CONTROL_STREAM_LINGER_TIMEOUT_SEC 2
|
||||||
|
|
@ -156,6 +164,7 @@ static const short packetTypesGen3[] = {
|
||||||
-1, // Rumble triggers (unused)
|
-1, // Rumble triggers (unused)
|
||||||
-1, // Set motion event (unused)
|
-1, // Set motion event (unused)
|
||||||
-1, // Set RGB LED (unused)
|
-1, // Set RGB LED (unused)
|
||||||
|
-1,
|
||||||
};
|
};
|
||||||
static const short packetTypesGen4[] = {
|
static const short packetTypesGen4[] = {
|
||||||
0x0606, // Request IDR frame
|
0x0606, // Request IDR frame
|
||||||
|
|
@ -170,6 +179,7 @@ static const short packetTypesGen4[] = {
|
||||||
-1, // Rumble triggers (unused)
|
-1, // Rumble triggers (unused)
|
||||||
-1, // Set motion event (unused)
|
-1, // Set motion event (unused)
|
||||||
-1, // Set RGB LED (unused)
|
-1, // Set RGB LED (unused)
|
||||||
|
-1,
|
||||||
};
|
};
|
||||||
static const short packetTypesGen5[] = {
|
static const short packetTypesGen5[] = {
|
||||||
0x0305, // Start A
|
0x0305, // Start A
|
||||||
|
|
@ -184,6 +194,7 @@ static const short packetTypesGen5[] = {
|
||||||
-1, // Rumble triggers (unused)
|
-1, // Rumble triggers (unused)
|
||||||
-1, // Set motion event (unused)
|
-1, // Set motion event (unused)
|
||||||
-1, // Set RGB LED (unused)
|
-1, // Set RGB LED (unused)
|
||||||
|
-1,
|
||||||
};
|
};
|
||||||
static const short packetTypesGen7[] = {
|
static const short packetTypesGen7[] = {
|
||||||
0x0305, // Start A
|
0x0305, // Start A
|
||||||
|
|
@ -198,6 +209,7 @@ static const short packetTypesGen7[] = {
|
||||||
-1, // Rumble triggers (unused)
|
-1, // Rumble triggers (unused)
|
||||||
-1, // Set motion event (unused)
|
-1, // Set motion event (unused)
|
||||||
-1, // Set RGB LED (unused)
|
-1, // Set RGB LED (unused)
|
||||||
|
-1,
|
||||||
};
|
};
|
||||||
static const short packetTypesGen7Enc[] = {
|
static const short packetTypesGen7Enc[] = {
|
||||||
0x0302, // Request IDR frame
|
0x0302, // Request IDR frame
|
||||||
|
|
@ -213,6 +225,7 @@ static const short packetTypesGen7Enc[] = {
|
||||||
0x5501, // Set motion event (Sunshine protocol extension)
|
0x5501, // Set motion event (Sunshine protocol extension)
|
||||||
0x5502, // Set RGB LED (Sunshine protocol extension)
|
0x5502, // Set RGB LED (Sunshine protocol extension)
|
||||||
0x5503, // Set Adaptive Triggers (Sunshine protocol extension)
|
0x5503, // Set Adaptive Triggers (Sunshine protocol extension)
|
||||||
|
0x5504,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char requestIdrFrameGen3[] = { 0, 0 };
|
static const char requestIdrFrameGen3[] = { 0, 0 };
|
||||||
|
|
@ -300,7 +313,7 @@ static bool supportsIdrFrameRequest;
|
||||||
int initializeControlStream(void) {
|
int initializeControlStream(void) {
|
||||||
stopping = false;
|
stopping = false;
|
||||||
PltCreateEvent(&idrFrameRequiredEvent);
|
PltCreateEvent(&idrFrameRequiredEvent);
|
||||||
LbqInitializeLinkedBlockingQueue(&invalidReferenceFrameTuples, 20);
|
LbqInitializeLinkedBlockingQueue(&referenceFrameControlQueue, 20);
|
||||||
LbqInitializeLinkedBlockingQueue(&frameFecStatusQueue, 8); // Limits number of frame status reports per periodic ping interval
|
LbqInitializeLinkedBlockingQueue(&frameFecStatusQueue, 8); // Limits number of frame status reports per periodic ping interval
|
||||||
LbqInitializeLinkedBlockingQueue(&asyncCallbackQueue, 30);
|
LbqInitializeLinkedBlockingQueue(&asyncCallbackQueue, 30);
|
||||||
PltCreateMutex(&enetMutex);
|
PltCreateMutex(&enetMutex);
|
||||||
|
|
@ -375,7 +388,7 @@ void destroyControlStream(void) {
|
||||||
PltDestroyCryptoContext(encryptionCtx);
|
PltDestroyCryptoContext(encryptionCtx);
|
||||||
PltDestroyCryptoContext(decryptionCtx);
|
PltDestroyCryptoContext(decryptionCtx);
|
||||||
PltCloseEvent(&idrFrameRequiredEvent);
|
PltCloseEvent(&idrFrameRequiredEvent);
|
||||||
freeBasicLbqList(LbqDestroyLinkedBlockingQueue(&invalidReferenceFrameTuples));
|
freeBasicLbqList(LbqDestroyLinkedBlockingQueue(&referenceFrameControlQueue));
|
||||||
freeBasicLbqList(LbqDestroyLinkedBlockingQueue(&frameFecStatusQueue));
|
freeBasicLbqList(LbqDestroyLinkedBlockingQueue(&frameFecStatusQueue));
|
||||||
freeBasicLbqList(LbqDestroyLinkedBlockingQueue(&asyncCallbackQueue));
|
freeBasicLbqList(LbqDestroyLinkedBlockingQueue(&asyncCallbackQueue));
|
||||||
|
|
||||||
|
|
@ -386,12 +399,15 @@ static void queueFrameInvalidationTuple(uint32_t startFrame, uint32_t endFrame)
|
||||||
LC_ASSERT(startFrame <= endFrame);
|
LC_ASSERT(startFrame <= endFrame);
|
||||||
|
|
||||||
if (isReferenceFrameInvalidationEnabled()) {
|
if (isReferenceFrameInvalidationEnabled()) {
|
||||||
PQUEUED_FRAME_INVALIDATION_TUPLE qfit;
|
PQUEUED_REFERENCE_FRAME_CONTROL qfit;
|
||||||
qfit = malloc(sizeof(*qfit));
|
qfit = malloc(sizeof(*qfit));
|
||||||
if (qfit != NULL) {
|
if (qfit != NULL) {
|
||||||
qfit->startFrame = startFrame;
|
*qfit = (QUEUED_REFERENCE_FRAME_CONTROL){
|
||||||
qfit->endFrame = endFrame;
|
.startFrame = startFrame,
|
||||||
if (LbqOfferQueueItem(&invalidReferenceFrameTuples, qfit, &qfit->entry) == LBQ_BOUND_EXCEEDED) {
|
.endFrame = endFrame,
|
||||||
|
.invalidate = true,
|
||||||
|
};
|
||||||
|
if (LbqOfferQueueItem(&referenceFrameControlQueue, qfit, &qfit->entry) == LBQ_BOUND_EXCEEDED) {
|
||||||
// Too many invalidation tuples, so we need an IDR frame now
|
// Too many invalidation tuples, so we need an IDR frame now
|
||||||
Limelog("RFI range list reached maximum size limit\n");
|
Limelog("RFI range list reached maximum size limit\n");
|
||||||
free(qfit);
|
free(qfit);
|
||||||
|
|
@ -411,7 +427,7 @@ static void queueFrameInvalidationTuple(uint32_t startFrame, uint32_t endFrame)
|
||||||
void LiRequestIdrFrame(void) {
|
void LiRequestIdrFrame(void) {
|
||||||
// Any reference frame invalidation requests should be dropped now.
|
// Any reference frame invalidation requests should be dropped now.
|
||||||
// We require a full IDR frame to recover.
|
// We require a full IDR frame to recover.
|
||||||
freeBasicLbqList(LbqFlushQueueItems(&invalidReferenceFrameTuples));
|
freeBasicLbqList(LbqFlushQueueItems(&referenceFrameControlQueue));
|
||||||
|
|
||||||
// Request the IDR frame
|
// Request the IDR frame
|
||||||
PltSetEvent(&idrFrameRequiredEvent);
|
PltSetEvent(&idrFrameRequiredEvent);
|
||||||
|
|
@ -423,9 +439,29 @@ void connectionDetectedFrameLoss(uint32_t startFrame, uint32_t endFrame) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// When we receive a frame, update the number of our current frame
|
// When we receive a frame, update the number of our current frame
|
||||||
void connectionReceivedCompleteFrame(uint32_t frameIndex) {
|
// and send ACK control message if the frame is LTR
|
||||||
|
void connectionReceivedCompleteFrame(uint32_t frameIndex, bool frameIsLTR) {
|
||||||
lastGoodFrame = frameIndex;
|
lastGoodFrame = frameIndex;
|
||||||
intervalGoodFrameCount++;
|
intervalGoodFrameCount++;
|
||||||
|
|
||||||
|
if (frameIsLTR && IS_SUNSHINE() && isReferenceFrameInvalidationEnabled()) {
|
||||||
|
// Queue LTR frame ACK control message
|
||||||
|
PQUEUED_REFERENCE_FRAME_CONTROL qfit;
|
||||||
|
qfit = malloc(sizeof(*qfit));
|
||||||
|
if (qfit != NULL) {
|
||||||
|
*qfit = (QUEUED_REFERENCE_FRAME_CONTROL){
|
||||||
|
.startFrame = frameIndex,
|
||||||
|
.invalidate = false,
|
||||||
|
};
|
||||||
|
if (LbqOfferQueueItem(&referenceFrameControlQueue, qfit, &qfit->entry) == LBQ_BOUND_EXCEEDED) {
|
||||||
|
// This shouldn't happen and indicates that something has gone wrong with the queue
|
||||||
|
LC_ASSERT(false);
|
||||||
|
Limelog("Couldn't queue LTR ACK because the list has reached maximum size limit\n");
|
||||||
|
free(qfit);
|
||||||
|
LiRequestIdrFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void connectionSendFrameFecStatus(PSS_FRAME_FEC_STATUS fecStatus) {
|
void connectionSendFrameFecStatus(PSS_FRAME_FEC_STATUS fecStatus) {
|
||||||
|
|
@ -986,6 +1022,12 @@ static void asyncCallbackThreadFunc(void* context) {
|
||||||
queuedCb->data.dsAdaptiveTrigger.left,
|
queuedCb->data.dsAdaptiveTrigger.left,
|
||||||
queuedCb->data.dsAdaptiveTrigger.right);
|
queuedCb->data.dsAdaptiveTrigger.right);
|
||||||
break;
|
break;
|
||||||
|
case IDX_SET_INPUT_POLICY:
|
||||||
|
ListenerCallbacks.setInputPolicy(queuedCb->data.setInputPolicy.allowKeyboard,
|
||||||
|
queuedCb->data.setInputPolicy.allowMouse,
|
||||||
|
queuedCb->data.setInputPolicy.allowGamepad,
|
||||||
|
queuedCb->data.setInputPolicy.reason);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Unhandled packet type from queueAsyncCallback()
|
// Unhandled packet type from queueAsyncCallback()
|
||||||
LC_ASSERT(false);
|
LC_ASSERT(false);
|
||||||
|
|
@ -1002,7 +1044,8 @@ static bool needsAsyncCallback(unsigned short packetType) {
|
||||||
packetType == packetTypes[IDX_SET_MOTION_EVENT] ||
|
packetType == packetTypes[IDX_SET_MOTION_EVENT] ||
|
||||||
packetType == packetTypes[IDX_SET_RGB_LED] ||
|
packetType == packetTypes[IDX_SET_RGB_LED] ||
|
||||||
packetType == packetTypes[IDX_HDR_INFO] ||
|
packetType == packetTypes[IDX_HDR_INFO] ||
|
||||||
packetType == packetTypes[IDX_DS_ADAPTIVE_TRIGGERS];
|
packetType == packetTypes[IDX_DS_ADAPTIVE_TRIGGERS] ||
|
||||||
|
packetType == packetTypes[IDX_SET_INPUT_POLICY];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void queueAsyncCallback(PNVCTL_ENET_PACKET_HEADER_V1 ctlHdr, int packetLength) {
|
static void queueAsyncCallback(PNVCTL_ENET_PACKET_HEADER_V1 ctlHdr, int packetLength) {
|
||||||
|
|
@ -1063,6 +1106,13 @@ static void queueAsyncCallback(PNVCTL_ENET_PACKET_HEADER_V1 ctlHdr, int packetLe
|
||||||
BbGetBytes(&bb, queuedCb->data.dsAdaptiveTrigger.right, DS_EFFECT_PAYLOAD_SIZE);
|
BbGetBytes(&bb, queuedCb->data.dsAdaptiveTrigger.right, DS_EFFECT_PAYLOAD_SIZE);
|
||||||
queuedCb->typeIndex = IDX_DS_ADAPTIVE_TRIGGERS;
|
queuedCb->typeIndex = IDX_DS_ADAPTIVE_TRIGGERS;
|
||||||
}
|
}
|
||||||
|
else if (ctlHdr->type == packetTypes[IDX_SET_INPUT_POLICY]) {
|
||||||
|
BbGet8(&bb, &queuedCb->data.setInputPolicy.allowKeyboard);
|
||||||
|
BbGet8(&bb, &queuedCb->data.setInputPolicy.allowMouse);
|
||||||
|
BbGet8(&bb, &queuedCb->data.setInputPolicy.allowGamepad);
|
||||||
|
BbGet8(&bb, &queuedCb->data.setInputPolicy.reason);
|
||||||
|
queuedCb->typeIndex = IDX_SET_INPUT_POLICY;
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
// Unhandled packet type from needsAsyncCallback()
|
// Unhandled packet type from needsAsyncCallback()
|
||||||
LC_ASSERT(false);
|
LC_ASSERT(false);
|
||||||
|
|
@ -1512,22 +1562,22 @@ static void requestIdrFrame(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void requestInvalidateReferenceFrames(uint32_t startFrame, uint32_t endFrame) {
|
static void requestInvalidateReferenceFrames(uint32_t startFrame, uint32_t endFrame) {
|
||||||
int64_t payload[3];
|
|
||||||
|
|
||||||
LC_ASSERT(startFrame <= endFrame);
|
LC_ASSERT(startFrame <= endFrame);
|
||||||
LC_ASSERT(isReferenceFrameInvalidationEnabled());
|
LC_ASSERT(isReferenceFrameInvalidationEnabled());
|
||||||
|
|
||||||
payload[0] = LE64(startFrame);
|
SS_RFI_REQUEST payload = {
|
||||||
payload[1] = LE64(endFrame);
|
.firstFrameIndex = LE32(startFrame),
|
||||||
payload[2] = 0;
|
.lastFrameIndex = LE32(endFrame),
|
||||||
|
};
|
||||||
|
|
||||||
// Send the reference frame invalidation request and read the response
|
// Send the reference frame invalidation request and read the response
|
||||||
if (!sendMessageAndDiscardReply(packetTypes[IDX_INVALIDATE_REF_FRAMES],
|
if (!sendMessageAndDiscardReply(packetTypes[IDX_INVALIDATE_REF_FRAMES],
|
||||||
sizeof(payload),
|
sizeof(payload),
|
||||||
payload, CTRL_CHANNEL_URGENT,
|
&payload,
|
||||||
|
CTRL_CHANNEL_URGENT,
|
||||||
ENET_PACKET_FLAG_RELIABLE,
|
ENET_PACKET_FLAG_RELIABLE,
|
||||||
false)) {
|
false)) {
|
||||||
Limelog("Request Invaldiate Reference Frames: Transaction failed: %d\n", (int)LastSocketError());
|
Limelog("Request Invalidate Reference Frames: Transaction failed: %d\n", (int)LastSocketError());
|
||||||
ListenerCallbacks.connectionTerminated(LastSocketFail());
|
ListenerCallbacks.connectionTerminated(LastSocketFail());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1535,32 +1585,65 @@ static void requestInvalidateReferenceFrames(uint32_t startFrame, uint32_t endFr
|
||||||
Limelog("Invalidate reference frame request sent (%d to %d)\n", startFrame, endFrame);
|
Limelog("Invalidate reference frame request sent (%d to %d)\n", startFrame, endFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void invalidateRefFramesFunc(void* context) {
|
static void confirmLongtermReferenceFrame(uint32_t frameIndex) {
|
||||||
|
LC_ASSERT(isReferenceFrameInvalidationEnabled());
|
||||||
|
|
||||||
|
SS_LTR_FRAME_ACK payload = {
|
||||||
|
.frameIndex = LE32(frameIndex),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send LTR frame ACK and don't wait for response
|
||||||
|
if (!sendMessageAndForget(SS_LTR_FRAME_ACK_PTYPE,
|
||||||
|
sizeof(payload),
|
||||||
|
&payload,
|
||||||
|
CTRL_CHANNEL_URGENT,
|
||||||
|
ENET_PACKET_FLAG_RELIABLE,
|
||||||
|
false)) {
|
||||||
|
Limelog("LTR frame ACK: Transaction failed: %d\n", (int)LastSocketError());
|
||||||
|
ListenerCallbacks.connectionTerminated(LastSocketFail());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void referenceFrameControlFunc(void* context) {
|
||||||
LC_ASSERT(isReferenceFrameInvalidationEnabled());
|
LC_ASSERT(isReferenceFrameInvalidationEnabled());
|
||||||
|
|
||||||
while (!PltIsThreadInterrupted(&invalidateRefFramesThread)) {
|
while (!PltIsThreadInterrupted(&invalidateRefFramesThread)) {
|
||||||
PQUEUED_FRAME_INVALIDATION_TUPLE qfit;
|
PQUEUED_REFERENCE_FRAME_CONTROL qfit;
|
||||||
uint32_t startFrame;
|
uint32_t invalidateStartFrame;
|
||||||
uint32_t endFrame;
|
uint32_t invalidateEndFrame;
|
||||||
|
bool invalidate = false;
|
||||||
|
|
||||||
// Wait for a reference frame invalidation request or a request to shutdown
|
// Wait for a reference frame control message or a request to shutdown
|
||||||
if (LbqWaitForQueueElement(&invalidReferenceFrameTuples, (void**)&qfit) != LBQ_SUCCESS) {
|
if (LbqWaitForQueueElement(&referenceFrameControlQueue, (void**)&qfit) != LBQ_SUCCESS) {
|
||||||
// Bail if we're stopping
|
// Bail if we're stopping
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
startFrame = qfit->startFrame;
|
|
||||||
endFrame = qfit->endFrame;
|
|
||||||
|
|
||||||
// Aggregate all lost frames into one range
|
|
||||||
do {
|
do {
|
||||||
LC_ASSERT(qfit->endFrame >= endFrame);
|
if (qfit->invalidate) {
|
||||||
endFrame = qfit->endFrame;
|
if (!invalidate) {
|
||||||
|
invalidateStartFrame = qfit->startFrame;
|
||||||
|
invalidateEndFrame = qfit->endFrame;
|
||||||
|
invalidate = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Aggregate all lost frames into one range
|
||||||
|
LC_ASSERT(qfit->endFrame >= invalidateEndFrame);
|
||||||
|
invalidateEndFrame = qfit->endFrame;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Send LTR frame ACK
|
||||||
|
confirmLongtermReferenceFrame(qfit->startFrame);
|
||||||
|
}
|
||||||
free(qfit);
|
free(qfit);
|
||||||
} while (LbqPollQueueElement(&invalidReferenceFrameTuples, (void**)&qfit) == LBQ_SUCCESS);
|
} while (LbqPollQueueElement(&referenceFrameControlQueue, (void**)&qfit) == LBQ_SUCCESS);
|
||||||
|
|
||||||
|
if (invalidate) {
|
||||||
// Send the reference frame invalidation request
|
// Send the reference frame invalidation request
|
||||||
requestInvalidateReferenceFrames(startFrame, endFrame);
|
requestInvalidateReferenceFrames(invalidateStartFrame, invalidateEndFrame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1574,8 +1657,8 @@ static void requestIdrFrameFunc(void* context) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any pending reference frame invalidation requests are now redundant
|
// Any pending RFI requests and LTR frame ACK messages are now redundant
|
||||||
freeBasicLbqList(LbqFlushQueueItems(&invalidReferenceFrameTuples));
|
freeBasicLbqList(LbqFlushQueueItems(&referenceFrameControlQueue));
|
||||||
|
|
||||||
// Request the IDR frame
|
// Request the IDR frame
|
||||||
requestIdrFrame();
|
requestIdrFrame();
|
||||||
|
|
@ -1585,7 +1668,7 @@ static void requestIdrFrameFunc(void* context) {
|
||||||
// Stops the control stream
|
// Stops the control stream
|
||||||
int stopControlStream(void) {
|
int stopControlStream(void) {
|
||||||
stopping = true;
|
stopping = true;
|
||||||
LbqSignalQueueShutdown(&invalidReferenceFrameTuples);
|
LbqSignalQueueShutdown(&referenceFrameControlQueue);
|
||||||
LbqSignalQueueShutdown(&frameFecStatusQueue);
|
LbqSignalQueueShutdown(&frameFecStatusQueue);
|
||||||
LbqSignalQueueDrain(&asyncCallbackQueue);
|
LbqSignalQueueDrain(&asyncCallbackQueue);
|
||||||
PltSetEvent(&idrFrameRequiredEvent);
|
PltSetEvent(&idrFrameRequiredEvent);
|
||||||
|
|
@ -1644,6 +1727,34 @@ int sendInputPacketOnControlStream(unsigned char* data, int length, uint8_t chan
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LiSendSessionInputPolicy(bool allowKeyboard, bool allowMouse, bool allowGamepad, uint8_t reason) {
|
||||||
|
struct {
|
||||||
|
uint8_t allowKeyboard;
|
||||||
|
uint8_t allowMouse;
|
||||||
|
uint8_t allowGamepad;
|
||||||
|
uint8_t reason;
|
||||||
|
} payload;
|
||||||
|
|
||||||
|
if (!IS_SUNSHINE() || AppVersionQuad[0] < 7) {
|
||||||
|
return LI_ERR_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (client == NULL || peer == NULL || stopping) {
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.allowKeyboard = allowKeyboard ? 1 : 0;
|
||||||
|
payload.allowMouse = allowMouse ? 1 : 0;
|
||||||
|
payload.allowGamepad = allowGamepad ? 1 : 0;
|
||||||
|
payload.reason = reason;
|
||||||
|
|
||||||
|
if (sendMessageAndForget(0x5504, sizeof(payload), &payload, CTRL_CHANNEL_GENERIC, ENET_PACKET_FLAG_RELIABLE, false) == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Called by the input stream to flush queued packets before a batching wait
|
// Called by the input stream to flush queued packets before a batching wait
|
||||||
void flushInputOnControlStream(void) {
|
void flushInputOnControlStream(void) {
|
||||||
if (AppVersionQuad[0] >= 5) {
|
if (AppVersionQuad[0] >= 5) {
|
||||||
|
|
@ -1670,7 +1781,10 @@ bool isControlDataInTransit(void) {
|
||||||
bool LiGetEstimatedRttInfo(uint32_t* estimatedRtt, uint32_t* estimatedRttVariance) {
|
bool LiGetEstimatedRttInfo(uint32_t* estimatedRtt, uint32_t* estimatedRttVariance) {
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
PltLockMutex(&enetMutex);
|
// We do not acquire enetMutex here because we're just reading metrics
|
||||||
|
// and observing a torn write every once in a while is totally fine.
|
||||||
|
// The peer pointer points to memory reserved inside the client object,
|
||||||
|
// so it's guaranteed that it will never go away underneath us.
|
||||||
if (peer != NULL && peer->state == ENET_PEER_STATE_CONNECTED) {
|
if (peer != NULL && peer->state == ENET_PEER_STATE_CONNECTED) {
|
||||||
if (estimatedRtt != NULL) {
|
if (estimatedRtt != NULL) {
|
||||||
*estimatedRtt = peer->roundTripTime;
|
*estimatedRtt = peer->roundTripTime;
|
||||||
|
|
@ -1682,7 +1796,6 @@ bool LiGetEstimatedRttInfo(uint32_t* estimatedRtt, uint32_t* estimatedRttVarianc
|
||||||
|
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
PltUnlockMutex(&enetMutex);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -1970,7 +2083,7 @@ int startControlStream(void) {
|
||||||
|
|
||||||
// Only create the reference frame invalidation thread if RFI is enabled
|
// Only create the reference frame invalidation thread if RFI is enabled
|
||||||
if (isReferenceFrameInvalidationEnabled()) {
|
if (isReferenceFrameInvalidationEnabled()) {
|
||||||
err = PltCreateThread("InvRefFrames", invalidateRefFramesFunc, NULL, &invalidateRefFramesThread);
|
err = PltCreateThread("InvRefFrames", referenceFrameControlFunc, NULL, &invalidateRefFramesThread);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
stopping = true;
|
stopping = true;
|
||||||
PltSetEvent(&idrFrameRequiredEvent);
|
PltSetEvent(&idrFrameRequiredEvent);
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ static void fakeClRumbleTriggers(uint16_t controllerNumber, uint16_t leftTrigger
|
||||||
static void fakeClSetMotionEventState(uint16_t controllerNumber, uint8_t motionType, uint16_t reportRateHz) {}
|
static void fakeClSetMotionEventState(uint16_t controllerNumber, uint8_t motionType, uint16_t reportRateHz) {}
|
||||||
static void fakeClSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlags, uint8_t typeLeft, uint8_t typeRight, uint8_t *left, uint8_t *right) {};
|
static void fakeClSetAdaptiveTriggers(uint16_t controllerNumber, uint8_t eventFlags, uint8_t typeLeft, uint8_t typeRight, uint8_t *left, uint8_t *right) {};
|
||||||
static void fakeClSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b) {}
|
static void fakeClSetControllerLED(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b) {}
|
||||||
|
static void fakeClSetInputPolicy(uint8_t allowKeyboard, uint8_t allowMouse, uint8_t allowGamepad, uint8_t reason) {}
|
||||||
|
|
||||||
static CONNECTION_LISTENER_CALLBACKS fakeClCallbacks = {
|
static CONNECTION_LISTENER_CALLBACKS fakeClCallbacks = {
|
||||||
.stageStarting = fakeClStageStarting,
|
.stageStarting = fakeClStageStarting,
|
||||||
|
|
@ -56,6 +57,7 @@ static CONNECTION_LISTENER_CALLBACKS fakeClCallbacks = {
|
||||||
.setMotionEventState = fakeClSetMotionEventState,
|
.setMotionEventState = fakeClSetMotionEventState,
|
||||||
.setControllerLED = fakeClSetControllerLED,
|
.setControllerLED = fakeClSetControllerLED,
|
||||||
.setAdaptiveTriggers = fakeClSetAdaptiveTriggers,
|
.setAdaptiveTriggers = fakeClSetAdaptiveTriggers,
|
||||||
|
.setInputPolicy = fakeClSetInputPolicy,
|
||||||
};
|
};
|
||||||
|
|
||||||
void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_RENDERER_CALLBACKS* arCallbacks,
|
void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_RENDERER_CALLBACKS* arCallbacks,
|
||||||
|
|
@ -146,5 +148,8 @@ void fixupMissingCallbacks(PDECODER_RENDERER_CALLBACKS* drCallbacks, PAUDIO_REND
|
||||||
if ((*clCallbacks)->setAdaptiveTriggers == NULL) {
|
if ((*clCallbacks)->setAdaptiveTriggers == NULL) {
|
||||||
(*clCallbacks)->setAdaptiveTriggers = fakeClSetAdaptiveTriggers;
|
(*clCallbacks)->setAdaptiveTriggers = fakeClSetAdaptiveTriggers;
|
||||||
}
|
}
|
||||||
|
if ((*clCallbacks)->setInputPolicy == NULL) {
|
||||||
|
(*clCallbacks)->setInputPolicy = fakeClSetInputPolicy;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -724,9 +724,14 @@ int LiSendMouseMoveEvent(short deltaX, short deltaY) {
|
||||||
|
|
||||||
// Queue a packet holder if this is the only pending relative mouse event
|
// Queue a packet holder if this is the only pending relative mouse event
|
||||||
if (!currentRelativeMouseState.dirty) {
|
if (!currentRelativeMouseState.dirty) {
|
||||||
|
// Set the dirty flag to claim ownership of inserting the packet holder
|
||||||
|
// and unlock to allow other threads to enqueue or process input.
|
||||||
|
currentRelativeMouseState.dirty = true;
|
||||||
|
PltUnlockMutex(&batchedInputMutex);
|
||||||
|
|
||||||
holder = allocatePacketHolder(0);
|
holder = allocatePacketHolder(0);
|
||||||
if (holder == NULL) {
|
if (holder == NULL) {
|
||||||
PltUnlockMutex(&batchedInputMutex);
|
currentRelativeMouseState.dirty = false;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -747,22 +752,21 @@ int LiSendMouseMoveEvent(short deltaX, short deltaY) {
|
||||||
// Remaining fields are set in the input thread based on the latest currentRelativeMouseState values
|
// Remaining fields are set in the input thread based on the latest currentRelativeMouseState values
|
||||||
|
|
||||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||||
if (err == LBQ_SUCCESS) {
|
if (err != LBQ_SUCCESS) {
|
||||||
currentRelativeMouseState.dirty = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||||
Limelog("Input queue reached maximum size limit\n");
|
Limelog("Input queue reached maximum size limit\n");
|
||||||
freePacketHolder(holder);
|
freePacketHolder(holder);
|
||||||
|
|
||||||
|
// We weren't able to insert the entry, so let the next call try again
|
||||||
|
currentRelativeMouseState.dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// There's already a packet holder queued to send this event
|
// There's already a packet holder queued to send this event
|
||||||
|
PltUnlockMutex(&batchedInputMutex);
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PltUnlockMutex(&batchedInputMutex);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -785,9 +789,14 @@ int LiSendMousePositionEvent(short x, short y, short referenceWidth, short refer
|
||||||
|
|
||||||
// Queue a packet holder if this is the only pending absolute mouse event
|
// Queue a packet holder if this is the only pending absolute mouse event
|
||||||
if (!currentAbsoluteMouseState.dirty) {
|
if (!currentAbsoluteMouseState.dirty) {
|
||||||
|
// Set the dirty flag to claim ownership of inserting the packet holder
|
||||||
|
// and unlock to allow other threads to enqueue or process input.
|
||||||
|
currentAbsoluteMouseState.dirty = true;
|
||||||
|
PltUnlockMutex(&batchedInputMutex);
|
||||||
|
|
||||||
holder = allocatePacketHolder(0);
|
holder = allocatePacketHolder(0);
|
||||||
if (holder == NULL) {
|
if (holder == NULL) {
|
||||||
PltUnlockMutex(&batchedInputMutex);
|
currentAbsoluteMouseState.dirty = false;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -803,22 +812,21 @@ int LiSendMousePositionEvent(short x, short y, short referenceWidth, short refer
|
||||||
// Remaining fields are set in the input thread based on the latest currentAbsoluteMouseState values
|
// Remaining fields are set in the input thread based on the latest currentAbsoluteMouseState values
|
||||||
|
|
||||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||||
if (err == LBQ_SUCCESS) {
|
if (err != LBQ_SUCCESS) {
|
||||||
currentAbsoluteMouseState.dirty = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||||
Limelog("Input queue reached maximum size limit\n");
|
Limelog("Input queue reached maximum size limit\n");
|
||||||
freePacketHolder(holder);
|
freePacketHolder(holder);
|
||||||
|
|
||||||
|
// We weren't able to insert the entry, so let the next call try again
|
||||||
|
currentAbsoluteMouseState.dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// There's already a packet holder queued to send this event
|
// There's already a packet holder queued to send this event
|
||||||
|
PltUnlockMutex(&batchedInputMutex);
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PltUnlockMutex(&batchedInputMutex);
|
|
||||||
|
|
||||||
// This is not thread safe, but it's not a big deal because callers that want to
|
// This is not thread safe, but it's not a big deal because callers that want to
|
||||||
// use LiSendRelativeMotionAsMousePositionEvent() must not mix these function
|
// use LiSendRelativeMotionAsMousePositionEvent() must not mix these function
|
||||||
// without synchronization (otherwise the state of the cursor on the host is
|
// without synchronization (otherwise the state of the cursor on the host is
|
||||||
|
|
@ -1536,9 +1544,14 @@ int LiSendControllerMotionEvent(uint8_t controllerNumber, uint8_t motionType, fl
|
||||||
|
|
||||||
// Queue a packet holder if this is the only pending sensor event
|
// Queue a packet holder if this is the only pending sensor event
|
||||||
if (!currentGamepadSensorState[controllerNumber][motionType - 1].dirty) {
|
if (!currentGamepadSensorState[controllerNumber][motionType - 1].dirty) {
|
||||||
|
// Set the dirty flag to claim ownership of inserting the packet holder
|
||||||
|
// and unlock to allow other threads to enqueue or process input.
|
||||||
|
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = true;
|
||||||
|
PltUnlockMutex(&batchedInputMutex);
|
||||||
|
|
||||||
holder = allocatePacketHolder(0);
|
holder = allocatePacketHolder(0);
|
||||||
if (holder == NULL) {
|
if (holder == NULL) {
|
||||||
PltUnlockMutex(&batchedInputMutex);
|
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = false;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1554,22 +1567,21 @@ int LiSendControllerMotionEvent(uint8_t controllerNumber, uint8_t motionType, fl
|
||||||
// Remaining fields are set in the input thread based on the latest currentGamepadSensorState values
|
// Remaining fields are set in the input thread based on the latest currentGamepadSensorState values
|
||||||
|
|
||||||
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
err = LbqOfferQueueItem(&packetQueue, holder, &holder->entry);
|
||||||
if (err == LBQ_SUCCESS) {
|
if (err != LBQ_SUCCESS) {
|
||||||
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
LC_ASSERT(err == LBQ_BOUND_EXCEEDED);
|
||||||
Limelog("Input queue reached maximum size limit\n");
|
Limelog("Input queue reached maximum size limit\n");
|
||||||
freePacketHolder(holder);
|
freePacketHolder(holder);
|
||||||
|
|
||||||
|
// We weren't able to insert the entry, so let the next call try again
|
||||||
|
currentGamepadSensorState[controllerNumber][motionType - 1].dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// There's already a packet holder queued to send this event
|
// There's already a packet holder queued to send this event
|
||||||
|
PltUnlockMutex(&batchedInputMutex);
|
||||||
err = 0;
|
err = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
PltUnlockMutex(&batchedInputMutex);
|
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ extern uint32_t EncryptionFeaturesEnabled;
|
||||||
|
|
||||||
// ENet channel ID values
|
// ENet channel ID values
|
||||||
#define CTRL_CHANNEL_GENERIC 0x00
|
#define CTRL_CHANNEL_GENERIC 0x00
|
||||||
#define CTRL_CHANNEL_URGENT 0x01 // IDR and reference frame invalidation requests
|
#define CTRL_CHANNEL_URGENT 0x01 // IDR, LTR ACK and RFI
|
||||||
#define CTRL_CHANNEL_KEYBOARD 0x02
|
#define CTRL_CHANNEL_KEYBOARD 0x02
|
||||||
#define CTRL_CHANNEL_MOUSE 0x03
|
#define CTRL_CHANNEL_MOUSE 0x03
|
||||||
#define CTRL_CHANNEL_PEN 0x04
|
#define CTRL_CHANNEL_PEN 0x04
|
||||||
|
|
@ -119,7 +119,7 @@ int startControlStream(void);
|
||||||
int stopControlStream(void);
|
int stopControlStream(void);
|
||||||
void destroyControlStream(void);
|
void destroyControlStream(void);
|
||||||
void connectionDetectedFrameLoss(uint32_t startFrame, uint32_t endFrame);
|
void connectionDetectedFrameLoss(uint32_t startFrame, uint32_t endFrame);
|
||||||
void connectionReceivedCompleteFrame(uint32_t frameIndex);
|
void connectionReceivedCompleteFrame(uint32_t frameIndex, bool frameIsLTR);
|
||||||
void connectionSawFrame(uint32_t frameIndex);
|
void connectionSawFrame(uint32_t frameIndex);
|
||||||
void connectionSendFrameFecStatus(PSS_FRAME_FEC_STATUS fecStatus);
|
void connectionSendFrameFecStatus(PSS_FRAME_FEC_STATUS fecStatus);
|
||||||
int sendInputPacketOnControlStream(unsigned char* data, int length, uint8_t channelId, uint32_t flags, bool moreData);
|
int sendInputPacketOnControlStream(unsigned char* data, int length, uint8_t channelId, uint32_t flags, bool moreData);
|
||||||
|
|
|
||||||
|
|
@ -483,6 +483,8 @@ typedef void(*ConnListenerSetAdaptiveTriggers)(uint16_t controllerNumber, uint8_
|
||||||
// This callback is invoked to set a controller's RGB LED (if present).
|
// This callback is invoked to set a controller's RGB LED (if present).
|
||||||
typedef void(*ConnListenerSetControllerLED)(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b);
|
typedef void(*ConnListenerSetControllerLED)(uint16_t controllerNumber, uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
|
||||||
|
typedef void(*ConnListenerSetInputPolicy)(uint8_t allowKeyboard, uint8_t allowMouse, uint8_t allowGamepad, uint8_t reason);
|
||||||
|
|
||||||
typedef struct _CONNECTION_LISTENER_CALLBACKS {
|
typedef struct _CONNECTION_LISTENER_CALLBACKS {
|
||||||
ConnListenerStageStarting stageStarting;
|
ConnListenerStageStarting stageStarting;
|
||||||
ConnListenerStageComplete stageComplete;
|
ConnListenerStageComplete stageComplete;
|
||||||
|
|
@ -497,6 +499,7 @@ typedef struct _CONNECTION_LISTENER_CALLBACKS {
|
||||||
ConnListenerSetMotionEventState setMotionEventState;
|
ConnListenerSetMotionEventState setMotionEventState;
|
||||||
ConnListenerSetControllerLED setControllerLED;
|
ConnListenerSetControllerLED setControllerLED;
|
||||||
ConnListenerSetAdaptiveTriggers setAdaptiveTriggers;
|
ConnListenerSetAdaptiveTriggers setAdaptiveTriggers;
|
||||||
|
ConnListenerSetInputPolicy setInputPolicy;
|
||||||
} CONNECTION_LISTENER_CALLBACKS, *PCONNECTION_LISTENER_CALLBACKS;
|
} CONNECTION_LISTENER_CALLBACKS, *PCONNECTION_LISTENER_CALLBACKS;
|
||||||
|
|
||||||
// Use this function to zero the connection callbacks when allocated on the stack or heap
|
// Use this function to zero the connection callbacks when allocated on the stack or heap
|
||||||
|
|
@ -837,6 +840,12 @@ int LiSendHighResScrollEvent(short scrollAmount);
|
||||||
int LiSendHScrollEvent(signed char scrollClicks);
|
int LiSendHScrollEvent(signed char scrollClicks);
|
||||||
int LiSendHighResHScrollEvent(short scrollAmount);
|
int LiSendHighResHScrollEvent(short scrollAmount);
|
||||||
|
|
||||||
|
#define LI_SESSION_INPUT_POLICY_REASON_STREAM_START 0x00
|
||||||
|
#define LI_SESSION_INPUT_POLICY_REASON_USER_TOGGLE 0x01
|
||||||
|
#define LI_SESSION_INPUT_POLICY_REASON_HOST_ACK 0x02
|
||||||
|
#define LI_SESSION_INPUT_POLICY_REASON_HOST_OVERRIDE 0x03
|
||||||
|
int LiSendSessionInputPolicy(bool allowKeyboard, bool allowMouse, bool allowGamepad, uint8_t reason);
|
||||||
|
|
||||||
// This function returns a time in microseconds with an implementation-defined epoch.
|
// This function returns a time in microseconds with an implementation-defined epoch.
|
||||||
// It should only ever be compared with the return value from a previous call to itself.
|
// It should only ever be compared with the return value from a previous call to itself.
|
||||||
uint64_t LiGetMicroseconds(void);
|
uint64_t LiGetMicroseconds(void);
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,7 @@ void* ThreadProc(void* context) {
|
||||||
|
|
||||||
ctx->entry(ctx->context);
|
ctx->entry(ctx->context);
|
||||||
|
|
||||||
#if defined(__vita__)
|
free(ctx);
|
||||||
free(ctx);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(LC_WINDOWS) || defined(__vita__) || defined(__WIIU__) || defined(__3DS__)
|
#if defined(LC_WINDOWS) || defined(__vita__) || defined(__WIIU__) || defined(__3DS__)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -288,11 +286,14 @@ int PltCreateThread(const char* name, ThreadEntry entry, void* context, PLT_THRE
|
||||||
|
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
|
|
||||||
|
#ifdef __vita__
|
||||||
pthread_attr_setstacksize(&attr, 0x100000);
|
pthread_attr_setstacksize(&attr, 0x100000);
|
||||||
|
#endif
|
||||||
|
|
||||||
ctx->name = name;
|
ctx->name = name;
|
||||||
|
|
||||||
int err = pthread_create(&thread->thread, &attr, ThreadProc, ctx);
|
int err = pthread_create(&thread->thread, &attr, ThreadProc, ctx);
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
free(ctx);
|
free(ctx);
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
|
|
@ -568,6 +568,40 @@ Exit:
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int getLocalAddressByUdpConnect(const struct sockaddr_storage* targetAddr, SOCKADDR_LEN targetAddrLen, unsigned short targetPort,
|
||||||
|
struct sockaddr_storage* localAddr, SOCKADDR_LEN* localAddrLen) {
|
||||||
|
SOCKET udpSocket;
|
||||||
|
LC_SOCKADDR connAddr;
|
||||||
|
|
||||||
|
LC_ASSERT(targetPort != 0);
|
||||||
|
|
||||||
|
udpSocket = createSocket(targetAddr->ss_family, SOCK_DGRAM, IPPROTO_UDP, false);
|
||||||
|
if (udpSocket == INVALID_SOCKET) {
|
||||||
|
return LastSocketError();
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&connAddr, targetAddr, targetAddrLen);
|
||||||
|
SET_PORT(&connAddr, RtspPortNumber);
|
||||||
|
|
||||||
|
if (connect(udpSocket, (struct sockaddr*)&connAddr, targetAddrLen) < 0) {
|
||||||
|
int err = LastSocketError();
|
||||||
|
Limelog("UDP connect() failed: %d\n", err);
|
||||||
|
closeSocket(udpSocket);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*localAddrLen = sizeof(*localAddr);
|
||||||
|
if (getsockname(udpSocket, (struct sockaddr*)localAddr, localAddrLen) < 0) {
|
||||||
|
int err = LastSocketError();
|
||||||
|
Limelog("getsockname() failed: %d\n", err);
|
||||||
|
closeSocket(udpSocket);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeSocket(udpSocket);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// See TCP_MAXSEG note in connectTcpSocket() above for more information.
|
// See TCP_MAXSEG note in connectTcpSocket() above for more information.
|
||||||
// TCP_NODELAY must be enabled on the socket for this function to work!
|
// TCP_NODELAY must be enabled on the socket for this function to work!
|
||||||
int sendMtuSafe(SOCKET s, char* buffer, int size) {
|
int sendMtuSafe(SOCKET s, char* buffer, int size) {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ void addrToUrlSafeString(struct sockaddr_storage* addr, char* string, size_t str
|
||||||
|
|
||||||
SOCKET createSocket(int addressFamily, int socketType, int protocol, bool nonBlocking);
|
SOCKET createSocket(int addressFamily, int socketType, int protocol, bool nonBlocking);
|
||||||
SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec);
|
SOCKET connectTcpSocket(struct sockaddr_storage* dstaddr, SOCKADDR_LEN addrlen, unsigned short port, int timeoutSec);
|
||||||
|
int getLocalAddressByUdpConnect(const struct sockaddr_storage* targetAddr, SOCKADDR_LEN targetAddrLen, unsigned short targetPort,
|
||||||
|
struct sockaddr_storage* localAddr, SOCKADDR_LEN* localAddrLen);
|
||||||
int sendMtuSafe(SOCKET s, char* buffer, int size);
|
int sendMtuSafe(SOCKET s, char* buffer, int size);
|
||||||
SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCKADDR_LEN addrLen, int bufferSize, int socketQosType);
|
SOCKET bindUdpSocket(int addressFamily, struct sockaddr_storage* localAddr, SOCKADDR_LEN addrLen, int bufferSize, int socketQosType);
|
||||||
int enableNoDelay(SOCKET s);
|
int enableNoDelay(SOCKET s);
|
||||||
|
|
|
||||||
|
|
@ -382,9 +382,9 @@ cleanup_packets:
|
||||||
|
|
||||||
// Check all NV_VIDEO_PACKET fields except FEC stuff which differs in the recovered packet
|
// Check all NV_VIDEO_PACKET fields except FEC stuff which differs in the recovered packet
|
||||||
LC_ASSERT_VT(nvPacket->flags == droppedNvPacket->flags);
|
LC_ASSERT_VT(nvPacket->flags == droppedNvPacket->flags);
|
||||||
|
LC_ASSERT_VT(nvPacket->extraFlags == droppedNvPacket->extraFlags);
|
||||||
LC_ASSERT_VT(nvPacket->frameIndex == droppedNvPacket->frameIndex);
|
LC_ASSERT_VT(nvPacket->frameIndex == droppedNvPacket->frameIndex);
|
||||||
LC_ASSERT_VT(nvPacket->streamPacketIndex == droppedNvPacket->streamPacketIndex);
|
LC_ASSERT_VT(nvPacket->streamPacketIndex == droppedNvPacket->streamPacketIndex);
|
||||||
LC_ASSERT_VT(nvPacket->reserved == droppedNvPacket->reserved);
|
|
||||||
LC_ASSERT_VT(!queue->multiFecCapable || nvPacket->multiFecBlocks == droppedNvPacket->multiFecBlocks);
|
LC_ASSERT_VT(!queue->multiFecCapable || nvPacket->multiFecBlocks == droppedNvPacket->multiFecBlocks);
|
||||||
|
|
||||||
// Check the data itself - use memcmp() and only loop if an error is detected
|
// Check the data itself - use memcmp() and only loop if an error is detected
|
||||||
|
|
|
||||||
|
|
@ -477,18 +477,6 @@ static bool transactRtspMessageTcp(PRTSP_MESSAGE request, PRTSP_MESSAGE response
|
||||||
// Decrypt (if necessary) and deserialize the RTSP response
|
// Decrypt (if necessary) and deserialize the RTSP response
|
||||||
ret = unsealRtspMessage(responseBuffer, offset, response);
|
ret = unsealRtspMessage(responseBuffer, offset, response);
|
||||||
|
|
||||||
// Fetch the local address for this socket if it's not populated yet
|
|
||||||
if (LocalAddr.ss_family == 0) {
|
|
||||||
SOCKADDR_LEN addrLen = (SOCKADDR_LEN)sizeof(LocalAddr);
|
|
||||||
if (getsockname(sock, (struct sockaddr*)&LocalAddr, &addrLen) < 0) {
|
|
||||||
Limelog("Failed to get local address: %d\n", LastSocketError());
|
|
||||||
memset(&LocalAddr, 0, sizeof(LocalAddr));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LC_ASSERT(addrLen == AddrLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Exit:
|
Exit:
|
||||||
if (serializedMessage != NULL) {
|
if (serializedMessage != NULL) {
|
||||||
free(serializedMessage);
|
free(serializedMessage);
|
||||||
|
|
|
||||||
20
src/Video.h
20
src/Video.h
|
|
@ -22,11 +22,13 @@ typedef struct _ENC_VIDEO_HEADER {
|
||||||
#define FLAG_EOF 0x2
|
#define FLAG_EOF 0x2
|
||||||
#define FLAG_SOF 0x4
|
#define FLAG_SOF 0x4
|
||||||
|
|
||||||
|
#define NV_VIDEO_PACKET_EXTRA_FLAG_LTR_FRAME 0x1
|
||||||
|
|
||||||
typedef struct _NV_VIDEO_PACKET {
|
typedef struct _NV_VIDEO_PACKET {
|
||||||
uint32_t streamPacketIndex;
|
uint32_t streamPacketIndex;
|
||||||
uint32_t frameIndex;
|
uint32_t frameIndex;
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
uint8_t reserved;
|
uint8_t extraFlags;
|
||||||
uint8_t multiFecFlags;
|
uint8_t multiFecFlags;
|
||||||
uint8_t multiFecBlocks;
|
uint8_t multiFecBlocks;
|
||||||
uint32_t fecInfo;
|
uint32_t fecInfo;
|
||||||
|
|
@ -67,4 +69,20 @@ typedef struct _SS_FRAME_FEC_STATUS {
|
||||||
uint8_t multiFecBlockCount;
|
uint8_t multiFecBlockCount;
|
||||||
} SS_FRAME_FEC_STATUS, *PSS_FRAME_FEC_STATUS;
|
} SS_FRAME_FEC_STATUS, *PSS_FRAME_FEC_STATUS;
|
||||||
|
|
||||||
|
// Fields are little-endian
|
||||||
|
#define SS_LTR_FRAME_ACK_PTYPE 0x0350
|
||||||
|
typedef struct _SS_LTR_FRAME_ACK {
|
||||||
|
uint32_t frameIndex;
|
||||||
|
uint32_t reserved;
|
||||||
|
} SS_LTR_FRAME_ACK, *PSS_LTR_FRAME_ACK;
|
||||||
|
|
||||||
|
// Fields are little-endian
|
||||||
|
#define SS_RFI_REQUEST_PTYPE 0x0301
|
||||||
|
typedef struct _SS_RFI_REQUEST {
|
||||||
|
uint32_t firstFrameIndex;
|
||||||
|
uint32_t reserved1;
|
||||||
|
uint32_t lastFrameIndex;
|
||||||
|
uint32_t reserved2[3];
|
||||||
|
} SS_RFI_REQUEST, *PSS_RFI_REQUEST;
|
||||||
|
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
|
||||||
|
|
@ -466,7 +466,7 @@ static bool isIdrFrameStart(PBUFFER_DESC buffer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reassemble the frame with the given frame number
|
// Reassemble the frame with the given frame number
|
||||||
static void reassembleFrame(int frameNumber) {
|
static void reassembleFrame(int frameNumber, bool frameIsLTR) {
|
||||||
if (nalChainHead != NULL) {
|
if (nalChainHead != NULL) {
|
||||||
QUEUED_DECODE_UNIT qduDS;
|
QUEUED_DECODE_UNIT qduDS;
|
||||||
PQUEUED_DECODE_UNIT qdu;
|
PQUEUED_DECODE_UNIT qdu;
|
||||||
|
|
@ -539,7 +539,7 @@ static void reassembleFrame(int frameNumber) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the control connection
|
// Notify the control connection
|
||||||
connectionReceivedCompleteFrame(frameNumber);
|
connectionReceivedCompleteFrame(frameNumber, frameIsLTR);
|
||||||
|
|
||||||
// Clear frame drops
|
// Clear frame drops
|
||||||
consecutiveFrameDrops = 0;
|
consecutiveFrameDrops = 0;
|
||||||
|
|
@ -748,6 +748,7 @@ static void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length,
|
||||||
BUFFER_DESC currentPos;
|
BUFFER_DESC currentPos;
|
||||||
uint32_t frameIndex;
|
uint32_t frameIndex;
|
||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
|
uint8_t extraFlags;
|
||||||
bool firstPacket, lastPacket;
|
bool firstPacket, lastPacket;
|
||||||
uint32_t streamPacketIndex;
|
uint32_t streamPacketIndex;
|
||||||
uint8_t fecCurrentBlockNumber;
|
uint8_t fecCurrentBlockNumber;
|
||||||
|
|
@ -765,6 +766,7 @@ static void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length,
|
||||||
fecLastBlockNumber = (videoPacket->multiFecBlocks >> 6) & 0x3;
|
fecLastBlockNumber = (videoPacket->multiFecBlocks >> 6) & 0x3;
|
||||||
frameIndex = videoPacket->frameIndex;
|
frameIndex = videoPacket->frameIndex;
|
||||||
flags = videoPacket->flags;
|
flags = videoPacket->flags;
|
||||||
|
extraFlags = videoPacket->extraFlags;
|
||||||
firstPacket = isFirstPacket(flags, fecCurrentBlockNumber);
|
firstPacket = isFirstPacket(flags, fecCurrentBlockNumber);
|
||||||
lastPacket = (flags & FLAG_EOF) && fecCurrentBlockNumber == fecLastBlockNumber;
|
lastPacket = (flags & FLAG_EOF) && fecCurrentBlockNumber == fecLastBlockNumber;
|
||||||
|
|
||||||
|
|
@ -1119,7 +1121,7 @@ static void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reassembleFrame(frameIndex);
|
reassembleFrame(frameIndex, extraFlags & NV_VIDEO_PACKET_EXTRA_FLAG_LTR_FRAME);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue