From 7dd428534ecc02157f1e5c62ea38090f0531ec0c Mon Sep 17 00:00:00 2001 From: Kekelic Date: Tue, 26 Mar 2024 20:43:46 +0100 Subject: [PATCH 1/7] Change RtpPacket class to parse header extension if exists --- .../java/androidx/media3/exoplayer/rtsp/RtpPacket.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java index 7f3add3b75..535067e860 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java @@ -143,6 +143,8 @@ public final class RtpPacket { public static final int MIN_SEQUENCE_NUMBER = 0; public static final int MAX_SEQUENCE_NUMBER = 0xFFFF; public static final int CSRC_SIZE = 4; + public static final int EXTENSION_SIZE = 16; + /** Returns the next sequence number of the {@code sequenceNumber}. */ public static int getNextSequenceNumber(int sequenceNumber) { @@ -205,6 +207,8 @@ public final class RtpPacket { byte version = (byte) (firstByte >> 6); boolean padding = ((firstByte >> 5) & 0x1) == 1; byte csrcCount = (byte) (firstByte & 0xF); + boolean hasExtension = ((firstByte & 0x10) >> 4) == 1; + if (version != RTP_VERSION) { return null; @@ -233,6 +237,12 @@ public final class RtpPacket { csrc = EMPTY; } + //Extension. + if (hasExtension){ + byte[] extension = new byte[EXTENSION_SIZE]; + packetBuffer.readBytes(extension, 0, EXTENSION_SIZE); + } + // Everything else will be RTP payload. byte[] payloadData = new byte[packetBuffer.bytesLeft()]; packetBuffer.readBytes(payloadData, 0, packetBuffer.bytesLeft()); From 1ecb7c3fbf61b87abc5be9d57ecf2f3dab8cef12 Mon Sep 17 00:00:00 2001 From: Kekelic Date: Tue, 26 Mar 2024 20:48:41 +0100 Subject: [PATCH 2/7] Format changes --- .../src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java index 535067e860..2863a1f216 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java @@ -145,7 +145,6 @@ public final class RtpPacket { public static final int CSRC_SIZE = 4; public static final int EXTENSION_SIZE = 16; - /** Returns the next sequence number of the {@code sequenceNumber}. */ public static int getNextSequenceNumber(int sequenceNumber) { return IntMath.mod(sequenceNumber + 1, MAX_SEQUENCE_NUMBER + 1); @@ -209,7 +208,6 @@ public final class RtpPacket { byte csrcCount = (byte) (firstByte & 0xF); boolean hasExtension = ((firstByte & 0x10) >> 4) == 1; - if (version != RTP_VERSION) { return null; } From 5a8a250a1a29d4c87200b0f64757cc9e29f15242 Mon Sep 17 00:00:00 2001 From: Kekelic Date: Mon, 29 Apr 2024 11:41:48 +0200 Subject: [PATCH 3/7] Add reading length of extension payload from header extension in RtpPacket --- .../media3/exoplayer/rtsp/RtpPacket.java | 65 +++++++++++++++++-- .../media3/exoplayer/rtsp/RtpPacketTest.java | 63 ++++++++++++++++++ 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java index 2863a1f216..3b03901c5b 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java @@ -63,12 +63,15 @@ public final class RtpPacket { /** Builder class for an {@link RtpPacket} */ public static final class Builder { private boolean padding; + private boolean extension; private boolean marker; private byte payloadType; private int sequenceNumber; private long timestamp; private int ssrc; private byte[] csrc = EMPTY; + private byte[] headerExtension = EMPTY; + private byte[] extensionPayload = EMPTY; private byte[] payloadData = EMPTY; /** Sets the {@link RtpPacket#padding}. The default is false. */ @@ -78,6 +81,13 @@ public final class RtpPacket { return this; } + /** Sets the {@link RtpPacket#extension}. The default is false. */ + @CanIgnoreReturnValue + public Builder setExtension(boolean extension) { + this.extension = extension; + return this; + } + /** Sets {@link RtpPacket#marker}. The default is false. */ @CanIgnoreReturnValue public Builder setMarker(boolean marker) { @@ -122,6 +132,26 @@ public final class RtpPacket { return this; } + /** + * Sets {@link RtpPacket#headerExtension}. The default is an empty byte array. + */ + @CanIgnoreReturnValue + public Builder setHeaderExtension(byte[] headerExtension) { + checkNotNull(headerExtension); + this.headerExtension = headerExtension; + return this; + } + + /** + * Sets {@link RtpPacket#extensionPayload}. The default is an empty byte array. + */ + @CanIgnoreReturnValue + public Builder setExtensionPayload(byte[] extensionPayload) { + checkNotNull(extensionPayload); + this.extensionPayload = extensionPayload; + return this; + } + /** Sets {@link RtpPacket#payloadData}. The default is an empty byte array. */ @CanIgnoreReturnValue public Builder setPayloadData(byte[] payloadData) { @@ -143,7 +173,7 @@ public final class RtpPacket { public static final int MIN_SEQUENCE_NUMBER = 0; public static final int MAX_SEQUENCE_NUMBER = 0xFFFF; public static final int CSRC_SIZE = 4; - public static final int EXTENSION_SIZE = 16; + public static final int HEADER_EXTENSION_SIZE = 4; /** Returns the next sequence number of the {@code sequenceNumber}. */ public static int getNextSequenceNumber(int sequenceNumber) { @@ -187,6 +217,12 @@ public final class RtpPacket { /** The RTP CSRC fields (Optional, up to 15 items). */ public final byte[] csrc; + /** The RTP header extension fields (Optional, 32 bits). */ + public final byte[] headerExtension; + + /** The RTP extension payload fields (Optional). */ + public final byte[] extensionPayload; + public final byte[] payloadData; /** @@ -236,9 +272,21 @@ public final class RtpPacket { } //Extension. - if (hasExtension){ - byte[] extension = new byte[EXTENSION_SIZE]; - packetBuffer.readBytes(extension, 0, EXTENSION_SIZE); + byte[] headerExtension; + byte[] extensionPayload; + if (hasExtension) { + headerExtension = new byte[HEADER_EXTENSION_SIZE]; + packetBuffer.readBytes(headerExtension, 0, HEADER_EXTENSION_SIZE); + int extensionPayloadLength = (headerExtension[2] & 0xFF) << 8 | (headerExtension[3] & 0xFF); + if (extensionPayloadLength != 0) { + extensionPayload = new byte[extensionPayloadLength * 4]; + packetBuffer.readBytes(extensionPayload, 0, extensionPayloadLength * 4); + }else { + extensionPayload = EMPTY; + } + }else { + headerExtension = EMPTY; + extensionPayload = EMPTY; } // Everything else will be RTP payload. @@ -254,6 +302,9 @@ public final class RtpPacket { .setTimestamp(timestamp) .setSsrc(ssrc) .setCsrc(csrc) + .setExtension(hasExtension) + .setHeaderExtension(headerExtension) + .setExtensionPayload(extensionPayload) .setPayloadData(payloadData) .build(); } @@ -272,7 +323,7 @@ public final class RtpPacket { private RtpPacket(Builder builder) { this.padding = builder.padding; - this.extension = false; + this.extension = builder.extension; this.marker = builder.marker; this.payloadType = builder.payloadType; this.sequenceNumber = builder.sequenceNumber; @@ -280,6 +331,8 @@ public final class RtpPacket { this.ssrc = builder.ssrc; this.csrc = builder.csrc; this.csrcCount = (byte) (this.csrc.length / CSRC_SIZE); + this.headerExtension = builder.headerExtension; + this.extensionPayload = builder.extensionPayload; this.payloadData = builder.payloadData; } @@ -318,6 +371,8 @@ public final class RtpPacket { .putInt((int) timestamp) .putInt(ssrc) .put(csrc) + .put(headerExtension) + .put(extensionPayload) .put(payloadData); return packetLength; } diff --git a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java index a01c31da47..d2aa851379 100644 --- a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java +++ b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java @@ -68,6 +68,29 @@ public final class RtpPacketTest { Arrays.copyOfRange( rtpDataWithLargeTimestamp, RtpPacket.MIN_HEADER_SIZE, rtpDataWithLargeTimestamp.length); + /* + 10.. .... = Version: RFC 1889 Version (2) + ..0. .... = Padding: False + ...0 .... = Extension: True + .... 0000 = Contributing source identifiers count: 0 + 1... .... = Marker: False + Payload type: DynamicRTP-Type-96 (96) + Sequence number: 61514 + Timestamp: 2000000000 + Synchronization Source identifier: 0x35ff2773 (905914227) + extension: 00a20003a94f000062150100cbca0100 + Payload: 7c85b841bc439048000834f1a6943c00040bf038ee4de07acb6d… + */ + private final byte[] rtpDataWithHeaderExtension = + getBytesFromHexString("9060f04a7735940035ff277300a20003a94f000062150100cbca01007c85b841bc439048000834f1a6943c00040bf038ee4de07acb6dc67cb44716d9b61800600f1041214b121de2af09a8063ff2d88fedf7f565eafb9c44412a8e5a247d0ac76a6a8566a6ff593f9711114b6c625ca1363950ae8524a37c75c509a806833fd4bbeb6dda6db697aef12d709a80910e522bb3e2e793eb3c37995c4429448f2ba8b16bcb825ca11c3dffb3ff50ba8c5a3e5ffaff978b7e1350037d7dce4ddc906dbfff50ba8069ace7a9df442fffde2afc26a004b076dd611ebedb8bf15fd9596fc47e03e1008a32013d454401f8590ea42b6a67a3cf4da90aa006aca053283cf09c0e42c000444519045f3a0002c4c0e5e0f81e8316c5b16cbf3737c462bd5f87cc66b3e508a10128ac18d5656c78a6e293f10e0252c1819c2040fc5b16fe222296c4da247284ae892a16de65db7236a9bab718da108d05f09bf85d22bebb3ff11ff178b8da7c52c52fc4428b07ae1f1a9e0e3cc963136d542b2d698b6e84d572fbfbffcf1eafaf5af5e3d4092e33443b7b7ff09a8008ae5d661597bdbcfeb54dbd944c711014a2161cd76dc63b5087d087befe7ffb97e5484d5025f4e5fda36ffdcb500975c917ac1357fffea6484d401565b4a77bba2ff34135b711004583df39c7a16669f2840edca371fec8f575fff5f8f5049f3b1fff7d709a8338aaffeff1ad4981350cb5bffbfef09a87f043fff5f426a1132f7f5ffd09a821786b3fff7d426a057b80dfff6fe84d430c37fe9a7baed09a800769f54dd97df7dbbd3dc9e23e23158888250743e4da9d997974501d0879f2fe581b62b3f7711825831ca84b9421d3e4fa4d0874bc9ef895b771190dac47e55003a076252c032ccac0d4583c67078e3f101a946e260152754c800402a134002756425e7bb77f97e2305b1094e1c2c067a62284172a506a260011df686ed758e28c461702e001778034a407a1b16cd4ff11105bb0003784c552d810f6e5c5ce79e03e84381fd1008d9e471b8bb0cb2e20c70a34a8ab3fff1100a945d03f257e3cc499e2be9ae9072000f94896b5ca506e1c577fb28fc442b2503e0bce41bc1abacb5e05d6320455c3f3505681ec1c1a888a3240aab928b10001004f2c10782ac03e640ba8691007cc559a827a3cbfe221e600288540a914651e4be0e87c007b983f8cc550080038df00d6c8001818aa92448153c01ef80fc3b362084bf97f111059618ed4580e84505d02d6242f5d8c8e03f9e0705036e1400025385840c64c3a16da22241291e7e6fc9f1111ef1e738583a11ef2b553f1e62380ce79c845ac2901008910430ea3c00040078592b101bb030b2e33d97f111ac5000811454c01a83a00f138d0c0022d06cc09476727596154030742a4141bc3415c9264021789002af000f9e1b8bfcbf88852be0e4e2381400a8d266a1e00e1a9f10f02c1610c6780f29f05440792d0210684000a79f0625601b88c16e958f5eb6583012a944252f8ca00ba920f9fc7ad1500025061e018ca88000b8d7bfc132625ae297f8885641a000100c052c006be1d8000801faf80014b19614e521f0e87c9f21b000170d4c410c0372c007a4320d40600315906e156fda684435100f47855e2c05c73a5985d4009135a0e927d09e1dead7fffb752c63af06890b07fb9531297e0d0af0e03b1fb7c9d3f20a02fb1d7bca101c2c05064c0b78b2cb0ff0ba800dec69a27563d71bbb570bb95510da8fad5695a1814378ab8b7e135001be5e47055c6ba97975bbd75be5f4c826a2ee52bffdbdb6e96750516c6ab5077a0f5035b219ffbfa8f50997537f7aecbe223426e66b5426b057565b5f25bc7a98c6f047ffd7c26a037df97eaffedc4629dccd6a8448d0afa8b742146e4fbf09a80eeeecb2aed7ff7e221ab99a3aaf312d83fcaa857e25ece7f9950ba847b6574eafb7fffbf16f09a879268b7fff951d04d4007eabcf048f7cdedd046c754d447f11fd47a8011ba4b0ed3fdfe69"); + + private final byte[] rtpDataExtension = + getBytesFromHexString("00a20003a94f000062150100cbca0100"); + + private final byte[] rtpWithHeaderExtensionPayloadData = + Arrays.copyOfRange( + rtpDataWithHeaderExtension, RtpPacket.MIN_HEADER_SIZE + rtpDataExtension.length, rtpDataWithHeaderExtension.length); + @Test public void parseRtpPacket() { RtpPacket packet = checkNotNull(RtpPacket.parse(rtpData, rtpData.length)); @@ -103,6 +126,24 @@ public final class RtpPacketTest { assertThat(packet.payloadData).isEqualTo(rtpWithLargeTimestampPayloadData); } + @Test + public void parseRtpPacketWithHeaderExtension() { + RtpPacket packet = + checkNotNull(RtpPacket.parse(rtpDataWithHeaderExtension, rtpDataWithHeaderExtension.length)); + + assertThat(packet.version).isEqualTo(RtpPacket.RTP_VERSION); + assertThat(packet.padding).isFalse(); + assertThat(packet.extension).isTrue(); + assertThat(packet.csrcCount).isEqualTo(0); + assertThat(packet.csrc).hasLength(0); + assertThat(packet.marker).isFalse(); + assertThat(packet.payloadType).isEqualTo(96); + assertThat(packet.sequenceNumber).isEqualTo(61514); + assertThat(packet.timestamp).isEqualTo(2000000000); + assertThat(packet.ssrc).isEqualTo(0x35ff2773); + assertThat(packet.payloadData).isEqualTo(rtpWithHeaderExtensionPayloadData); + } + @Test public void writetoBuffer_withProperlySizedBuffer_writesPacket() { int packetByteLength = rtpData.length; @@ -187,6 +228,28 @@ public final class RtpPacketTest { assertThat(builtPacketBytes).isEqualTo(rtpDataWithLargeTimestamp); } + @Test + public void buildRtpPacketWithHeaderExtension_matchesPacketData() { + RtpPacket builtPacket = + new RtpPacket.Builder() + .setPadding(false) + .setExtension(true) + .setMarker(false) + .setPayloadType((byte) 96) + .setSequenceNumber(61514) + .setTimestamp(2000000000) + .setSsrc(0x35ff2773) + .setHeaderExtension(Arrays.copyOfRange(rtpDataExtension, 0, RtpPacket.HEADER_EXTENSION_SIZE)) + .setExtensionPayload(Arrays.copyOfRange(rtpDataExtension, RtpPacket.HEADER_EXTENSION_SIZE, rtpDataExtension.length)) + .setPayloadData(rtpWithHeaderExtensionPayloadData) + .build(); + + int packetSize = RtpPacket.MIN_HEADER_SIZE + rtpDataExtension.length + builtPacket.payloadData.length; + byte[] builtPacketBytes = new byte[packetSize]; + builtPacket.writeToBuffer(builtPacketBytes, /* offset= */ 0, packetSize); + assertThat(builtPacketBytes).isEqualTo(rtpDataWithHeaderExtension); + } + @Test public void getNextSequenceNumber_invokingAtWrapOver() { assertThat(getNextSequenceNumber(65534)).isEqualTo(65535); From 84f8beb884927b7ab0186d3dec8afd8486114461 Mon Sep 17 00:00:00 2001 From: Kekelic Date: Mon, 8 Jul 2024 09:24:46 +0200 Subject: [PATCH 4/7] Remove saving unnecessary header extension data --- .../media3/exoplayer/rtsp/RtpPacket.java | 64 ++----------------- .../media3/exoplayer/rtsp/RtpPacketTest.java | 24 +------ 2 files changed, 7 insertions(+), 81 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java index 3b03901c5b..aca1185aa7 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java @@ -63,15 +63,12 @@ public final class RtpPacket { /** Builder class for an {@link RtpPacket} */ public static final class Builder { private boolean padding; - private boolean extension; private boolean marker; private byte payloadType; private int sequenceNumber; private long timestamp; private int ssrc; private byte[] csrc = EMPTY; - private byte[] headerExtension = EMPTY; - private byte[] extensionPayload = EMPTY; private byte[] payloadData = EMPTY; /** Sets the {@link RtpPacket#padding}. The default is false. */ @@ -81,13 +78,6 @@ public final class RtpPacket { return this; } - /** Sets the {@link RtpPacket#extension}. The default is false. */ - @CanIgnoreReturnValue - public Builder setExtension(boolean extension) { - this.extension = extension; - return this; - } - /** Sets {@link RtpPacket#marker}. The default is false. */ @CanIgnoreReturnValue public Builder setMarker(boolean marker) { @@ -132,26 +122,6 @@ public final class RtpPacket { return this; } - /** - * Sets {@link RtpPacket#headerExtension}. The default is an empty byte array. - */ - @CanIgnoreReturnValue - public Builder setHeaderExtension(byte[] headerExtension) { - checkNotNull(headerExtension); - this.headerExtension = headerExtension; - return this; - } - - /** - * Sets {@link RtpPacket#extensionPayload}. The default is an empty byte array. - */ - @CanIgnoreReturnValue - public Builder setExtensionPayload(byte[] extensionPayload) { - checkNotNull(extensionPayload); - this.extensionPayload = extensionPayload; - return this; - } - /** Sets {@link RtpPacket#payloadData}. The default is an empty byte array. */ @CanIgnoreReturnValue public Builder setPayloadData(byte[] payloadData) { @@ -173,7 +143,6 @@ public final class RtpPacket { public static final int MIN_SEQUENCE_NUMBER = 0; public static final int MAX_SEQUENCE_NUMBER = 0xFFFF; public static final int CSRC_SIZE = 4; - public static final int HEADER_EXTENSION_SIZE = 4; /** Returns the next sequence number of the {@code sequenceNumber}. */ public static int getNextSequenceNumber(int sequenceNumber) { @@ -217,12 +186,6 @@ public final class RtpPacket { /** The RTP CSRC fields (Optional, up to 15 items). */ public final byte[] csrc; - /** The RTP header extension fields (Optional, 32 bits). */ - public final byte[] headerExtension; - - /** The RTP extension payload fields (Optional). */ - public final byte[] extensionPayload; - public final byte[] payloadData; /** @@ -272,21 +235,13 @@ public final class RtpPacket { } //Extension. - byte[] headerExtension; - byte[] extensionPayload; if (hasExtension) { - headerExtension = new byte[HEADER_EXTENSION_SIZE]; - packetBuffer.readBytes(headerExtension, 0, HEADER_EXTENSION_SIZE); - int extensionPayloadLength = (headerExtension[2] & 0xFF) << 8 | (headerExtension[3] & 0xFF); - if (extensionPayloadLength != 0) { - extensionPayload = new byte[extensionPayloadLength * 4]; - packetBuffer.readBytes(extensionPayload, 0, extensionPayloadLength * 4); - }else { - extensionPayload = EMPTY; + int headerExtensionProfileData = packetBuffer.readShort(); + int headerExtensionPayloadLength = packetBuffer.readShort(); + if (headerExtensionPayloadLength != 0) { + byte[] extensionPayload = new byte[headerExtensionPayloadLength * 4]; + packetBuffer.readBytes(extensionPayload, 0, headerExtensionPayloadLength * 4); } - }else { - headerExtension = EMPTY; - extensionPayload = EMPTY; } // Everything else will be RTP payload. @@ -302,9 +257,6 @@ public final class RtpPacket { .setTimestamp(timestamp) .setSsrc(ssrc) .setCsrc(csrc) - .setExtension(hasExtension) - .setHeaderExtension(headerExtension) - .setExtensionPayload(extensionPayload) .setPayloadData(payloadData) .build(); } @@ -323,7 +275,7 @@ public final class RtpPacket { private RtpPacket(Builder builder) { this.padding = builder.padding; - this.extension = builder.extension; + this.extension = false; this.marker = builder.marker; this.payloadType = builder.payloadType; this.sequenceNumber = builder.sequenceNumber; @@ -331,8 +283,6 @@ public final class RtpPacket { this.ssrc = builder.ssrc; this.csrc = builder.csrc; this.csrcCount = (byte) (this.csrc.length / CSRC_SIZE); - this.headerExtension = builder.headerExtension; - this.extensionPayload = builder.extensionPayload; this.payloadData = builder.payloadData; } @@ -371,8 +321,6 @@ public final class RtpPacket { .putInt((int) timestamp) .putInt(ssrc) .put(csrc) - .put(headerExtension) - .put(extensionPayload) .put(payloadData); return packetLength; } diff --git a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java index d2aa851379..89e9e4b1c9 100644 --- a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java +++ b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java @@ -133,7 +133,7 @@ public final class RtpPacketTest { assertThat(packet.version).isEqualTo(RtpPacket.RTP_VERSION); assertThat(packet.padding).isFalse(); - assertThat(packet.extension).isTrue(); + assertThat(packet.extension).isFalse(); assertThat(packet.csrcCount).isEqualTo(0); assertThat(packet.csrc).hasLength(0); assertThat(packet.marker).isFalse(); @@ -228,28 +228,6 @@ public final class RtpPacketTest { assertThat(builtPacketBytes).isEqualTo(rtpDataWithLargeTimestamp); } - @Test - public void buildRtpPacketWithHeaderExtension_matchesPacketData() { - RtpPacket builtPacket = - new RtpPacket.Builder() - .setPadding(false) - .setExtension(true) - .setMarker(false) - .setPayloadType((byte) 96) - .setSequenceNumber(61514) - .setTimestamp(2000000000) - .setSsrc(0x35ff2773) - .setHeaderExtension(Arrays.copyOfRange(rtpDataExtension, 0, RtpPacket.HEADER_EXTENSION_SIZE)) - .setExtensionPayload(Arrays.copyOfRange(rtpDataExtension, RtpPacket.HEADER_EXTENSION_SIZE, rtpDataExtension.length)) - .setPayloadData(rtpWithHeaderExtensionPayloadData) - .build(); - - int packetSize = RtpPacket.MIN_HEADER_SIZE + rtpDataExtension.length + builtPacket.payloadData.length; - byte[] builtPacketBytes = new byte[packetSize]; - builtPacket.writeToBuffer(builtPacketBytes, /* offset= */ 0, packetSize); - assertThat(builtPacketBytes).isEqualTo(rtpDataWithHeaderExtension); - } - @Test public void getNextSequenceNumber_invokingAtWrapOver() { assertThat(getNextSequenceNumber(65534)).isEqualTo(65535); From 47f4dabb982c721b93b407f7ba6db58b1073a454 Mon Sep 17 00:00:00 2001 From: Kekelic Date: Tue, 9 Jul 2024 08:26:47 +0200 Subject: [PATCH 5/7] Fix documentation and improve readability of unit test with rtpDataWithHeaderExtension example --- .../java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java index 89e9e4b1c9..cade2eb0f8 100644 --- a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java +++ b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java @@ -71,9 +71,9 @@ public final class RtpPacketTest { /* 10.. .... = Version: RFC 1889 Version (2) ..0. .... = Padding: False - ...0 .... = Extension: True + ...1 .... = Extension: True .... 0000 = Contributing source identifiers count: 0 - 1... .... = Marker: False + 0... .... = Marker: False Payload type: DynamicRTP-Type-96 (96) Sequence number: 61514 Timestamp: 2000000000 @@ -127,12 +127,13 @@ public final class RtpPacketTest { } @Test - public void parseRtpPacketWithHeaderExtension() { + public void parseRtpPacketWithHeaderExtension_createsRtpPacketWithoutHeaderExtension() { RtpPacket packet = checkNotNull(RtpPacket.parse(rtpDataWithHeaderExtension, rtpDataWithHeaderExtension.length)); assertThat(packet.version).isEqualTo(RtpPacket.RTP_VERSION); assertThat(packet.padding).isFalse(); + //created RtpPacket object will parse but not save the extension data assertThat(packet.extension).isFalse(); assertThat(packet.csrcCount).isEqualTo(0); assertThat(packet.csrc).hasLength(0); From 71bc177e391013fda26578c3785f146f05ca71d7 Mon Sep 17 00:00:00 2001 From: microkatz Date: Thu, 31 Oct 2024 11:57:38 +0000 Subject: [PATCH 6/7] Format with google-java-format --- .../androidx/media3/exoplayer/rtsp/RtpPacket.java | 2 +- .../media3/exoplayer/rtsp/RtpPacketTest.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java index aca1185aa7..766906c4e9 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java @@ -234,7 +234,7 @@ public final class RtpPacket { csrc = EMPTY; } - //Extension. + // Extension. if (hasExtension) { int headerExtensionProfileData = packetBuffer.readShort(); int headerExtensionPayloadLength = packetBuffer.readShort(); diff --git a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java index cade2eb0f8..2300d96711 100644 --- a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java +++ b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtpPacketTest.java @@ -82,14 +82,16 @@ public final class RtpPacketTest { Payload: 7c85b841bc439048000834f1a6943c00040bf038ee4de07acb6d… */ private final byte[] rtpDataWithHeaderExtension = - getBytesFromHexString("9060f04a7735940035ff277300a20003a94f000062150100cbca01007c85b841bc439048000834f1a6943c00040bf038ee4de07acb6dc67cb44716d9b61800600f1041214b121de2af09a8063ff2d88fedf7f565eafb9c44412a8e5a247d0ac76a6a8566a6ff593f9711114b6c625ca1363950ae8524a37c75c509a806833fd4bbeb6dda6db697aef12d709a80910e522bb3e2e793eb3c37995c4429448f2ba8b16bcb825ca11c3dffb3ff50ba8c5a3e5ffaff978b7e1350037d7dce4ddc906dbfff50ba8069ace7a9df442fffde2afc26a004b076dd611ebedb8bf15fd9596fc47e03e1008a32013d454401f8590ea42b6a67a3cf4da90aa006aca053283cf09c0e42c000444519045f3a0002c4c0e5e0f81e8316c5b16cbf3737c462bd5f87cc66b3e508a10128ac18d5656c78a6e293f10e0252c1819c2040fc5b16fe222296c4da247284ae892a16de65db7236a9bab718da108d05f09bf85d22bebb3ff11ff178b8da7c52c52fc4428b07ae1f1a9e0e3cc963136d542b2d698b6e84d572fbfbffcf1eafaf5af5e3d4092e33443b7b7ff09a8008ae5d661597bdbcfeb54dbd944c711014a2161cd76dc63b5087d087befe7ffb97e5484d5025f4e5fda36ffdcb500975c917ac1357fffea6484d401565b4a77bba2ff34135b711004583df39c7a16669f2840edca371fec8f575fff5f8f5049f3b1fff7d709a8338aaffeff1ad4981350cb5bffbfef09a87f043fff5f426a1132f7f5ffd09a821786b3fff7d426a057b80dfff6fe84d430c37fe9a7baed09a800769f54dd97df7dbbd3dc9e23e23158888250743e4da9d997974501d0879f2fe581b62b3f7711825831ca84b9421d3e4fa4d0874bc9ef895b771190dac47e55003a076252c032ccac0d4583c67078e3f101a946e260152754c800402a134002756425e7bb77f97e2305b1094e1c2c067a62284172a506a260011df686ed758e28c461702e001778034a407a1b16cd4ff11105bb0003784c552d810f6e5c5ce79e03e84381fd1008d9e471b8bb0cb2e20c70a34a8ab3fff1100a945d03f257e3cc499e2be9ae9072000f94896b5ca506e1c577fb28fc442b2503e0bce41bc1abacb5e05d6320455c3f3505681ec1c1a888a3240aab928b10001004f2c10782ac03e640ba8691007cc559a827a3cbfe221e600288540a914651e4be0e87c007b983f8cc550080038df00d6c8001818aa92448153c01ef80fc3b362084bf97f111059618ed4580e84505d02d6242f5d8c8e03f9e0705036e1400025385840c64c3a16da22241291e7e6fc9f1111ef1e738583a11ef2b553f1e62380ce79c845ac2901008910430ea3c00040078592b101bb030b2e33d97f111ac5000811454c01a83a00f138d0c0022d06cc09476727596154030742a4141bc3415c9264021789002af000f9e1b8bfcbf88852be0e4e2381400a8d266a1e00e1a9f10f02c1610c6780f29f05440792d0210684000a79f0625601b88c16e958f5eb6583012a944252f8ca00ba920f9fc7ad1500025061e018ca88000b8d7bfc132625ae297f8885641a000100c052c006be1d8000801faf80014b19614e521f0e87c9f21b000170d4c410c0372c007a4320d40600315906e156fda684435100f47855e2c05c73a5985d4009135a0e927d09e1dead7fffb752c63af06890b07fb9531297e0d0af0e03b1fb7c9d3f20a02fb1d7bca101c2c05064c0b78b2cb0ff0ba800dec69a27563d71bbb570bb95510da8fad5695a1814378ab8b7e135001be5e47055c6ba97975bbd75be5f4c826a2ee52bffdbdb6e96750516c6ab5077a0f5035b219ffbfa8f50997537f7aecbe223426e66b5426b057565b5f25bc7a98c6f047ffd7c26a037df97eaffedc4629dccd6a8448d0afa8b742146e4fbf09a80eeeecb2aed7ff7e221ab99a3aaf312d83fcaa857e25ece7f9950ba847b6574eafb7fffbf16f09a879268b7fff951d04d4007eabcf048f7cdedd046c754d447f11fd47a8011ba4b0ed3fdfe69"); + getBytesFromHexString( + "9060f04a7735940035ff277300a20003a94f000062150100cbca01007c85b841bc439048000834f1a6943c00040bf038ee4de07acb6dc67cb44716d9b61800600f1041214b121de2af09a8063ff2d88fedf7f565eafb9c44412a8e5a247d0ac76a6a8566a6ff593f9711114b6c625ca1363950ae8524a37c75c509a806833fd4bbeb6dda6db697aef12d709a80910e522bb3e2e793eb3c37995c4429448f2ba8b16bcb825ca11c3dffb3ff50ba8c5a3e5ffaff978b7e1350037d7dce4ddc906dbfff50ba8069ace7a9df442fffde2afc26a004b076dd611ebedb8bf15fd9596fc47e03e1008a32013d454401f8590ea42b6a67a3cf4da90aa006aca053283cf09c0e42c000444519045f3a0002c4c0e5e0f81e8316c5b16cbf3737c462bd5f87cc66b3e508a10128ac18d5656c78a6e293f10e0252c1819c2040fc5b16fe222296c4da247284ae892a16de65db7236a9bab718da108d05f09bf85d22bebb3ff11ff178b8da7c52c52fc4428b07ae1f1a9e0e3cc963136d542b2d698b6e84d572fbfbffcf1eafaf5af5e3d4092e33443b7b7ff09a8008ae5d661597bdbcfeb54dbd944c711014a2161cd76dc63b5087d087befe7ffb97e5484d5025f4e5fda36ffdcb500975c917ac1357fffea6484d401565b4a77bba2ff34135b711004583df39c7a16669f2840edca371fec8f575fff5f8f5049f3b1fff7d709a8338aaffeff1ad4981350cb5bffbfef09a87f043fff5f426a1132f7f5ffd09a821786b3fff7d426a057b80dfff6fe84d430c37fe9a7baed09a800769f54dd97df7dbbd3dc9e23e23158888250743e4da9d997974501d0879f2fe581b62b3f7711825831ca84b9421d3e4fa4d0874bc9ef895b771190dac47e55003a076252c032ccac0d4583c67078e3f101a946e260152754c800402a134002756425e7bb77f97e2305b1094e1c2c067a62284172a506a260011df686ed758e28c461702e001778034a407a1b16cd4ff11105bb0003784c552d810f6e5c5ce79e03e84381fd1008d9e471b8bb0cb2e20c70a34a8ab3fff1100a945d03f257e3cc499e2be9ae9072000f94896b5ca506e1c577fb28fc442b2503e0bce41bc1abacb5e05d6320455c3f3505681ec1c1a888a3240aab928b10001004f2c10782ac03e640ba8691007cc559a827a3cbfe221e600288540a914651e4be0e87c007b983f8cc550080038df00d6c8001818aa92448153c01ef80fc3b362084bf97f111059618ed4580e84505d02d6242f5d8c8e03f9e0705036e1400025385840c64c3a16da22241291e7e6fc9f1111ef1e738583a11ef2b553f1e62380ce79c845ac2901008910430ea3c00040078592b101bb030b2e33d97f111ac5000811454c01a83a00f138d0c0022d06cc09476727596154030742a4141bc3415c9264021789002af000f9e1b8bfcbf88852be0e4e2381400a8d266a1e00e1a9f10f02c1610c6780f29f05440792d0210684000a79f0625601b88c16e958f5eb6583012a944252f8ca00ba920f9fc7ad1500025061e018ca88000b8d7bfc132625ae297f8885641a000100c052c006be1d8000801faf80014b19614e521f0e87c9f21b000170d4c410c0372c007a4320d40600315906e156fda684435100f47855e2c05c73a5985d4009135a0e927d09e1dead7fffb752c63af06890b07fb9531297e0d0af0e03b1fb7c9d3f20a02fb1d7bca101c2c05064c0b78b2cb0ff0ba800dec69a27563d71bbb570bb95510da8fad5695a1814378ab8b7e135001be5e47055c6ba97975bbd75be5f4c826a2ee52bffdbdb6e96750516c6ab5077a0f5035b219ffbfa8f50997537f7aecbe223426e66b5426b057565b5f25bc7a98c6f047ffd7c26a037df97eaffedc4629dccd6a8448d0afa8b742146e4fbf09a80eeeecb2aed7ff7e221ab99a3aaf312d83fcaa857e25ece7f9950ba847b6574eafb7fffbf16f09a879268b7fff951d04d4007eabcf048f7cdedd046c754d447f11fd47a8011ba4b0ed3fdfe69"); - private final byte[] rtpDataExtension = - getBytesFromHexString("00a20003a94f000062150100cbca0100"); + private final byte[] rtpDataExtension = getBytesFromHexString("00a20003a94f000062150100cbca0100"); private final byte[] rtpWithHeaderExtensionPayloadData = Arrays.copyOfRange( - rtpDataWithHeaderExtension, RtpPacket.MIN_HEADER_SIZE + rtpDataExtension.length, rtpDataWithHeaderExtension.length); + rtpDataWithHeaderExtension, + RtpPacket.MIN_HEADER_SIZE + rtpDataExtension.length, + rtpDataWithHeaderExtension.length); @Test public void parseRtpPacket() { @@ -129,11 +131,12 @@ public final class RtpPacketTest { @Test public void parseRtpPacketWithHeaderExtension_createsRtpPacketWithoutHeaderExtension() { RtpPacket packet = - checkNotNull(RtpPacket.parse(rtpDataWithHeaderExtension, rtpDataWithHeaderExtension.length)); + checkNotNull( + RtpPacket.parse(rtpDataWithHeaderExtension, rtpDataWithHeaderExtension.length)); assertThat(packet.version).isEqualTo(RtpPacket.RTP_VERSION); assertThat(packet.padding).isFalse(); - //created RtpPacket object will parse but not save the extension data + // created RtpPacket object will parse but not save the extension data assertThat(packet.extension).isFalse(); assertThat(packet.csrcCount).isEqualTo(0); assertThat(packet.csrc).hasLength(0); From 0e020f778cab07b9effffb190b4e121d8e8ada1a Mon Sep 17 00:00:00 2001 From: microkatz Date: Thu, 31 Oct 2024 12:20:50 +0000 Subject: [PATCH 7/7] Added release note --- RELEASENOTES.md | 2 ++ .../java/androidx/media3/exoplayer/rtsp/RtpPacket.java | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 650b200160..78fa5c9a71 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -62,6 +62,8 @@ * DASH Extension: * Smooth Streaming Extension: * RTSP Extension: + * Fix crashing when parsing of RTP packets with header extensions + ([#1225](https://github.com/androidx/media/pull/1225)). * Decoder Extensions (FFmpeg, VP9, AV1, etc.): * Add the MPEG-H decoder module which uses the native MPEG-H decoder module to decode MPEG-H audio diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java index 766906c4e9..fbf4b144b8 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPacket.java @@ -205,7 +205,7 @@ public final class RtpPacket { byte version = (byte) (firstByte >> 6); boolean padding = ((firstByte >> 5) & 0x1) == 1; byte csrcCount = (byte) (firstByte & 0xF); - boolean hasExtension = ((firstByte & 0x10) >> 4) == 1; + boolean hasExtension = ((firstByte >> 4) & 0x1) == 1; if (version != RTP_VERSION) { return null; @@ -236,11 +236,11 @@ public final class RtpPacket { // Extension. if (hasExtension) { - int headerExtensionProfileData = packetBuffer.readShort(); + // Skip profile-defined data + packetBuffer.skipBytes(2); int headerExtensionPayloadLength = packetBuffer.readShort(); if (headerExtensionPayloadLength != 0) { - byte[] extensionPayload = new byte[headerExtensionPayloadLength * 4]; - packetBuffer.readBytes(extensionPayload, 0, headerExtensionPayloadLength * 4); + packetBuffer.skipBytes(headerExtensionPayloadLength * 4); } }