mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2025-08-18 01:15:46 +00:00
Rework sequencing code and fix audio dropout across 64K boundary in the process
This commit is contained in:
parent
bf737dc959
commit
c4692a5b5f
@ -21,7 +21,18 @@ extern AUDIO_RENDERER_CALLBACKS AudioCallbacks;
|
||||
extern int NegotiatedVideoFormat;
|
||||
extern volatile int ConnectionInterrupted;
|
||||
|
||||
int isBeforeSignedInt(int numA, int numB, int ambiguousCase);
|
||||
#ifndef UINT24_MAX
|
||||
#define UINT24_MAX 0xFFFFFF
|
||||
#endif
|
||||
|
||||
#define U16(x) ((unsigned short) ((x) & UINT16_MAX))
|
||||
#define U24(x) ((unsigned int) ((x) & UINT24_MAX))
|
||||
#define U32(x) ((unsigned int) ((x) & UINT32_MAX))
|
||||
|
||||
#define isBefore16(x, y) (U16((x) - (y)) > (UINT16_MAX/2))
|
||||
#define isBefore24(x, y) (U24((x) - (y)) > (UINT24_MAX/2))
|
||||
#define isBefore32(x, y) (U32((x) - (y)) > (UINT32_MAX/2))
|
||||
|
||||
int serviceEnetHost(ENetHost* client, ENetEvent* event, enet_uint32 timeoutMs);
|
||||
int extractVersionQuadFromString(const char* string, int* quad);
|
||||
int isReferenceFrameInvalidationEnabled(void);
|
||||
|
17
src/Misc.c
17
src/Misc.c
@ -31,23 +31,6 @@ int serviceEnetHost(ENetHost* client, ENetEvent* event, enet_uint32 timeoutMs) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int isBeforeSignedInt(int numA, int numB, int ambiguousCase) {
|
||||
// This should be the common case for most callers
|
||||
if (numA == numB) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If numA and numB have the same signs,
|
||||
// we can just do a regular comparison.
|
||||
if ((numA < 0 && numB < 0) || (numA >= 0 && numB >= 0)) {
|
||||
return numA < numB;
|
||||
}
|
||||
else {
|
||||
// The sign switch is ambiguous
|
||||
return ambiguousCase;
|
||||
}
|
||||
}
|
||||
|
||||
int extractVersionQuadFromString(const char* string, int* quad) {
|
||||
char versionString[128];
|
||||
char* nextDot;
|
||||
|
@ -2,9 +2,6 @@
|
||||
#include "RtpFecQueue.h"
|
||||
#include "rs.h"
|
||||
|
||||
#define ushort(x) ((unsigned short) ((x) % (UINT16_MAX+1)))
|
||||
#define isBefore(x, y) (ushort((x) - (y)) > (UINT16_MAX/2))
|
||||
|
||||
void RtpfInitializeQueue(PRTP_FEC_QUEUE queue) {
|
||||
reed_solomon_init();
|
||||
memset(queue, 0, sizeof(*queue));
|
||||
@ -31,7 +28,7 @@ void RtpfCleanupQueue(PRTP_FEC_QUEUE queue) {
|
||||
static int queuePacket(PRTP_FEC_QUEUE queue, PRTPFEC_QUEUE_ENTRY newEntry, int head, PRTP_PACKET packet, int length, int isParity) {
|
||||
PRTPFEC_QUEUE_ENTRY entry;
|
||||
|
||||
LC_ASSERT(!isBefore(packet->sequenceNumber, queue->nextRtpSequenceNumber));
|
||||
LC_ASSERT(!isBefore16(packet->sequenceNumber, queue->nextRtpSequenceNumber));
|
||||
|
||||
// Don't queue duplicates either
|
||||
entry = queue->bufferHead;
|
||||
@ -77,7 +74,7 @@ static int queuePacket(PRTP_FEC_QUEUE queue, PRTPFEC_QUEUE_ENTRY newEntry, int h
|
||||
|
||||
// Returns 0 if the frame is completely constructed
|
||||
static int reconstructFrame(PRTP_FEC_QUEUE queue) {
|
||||
int totalPackets = ushort(queue->bufferHighestSequenceNumber - queue->bufferLowestSequenceNumber) + 1;
|
||||
int totalPackets = U16(queue->bufferHighestSequenceNumber - queue->bufferLowestSequenceNumber) + 1;
|
||||
int totalParityPackets = (queue->bufferDataPackets * queue->fecPercentage + 99) / 100;
|
||||
int parityPackets = totalPackets - queue->bufferDataPackets;
|
||||
int missingPackets = totalPackets - queue->bufferSize;
|
||||
@ -120,7 +117,7 @@ static int reconstructFrame(PRTP_FEC_QUEUE queue) {
|
||||
|
||||
PRTPFEC_QUEUE_ENTRY entry = queue->bufferHead;
|
||||
while (entry != NULL) {
|
||||
int index = ushort(entry->packet->sequenceNumber - queue->bufferLowestSequenceNumber);
|
||||
int index = U16(entry->packet->sequenceNumber - queue->bufferLowestSequenceNumber);
|
||||
packets[index] = (unsigned char*) entry->packet;
|
||||
marks[index] = 0;
|
||||
|
||||
@ -156,7 +153,7 @@ cleanup_packets:
|
||||
if (ret == 0 && i < queue->bufferDataPackets) {
|
||||
PRTPFEC_QUEUE_ENTRY queueEntry = (PRTPFEC_QUEUE_ENTRY)&packets[i][receiveSize];
|
||||
PRTP_PACKET rtpPacket = (PRTP_PACKET) packets[i];
|
||||
rtpPacket->sequenceNumber = ushort(i + queue->bufferLowestSequenceNumber);
|
||||
rtpPacket->sequenceNumber = U16(i + queue->bufferLowestSequenceNumber);
|
||||
rtpPacket->header = queue->bufferHead->packet->header;
|
||||
|
||||
int dataOffset = sizeof(*rtpPacket);
|
||||
@ -172,7 +169,7 @@ cleanup_packets:
|
||||
// discarded by decoders. It's not safe to strip all zero padding because
|
||||
// it may be a legitimate part of the H.264 bytestream.
|
||||
|
||||
LC_ASSERT(isBefore(rtpPacket->sequenceNumber, queue->bufferFirstParitySequenceNumber));
|
||||
LC_ASSERT(isBefore16(rtpPacket->sequenceNumber, queue->bufferFirstParitySequenceNumber));
|
||||
queuePacket(queue, queueEntry, 0, rtpPacket, StreamConfig.packetSize + dataOffset, 0);
|
||||
} else if (packets[i] != NULL) {
|
||||
free(packets[i]);
|
||||
@ -215,7 +212,7 @@ static void removeEntry(PRTP_FEC_QUEUE queue, PRTPFEC_QUEUE_ENTRY entry) {
|
||||
}
|
||||
|
||||
int RtpfAddPacket(PRTP_FEC_QUEUE queue, PRTP_PACKET packet, int length, PRTPFEC_QUEUE_ENTRY packetEntry) {
|
||||
if (isBefore(packet->sequenceNumber, queue->nextRtpSequenceNumber)) {
|
||||
if (isBefore16(packet->sequenceNumber, queue->nextRtpSequenceNumber)) {
|
||||
// Reject packets behind our current sequence number
|
||||
return RTPF_RET_REJECTED;
|
||||
}
|
||||
@ -227,7 +224,7 @@ int RtpfAddPacket(PRTP_FEC_QUEUE queue, PRTP_PACKET packet, int length, PRTPFEC_
|
||||
|
||||
PNV_VIDEO_PACKET nvPacket = (PNV_VIDEO_PACKET)(((char*)packet) + dataOffset);
|
||||
|
||||
if (isBefore(nvPacket->frameIndex, queue->currentFrameNumber)) {
|
||||
if (isBefore16(nvPacket->frameIndex, queue->currentFrameNumber)) {
|
||||
// Reject frames behind our current frame number
|
||||
return RTPF_RET_REJECTED;
|
||||
}
|
||||
@ -257,21 +254,21 @@ int RtpfAddPacket(PRTP_FEC_QUEUE queue, PRTP_PACKET packet, int length, PRTPFEC_
|
||||
queue->bufferSize = 0;
|
||||
|
||||
int fecIndex = (nvPacket->fecInfo & 0xFF000) >> 12;
|
||||
queue->bufferLowestSequenceNumber = ushort(packet->sequenceNumber - fecIndex);
|
||||
queue->bufferLowestSequenceNumber = U16(packet->sequenceNumber - fecIndex);
|
||||
queue->receivedBufferDataPackets = 0;
|
||||
queue->bufferHighestSequenceNumber = packet->sequenceNumber;
|
||||
queue->bufferDataPackets = ((nvPacket->fecInfo & 0xFFF00000) >> 20) / 4;
|
||||
queue->fecPercentage = ((nvPacket->fecInfo & 0xFF0) >> 4);
|
||||
queue->bufferFirstParitySequenceNumber = ushort(queue->bufferLowestSequenceNumber + queue->bufferDataPackets);
|
||||
} else if (isBefore(queue->bufferHighestSequenceNumber, packet->sequenceNumber)) {
|
||||
queue->bufferFirstParitySequenceNumber = U16(queue->bufferLowestSequenceNumber + queue->bufferDataPackets);
|
||||
} else if (isBefore16(queue->bufferHighestSequenceNumber, packet->sequenceNumber)) {
|
||||
queue->bufferHighestSequenceNumber = packet->sequenceNumber;
|
||||
}
|
||||
|
||||
if (!queuePacket(queue, packetEntry, 0, packet, length, !isBefore(packet->sequenceNumber, queue->bufferFirstParitySequenceNumber))) {
|
||||
if (!queuePacket(queue, packetEntry, 0, packet, length, !isBefore16(packet->sequenceNumber, queue->bufferFirstParitySequenceNumber))) {
|
||||
return RTPF_RET_REJECTED;
|
||||
}
|
||||
else {
|
||||
if (isBefore(packet->sequenceNumber, queue->bufferFirstParitySequenceNumber)) {
|
||||
if (isBefore16(packet->sequenceNumber, queue->bufferFirstParitySequenceNumber)) {
|
||||
queue->receivedBufferDataPackets++;
|
||||
}
|
||||
|
||||
@ -326,7 +323,7 @@ PRTPFEC_QUEUE_ENTRY RtpfGetQueuedPacket(PRTP_FEC_QUEUE queue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (queuedEntry == NULL || isBefore(entry->packet->sequenceNumber, lowestRtpSequenceNumber)) {
|
||||
if (queuedEntry == NULL || isBefore16(entry->packet->sequenceNumber, lowestRtpSequenceNumber)) {
|
||||
lowestRtpSequenceNumber = entry->packet->sequenceNumber;
|
||||
queuedEntry = entry;
|
||||
}
|
||||
|
@ -19,15 +19,11 @@ void RtpqCleanupQueue(PRTP_REORDER_QUEUE queue) {
|
||||
|
||||
// newEntry is contained within the packet buffer so we free the whole entry by freeing entry->packet
|
||||
static int queuePacket(PRTP_REORDER_QUEUE queue, PRTP_QUEUE_ENTRY newEntry, int head, PRTP_PACKET packet) {
|
||||
if (queue->nextRtpSequenceNumber != UINT16_MAX) {
|
||||
PRTP_QUEUE_ENTRY entry;
|
||||
|
||||
// Don't queue packets we're already ahead of
|
||||
if (isBeforeSignedInt(packet->sequenceNumber, queue->nextRtpSequenceNumber, 0)) {
|
||||
return 0;
|
||||
}
|
||||
LC_ASSERT(!isBefore16(packet->sequenceNumber, queue->nextRtpSequenceNumber));
|
||||
|
||||
// Don't queue duplicates either
|
||||
// Don't queue duplicates
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (entry->packet->sequenceNumber == packet->sequenceNumber) {
|
||||
@ -36,7 +32,6 @@ static int queuePacket(PRTP_REORDER_QUEUE queue, PRTP_QUEUE_ENTRY newEntry, int
|
||||
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
newEntry->packet = packet;
|
||||
newEntry->queueTimeMs = PltGetMillis();
|
||||
@ -93,7 +88,7 @@ static PRTP_QUEUE_ENTRY getEntryByLowestSeq(PRTP_REORDER_QUEUE queue) {
|
||||
lowestSeqEntry = queue->queueHead;
|
||||
entry = queue->queueHead;
|
||||
while (entry != NULL) {
|
||||
if (isBeforeSignedInt(entry->packet->sequenceNumber, lowestSeqEntry->packet->sequenceNumber, 1)) {
|
||||
if (isBefore16(entry->packet->sequenceNumber, lowestSeqEntry->packet->sequenceNumber)) {
|
||||
lowestSeqEntry = entry;
|
||||
}
|
||||
|
||||
@ -163,7 +158,7 @@ static PRTP_QUEUE_ENTRY validateQueueConstraints(PRTP_REORDER_QUEUE queue) {
|
||||
|
||||
int RtpqAddPacket(PRTP_REORDER_QUEUE queue, PRTP_PACKET packet, PRTP_QUEUE_ENTRY packetEntry) {
|
||||
if (queue->nextRtpSequenceNumber != UINT16_MAX &&
|
||||
isBeforeSignedInt(packet->sequenceNumber, queue->nextRtpSequenceNumber, 0)) {
|
||||
isBefore16(packet->sequenceNumber, queue->nextRtpSequenceNumber)) {
|
||||
// Reject packets behind our current sequence number
|
||||
return RTPQ_RET_REJECTED;
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ int getNextQueuedDecodeUnit(PQUEUED_DECODE_UNIT* qdu);
|
||||
#define FLAG_SOF 0x4
|
||||
|
||||
typedef struct _NV_VIDEO_PACKET {
|
||||
int streamPacketIndex;
|
||||
int frameIndex;
|
||||
unsigned int streamPacketIndex;
|
||||
unsigned int frameIndex;
|
||||
char flags;
|
||||
char reserved[3];
|
||||
int fecInfo;
|
||||
|
@ -6,19 +6,17 @@
|
||||
static PLENTRY nalChainHead;
|
||||
static int nalChainDataLength;
|
||||
|
||||
static int nextFrameNumber;
|
||||
static int startFrameNumber;
|
||||
static unsigned int nextFrameNumber;
|
||||
static unsigned int startFrameNumber;
|
||||
static int waitingForNextSuccessfulFrame;
|
||||
static int waitingForIdrFrame;
|
||||
static int lastPacketInStream;
|
||||
static unsigned int lastPacketInStream;
|
||||
static int decodingFrame;
|
||||
static int strictIdrFrameWait;
|
||||
static unsigned long long firstPacketReceiveTime;
|
||||
|
||||
#define TRUNCATE_24BIT(x) ((x) & 0xFFFFFF)
|
||||
|
||||
#define CONSECUTIVE_DROP_LIMIT 120
|
||||
static int consecutiveFrameDrops;
|
||||
static unsigned int consecutiveFrameDrops;
|
||||
|
||||
static LINKED_BLOCKING_QUEUE decodeUnitQueue;
|
||||
|
||||
@ -38,7 +36,7 @@ void initializeVideoDepacketizer(int pktSize) {
|
||||
startFrameNumber = 0;
|
||||
waitingForNextSuccessfulFrame = 0;
|
||||
waitingForIdrFrame = 1;
|
||||
lastPacketInStream = -1;
|
||||
lastPacketInStream = UINT32_MAX;
|
||||
decodingFrame = 0;
|
||||
firstPacketReceiveTime = 0;
|
||||
strictIdrFrameWait = !isReferenceFrameInvalidationEnabled();
|
||||
@ -441,8 +439,8 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length, unsigned long l
|
||||
BUFFER_DESC currentPos;
|
||||
int frameIndex;
|
||||
char flags;
|
||||
int firstPacket;
|
||||
int streamPacketIndex;
|
||||
unsigned int firstPacket;
|
||||
unsigned int streamPacketIndex;
|
||||
|
||||
// Mask the top 8 bits from the SPI
|
||||
videoPacket->streamPacketIndex >>= 8;
|
||||
@ -459,8 +457,8 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length, unsigned long l
|
||||
streamPacketIndex = videoPacket->streamPacketIndex;
|
||||
|
||||
// The packets and frames must be in sequence from the FEC queue
|
||||
LC_ASSERT(!isBeforeSignedInt(streamPacketIndex, TRUNCATE_24BIT(lastPacketInStream + 1), 0));
|
||||
LC_ASSERT(!isBeforeSignedInt(frameIndex, nextFrameNumber, 0));
|
||||
LC_ASSERT(!isBefore24(streamPacketIndex, U24(lastPacketInStream + 1)));
|
||||
LC_ASSERT(!isBefore32(frameIndex, nextFrameNumber));
|
||||
|
||||
// Notify the listener of the latest frame we've seen from the PC
|
||||
connectionSawFrame(frameIndex);
|
||||
@ -472,7 +470,7 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length, unsigned long l
|
||||
// miss one in between
|
||||
if (firstPacket) {
|
||||
// Make sure this is the next consecutive frame
|
||||
if (isBeforeSignedInt(nextFrameNumber, frameIndex, 1)) {
|
||||
if (isBefore32(nextFrameNumber, frameIndex)) {
|
||||
Limelog("Network dropped an entire frame\n");
|
||||
nextFrameNumber = frameIndex;
|
||||
|
||||
@ -491,7 +489,7 @@ void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length, unsigned long l
|
||||
|
||||
// This must be the first packet in a frame or be contiguous with the last
|
||||
// packet received.
|
||||
LC_ASSERT(firstPacket || streamPacketIndex == TRUNCATE_24BIT(lastPacketInStream + 1));
|
||||
LC_ASSERT(firstPacket || streamPacketIndex == U24(lastPacketInStream + 1));
|
||||
|
||||
lastPacketInStream = streamPacketIndex;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user