From 7c346c31040c6ed736d9d4b3a7cbc4b0aca1849c Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 20 Jun 2021 14:55:44 -0500 Subject: [PATCH] Fix handling of older GFE versions in surround sound mode 7.1 surround sound would usually behave like CBR content, which never tripped the logic to detect this incompatibility. Now we will both detect it via hardcoded version number check, but also detect when the FEC base sequence number constraint is violated too. --- src/RtpAudioQueue.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/RtpAudioQueue.c b/src/RtpAudioQueue.c index a1279f6..0453b14 100644 --- a/src/RtpAudioQueue.c +++ b/src/RtpAudioQueue.c @@ -25,6 +25,24 @@ void RtpaInitializeQueue(PRTP_AUDIO_QUEUE queue) { // full FEC block before reporting losses, out of order packets, etc. queue->synchronizing = true; + // Older versions of GFE violate some invariants that our FEC code requires, so we turn it off for + // anything older than GFE 3.19 just to be safe. GFE seems to have changed to the "modern" behavior + // between GFE 3.18 and 3.19. + // + // In the case of GFE 3.13, it does send FEC packets but it requires very special handling because: + // a) data and FEC shards may vary in size + // b) FEC blocks can start on boundaries that are not multiples of RTPA_DATA_SHARDS + // + // It doesn't seem worth it to sink a bunch of hours into figure out how to properly handle audio FEC + // for a 3 year old version of GFE that almost nobody uses. Instead, we'll just disable the FEC queue + // entirely and pass all audio data straight to the decoder. + // + if (!APP_VERSION_AT_LEAST(7, 1, 415)) { + Limelog("Audio FEC has been disabled due to an incompatibility with your host's old software.\n"); + Limelog("Audio quality may suffer on unreliable network connections due to lack of FEC!\n"); + queue->incompatibleServer = true; + } + reed_solomon_init(); // The number of data and parity shards is constant, so we can reuse @@ -239,6 +257,18 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK return NULL; } + if (fecBlockBaseSeqNum % RTPA_DATA_SHARDS != 0) { + // The FEC blocks must start on a RTPA_DATA_SHARDS boundary for our queuing logic to work. This isn't + // the case for older versions of GeForce Experience (at least 3.13). Disable the FEC logic if this + // invariant is validated. + Limelog("Invalid FEC block base sequence number (got %u, expected %u)\n", + fecBlockBaseSeqNum, (fecBlockBaseSeqNum / RTPA_DATA_SHARDS) * RTPA_DATA_SHARDS); + Limelog("Audio FEC has been disabled due to an incompatibility with your host's old software!\n"); + LC_ASSERT(fecBlockBaseSeqNum % RTPA_DATA_SHARDS == 0); + queue->incompatibleServer = true; + return NULL; + } + blockSize = length - sizeof(RTP_PACKET) - sizeof(AUDIO_FEC_HEADER); } else { @@ -274,17 +304,9 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK if (existingBlock->blockSize != blockSize) { // This can happen with older versions of GeForce Experience (3.13) and Sunshine that don't use a // constant size for audio packets. - // - // In the case of GFE 3.13, it does send FEC packets but it requires very special handling because: - // a) data and FEC shards may vary in size - // b) FEC blocks can start on boundaries that are not multiples of RTPA_DATA_SHARDS - // - // It doesn't seem worth it to sink a bunch of hours into figure out how to properly handle audio FEC - // for a 3 year old version of GFE that almost nobody uses. Instead, we'll just disable the FEC queue - // entirely and pass all audio data straight to the decoder. - // Limelog("Audio block size mismatch (got %u, expected %u)\n", blockSize, existingBlock->blockSize); Limelog("Audio FEC has been disabled due to an incompatibility with your host's old software!\n"); + LC_ASSERT(existingBlock->blockSize == blockSize); queue->incompatibleServer = true; return NULL; }