From 84bd523cf59d4854a0a4cee84d3ba5556e2c59ce Mon Sep 17 00:00:00 2001 From: Dmytro Bogovych Date: Sat, 23 May 2026 13:19:14 +0300 Subject: [PATCH] record loss events when jitter buffer drops packets via high-water mark The drop loop advanced mLastSeqno one packet at a time silently, so any real on-wire sequence-number gaps between dropped packets disappeared from the loss timeline. Now check the gap against the previous mLastSeqno before each drop and emit a PacketLossEvent if needed. Co-Authored-By: Claude Opus 4.7 --- src/engine/media/MT_AudioReceiver.cpp | 28 +++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/engine/media/MT_AudioReceiver.cpp b/src/engine/media/MT_AudioReceiver.cpp index 6c7c3ce0..2df5557c 100644 --- a/src/engine/media/MT_AudioReceiver.cpp +++ b/src/engine/media/MT_AudioReceiver.cpp @@ -200,9 +200,33 @@ RtpBuffer::FetchResult RtpBuffer::fetch() ICELogMedia( << "Dropping RTP packets from jitter buffer"); total -= mPacketList.front()->timelength(); + // Before advancing mLastSeqno over the dropped packet, record a loss event for any + // sequence-number gap on the wire between the previous packet we saw and this one. + // Without this, drops silently mask real packet loss that happened between them. + auto droppingPacket = mPacketList.front(); + uint32_t droppingSeq = droppingPacket->rtp()->GetExtendedSequenceNumber(); + if (mLastSeqno) + { + int gap = (int64_t)droppingSeq - (int64_t)*mLastSeqno - 1; + if (gap > 0) + { + mStat.mPacketLoss += gap; + if (mStat.mPacketLossTimeline.empty() || (mStat.mPacketLossTimeline.back().mEndSeqno != droppingSeq)) + { + auto gapStart = RtpHelper::toMicroseconds(*mLastReceiveTime); + auto gapEnd = RtpHelper::toMicroseconds(droppingPacket->rtp()->GetReceiveTime()); + mStat.mPacketLossTimeline.emplace_back(PacketLossEvent{.mStartSeqno = *mLastSeqno, + .mEndSeqno = droppingSeq, + .mGap = gap, + .mTimestampStart = gapStart, + .mTimestampEnd = gapEnd}); + } + } + } + // Save it as last packet however - to not confuse loss packet counter - mFetchedPacket = mPacketList.front(); - mLastSeqno = mFetchedPacket->rtp()->GetExtendedSequenceNumber(); + mFetchedPacket = droppingPacket; + mLastSeqno = droppingSeq; mLastReceiveTime = mFetchedPacket->rtp()->GetReceiveTime(); // Erase from packet list