mirror of
https://github.com/moonlight-stream/moonlight-common-c.git
synced 2026-02-16 10:30:59 +00:00
Rework RFI to work reliably with HEVC
This commit is contained in:
@@ -11,6 +11,7 @@ static unsigned int nextFrameNumber;
|
||||
static unsigned int startFrameNumber;
|
||||
static bool waitingForNextSuccessfulFrame;
|
||||
static bool waitingForIdrFrame;
|
||||
static bool waitingForRefInvalFrame;
|
||||
static unsigned int lastPacketInStream;
|
||||
static bool decodingFrame;
|
||||
static bool strictIdrFrameWait;
|
||||
@@ -58,6 +59,7 @@ void initializeVideoDepacketizer(int pktSize) {
|
||||
startFrameNumber = 0;
|
||||
waitingForNextSuccessfulFrame = false;
|
||||
waitingForIdrFrame = true;
|
||||
waitingForRefInvalFrame = false;
|
||||
lastPacketInStream = UINT32_MAX;
|
||||
decodingFrame = false;
|
||||
firstPacketReceiveTime = 0;
|
||||
@@ -90,11 +92,14 @@ static void dropFrameState(void) {
|
||||
// We're dropping frame state now
|
||||
dropStatePending = false;
|
||||
|
||||
// We'll need an IDR frame now if we're in strict mode
|
||||
// or if we've never seen one before
|
||||
if (strictIdrFrameWait || !idrFrameProcessed) {
|
||||
if (strictIdrFrameWait || !idrFrameProcessed || waitingForIdrFrame) {
|
||||
// We'll need an IDR frame now if we're in non-RFI mode, if we've never
|
||||
// received an IDR frame, or if we explicitly need an IDR frame.
|
||||
waitingForIdrFrame = true;
|
||||
}
|
||||
else {
|
||||
waitingForRefInvalFrame = true;
|
||||
}
|
||||
|
||||
// Count the number of consecutive frames dropped
|
||||
consecutiveFrameDrops++;
|
||||
@@ -558,6 +563,7 @@ static void processRtpPayloadSlow(PBUFFER_DESC currentPos, PLENTRY_INTERNAL* exi
|
||||
if (isSeqReferenceFrameStart(currentPos)) {
|
||||
// No longer waiting for an IDR frame
|
||||
waitingForIdrFrame = false;
|
||||
waitingForRefInvalFrame = false;
|
||||
|
||||
// Cancel any pending IDR frame request
|
||||
waitingForNextSuccessfulFrame = false;
|
||||
@@ -701,6 +707,35 @@ static void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length,
|
||||
|
||||
// If this is the first packet, skip the frame header (if one exists)
|
||||
if (firstPacket) {
|
||||
// Parse the frame type from the header
|
||||
if (APP_VERSION_AT_LEAST(7, 1, 415)) {
|
||||
switch (currentPos.data[currentPos.offset + 3]) {
|
||||
case 1: // Normal P-frame
|
||||
break;
|
||||
case 2: // IDR frame
|
||||
case 4: // Intra-refresh
|
||||
case 5: // P-frame with reference frames invalidated
|
||||
if (waitingForRefInvalFrame) {
|
||||
Limelog("Next post-invalidation frame is: %d (%s-frame)\n",
|
||||
frameIndex,
|
||||
currentPos.data[currentPos.offset + 3] == 5 ? "P" : "I");
|
||||
waitingForRefInvalFrame = false;
|
||||
waitingForNextSuccessfulFrame = false;
|
||||
}
|
||||
break;
|
||||
case 104: // Sunshine hardcoded header
|
||||
break;
|
||||
default:
|
||||
Limelog("Unrecognized frame type: %d", currentPos.data[currentPos.offset + 3]);
|
||||
LC_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Hope for the best with older servers
|
||||
waitingForRefInvalFrame = false;
|
||||
}
|
||||
|
||||
if (APP_VERSION_AT_LEAST(7, 1, 446)) {
|
||||
// >= 7.1.446 uses 2 different header lengths based on the first byte:
|
||||
// 0x01 indicates an 8 byte header
|
||||
@@ -793,22 +828,34 @@ static void processRtpPayload(PNV_VIDEO_PACKET videoPacket, int length,
|
||||
decodingFrame = false;
|
||||
nextFrameNumber = frameIndex + 1;
|
||||
|
||||
// If waiting for next successful frame and we got here
|
||||
// with an end flag, we can send a message to the server
|
||||
if (waitingForNextSuccessfulFrame) {
|
||||
// This is the next successful frame after a loss event
|
||||
connectionDetectedFrameLoss(startFrameNumber, frameIndex - 1);
|
||||
// If we can't submit this frame due to a discontinuity in the bitstream,
|
||||
// inform the host (if needed) and drop the data.
|
||||
if (waitingForIdrFrame || waitingForRefInvalFrame) {
|
||||
// IDR wait takes priority over RFI wait (and an IDR frame will satisfy both)
|
||||
if (waitingForIdrFrame) {
|
||||
Limelog("Waiting for IDR frame\n");
|
||||
|
||||
// If waiting for next successful frame and we got here
|
||||
// with an end flag, we can send a message to the server
|
||||
if (waitingForNextSuccessfulFrame) {
|
||||
// This is the next successful frame after a loss event
|
||||
connectionDetectedFrameLoss(startFrameNumber, frameIndex);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If we need an RFI frame first, then drop this frame
|
||||
// and update the reference frame invalidation window.
|
||||
Limelog("Waiting for RFI frame\n");
|
||||
connectionDetectedFrameLoss(startFrameNumber, frameIndex);
|
||||
}
|
||||
|
||||
waitingForNextSuccessfulFrame = false;
|
||||
}
|
||||
|
||||
// If we need an IDR frame first, then drop this frame
|
||||
if (waitingForIdrFrame) {
|
||||
Limelog("Waiting for IDR frame\n");
|
||||
|
||||
dropFrameState();
|
||||
return;
|
||||
}
|
||||
|
||||
LC_ASSERT(!waitingForNextSuccessfulFrame);
|
||||
|
||||
// Carry out any pending state drops. We can't just do this
|
||||
// arbitrarily in the middle of processing a frame because
|
||||
// may cause the depacketizer state to become corrupted. For
|
||||
|
||||
Reference in New Issue
Block a user