diff --git a/limelight-common/InputStream.c b/limelight-common/InputStream.c index cc59348..f0a6c50 100644 --- a/limelight-common/InputStream.c +++ b/limelight-common/InputStream.c @@ -94,6 +94,35 @@ void destroyInputStream(void) { initialized = 0; } +// Checks if values are compatible with controller batching +static int checkDirs(short currentVal, short newVal, int* dir) { + if (currentVal == newVal) { + return 1; + } + + // We want to send a new packet if we've now zeroed an axis + if (newVal == 0) { + return 0; + } + + if (*dir == 0) { + if (newVal < currentVal) { + *dir = -1; + } + else { + *dir = 1; + } + } + else if (*dir == -1) { + return newVal < currentVal; + } + else if (newVal < currentVal) { + return 0; + } + + return 1; +} + #define OAES_DATA_OFFSET 32 /* Input thread proc */ @@ -112,6 +141,106 @@ static void inputSendThreadProc(void* context) { listenerCallbacks->connectionTerminated(err); return; } + + // If it's a multi-controller packet we can do batching + if (holder->packet.multiController.header.packetType == htonl(PACKET_TYPE_MULTI_CONTROLLER)) { + PPACKET_HOLDER controllerBatchHolder; + PNV_MULTI_CONTROLLER_PACKET origPkt; + int dirs[6]; + + origPkt = &holder->packet.multiController; + for (;;) { + PNV_MULTI_CONTROLLER_PACKET newPkt; + + // Peek at the next packet + if (LbqPeekQueueElement(&packetQueue, (void**)&controllerBatchHolder) != LBQ_SUCCESS) { + break; + } + + // If it's not a controller packet, we're done + if (controllerBatchHolder->packet.multiController.header.packetType != htonl(PACKET_TYPE_MULTI_CONTROLLER)) { + break; + } + + // Check if it's able to be batched + newPkt = &controllerBatchHolder->packet.multiController; + if (newPkt->buttonFlags != origPkt->buttonFlags || + newPkt->controllerNumber != origPkt->controllerNumber || + !checkDirs(origPkt->leftTrigger, newPkt->leftTrigger, &dirs[0]) || + !checkDirs(origPkt->rightTrigger, newPkt->rightTrigger, &dirs[1]) || + !checkDirs(origPkt->leftStickX, newPkt->leftStickX, &dirs[2]) || + !checkDirs(origPkt->leftStickY, newPkt->leftStickY, &dirs[3]) || + !checkDirs(origPkt->rightStickX, newPkt->rightStickX, &dirs[4]) || + !checkDirs(origPkt->rightStickY, newPkt->rightStickY, &dirs[5])) { + // Batching not allowed + break; + } + + // Remove the batchable controller packet + if (LbqPollQueueElement(&packetQueue, (void**)&controllerBatchHolder) != LBQ_SUCCESS) { + break; + } + + // Update the original packet + origPkt->leftTrigger = newPkt->leftTrigger; + origPkt->rightTrigger = newPkt->rightTrigger; + origPkt->leftStickX = newPkt->leftStickX; + origPkt->leftStickY = newPkt->leftStickY; + origPkt->rightStickX = newPkt->rightStickX; + origPkt->rightStickY = newPkt->rightStickY; + + // Free the batched packet holder + free(controllerBatchHolder); + } + } + // If it's a mouse move packet, we can also do batching + else if (holder->packet.mouseMove.header.packetType == htonl(PACKET_TYPE_MOUSE_MOVE)) { + PPACKET_HOLDER mouseBatchHolder; + int totalDeltaX = htons(holder->packet.mouseMove.deltaX); + int totalDeltaY = htons(holder->packet.mouseMove.deltaY); + + for (;;) { + int partialDeltaX; + int partialDeltaY; + + // Peek at the next packet + if (LbqPeekQueueElement(&packetQueue, (void**)&mouseBatchHolder) != LBQ_SUCCESS) { + break; + } + + // If it's not a mouse move packet, we're done + if (mouseBatchHolder->packet.mouseMove.header.packetType != htonl(PACKET_TYPE_MOUSE_MOVE)) { + break; + } + + partialDeltaX = htons(mouseBatchHolder->packet.mouseMove.deltaX); + partialDeltaY = htons(mouseBatchHolder->packet.mouseMove.deltaY); + + // Check for overflow + if (partialDeltaX + totalDeltaX > INT16_MAX || + partialDeltaX + totalDeltaX < INT16_MIN || + partialDeltaY + totalDeltaY > INT16_MAX || + partialDeltaY + totalDeltaY < INT16_MIN) { + // Total delta would overflow our 16-bit short + break; + } + + // Remove the batchable mouse move packet + if (LbqPollQueueElement(&packetQueue, (void**)&mouseBatchHolder) != LBQ_SUCCESS) { + break; + } + + totalDeltaX += partialDeltaX; + totalDeltaY += partialDeltaY; + + // Free the batched packet holder + free(mouseBatchHolder); + } + + // Update the original packet + holder->packet.mouseMove.deltaX = htons((short)totalDeltaX); + holder->packet.mouseMove.deltaY = htons((short)totalDeltaY); + } encryptedSize = sizeof(encryptedBuffer); err = oaes_encrypt(oaesContext, (const unsigned char*) &holder->packet, holder->packetLength, diff --git a/limelight-common/LinkedBlockingQueue.c b/limelight-common/LinkedBlockingQueue.c index c30b165..25c6980 100644 --- a/limelight-common/LinkedBlockingQueue.c +++ b/limelight-common/LinkedBlockingQueue.c @@ -85,6 +85,60 @@ int LbqOfferQueueItem(PLINKED_BLOCKING_QUEUE queueHead, void* data, PLINKED_BLOC return LBQ_SUCCESS; } +// This must be synchronized with LbqFlushQueueItems by the caller +int LbqPeekQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data) { + if (queueHead->head == NULL) { + return LBQ_NO_ELEMENT; + } + + PltLockMutex(&queueHead->mutex); + + if (queueHead->head == NULL) { + PltUnlockMutex(&queueHead->mutex); + return LBQ_NO_ELEMENT; + } + + *data = queueHead->head->data; + + PltUnlockMutex(&queueHead->mutex); + + return LBQ_SUCCESS; +} + +int LbqPollQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data) { + PLINKED_BLOCKING_QUEUE_ENTRY entry; + + if (queueHead->head == NULL) { + return LBQ_NO_ELEMENT; + } + + PltLockMutex(&queueHead->mutex); + + if (queueHead->head == NULL) { + PltUnlockMutex(&queueHead->mutex); + return LBQ_NO_ELEMENT; + } + + entry = queueHead->head; + queueHead->head = entry->flink; + queueHead->currentSize--; + if (queueHead->head == NULL) { + LC_ASSERT(queueHead->currentSize == 0); + queueHead->tail = NULL; + PltClearEvent(&queueHead->containsDataEvent); + } + else { + LC_ASSERT(queueHead->currentSize != 0); + queueHead->head->blink = NULL; + } + + *data = entry->data; + + PltUnlockMutex(&queueHead->mutex); + + return LBQ_SUCCESS; +} + int LbqWaitForQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data) { PLINKED_BLOCKING_QUEUE_ENTRY entry; int err; diff --git a/limelight-common/LinkedBlockingQueue.h b/limelight-common/LinkedBlockingQueue.h index 59a2571..4be7226 100644 --- a/limelight-common/LinkedBlockingQueue.h +++ b/limelight-common/LinkedBlockingQueue.h @@ -6,6 +6,7 @@ #define LBQ_SUCCESS 0 #define LBQ_INTERRUPTED 1 #define LBQ_BOUND_EXCEEDED 2 +#define LBQ_NO_ELEMENT 3 typedef struct _LINKED_BLOCKING_QUEUE_ENTRY { struct _LINKED_BLOCKING_QUEUE_ENTRY *flink; @@ -25,5 +26,7 @@ typedef struct _LINKED_BLOCKING_QUEUE { int LbqInitializeLinkedBlockingQueue(PLINKED_BLOCKING_QUEUE queueHead, int sizeBound); int LbqOfferQueueItem(PLINKED_BLOCKING_QUEUE queueHead, void* data, PLINKED_BLOCKING_QUEUE_ENTRY entry); int LbqWaitForQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data); +int LbqPollQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data); +int LbqPeekQueueElement(PLINKED_BLOCKING_QUEUE queueHead, void** data); PLINKED_BLOCKING_QUEUE_ENTRY LbqDestroyLinkedBlockingQueue(PLINKED_BLOCKING_QUEUE queueHead); PLINKED_BLOCKING_QUEUE_ENTRY LbqFlushQueueItems(PLINKED_BLOCKING_QUEUE queueHead);