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.
This commit is contained in:
Cameron Gutman 2021-06-09 02:31:55 -05:00
parent c64ba99654
commit b471edbb80
2 changed files with 25 additions and 3 deletions

View File

@ -129,6 +129,23 @@ static PRTPA_FEC_BLOCK getFecBlockForRtpPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACK
return NULL; 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 // This is a data packet, so we will need to synthesize an FEC header
fecBlockPayloadType = packet->packetType; fecBlockPayloadType = packet->packetType;
fecBlockBaseSeqNum = (packet->sequenceNumber / RTPA_DATA_SHARDS) * RTPA_DATA_SHARDS; fecBlockBaseSeqNum = (packet->sequenceNumber / RTPA_DATA_SHARDS) * RTPA_DATA_SHARDS;
@ -393,9 +410,11 @@ static bool enforceQueueConstraints(PRTP_AUDIO_QUEUE queue) {
return false; return false;
} }
// We will consider the FEC block irrecoverably lost if the entire duration of the // We will consider the FEC block irrecoverably lost if either:
// audio in the FEC block has elapsed (plus a little bit) without completing the block. // 1) We have not received OOS data, yet this data is from a future FEC block
if (PltGetMillis() - queue->blockHead->queueTimeMs > (uint32_t)(AudioPacketDuration * RTPA_DATA_SHARDS) + RTPQ_OOS_WAIT_TIME_MS) { // 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", 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,
queue->blockHead->fecHeader.baseSequenceNumber + RTPA_DATA_SHARDS - 1, queue->blockHead->fecHeader.baseSequenceNumber + RTPA_DATA_SHARDS - 1,

View File

@ -52,6 +52,9 @@ typedef struct _RTP_AUDIO_QUEUE {
uint16_t nextRtpSequenceNumber; uint16_t nextRtpSequenceNumber;
uint16_t oldestRtpBaseSequenceNumber; uint16_t oldestRtpBaseSequenceNumber;
uint16_t lastOosSequenceNumber;
bool receivedOosData;
} RTP_AUDIO_QUEUE, *PRTP_AUDIO_QUEUE; } RTP_AUDIO_QUEUE, *PRTP_AUDIO_QUEUE;
#define RTPQ_RET_PACKET_CONSUMED 0x1 #define RTPQ_RET_PACKET_CONSUMED 0x1