diff --git a/src/AudioStream.c b/src/AudioStream.c index 6b87aab..2152544 100644 --- a/src/AudioStream.c +++ b/src/AudioStream.c @@ -153,13 +153,17 @@ static bool queuePacketToLbq(PQUEUED_AUDIO_PACKET* packet) { } static void decodeInputData(PQUEUED_AUDIO_PACKET packet) { - PRTP_PACKET rtp; + // If the packet size is zero, this is a placeholder for a missing + // packet. Trigger packet loss concealment logic in libopus by + // invoking the decoder with a NULL buffer. + if (packet->header.size == 0) { + AudioCallbacks.decodeAndPlaySample(NULL, 0); + return; + } - rtp = (PRTP_PACKET)&packet->data[0]; + PRTP_PACKET rtp = (PRTP_PACKET)&packet->data[0]; if (lastSeq != 0 && (unsigned short)(lastSeq + 1) != rtp->sequenceNumber) { Limelog("Received OOS audio data (expected %d, but got %d)\n", lastSeq + 1, rtp->sequenceNumber); - - AudioCallbacks.decodeAndPlaySample(NULL, 0); } lastSeq = rtp->sequenceNumber; diff --git a/src/RtpAudioQueue.c b/src/RtpAudioQueue.c index 2b1dc9a..31fae36 100644 --- a/src/RtpAudioQueue.c +++ b/src/RtpAudioQueue.c @@ -501,21 +501,31 @@ int RtpaAddPacket(PRTP_AUDIO_QUEUE queue, PRTP_PACKET packet, uint16_t length) { PRTP_PACKET RtpaGetQueuedPacket(PRTP_AUDIO_QUEUE queue, uint16_t customHeaderLength, uint16_t* length) { validateFecBlockState(queue); - // If we're returning audio data even with discontinuities, find the next data packet + // If we're returning audio data even with discontinuities, we'll fill in blank entries + // for packets that were lost and could not be recovered. if (queue->blockHead != NULL && queue->blockHead->allowDiscontinuity) { PRTPA_FEC_BLOCK nextBlock = queue->blockHead; + PRTP_PACKET lostPacket; - while (nextBlock->nextDataPacketIndex < RTPA_DATA_SHARDS) { - LC_ASSERT(nextBlock->fecHeader.baseSequenceNumber + nextBlock->nextDataPacketIndex == queue->nextRtpSequenceNumber); - if (nextBlock->marks[nextBlock->nextDataPacketIndex]) { - // This packet is missing. Skip it. - nextBlock->nextDataPacketIndex++; - queue->nextRtpSequenceNumber++; - } - else { - LC_ASSERT(queueHasPacketReady(queue)); - break; + LC_ASSERT(nextBlock->fecHeader.baseSequenceNumber + nextBlock->nextDataPacketIndex == queue->nextRtpSequenceNumber); + if (nextBlock->marks[nextBlock->nextDataPacketIndex]) { + // This packet is missing. Return an empty entry to let the caller + // know to perform packet loss concealment for this frame. + lostPacket = malloc(customHeaderLength); + if (lostPacket == NULL) { + return NULL; } + + // Lost packet placeholder entries have no associated data + *length = 0; + + // Move on to the next data shard + nextBlock->nextDataPacketIndex++; + queue->nextRtpSequenceNumber++; + } + else { + lostPacket = NULL; + LC_ASSERT(queueHasPacketReady(queue)); } // If we've read everything from this FEC block, remove and free it @@ -525,6 +535,10 @@ PRTP_PACKET RtpaGetQueuedPacket(PRTP_AUDIO_QUEUE queue, uint16_t customHeaderLen else { validateFecBlockState(queue); } + + if (lostPacket != NULL) { + return lostPacket; + } } // Return the next RTP sequence number by indexing into the most recent FEC block