Fix sequence number calculation logic.
Issue: google/ExoPlayer#9697 Before, the `MAX_SEQUENCE_NUMBER` is 65535, such that the logic to get the next sequence number: `previousSeqNumber + 1 % MAX_SEQUENCE_NUMBER` yields 0 when `previousSeqNumber` is 65534. However, the next sequence number should be 65535. PiperOrigin-RevId: 410530098
This commit is contained in:
parent
26153add56
commit
f527fb729c
@ -24,6 +24,7 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import com.google.common.math.IntMath;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -134,6 +135,16 @@ public final class RtpPacket {
|
|||||||
public static final int MAX_SEQUENCE_NUMBER = 0xFFFF;
|
public static final int MAX_SEQUENCE_NUMBER = 0xFFFF;
|
||||||
public static final int CSRC_SIZE = 4;
|
public static final int CSRC_SIZE = 4;
|
||||||
|
|
||||||
|
/** Returns the next sequence number of the {@code sequenceNumber}. */
|
||||||
|
public static int getNextSequenceNumber(int sequenceNumber) {
|
||||||
|
return IntMath.mod(sequenceNumber + 1, MAX_SEQUENCE_NUMBER + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the previous sequence number from the {@code sequenceNumber}. */
|
||||||
|
public static int getPreviousSequenceNumber(int sequenceNumber) {
|
||||||
|
return IntMath.mod(sequenceNumber - 1, MAX_SEQUENCE_NUMBER + 1);
|
||||||
|
}
|
||||||
|
|
||||||
private static final byte[] EMPTY = new byte[0];
|
private static final byte[] EMPTY = new byte[0];
|
||||||
|
|
||||||
/** The RTP version field (Word 0, bits 0-1), should always be 2. */
|
/** The RTP version field (Word 0, bits 0-1), should always be 2. */
|
||||||
|
@ -34,8 +34,6 @@ import java.util.TreeSet;
|
|||||||
/** The maximum sequence number discontinuity allowed without resetting the re-ordering buffer. */
|
/** The maximum sequence number discontinuity allowed without resetting the re-ordering buffer. */
|
||||||
@VisibleForTesting /* package */ static final int MAX_SEQUENCE_LEAP_ALLOWED = 1000;
|
@VisibleForTesting /* package */ static final int MAX_SEQUENCE_LEAP_ALLOWED = 1000;
|
||||||
|
|
||||||
private static final int MAX_SEQUENCE_NUMBER = RtpPacket.MAX_SEQUENCE_NUMBER;
|
|
||||||
|
|
||||||
/** Queue size threshold for resetting the queue. 5000 packets equate about 7MB in buffer size. */
|
/** Queue size threshold for resetting the queue. 5000 packets equate about 7MB in buffer size. */
|
||||||
private static final int QUEUE_SIZE_THRESHOLD_FOR_RESET = 5000;
|
private static final int QUEUE_SIZE_THRESHOLD_FOR_RESET = 5000;
|
||||||
|
|
||||||
@ -96,13 +94,13 @@ import java.util.TreeSet;
|
|||||||
int packetSequenceNumber = packet.sequenceNumber;
|
int packetSequenceNumber = packet.sequenceNumber;
|
||||||
if (!started) {
|
if (!started) {
|
||||||
reset();
|
reset();
|
||||||
lastDequeuedSequenceNumber = prevSequenceNumber(packetSequenceNumber);
|
lastDequeuedSequenceNumber = RtpPacket.getPreviousSequenceNumber(packetSequenceNumber);
|
||||||
started = true;
|
started = true;
|
||||||
addToQueue(new RtpPacketContainer(packet, receivedTimestampMs));
|
addToQueue(new RtpPacketContainer(packet, receivedTimestampMs));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int expectedSequenceNumber = nextSequenceNumber(lastReceivedSequenceNumber);
|
int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(lastReceivedSequenceNumber);
|
||||||
// A positive shift means the packet succeeds the last received packet.
|
// A positive shift means the packet succeeds the last received packet.
|
||||||
int sequenceNumberShift =
|
int sequenceNumberShift =
|
||||||
calculateSequenceNumberShift(packetSequenceNumber, expectedSequenceNumber);
|
calculateSequenceNumberShift(packetSequenceNumber, expectedSequenceNumber);
|
||||||
@ -114,7 +112,7 @@ import java.util.TreeSet;
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Discard all previous received packets and start subsequent receiving from here.
|
// Discard all previous received packets and start subsequent receiving from here.
|
||||||
lastDequeuedSequenceNumber = prevSequenceNumber(packetSequenceNumber);
|
lastDequeuedSequenceNumber = RtpPacket.getPreviousSequenceNumber(packetSequenceNumber);
|
||||||
packetQueue.clear();
|
packetQueue.clear();
|
||||||
addToQueue(new RtpPacketContainer(packet, receivedTimestampMs));
|
addToQueue(new RtpPacketContainer(packet, receivedTimestampMs));
|
||||||
return true;
|
return true;
|
||||||
@ -140,7 +138,7 @@ import java.util.TreeSet;
|
|||||||
RtpPacketContainer packetContainer = packetQueue.first();
|
RtpPacketContainer packetContainer = packetQueue.first();
|
||||||
int packetSequenceNumber = packetContainer.packet.sequenceNumber;
|
int packetSequenceNumber = packetContainer.packet.sequenceNumber;
|
||||||
|
|
||||||
if (packetSequenceNumber == nextSequenceNumber(lastDequeuedSequenceNumber)
|
if (packetSequenceNumber == RtpPacket.getNextSequenceNumber(lastDequeuedSequenceNumber)
|
||||||
|| cutoffTimestampMs >= packetContainer.receivedTimestampMs) {
|
|| cutoffTimestampMs >= packetContainer.receivedTimestampMs) {
|
||||||
packetQueue.pollFirst();
|
packetQueue.pollFirst();
|
||||||
lastDequeuedSequenceNumber = packetSequenceNumber;
|
lastDequeuedSequenceNumber = packetSequenceNumber;
|
||||||
@ -168,16 +166,6 @@ import java.util.TreeSet;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int nextSequenceNumber(int sequenceNumber) {
|
|
||||||
return (sequenceNumber + 1) % MAX_SEQUENCE_NUMBER;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int prevSequenceNumber(int sequenceNumber) {
|
|
||||||
return sequenceNumber == 0
|
|
||||||
? MAX_SEQUENCE_NUMBER - 1
|
|
||||||
: (sequenceNumber - 1) % MAX_SEQUENCE_NUMBER;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the sequence number shift, accounting for wrapping around.
|
* Calculates the sequence number shift, accounting for wrapping around.
|
||||||
*
|
*
|
||||||
@ -193,7 +181,7 @@ import java.util.TreeSet;
|
|||||||
int shift =
|
int shift =
|
||||||
min(sequenceNumber, previousSequenceNumber)
|
min(sequenceNumber, previousSequenceNumber)
|
||||||
- max(sequenceNumber, previousSequenceNumber)
|
- max(sequenceNumber, previousSequenceNumber)
|
||||||
+ MAX_SEQUENCE_NUMBER;
|
+ RtpPacket.MAX_SEQUENCE_NUMBER;
|
||||||
// Check whether this is actually an wrap-over. For example, it is a wrap around if receiving
|
// Check whether this is actually an wrap-over. For example, it is a wrap around if receiving
|
||||||
// 65500 (prevSequenceNumber) after 1 (sequenceNumber); but it is not when prevSequenceNumber
|
// 65500 (prevSequenceNumber) after 1 (sequenceNumber); but it is not when prevSequenceNumber
|
||||||
// is 30000.
|
// is 30000.
|
||||||
|
@ -259,7 +259,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
fuScratchBuffer.setPosition(1);
|
fuScratchBuffer.setPosition(1);
|
||||||
} else {
|
} else {
|
||||||
// Check that this packet is in the sequence of the previous packet.
|
// Check that this packet is in the sequence of the previous packet.
|
||||||
int expectedSequenceNumber = (previousSequenceNumber + 1) % RtpPacket.MAX_SEQUENCE_NUMBER;
|
int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber);
|
||||||
if (packetSequenceNumber != expectedSequenceNumber) {
|
if (packetSequenceNumber != expectedSequenceNumber) {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
|
@ -18,6 +18,8 @@ package androidx.media3.exoplayer.rtsp;
|
|||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Util.getBytesFromHexString;
|
import static androidx.media3.common.util.Util.getBytesFromHexString;
|
||||||
|
import static androidx.media3.exoplayer.rtsp.RtpPacket.getNextSequenceNumber;
|
||||||
|
import static androidx.media3.exoplayer.rtsp.RtpPacket.getPreviousSequenceNumber;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
@ -184,4 +186,26 @@ public final class RtpPacketTest {
|
|||||||
builtPacket.writeToBuffer(builtPacketBytes, /* offset= */ 0, packetSize);
|
builtPacket.writeToBuffer(builtPacketBytes, /* offset= */ 0, packetSize);
|
||||||
assertThat(builtPacketBytes).isEqualTo(rtpDataWithLargeTimestamp);
|
assertThat(builtPacketBytes).isEqualTo(rtpDataWithLargeTimestamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getNextSequenceNumber_invokingAtWrapOver() {
|
||||||
|
assertThat(getNextSequenceNumber(65534)).isEqualTo(65535);
|
||||||
|
assertThat(getNextSequenceNumber(65535)).isEqualTo(0);
|
||||||
|
assertThat(getNextSequenceNumber(0)).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getPreviousSequenceNumber_invokingAtWrapOver() {
|
||||||
|
assertThat(getPreviousSequenceNumber(1)).isEqualTo(0);
|
||||||
|
assertThat(getPreviousSequenceNumber(0)).isEqualTo(65535);
|
||||||
|
assertThat(getPreviousSequenceNumber(65535)).isEqualTo(65534);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getSequenceNumber_isSymmetric() {
|
||||||
|
for (int i = 0; i < RtpPacket.MAX_SEQUENCE_NUMBER; i++) {
|
||||||
|
assertThat(getPreviousSequenceNumber(getNextSequenceNumber(i))).isEqualTo(i);
|
||||||
|
assertThat(getNextSequenceNumber(getPreviousSequenceNumber(i))).isEqualTo(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user