From b471edbb8035bb3d91669feea042126d3ee4d826 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 9 Jun 2021 02:31:55 -0500 Subject: [PATCH] Add a fast path to drop the audio FEC block with in-order packets If we have not seen any OOS audio data lately, we can assume that seeing the next FEC block means the previous block has completed transmission. If we don't have it all by then, just assume it was lost and move on. This reduces the perception of audio loss by reducing the change that we cause the audio device to underrun. --- src/RtpAudioQueue.c | 25 ++++++++++++++++++++++--- src/RtpAudioQueue.h | 3 +++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/RtpAudioQueue.c b/src/RtpAudioQueue.c index fef3d13..6e299bb 100644 --- a/src/RtpAudioQueue.c +++ b/src/RtpAudioQueue.c @@ -129,6 +129,23 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK return NULL; } + // Remember if we've received out-of-sequence packets lately. We can use + // this knowledge to more quickly give up on FEC blocks. + if (isBefore16(packet->sequenceNumber, queue->oldestRtpBaseSequenceNumber)) { + queue->lastOosSequenceNumber = packet->sequenceNumber; + if (!queue->receivedOosData) { + Limelog("Leaving fast audio recovery mode after OOS audio data (%u < %u)\n", + packet->sequenceNumber, queue->oldestRtpBaseSequenceNumber); + queue->receivedOosData = true; + } + } + // This condition looks odd, but it's just a simple way to check if we've gone + // more than 32767 packets without an OOS packet. + else if (queue->receivedOosData && isBefore16(queue->oldestRtpBaseSequenceNumber, queue->lastOosSequenceNumber)) { + Limelog("Entering fast audio recovery mode after sequenced audio data\n"); + queue->receivedOosData = false; + } + // This is a data packet, so we will need to synthesize an FEC header fecBlockPayloadType = packet->packetType; fecBlockBaseSeqNum = (packet->sequenceNumber / RTPA_DATA_SHARDS) * RTPA_DATA_SHARDS; @@ -393,9 +410,11 @@ static bool enforceQueueConstraints(PRTP_AUDIO_QUEUE queue) { return false; } - // We will consider the FEC block irrecoverably lost if the entire duration of the - // audio in the FEC block has elapsed (plus a little bit) without completing the block. - if (PltGetMillis() - queue->blockHead->queueTimeMs > (uint32_t)(AudioPacketDuration * RTPA_DATA_SHARDS) + RTPQ_OOS_WAIT_TIME_MS) { + // We will consider the FEC block irrecoverably lost if either: + // 1) We have not received OOS data, yet this data is from a future FEC block + // 2) The entire duration of the audio in the FEC block has elapsed (plus a little bit) + if (!queue->receivedOosData || + PltGetMillis() - queue->blockHead->queueTimeMs > (uint32_t)(AudioPacketDuration * RTPA_DATA_SHARDS) + RTPQ_OOS_WAIT_TIME_MS) { Limelog("Unable to recover audio data block %u to %u (%u+%u=%u received < %u needed)\n", queue->blockHead->fecHeader.baseSequenceNumber, queue->blockHead->fecHeader.baseSequenceNumber + RTPA_DATA_SHARDS - 1, diff --git a/src/RtpAudioQueue.h b/src/RtpAudioQueue.h index 0f7ed0a..e59e441 100644 --- a/src/RtpAudioQueue.h +++ b/src/RtpAudioQueue.h @@ -52,6 +52,9 @@ typedef struct _RTP_AUDIO_QUEUE { uint16_t nextRtpSequenceNumber; uint16_t oldestRtpBaseSequenceNumber; + + uint16_t lastOosSequenceNumber; + bool receivedOosData; } RTP_AUDIO_QUEUE, *PRTP_AUDIO_QUEUE; #define RTPQ_RET_PACKET_CONSUMED 0x1