Fix sequencing errors that lead to drops in audio or video for potentially long periods under the right conditions

This commit is contained in:
Cameron Gutman 2014-08-09 03:39:14 -07:00
parent 33ffbe151f
commit cc92f3829e
4 changed files with 36 additions and 10 deletions

View File

@ -45,7 +45,7 @@ public class RtpReorderQueue {
if (nextRtpSequenceNumber != Short.MAX_VALUE) { if (nextRtpSequenceNumber != Short.MAX_VALUE) {
// Don't queue packets we're already ahead of // Don't queue packets we're already ahead of
if (seq < nextRtpSequenceNumber) { if (SequenceHelper.isBeforeSigned(seq, nextRtpSequenceNumber, false)) {
return false; return false;
} }
@ -88,11 +88,15 @@ public class RtpReorderQueue {
} }
private RtpQueueEntry getEntryByLowestSeq() { private RtpQueueEntry getEntryByLowestSeq() {
short nextSeq = Short.MAX_VALUE; if (queue.isEmpty()) {
RtpQueueEntry lowestSeqEntry = null; return null;
}
RtpQueueEntry lowestSeqEntry = queue.getFirst();
short nextSeq = lowestSeqEntry.sequenceNumber;
for (RtpQueueEntry entry : queue) { for (RtpQueueEntry entry : queue) {
if (entry.sequenceNumber < nextSeq) { if (SequenceHelper.isBeforeSigned(entry.sequenceNumber, nextSeq, true)) {
lowestSeqEntry = entry; lowestSeqEntry = entry;
nextSeq = entry.sequenceNumber; nextSeq = entry.sequenceNumber;
} }
@ -140,7 +144,7 @@ public class RtpReorderQueue {
public RtpQueueStatus addPacket(RtpPacketFields packet) { public RtpQueueStatus addPacket(RtpPacketFields packet) {
if (nextRtpSequenceNumber != Short.MAX_VALUE && if (nextRtpSequenceNumber != Short.MAX_VALUE &&
packet.getRtpSequenceNumber() < nextRtpSequenceNumber) { SequenceHelper.isBeforeSigned(packet.getRtpSequenceNumber(), nextRtpSequenceNumber, false)) {
// Reject packets behind our current sequence number // Reject packets behind our current sequence number
return RtpQueueStatus.REJECTED; return RtpQueueStatus.REJECTED;
} }

View File

@ -0,0 +1,20 @@
package com.limelight.nvstream.av;
public class SequenceHelper {
public static boolean isBeforeSigned(int numA, int numB, boolean ambiguousCase) {
// This should be the common case for most callers
if (numA == numB) {
return false;
}
// If numA and numB have the same signs,
// we can just do a regular comparison.
if ((numA < 0 && numB < 0) || (numA >= 0 && numB >= 0)) {
return numA < numB;
}
else {
// The sign switch is ambiguous
return ambiguousCase;
}
}
}

View File

@ -4,6 +4,7 @@ import com.limelight.LimeLog;
import com.limelight.nvstream.av.ByteBufferDescriptor; import com.limelight.nvstream.av.ByteBufferDescriptor;
import com.limelight.nvstream.av.PopulatedBufferList; import com.limelight.nvstream.av.PopulatedBufferList;
import com.limelight.nvstream.av.RtpPacket; import com.limelight.nvstream.av.RtpPacket;
import com.limelight.nvstream.av.SequenceHelper;
public class AudioDepacketizer { public class AudioDepacketizer {
@ -88,7 +89,7 @@ public class AudioDepacketizer {
// Only tell the decoder if we got packets ahead of what we expected // Only tell the decoder if we got packets ahead of what we expected
// If the packet is behind the current sequence number, drop it // If the packet is behind the current sequence number, drop it
if (seq > (short)(lastSequenceNumber + 1)) { if (!SequenceHelper.isBeforeSigned(seq, (short)(lastSequenceNumber + 1), false)) {
decodeData(null, 0, 0); decodeData(null, 0, 0);
} }
else { else {

View File

@ -7,6 +7,7 @@ import com.limelight.nvstream.av.ByteBufferDescriptor;
import com.limelight.nvstream.av.DecodeUnit; import com.limelight.nvstream.av.DecodeUnit;
import com.limelight.nvstream.av.ConnectionStatusListener; import com.limelight.nvstream.av.ConnectionStatusListener;
import com.limelight.nvstream.av.PopulatedBufferList; import com.limelight.nvstream.av.PopulatedBufferList;
import com.limelight.nvstream.av.SequenceHelper;
public class VideoDepacketizer { public class VideoDepacketizer {
@ -232,12 +233,12 @@ public class VideoDepacketizer {
// Drop duplicates or re-ordered packets // Drop duplicates or re-ordered packets
int streamPacketIndex = packet.getStreamPacketIndex(); int streamPacketIndex = packet.getStreamPacketIndex();
if (streamPacketIndex < (int)(lastPacketInStream + 1)) { if (SequenceHelper.isBeforeSigned((short)streamPacketIndex, (short)(lastPacketInStream + 1), false)) {
return; return;
} }
// Drop packets from a previously completed frame // Drop packets from a previously completed frame
if (frameIndex < nextFrameNumber) { if (SequenceHelper.isBeforeSigned(frameIndex, nextFrameNumber, false)) {
return; return;
} }
@ -279,7 +280,7 @@ public class VideoDepacketizer {
// miss one in between // miss one in between
else if (firstPacket) { else if (firstPacket) {
// Make sure this is the next consecutive frame // Make sure this is the next consecutive frame
if (nextFrameNumber < frameIndex) { if (SequenceHelper.isBeforeSigned(nextFrameNumber, frameIndex, true)) {
LimeLog.warning("Network dropped an entire frame"); LimeLog.warning("Network dropped an entire frame");
nextFrameNumber = frameIndex; nextFrameNumber = frameIndex;
@ -287,7 +288,7 @@ public class VideoDepacketizer {
waitingForNextSuccessfulFrame = true; waitingForNextSuccessfulFrame = true;
dropAvcFrameState(); dropAvcFrameState();
} }
else if (nextFrameNumber > frameIndex){ else if (nextFrameNumber != frameIndex) {
// Duplicate packet or FEC dup // Duplicate packet or FEC dup
decodingFrame = false; decodingFrame = false;
return; return;