From 55bfe71384bfd55fcddc14fddf1cac53e9c22416 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Tue, 15 Mar 2022 17:37:51 +0530 Subject: [PATCH 1/5] Add support for RTSP H263 Added H263 RTP Packet reader and added support for H263 playback through RTSP. Change-Id: I348cc4d8e974b5275409b816a9d52aa29f593233 --- .../exoplayer/rtsp/RtpPayloadFormat.java | 7 + .../media3/exoplayer/rtsp/RtspMediaTrack.java | 8 + .../DefaultRtpPayloadReaderFactory.java | 2 + .../exoplayer/rtsp/reader/RtpH263Reader.java | 197 ++++++++++++++++++ 4 files changed, 214 insertions(+) create mode 100644 libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java index 297353167b..9c1995ed4c 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtpPayloadFormat.java @@ -38,6 +38,8 @@ public final class RtpPayloadFormat { private static final String RTP_MEDIA_AC3 = "AC3"; private static final String RTP_MEDIA_MPEG4_GENERIC = "MPEG4-GENERIC"; + private static final String RTP_MEDIA_H263_1998 = "H263-1998"; + private static final String RTP_MEDIA_H263_2000 = "H263-2000"; private static final String RTP_MEDIA_H264 = "H264"; private static final String RTP_MEDIA_H265 = "H265"; @@ -45,6 +47,8 @@ public final class RtpPayloadFormat { public static boolean isFormatSupported(MediaDescription mediaDescription) { switch (Ascii.toUpperCase(mediaDescription.rtpMapAttribute.mediaEncoding)) { case RTP_MEDIA_AC3: + case RTP_MEDIA_H263_1998: + case RTP_MEDIA_H263_2000: case RTP_MEDIA_H264: case RTP_MEDIA_H265: case RTP_MEDIA_MPEG4_GENERIC: @@ -65,6 +69,9 @@ public final class RtpPayloadFormat { switch (Ascii.toUpperCase(mediaType)) { case RTP_MEDIA_AC3: return MimeTypes.AUDIO_AC3; + case RTP_MEDIA_H263_1998: + case RTP_MEDIA_H263_2000: + return MimeTypes.VIDEO_H263; case RTP_MEDIA_H264: return MimeTypes.VIDEO_H264; case RTP_MEDIA_H265: diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java index 7547f1ea18..0285d00dde 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java @@ -56,6 +56,10 @@ import com.google.common.collect.ImmutableMap; private static final String GENERIC_CONTROL_ATTR = "*"; + /** Default width and height for H263. */ + private static final int DEFAULT_H263_WIDTH = 352; + private static final int DEFAULT_H263_HEIGHT = 288; + /** The track's associated {@link RtpPayloadFormat}. */ public final RtpPayloadFormat payloadFormat; /** The track's URI. */ @@ -121,6 +125,10 @@ import com.google.common.collect.ImmutableMap; checkArgument(!fmtpParameters.isEmpty()); processAacFmtpAttribute(formatBuilder, fmtpParameters, channelCount, clockRate); break; + case MimeTypes.VIDEO_H263: + // H263 does not require a FMTP attribute. So Setting default width and height. + formatBuilder.setWidth(DEFAULT_H263_WIDTH).setHeight(DEFAULT_H263_HEIGHT); + break; case MimeTypes.VIDEO_H264: checkArgument(!fmtpParameters.isEmpty()); processH264FmtpAttribute(formatBuilder, fmtpParameters); diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java index 888939b7e8..5c8150dbf4 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/DefaultRtpPayloadReaderFactory.java @@ -36,6 +36,8 @@ import androidx.media3.exoplayer.rtsp.RtpPayloadFormat; return new RtpAc3Reader(payloadFormat); case MimeTypes.AUDIO_AAC: return new RtpAacReader(payloadFormat); + case MimeTypes.VIDEO_H263: + return new RtpH263Reader(payloadFormat); case MimeTypes.VIDEO_H264: return new RtpH264Reader(payloadFormat); case MimeTypes.VIDEO_H265: diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java new file mode 100644 index 0000000000..1c12075f89 --- /dev/null +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java @@ -0,0 +1,197 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package androidx.media3.exoplayer.rtsp.reader; + +import static androidx.media3.common.util.Assertions.checkStateNotNull; +import static androidx.media3.common.util.Util.castNonNull; + +import androidx.media3.common.C; +import androidx.media3.common.ParserException; +import androidx.media3.common.util.Log; +import androidx.media3.common.util.ParsableByteArray; +import androidx.media3.common.util.Util; +import androidx.media3.exoplayer.rtsp.RtpPacket; +import androidx.media3.exoplayer.rtsp.RtpPayloadFormat; +import androidx.media3.extractor.ExtractorOutput; +import androidx.media3.extractor.TrackOutput; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; + +/** + * Parses a H263 byte stream carried on RTP packets, and extracts H263 individual video frames as + * defined in RFC4629. + */ +/* package */ final class RtpH263Reader implements RtpPayloadReader { + private static final String TAG = "RtpH263Reader"; + + private static final long MEDIA_CLOCK_FREQUENCY = 90_000; + + /** VOP unit type. */ + private static final int I_VOP = 0; + + private final RtpPayloadFormat payloadFormat; + + private @MonotonicNonNull TrackOutput trackOutput; + + private long firstReceivedTimestamp; + private int previousSequenceNumber; + /** The combined size of a sample that is fragmented into multiple RTP packets. */ + private int fragmentedSampleSizeBytes; + private static int width; + private static int height; + private static boolean isKeyFrame; + private boolean isOutputFormatSet; + private long startTimeOffsetUs; + + /** Creates an instance. */ + public RtpH263Reader(RtpPayloadFormat payloadFormat) { + this.payloadFormat = payloadFormat; + firstReceivedTimestamp = C.TIME_UNSET; + previousSequenceNumber = C.INDEX_UNSET; + fragmentedSampleSizeBytes = 0; + isKeyFrame = false; + isOutputFormatSet = false; + } + + @Override + public void createTracks(ExtractorOutput extractorOutput, int trackId) { + trackOutput = extractorOutput.track(trackId, C.TRACK_TYPE_VIDEO); + castNonNull(trackOutput).format(payloadFormat.format); + } + + @Override + public void onReceivingFirstPacket(long timestamp, int sequenceNumber) {} + + @Override + public void consume(ParsableByteArray data, long timestamp, int sequenceNumber, boolean rtpMarker) + throws ParserException { + checkStateNotNull(trackOutput); + + // H263 Header Payload Header, RFC4629 Section 5.1. + // 0 1 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | RR |P|V| PLEN |PEBIT| + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + int currPosition = data.getPosition(); + int header = data.readUnsignedShort(); + boolean pBit = ((header & 0x400) == 0x400); + + // Check if optional Video Redundancy Coding or PLEN or PEBIT is present, RFC4629 Section 5.1. + if ((header & 0x200) != 0 || (header & 0x1f8) != 0 || (header & 0x7) != 0) { + Log.w(TAG, "Packet discarded due to (VRC != 0) or (PLEN != 0) or (PEBIT != 0)"); + return; + } + int startCodePayload = data.peekUnsignedByte() & 0xfc; + if (pBit == true) { + if (startCodePayload < 128) { + Log.w(TAG, "Picture start Code (PSC) missing, Dropping packet."); + return; + } else { + // Setting first two bytes of the start code. Refer RFC4629 Section 5.1. + data.getData()[currPosition] = 0; + data.getData()[currPosition + 1] = 0; + data.setPosition(currPosition); + } + } else { + // Check that this packet is in the sequence of the previous packet. + int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber); + if (sequenceNumber != expectedSequenceNumber) { + Log.w( + TAG, + Util.formatInvariant( + "Received RTP packet with unexpected sequence number. Expected: %d; received: %d." + + " Dropping packet.", + expectedSequenceNumber, sequenceNumber)); + return; + } + } + + if (fragmentedSampleSizeBytes == 0) { + getBufferFlagsAndResolutionFromVop(data, isOutputFormatSet); + if (!isOutputFormatSet && isKeyFrame == true) { + if (width != payloadFormat.format.width || height != payloadFormat.format.height) { + trackOutput.format( + payloadFormat.format.buildUpon().setWidth(width).setHeight(height).build()); + } + isOutputFormatSet = true; + } + } + int fragmentSize = data.bytesLeft(); + // Write the video sample. + trackOutput.sampleData(data, fragmentSize); + fragmentedSampleSizeBytes += fragmentSize; + + if (rtpMarker) { + if (firstReceivedTimestamp == C.TIME_UNSET) { + firstReceivedTimestamp = timestamp; + } + long timeUs = toSampleUs(startTimeOffsetUs, timestamp, firstReceivedTimestamp); + trackOutput.sampleMetadata( + timeUs, + isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0, + fragmentedSampleSizeBytes, + /* offset= */ 0, + /* encryptionData= */ null); + fragmentedSampleSizeBytes = 0; + isKeyFrame = false; + } + previousSequenceNumber = sequenceNumber; + } + + @Override + public void seek(long nextRtpTimestamp, long timeUs) { + firstReceivedTimestamp = nextRtpTimestamp; + fragmentedSampleSizeBytes = 0; + startTimeOffsetUs = timeUs; + } + + // Internal methods. + /** + * Parses VOP Coding type and resolution. + */ + private void getBufferFlagsAndResolutionFromVop(ParsableByteArray data, boolean gotResolution) { + // Search for SHORT_VIDEO_START_MARKER (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0). + int currPosition = data.getPosition(); + if (data.readUnsignedInt() >> 10 == 0x20) { + int header = data.peekUnsignedByte(); + int vopType = ((header >> 1) & 0x01); + if (!gotResolution && vopType == I_VOP) { + int sourceFormat = ((header >> 2) & 0x07); + if (sourceFormat == 1) { + width = 128; + height = 96; + } else { + width = (short) (176 << (sourceFormat - 2)); + height = (short) (144 << (sourceFormat - 2)); + } + } + data.setPosition(currPosition); + isKeyFrame = (vopType == I_VOP ? true : false); + return; + } + data.setPosition(currPosition); + isKeyFrame = false; + } + + private static long toSampleUs( + long startTimeOffsetUs, long rtpTimestamp, long firstReceivedRtpTimestamp) { + return startTimeOffsetUs + + Util.scaleLargeTimestamp( + (rtpTimestamp - firstReceivedRtpTimestamp), + /* multiplier= */ C.MICROS_PER_SECOND, + /* divisor= */ MEDIA_CLOCK_FREQUENCY); + } +} From ff80a41f908cfe4b0d7203f466b1b4e4aa6c27d7 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Thu, 5 May 2022 14:01:06 +0530 Subject: [PATCH 2/5] Fix review comment in RTP H263 Reader Change-Id: I987baf379ecf3ba3f387cb38f22646023739addb --- .../media3/exoplayer/rtsp/RtspMediaTrack.java | 20 +++++++++++-- .../exoplayer/rtsp/reader/RtpH263Reader.java | 28 +++++++++++++------ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java index 69f23b9e34..0d2ff5912a 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java @@ -101,8 +101,23 @@ import com.google.common.collect.ImmutableMap; */ private static final int DEFAULT_VP8_HEIGHT = 240; - /** Default width and height for H263. */ + /** + * Default height for H263. + * + *

RFC4629 does not mandate codec specific data (like width and height) in the fmtp attribute. + * These values are taken from Android's software H263 decoder. + */ private static final int DEFAULT_H263_WIDTH = 352; + /** + * Default height for H263. + * + *

RFC4629 does not mandate codec specific data (like width and height) in the fmtp attribute. + * These values are taken from Android's software H263 decoder. + */ private static final int DEFAULT_H263_HEIGHT = 288; /** The track's associated {@link RtpPayloadFormat}. */ @@ -189,7 +204,8 @@ import com.google.common.collect.ImmutableMap; processMPEG4FmtpAttribute(formatBuilder, fmtpParameters); break; case MimeTypes.VIDEO_H263: - // H263 does not require a FMTP attribute. So Setting default width and height. + // H263 never uses fmtp width and height attributes (RFC4629 Section 8.2), setting default + // width and height. formatBuilder.setWidth(DEFAULT_H263_WIDTH).setHeight(DEFAULT_H263_HEIGHT); break; case MimeTypes.VIDEO_H264: diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java index 1c12075f89..f4f96cd1e9 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java @@ -30,28 +30,31 @@ import androidx.media3.extractor.TrackOutput; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * Parses a H263 byte stream carried on RTP packets, and extracts H263 individual video frames as - * defined in RFC4629. + * Parses a H263 byte stream carried on RTP packets, and extracts H263 frames as defined in RFC4629. */ /* package */ final class RtpH263Reader implements RtpPayloadReader { private static final String TAG = "RtpH263Reader"; private static final long MEDIA_CLOCK_FREQUENCY = 90_000; - /** VOP unit type. */ + /** I-frame VOP unit type. */ private static final int I_VOP = 0; private final RtpPayloadFormat payloadFormat; private @MonotonicNonNull TrackOutput trackOutput; + /** + * First received RTP timestamp. All RTP timestamps are dimension-less, the time base is defined + * by {@link #MEDIA_CLOCK_FREQUENCY}. + */ private long firstReceivedTimestamp; private int previousSequenceNumber; /** The combined size of a sample that is fragmented into multiple RTP packets. */ private int fragmentedSampleSizeBytes; - private static int width; - private static int height; - private static boolean isKeyFrame; + private int width; + private int height; + private boolean isKeyFrame; private boolean isOutputFormatSet; private long startTimeOffsetUs; @@ -144,7 +147,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0, fragmentedSampleSizeBytes, /* offset= */ 0, - /* encryptionData= */ null); + /* cryptoData= */ null); fragmentedSampleSizeBytes = 0; isKeyFrame = false; } @@ -163,12 +166,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * Parses VOP Coding type and resolution. */ private void getBufferFlagsAndResolutionFromVop(ParsableByteArray data, boolean gotResolution) { + // Picture Segment Packets (RFC4629 Section 6.1). // Search for SHORT_VIDEO_START_MARKER (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0). int currPosition = data.getPosition(); - if (data.readUnsignedInt() >> 10 == 0x20) { + long shortHeader = data.readUnsignedInt(); + if ((shortHeader & 0xffff) >> 10 == 0x20) { int header = data.peekUnsignedByte(); int vopType = ((header >> 1) & 0x01); if (!gotResolution && vopType == I_VOP) { + /** + * Parsing resolution from source format. + * + *

These values are taken from Android's software H263 decoder. + */ int sourceFormat = ((header >> 2) & 0x07); if (sourceFormat == 1) { width = 128; From f8cd3a974683a1b617a0c5e860fc2ca28ae41f42 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Thu, 5 May 2022 15:37:54 +0530 Subject: [PATCH 3/5] Fix some more review comment in RTP H263 Reader. Change-Id: If1f80a369b47319251e262c8f171091bb37e90c5 --- .../exoplayer/rtsp/reader/RtpH263Reader.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java index f4f96cd1e9..e3bb0b144a 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java @@ -39,6 +39,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** I-frame VOP unit type. */ private static final int I_VOP = 0; + private static final int PICTURE_START_CODE = 128; private final RtpPayloadFormat payloadFormat; @@ -71,7 +72,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override public void createTracks(ExtractorOutput extractorOutput, int trackId) { trackOutput = extractorOutput.track(trackId, C.TRACK_TYPE_VIDEO); - castNonNull(trackOutput).format(payloadFormat.format); + trackOutput.format(payloadFormat.format); } @Override @@ -90,24 +91,25 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ int currPosition = data.getPosition(); int header = data.readUnsignedShort(); - boolean pBit = ((header & 0x400) == 0x400); + boolean pBitIsSet = ((header & 0x400) > 0); - // Check if optional Video Redundancy Coding or PLEN or PEBIT is present, RFC4629 Section 5.1. + // Check if optional V (Video Redundancy Coding), PLEN or PEBIT is present, RFC4629 Section 5.1. if ((header & 0x200) != 0 || (header & 0x1f8) != 0 || (header & 0x7) != 0) { Log.w(TAG, "Packet discarded due to (VRC != 0) or (PLEN != 0) or (PEBIT != 0)"); return; } - int startCodePayload = data.peekUnsignedByte() & 0xfc; - if (pBit == true) { - if (startCodePayload < 128) { + + if (pBitIsSet == true) { + int startCodePayload = data.peekUnsignedByte() & 0xfc; + // Packets that begin with a Picture Start Code(100000). Refer RFC4629 Section 6.1.1. + if (startCodePayload < PICTURE_START_CODE) { Log.w(TAG, "Picture start Code (PSC) missing, Dropping packet."); return; - } else { - // Setting first two bytes of the start code. Refer RFC4629 Section 5.1. - data.getData()[currPosition] = 0; - data.getData()[currPosition + 1] = 0; - data.setPosition(currPosition); } + // Setting first two bytes of the start code. Refer RFC4629 Section 6.1.1. + data.getData()[currPosition] = 0; + data.getData()[currPosition + 1] = 0; + data.setPosition(currPosition); } else { // Check that this packet is in the sequence of the previous packet. int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber); @@ -124,7 +126,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; if (fragmentedSampleSizeBytes == 0) { getBufferFlagsAndResolutionFromVop(data, isOutputFormatSet); - if (!isOutputFormatSet && isKeyFrame == true) { + if (!isOutputFormatSet && isKeyFrame) { if (width != payloadFormat.format.width || height != payloadFormat.format.height) { trackOutput.format( payloadFormat.format.buildUpon().setWidth(width).setHeight(height).build()); @@ -168,7 +170,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; private void getBufferFlagsAndResolutionFromVop(ParsableByteArray data, boolean gotResolution) { // Picture Segment Packets (RFC4629 Section 6.1). // Search for SHORT_VIDEO_START_MARKER (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0). - int currPosition = data.getPosition(); + int currDataOffset = data.getPosition(); long shortHeader = data.readUnsignedInt(); if ((shortHeader & 0xffff) >> 10 == 0x20) { int header = data.peekUnsignedByte(); @@ -190,11 +192,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; height = (short) (144 << (sourceFormat - 2)); } } - data.setPosition(currPosition); + data.setPosition(currDataOffset); isKeyFrame = (vopType == I_VOP ? true : false); return; } - data.setPosition(currPosition); + data.setPosition(currDataOffset); isKeyFrame = false; } From aae9f23c79fe1e2ea0bc3274b3604c75f91b0dd8 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Mon, 9 May 2022 19:07:11 +0530 Subject: [PATCH 4/5] Fix some minor review comments in RTPH263Reader Change-Id: I0d728c695c9e11c5a50ef6f211bde614df4bbe71 --- .../exoplayer/rtsp/reader/RtpH263Reader.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java index e3bb0b144a..df721474d9 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java @@ -16,7 +16,6 @@ package androidx.media3.exoplayer.rtsp.reader; import static androidx.media3.common.util.Assertions.checkStateNotNull; -import static androidx.media3.common.util.Util.castNonNull; import androidx.media3.common.C; import androidx.media3.common.ParserException; @@ -99,9 +98,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; return; } - if (pBitIsSet == true) { + if (pBitIsSet) { int startCodePayload = data.peekUnsignedByte() & 0xfc; - // Packets that begin with a Picture Start Code(100000). Refer RFC4629 Section 6.1.1. + // Packets that begin with a Picture Start Code(100000). Refer RFC4629 Section 6.1. if (startCodePayload < PICTURE_START_CODE) { Log.w(TAG, "Picture start Code (PSC) missing, Dropping packet."); return; @@ -125,7 +124,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; } if (fragmentedSampleSizeBytes == 0) { - getBufferFlagsAndResolutionFromVop(data, isOutputFormatSet); + parseVopHeader(data, isOutputFormatSet); if (!isOutputFormatSet && isKeyFrame) { if (width != payloadFormat.format.width || height != payloadFormat.format.height) { trackOutput.format( @@ -167,14 +166,21 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Parses VOP Coding type and resolution. */ - private void getBufferFlagsAndResolutionFromVop(ParsableByteArray data, boolean gotResolution) { + private void parseVopHeader(ParsableByteArray data, boolean gotResolution) { // Picture Segment Packets (RFC4629 Section 6.1). // Search for SHORT_VIDEO_START_MARKER (0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0). int currDataOffset = data.getPosition(); + /** + * Parsing short header. + * + *

These values are taken from Android's software H263 decoder. + */ long shortHeader = data.readUnsignedInt(); - if ((shortHeader & 0xffff) >> 10 == 0x20) { + if (((shortHeader >> 10) & 0xffff) == 0x20) { int header = data.peekUnsignedByte(); - int vopType = ((header >> 1) & 0x01); + int vopType = ((header >> 1) & 0x1); if (!gotResolution && vopType == I_VOP) { /** * Parsing resolution from source format. From dfc424dbe651bc1e2d8f126834baaaf076bbcea6 Mon Sep 17 00:00:00 2001 From: Rakesh Kumar Date: Mon, 9 May 2022 19:13:55 +0530 Subject: [PATCH 5/5] Fix review comment in RTPH263Reader Change-Id: I6ae45dc710245769f36130ead4077d8e9980bf54 --- .../androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java index df721474d9..558bbacacf 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/reader/RtpH263Reader.java @@ -178,7 +178,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * >Android's software H263 decoder. */ long shortHeader = data.readUnsignedInt(); - if (((shortHeader >> 10) & 0xffff) == 0x20) { + if (((shortHeader >> 10) & 0x3f) == 0x20) { int header = data.peekUnsignedByte(); int vopType = ((header >> 1) & 0x1); if (!gotResolution && vopType == I_VOP) {