Update H263 Reader to handle missing frames/fragments.
Change-Id: I43dfbabcbe686c31cb54e6b95688af1fa35a3d24
This commit is contained in:
parent
05e728a31e
commit
a38059d109
@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.rtsp.reader;
|
package androidx.media3.exoplayer.rtsp.reader;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
@ -61,12 +62,22 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private boolean isKeyFrame;
|
private boolean isKeyFrame;
|
||||||
private boolean isOutputFormatSet;
|
private boolean isOutputFormatSet;
|
||||||
private long startTimeOffsetUs;
|
private long startTimeOffsetUs;
|
||||||
|
private long sampleTimeUsOfFragmentedSample;
|
||||||
|
/**
|
||||||
|
* Whether the first packet of a H263 frame is received, it mark the start of a H263 partition. A
|
||||||
|
* H263 frame can be split into multiple RTP packets.
|
||||||
|
*/
|
||||||
|
private boolean gotFirstPacketOfH263Frame;
|
||||||
|
|
||||||
/** Creates an instance. */
|
/** Creates an instance. */
|
||||||
public RtpH263Reader(RtpPayloadFormat payloadFormat) {
|
public RtpH263Reader(RtpPayloadFormat payloadFormat) {
|
||||||
this.payloadFormat = payloadFormat;
|
this.payloadFormat = payloadFormat;
|
||||||
firstReceivedTimestamp = C.TIME_UNSET;
|
firstReceivedTimestamp = C.TIME_UNSET;
|
||||||
previousSequenceNumber = C.INDEX_UNSET;
|
previousSequenceNumber = C.INDEX_UNSET;
|
||||||
|
isKeyFrame = false;
|
||||||
|
fragmentedSampleSizeBytes = 0;
|
||||||
|
sampleTimeUsOfFragmentedSample = C.TIME_UNSET;
|
||||||
|
gotFirstPacketOfH263Frame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -76,7 +87,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceivingFirstPacket(long timestamp, int sequenceNumber) {}
|
public void onReceivingFirstPacket(long timestamp, int sequenceNumber) {
|
||||||
|
checkState(firstReceivedTimestamp == C.TIME_UNSET);
|
||||||
|
firstReceivedTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consume(
|
public void consume(
|
||||||
@ -103,6 +117,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pBitIsSet) {
|
if (pBitIsSet) {
|
||||||
|
if (gotFirstPacketOfH263Frame && fragmentedSampleSizeBytes > 0) {
|
||||||
|
// Received new H263 fragment, output data of previous fragment to decoder.
|
||||||
|
outputSampleMetadataForFragmentedPackets();
|
||||||
|
}
|
||||||
|
gotFirstPacketOfH263Frame = true;
|
||||||
|
|
||||||
int payloadStartCode = data.peekUnsignedByte() & 0xFC;
|
int payloadStartCode = data.peekUnsignedByte() & 0xFC;
|
||||||
// Packets that begin with a Picture Start Code(100000). Refer RFC4629 Section 6.1.
|
// Packets that begin with a Picture Start Code(100000). Refer RFC4629 Section 6.1.
|
||||||
if (payloadStartCode < PICTURE_START_CODE) {
|
if (payloadStartCode < PICTURE_START_CODE) {
|
||||||
@ -113,10 +133,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
data.getData()[currentPosition] = 0;
|
data.getData()[currentPosition] = 0;
|
||||||
data.getData()[currentPosition + 1] = 0;
|
data.getData()[currentPosition + 1] = 0;
|
||||||
data.setPosition(currentPosition);
|
data.setPosition(currentPosition);
|
||||||
} else {
|
} else if (gotFirstPacketOfH263Frame) {
|
||||||
// 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 = RtpPacket.getNextSequenceNumber(previousSequenceNumber);
|
int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber);
|
||||||
if (sequenceNumber != expectedSequenceNumber) {
|
if (sequenceNumber < expectedSequenceNumber) {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(
|
||||||
@ -125,6 +145,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
expectedSequenceNumber, sequenceNumber));
|
expectedSequenceNumber, sequenceNumber));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"First payload octet of the H263 packet is not the beginning of a new H263 partition,"
|
||||||
|
+ " Dropping current packet.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fragmentedSampleSizeBytes == 0) {
|
if (fragmentedSampleSizeBytes == 0) {
|
||||||
@ -141,20 +167,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// Write the video sample.
|
// Write the video sample.
|
||||||
trackOutput.sampleData(data, fragmentSize);
|
trackOutput.sampleData(data, fragmentSize);
|
||||||
fragmentedSampleSizeBytes += fragmentSize;
|
fragmentedSampleSizeBytes += fragmentSize;
|
||||||
|
sampleTimeUsOfFragmentedSample =
|
||||||
|
toSampleUs(startTimeOffsetUs, timestamp, firstReceivedTimestamp);
|
||||||
|
|
||||||
if (rtpMarker) {
|
if (rtpMarker) {
|
||||||
if (firstReceivedTimestamp == C.TIME_UNSET) {
|
outputSampleMetadataForFragmentedPackets();
|
||||||
firstReceivedTimestamp = timestamp;
|
|
||||||
}
|
|
||||||
long timeUs = toSampleUs(startTimeOffsetUs, timestamp, firstReceivedTimestamp);
|
|
||||||
trackOutput.sampleMetadata(
|
|
||||||
timeUs,
|
|
||||||
isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0,
|
|
||||||
fragmentedSampleSizeBytes,
|
|
||||||
/* offset= */ 0,
|
|
||||||
/* cryptoData= */ null);
|
|
||||||
fragmentedSampleSizeBytes = 0;
|
|
||||||
isKeyFrame = false;
|
|
||||||
}
|
}
|
||||||
previousSequenceNumber = sequenceNumber;
|
previousSequenceNumber = sequenceNumber;
|
||||||
}
|
}
|
||||||
@ -211,6 +228,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
isKeyFrame = false;
|
isKeyFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs sample metadata.
|
||||||
|
*
|
||||||
|
* <p>Call this method only when receiving a end of VP8 partition
|
||||||
|
*/
|
||||||
|
private void outputSampleMetadataForFragmentedPackets() {
|
||||||
|
trackOutput.sampleMetadata(
|
||||||
|
sampleTimeUsOfFragmentedSample,
|
||||||
|
isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0,
|
||||||
|
fragmentedSampleSizeBytes,
|
||||||
|
/* offset= */ 0,
|
||||||
|
/* cryptoData= */ null);
|
||||||
|
fragmentedSampleSizeBytes = 0;
|
||||||
|
sampleTimeUsOfFragmentedSample = C.TIME_UNSET;
|
||||||
|
isKeyFrame = false;
|
||||||
|
gotFirstPacketOfH263Frame = false;
|
||||||
|
}
|
||||||
|
|
||||||
private static long toSampleUs(
|
private static long toSampleUs(
|
||||||
long startTimeOffsetUs, long rtpTimestamp, long firstReceivedRtpTimestamp) {
|
long startTimeOffsetUs, long rtpTimestamp, long firstReceivedRtpTimestamp) {
|
||||||
return startTimeOffsetUs
|
return startTimeOffsetUs
|
||||||
|
Loading…
x
Reference in New Issue
Block a user