Fix review comments in RtpH265Reader

This commit is contained in:
Rakesh Kumar 2022-02-08 16:41:45 +05:30
parent 9cb243647f
commit ace363e183
2 changed files with 72 additions and 81 deletions

View File

@ -169,6 +169,26 @@ import com.google.common.collect.ImmutableMap;
AacUtil.buildAacLcAudioSpecificConfig(sampleRate, channelCount)));
}
/** Returns H264/H265 initialization data from RTP parameter set. */
private static byte[] getInitializationDataFromParameterSet(String parameterSet) {
byte[] decodedParameterNalData = Base64.decode(parameterSet, Base64.DEFAULT);
byte[] decodedParameterNalUnit =
new byte[decodedParameterNalData.length + NAL_START_CODE.length];
System.arraycopy(
NAL_START_CODE,
/* srcPos= */ 0,
decodedParameterNalUnit,
/* destPos= */ 0,
NAL_START_CODE.length);
System.arraycopy(
decodedParameterNalData,
/* srcPos= */ 0,
decodedParameterNalUnit,
/* destPos= */ NAL_START_CODE.length,
decodedParameterNalData.length);
return decodedParameterNalUnit;
}
private static void processH264FmtpAttribute(
Format.Builder formatBuilder, ImmutableMap<String, String> fmtpAttributes) {
checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_PARAMS));
@ -200,25 +220,6 @@ import com.google.common.collect.ImmutableMap;
}
}
private static byte[] getInitializationDataFromParameterSet(String parameterSet) {
byte[] decodedParameterNalData = Base64.decode(parameterSet, Base64.DEFAULT);
byte[] decodedParameterNalUnit =
new byte[decodedParameterNalData.length + NAL_START_CODE.length];
System.arraycopy(
NAL_START_CODE,
/* srcPos= */ 0,
decodedParameterNalUnit,
/* destPos= */ 0,
NAL_START_CODE.length);
System.arraycopy(
decodedParameterNalData,
/* srcPos= */ 0,
decodedParameterNalUnit,
/* destPos= */ NAL_START_CODE.length,
decodedParameterNalData.length);
return decodedParameterNalUnit;
}
private static void processH265FmtpAttribute(
Format.Builder formatBuilder, ImmutableMap<String, String> fmtpAttributes) {
if (fmtpAttributes.containsKey(PARAMETER_SPROP_H265_MAX_DON_DIFF)) {
@ -228,23 +229,24 @@ import com.google.common.collect.ImmutableMap;
"non-zero sprop-max-don-diff is not supported");
}
checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_H265_VPS));
String spropVPS = checkNotNull(fmtpAttributes.get(PARAMETER_SPROP_H265_VPS));
checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_H265_SPS));
String spropSPS = checkNotNull(fmtpAttributes.get(PARAMETER_SPROP_H265_SPS));
checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_H265_PPS));
String spropPPS = checkNotNull(fmtpAttributes.get(PARAMETER_SPROP_H265_PPS));
checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_H265_VPS));
String spropVPS = checkNotNull(fmtpAttributes.get(PARAMETER_SPROP_H265_VPS));
byte[] vpsNalData = getInitializationDataFromParameterSet(spropVPS);
byte[] spsNalData = getInitializationDataFromParameterSet(spropSPS);
byte[] ppsNalData = getInitializationDataFromParameterSet(spropPPS);
ImmutableList<byte[]> initializationData = ImmutableList.of(vpsNalData, spsNalData, ppsNalData);
ImmutableList<byte[]> initializationData =
ImmutableList.of(
getInitializationDataFromParameterSet(spropVPS),
getInitializationDataFromParameterSet(spropSPS),
getInitializationDataFromParameterSet(spropPPS));
formatBuilder.setInitializationData(initializationData);
// Process SPS (Sequence Parameter Set).
byte[] spsNalDataWithStartCode = initializationData.get(1);
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnit(
spsNalData, NAL_START_CODE.length, spsNalData.length);
spsNalDataWithStartCode, NAL_START_CODE.length, spsNalDataWithStartCode.length);
formatBuilder.setPixelWidthHeightRatio(spsData.pixelWidthHeightRatio);
formatBuilder.setHeight(spsData.height);
formatBuilder.setWidth(spsData.width);

