Update VP9 Reader to handle missing frames/fragments.
Change-Id: I3cdc738b25fda1292ae07769656a3b18721b2402
This commit is contained in:
parent
05e728a31e
commit
db5ff90167
@ -16,6 +16,7 @@
|
|||||||
package androidx.media3.exoplayer.rtsp.reader;
|
package androidx.media3.exoplayer.rtsp.reader;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||||
|
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;
|
||||||
@ -54,6 +55,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private int previousSequenceNumber;
|
private int previousSequenceNumber;
|
||||||
/** The combined size of a sample that is fragmented into multiple RTP packets. */
|
/** The combined size of a sample that is fragmented into multiple RTP packets. */
|
||||||
private int fragmentedSampleSizeBytes;
|
private int fragmentedSampleSizeBytes;
|
||||||
|
private long sampleTimeUsOfFragmentedSample;
|
||||||
|
|
||||||
private int width;
|
private int width;
|
||||||
private int height;
|
private int height;
|
||||||
@ -64,11 +66,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private boolean gotFirstPacketOfVP9Frame;
|
private boolean gotFirstPacketOfVP9Frame;
|
||||||
|
|
||||||
private boolean reportedOutputFormat;
|
private boolean reportedOutputFormat;
|
||||||
|
private boolean isKeyFrame;
|
||||||
|
|
||||||
/** Creates an instance. */
|
/** Creates an instance. */
|
||||||
public RtpVp9Reader(RtpPayloadFormat payloadFormat) {
|
public RtpVp9Reader(RtpPayloadFormat payloadFormat) {
|
||||||
this.payloadFormat = payloadFormat;
|
this.payloadFormat = payloadFormat;
|
||||||
firstReceivedTimestamp = C.TIME_UNSET;
|
firstReceivedTimestamp = C.TIME_UNSET;
|
||||||
|
fragmentedSampleSizeBytes = 0;
|
||||||
|
sampleTimeUsOfFragmentedSample = C.TIME_UNSET;
|
||||||
// The start time offset must be 0 until the first seek.
|
// The start time offset must be 0 until the first seek.
|
||||||
startTimeOffsetUs = 0;
|
startTimeOffsetUs = 0;
|
||||||
previousSequenceNumber = C.INDEX_UNSET;
|
previousSequenceNumber = C.INDEX_UNSET;
|
||||||
@ -76,6 +81,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
height = C.LENGTH_UNSET;
|
height = C.LENGTH_UNSET;
|
||||||
gotFirstPacketOfVP9Frame = false;
|
gotFirstPacketOfVP9Frame = false;
|
||||||
reportedOutputFormat = false;
|
reportedOutputFormat = false;
|
||||||
|
isKeyFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -85,7 +91,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(
|
||||||
@ -93,11 +102,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
checkStateNotNull(trackOutput);
|
checkStateNotNull(trackOutput);
|
||||||
|
|
||||||
if (validateVp9Descriptor(data, sequenceNumber)) {
|
if (validateVp9Descriptor(data, sequenceNumber)) {
|
||||||
@C.BufferFlags int bufferFlags = 0;
|
if (fragmentedSampleSizeBytes == 0 && gotFirstPacketOfVP9Frame) {
|
||||||
if (fragmentedSampleSizeBytes == 0
|
isKeyFrame = (data.peekUnsignedByte() & 0x04) == 0;
|
||||||
&& gotFirstPacketOfVP9Frame
|
|
||||||
&& (data.peekUnsignedByte() & 0x04) == 0) {
|
|
||||||
bufferFlags = C.BUFFER_FLAG_KEY_FRAME;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reportedOutputFormat && width != C.LENGTH_UNSET && height != C.LENGTH_UNSET) {
|
if (!reportedOutputFormat && width != C.LENGTH_UNSET && height != C.LENGTH_UNSET) {
|
||||||
@ -112,20 +118,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// Write the video sample.
|
// Write the video sample.
|
||||||
trackOutput.sampleData(data, currentFragmentSizeBytes);
|
trackOutput.sampleData(data, currentFragmentSizeBytes);
|
||||||
fragmentedSampleSizeBytes += currentFragmentSizeBytes;
|
fragmentedSampleSizeBytes += currentFragmentSizeBytes;
|
||||||
|
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,
|
|
||||||
bufferFlags,
|
|
||||||
fragmentedSampleSizeBytes,
|
|
||||||
/* offset= */ 0,
|
|
||||||
/* cryptoData= */ null);
|
|
||||||
fragmentedSampleSizeBytes = 0;
|
|
||||||
gotFirstPacketOfVP9Frame = false;
|
|
||||||
}
|
}
|
||||||
previousSequenceNumber = sequenceNumber;
|
previousSequenceNumber = sequenceNumber;
|
||||||
}
|
}
|
||||||
@ -162,19 +159,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
// +-+-+-+-+-+-+-+-+
|
// +-+-+-+-+-+-+-+-+
|
||||||
|
|
||||||
int header = payload.readUnsignedByte();
|
int header = payload.readUnsignedByte();
|
||||||
if (!gotFirstPacketOfVP9Frame) {
|
if ((header & 0x08) == 0x08) {
|
||||||
if ((header & 0x08) == 0) {
|
if (gotFirstPacketOfVP9Frame && fragmentedSampleSizeBytes > 0) {
|
||||||
Log.w(
|
// Received new VP9 fragment, output data of previous fragment to decoder.
|
||||||
TAG,
|
outputSampleMetadataForFragmentedPackets();
|
||||||
"First payload octet of the RTP packet is not the beginning of a new VP9 partition,"
|
|
||||||
+ " Dropping current packet.");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
gotFirstPacketOfVP9Frame = true;
|
gotFirstPacketOfVP9Frame = true;
|
||||||
} else {
|
} else if (gotFirstPacketOfVP9Frame) {
|
||||||
// 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 (packetSequenceNumber != expectedSequenceNumber) {
|
if (packetSequenceNumber < expectedSequenceNumber) {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(
|
||||||
@ -183,6 +177,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
expectedSequenceNumber, packetSequenceNumber));
|
expectedSequenceNumber, packetSequenceNumber));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(
|
||||||
|
TAG,
|
||||||
|
"First payload octet of the RTP packet is not the beginning of a new VP9 partition,"
|
||||||
|
+ " Dropping current packet.");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if optional I header is present.
|
// Check if optional I header is present.
|
||||||
@ -250,6 +250,23 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
gotFirstPacketOfVP9Frame = 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