View File

@ -42,17 +42,28 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private static final long MEDIA_CLOCK_FREQUENCY = 90_000;
/** Offset of payload data within a FU type A payload. */
/** Offset of payload data within a FU payload. */
private static final int FU_PAYLOAD_OFFSET = 3;
/** Single Time Aggregation Packet type A. */
private static final int RTP_PACKET_TYPE_STAP_A = 48; // RFC7798 Section 4.4.2
/** Fragmentation Unit type A. */
private static final int RTP_PACKET_TYPE_FU_A = 49;
/**
* Aggregation Packet.
*
* @see <a
* href="https://datatracker.ietf.org/doc/html/draft-ietf-payload-rtp-h265-15#section-4.4.2">
* RFC7798 Section 4.4.2</a>
*/
private static final int RTP_PACKET_TYPE_AP = 48;
/**
* Fragmentation Unit.
*
* @see <a
* href="https://datatracker.ietf.org/doc/html/draft-ietf-payload-rtp-h265-15#section-4.4.3">
* RFC7798 Section 4.4.3</a>
*/
private static final int RTP_PACKET_TYPE_FU = 49;
/** IDR NAL unit type. */
private static final int NAL_IDR_W_LP = 19;
private static final int NAL_IDR_W_RADL = 19;
private static final int NAL_IDR_N_LP = 20;
/** Scratch for Fragmentation Unit RTP packets. */
@ -65,12 +76,10 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
private @MonotonicNonNull TrackOutput trackOutput;
@C.BufferFlags private int bufferFlags;
private long firstReceivedTimestamp;
private int previousSequenceNumber;
/** The combined size of a sample that is fragmented into multiple RTP packets. */
private int fragmentedSampleSizeBytes;
private long startTimeOffsetUs;
/** Creates an instance. */
@ -84,7 +93,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@Override
public void createTracks(ExtractorOutput extractorOutput, int trackId) {
trackOutput = extractorOutput.track(trackId, C.TRACK_TYPE_VIDEO);
castNonNull(trackOutput).format(payloadFormat.format);
}
@ -94,7 +102,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@Override
public void consume(ParsableByteArray data, long timestamp, int sequenceNumber, boolean rtpMarker)
throws ParserException {
int payloadType;
try {
// RFC7798 Section 1.1.4. NAL Unit Header
@ -104,11 +111,11 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
}
checkStateNotNull(trackOutput);
if (payloadType >= 0 && payloadType < RTP_PACKET_TYPE_STAP_A) {
if (payloadType >= 0 && payloadType < RTP_PACKET_TYPE_AP) {
processSingleNalUnitPacket(data);
} else if (payloadType == RTP_PACKET_TYPE_STAP_A) {
processSingleTimeAggregationPacket(data);
} else if (payloadType == RTP_PACKET_TYPE_FU_A) {
} else if (payloadType == RTP_PACKET_TYPE_AP) {
processAggregationPacket(data);
} else if (payloadType == RTP_PACKET_TYPE_FU) {
processFragmentationUnitPacket(data, sequenceNumber);
} else {
throw ParserException.createForMalformedManifest(
@ -173,46 +180,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
bufferFlags = getBufferFlagsFromNalType(nalHeaderType);
}
/**
* Processes STAP Type A packet (RFC7798 Section 4.4.2).
* Processes an AP packet (RFC7798 Section 4.4.2).
*
* <p>Outputs the received aggregation units (with start code prepended) to {@link #trackOutput}.
* Sets {@link #bufferFlags} and {@link #fragmentedSampleSizeBytes} accordingly.
*/
@RequiresNonNull("trackOutput")
private void processSingleTimeAggregationPacket(ParsableByteArray data) throws ParserException {
// An Example of an AP Packet Containing Two Aggregation
// Units without the DONL and DOND Fields.
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | RTP Header |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | PayloadHdr (Type=48) | NALU 1 Size |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NALU 1 HDR | |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- NALU 1 Data |
// | |
// | |
// + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | | NALU 2 Size | NALU 2 HDR |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | NALU 2 HDR | |
// +-+-+-+-+-+-+-+- NALU 2 Data |
// | |
// | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | :...OPTIONAL RTP padding |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
private void processAggregationPacket(ParsableByteArray data) throws ParserException {
throw ParserException.createForMalformedManifest(
"need to implement processSingleTimeAggregationPacket",
"need to implement processAggregationPacket",
/* cause= */ null);
}
/**
* Processes Fragmentation Unit Type A packet (RFC7798 Section 4.4.3).
* Processes Fragmentation Unit packet (RFC7798 Section 4.4.3).
*
* <p>This method will be invoked multiple times to receive a single frame that is broken down
* into a series of fragmentation units in multiple RTP packets.
@ -241,15 +223,21 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
// +-+-+-+-+-+-+-+-+
// |S|E| FuType |
// +---------------+
// Structure of HEVC NAL unit header
// +---------------+---------------+
// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |F| Type | LayerId | TID |
// +-------------+-----------------+
int tid = (data.getData()[1] & 0x7); // last 3 bits in byte 1 of payload header section 1.1.4
int tid =
(data.getData()[1] & 0x7); // last 3 bits in byte 1 of payload header, RFC7798 Section 1.1.4
int fuHeader = data.getData()[2];
int nalUnitType = fuHeader & 0x3F;
byte nalHeader[] = new byte[2];
nalHeader[0] = (byte) (nalUnitType << 1); // Section: 1.1.4
// According to section 1.1.4 in rfc7798, layerId is required to be zero so keeping its value
// zero and copying only tid.
nalHeader[0] = (byte) (nalUnitType << 1); // RFC7798 Section 1.1.4
// layerId must be zero according to RFC7798 Section 1.1.4, so copying the tid only
nalHeader[1] = (byte) tid;
boolean isFirstFuPacket = (fuHeader & 0x80) > 0;
boolean isLastFuPacket = (fuHeader & 0x40) > 0;
@ -258,12 +246,13 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
// Prepends starter code.
fragmentedSampleSizeBytes += writeStartCode();
// The bytes needed is 2 (NALU header) + payload size. The original data array has size 3
// (2 payload + 1 FU header) + payload size. Thus setting the correct header and set position
// to 1.
// Overwrite byte 1 of payload header with byte 0 of HEVC nal header
// Overwrite a few bytes in Rtp buffer to get HEVC NAL unit
// Rtp Byte 0 -> Ignore
// Rtp Byte 1 -> Byte 0 of HEVC NAL header
// Rtp Byte 2 -> Byte 1 of HEVC NAL header
// Rtp Payload -> HEVC NAL bytes, so leave them unchanged
// Set data position from byte 1 as byte 0 was ignored
data.getData()[1] = (byte) nalHeader[0];
// Overwrite byte FU Header with byte 1 of HEVC nal header
data.getData()[2] = (byte) nalHeader[1];
fuScratchBuffer.reset(data.getData());
fuScratchBuffer.setPosition(1);
@ -312,6 +301,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@C.BufferFlags
private static int getBufferFlagsFromNalType(int nalType) {
return (nalType == NAL_IDR_W_LP || nalType == NAL_IDR_N_LP) ? C.BUFFER_FLAG_KEY_FRAME : 0;
return (nalType == NAL_IDR_W_RADL || nalType == NAL_IDR_N_LP) ? C.BUFFER_FLAG_KEY_FRAME : 0;
}
}