Extend SPS parsing when building the initial MP4 HevcConfig and include the PAR…

Extend SPS parsing when building the initial MP4 HevcConfig and include the PAR for propagating it into the Format
parent afc549fb
......@@ -86,28 +86,11 @@ public final class CodecSpecificDataUtil {
}
/**
* Returns an RFC 6381 HEVC codec string based on the SPS NAL unit read from the provided bit
* array. The position of the bit array must be the start of an SPS NALU (nal_unit_header), and
* the position may be modified by this method.
* Builds a RFC 6381 HEVC codec string using the provided parameters.
*/
public static String buildHevcCodecStringFromSps(ParsableNalUnitBitArray bitArray) {
// Skip nal_unit_header, sps_video_parameter_set_id, sps_max_sub_layers_minus1 and
// sps_temporal_id_nesting_flag.
bitArray.skipBits(16 + 4 + 3 + 1);
int generalProfileSpace = bitArray.readBits(2);
boolean generalTierFlag = bitArray.readBit();
int generalProfileIdc = bitArray.readBits(5);
int generalProfileCompatibilityFlags = 0;
for (int i = 0; i < 32; i++) {
if (bitArray.readBit()) {
generalProfileCompatibilityFlags |= (1 << i);
}
}
int[] constraintBytes = new int[6];
for (int i = 0; i < constraintBytes.length; ++i) {
constraintBytes[i] = bitArray.readBits(8);
}
int generalLevelIdc = bitArray.readBits(8);
public static String buildHevcCodecString(
int generalProfileSpace, boolean generalTierFlag, int generalProfileIdc,
int generalProfileCompatibilityFlags, int[] constraintBytes, int generalLevelIdc) {
StringBuilder builder =
new StringBuilder(
Util.formatInvariant(
......
......@@ -33,7 +33,7 @@ public final class NalUnitUtilTest {
createByteArray(
0x00, 0x00, 0x01, 0x67, 0x4D, 0x40, 0x16, 0xEC, 0xA0, 0x50, 0x17, 0xFC, 0xB8, 0x08, 0x80,
0x00, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x0F, 0x47, 0x8B, 0x16, 0xCB);
private static final int SPS_TEST_DATA_OFFSET = 3;
private static final int SPS_TEST_DATA_OFFSET = 4;
@Test
public void findNalUnit() {
......@@ -121,9 +121,10 @@ public final class NalUnitUtilTest {
}
@Test
public void parseSpsNalUnit() {
public void parseSpsNalUnitPayload() {
NalUnitUtil.SpsData data =
NalUnitUtil.parseSpsNalUnit(SPS_TEST_DATA, SPS_TEST_DATA_OFFSET, SPS_TEST_DATA.length);
NalUnitUtil.parseSpsNalUnitPayload(
SPS_TEST_DATA, SPS_TEST_DATA_OFFSET, SPS_TEST_DATA.length);
assertThat(data.width).isEqualTo(640);
assertThat(data.height).isEqualTo(360);
assertThat(data.deltaPicOrderAlwaysZeroFlag).isFalse();
......
......@@ -1139,6 +1139,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
HevcConfig hevcConfig = HevcConfig.parse(parent);
initializationData = hevcConfig.initializationData;
out.nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
if (!pixelWidthHeightRatioFromPasp) {
pixelWidthHeightRatio = hevcConfig.pixelWidthHeightRatio;
}
codecs = hevcConfig.codecs;
} else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
@Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
......
......@@ -200,8 +200,8 @@ public final class H264Reader implements ElementaryStreamReader {
List<byte[]> initializationData = new ArrayList<>();
initializationData.add(Arrays.copyOf(sps.nalData, sps.nalLength));
initializationData.add(Arrays.copyOf(pps.nalData, pps.nalLength));
NalUnitUtil.SpsData spsData = NalUnitUtil.parseSpsNalUnit(sps.nalData, 3, sps.nalLength);
NalUnitUtil.PpsData ppsData = NalUnitUtil.parsePpsNalUnit(pps.nalData, 3, pps.nalLength);
NalUnitUtil.SpsData spsData = NalUnitUtil.parseSpsNalUnitPayload(sps.nalData, 4, sps.nalLength);
NalUnitUtil.PpsData ppsData = NalUnitUtil.parsePpsNalUnitPayload(pps.nalData, 4, pps.nalLength);
String codecs =
CodecSpecificDataUtil.buildAvcCodecString(
spsData.profileIdc,
......@@ -224,11 +224,11 @@ public final class H264Reader implements ElementaryStreamReader {
pps.reset();
}
} else if (sps.isCompleted()) {
NalUnitUtil.SpsData spsData = NalUnitUtil.parseSpsNalUnit(sps.nalData, 3, sps.nalLength);
NalUnitUtil.SpsData spsData = NalUnitUtil.parseSpsNalUnitPayload(sps.nalData, 4, sps.nalLength);
sampleReader.putSps(spsData);
sps.reset();
} else if (pps.isCompleted()) {
NalUnitUtil.PpsData ppsData = NalUnitUtil.parsePpsNalUnit(pps.nalData, 3, pps.nalLength);
NalUnitUtil.PpsData ppsData = NalUnitUtil.parsePpsNalUnitPayload(pps.nalData, 4, pps.nalLength);
sampleReader.putPps(ppsData);
pps.reset();
}
......
......@@ -243,10 +243,20 @@ public final class H265Reader implements ElementaryStreamReader {
bitArray.skipBits(40 + 4); // NAL header, sps_video_parameter_set_id
int maxSubLayersMinus1 = bitArray.readBits(3);
bitArray.skipBit(); // sps_temporal_id_nesting_flag
// profile_tier_level(1, sps_max_sub_layers_minus1)
bitArray.skipBits(88); // if (profilePresentFlag) {...}
bitArray.skipBits(8); // general_level_idc
int generalProfileSpace = bitArray.readBits(2);
boolean generalTierFlag = bitArray.readBit();
int generalProfileIdc = bitArray.readBits(5);
int generalProfileCompatibilityFlags = 0;
for (int i = 0; i < 32; i++) {
if (bitArray.readBit()) {
generalProfileCompatibilityFlags |= (1 << i);
}
}
int[] constraintBytes = new int[6];
for (int i = 0; i < constraintBytes.length; ++i) {
constraintBytes[i] = bitArray.readBits(8);
}
int generalLevelIdc = bitArray.readBits(8);
int toSkip = 0;
for (int i = 0; i < maxSubLayersMinus1; i++) {
if (bitArray.readBit()) { // sub_layer_profile_present_flag[i]
......@@ -359,7 +369,9 @@ public final class H265Reader implements ElementaryStreamReader {
// Parse the SPS to derive an RFC 6381 codecs string.
bitArray.reset(sps.nalData, 0, sps.nalLength);
bitArray.skipBits(24); // Skip start code.
String codecs = CodecSpecificDataUtil.buildHevcCodecStringFromSps(bitArray);
String codecs = CodecSpecificDataUtil.buildHevcCodecString(
generalProfileSpace, generalTierFlag, generalProfileIdc,
generalProfileCompatibilityFlags, constraintBytes, generalLevelIdc);
return new Format.Builder()
.setId(formatId)
......
......@@ -66,9 +66,8 @@ public final class AvcConfig {
@Nullable String codecs = null;
if (numSequenceParameterSets > 0) {
byte[] sps = initializationData.get(0);
SpsData spsData =
NalUnitUtil.parseSpsNalUnit(
initializationData.get(0), nalUnitLengthFieldLength, sps.length);
SpsData spsData = NalUnitUtil.parseSpsNalUnitPayload(sps,
nalUnitLengthFieldLength + 1, sps.length);
width = spsData.width;
height = spsData.height;
pixelWidthAspectRatio = spsData.pixelWidthAspectRatio;
......
......@@ -16,11 +16,11 @@
package com.google.android.exoplayer2.video;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
import com.google.android.exoplayer2.util.NalUnitUtil;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.ParsableNalUnitBitArray;
import java.util.Collections;
import java.util.List;
......@@ -58,6 +58,9 @@ public final class HevcConfig {
data.setPosition(csdStartPosition);
byte[] buffer = new byte[csdLength];
int bufferPosition = 0;
int width = Format.NO_VALUE;
int height = Format.NO_VALUE;
float pixelWidthAspectRatio = 1;
@Nullable String codecs = null;
for (int i = 0; i < numberOfArrays; i++) {
int nalUnitType = data.readUnsignedByte() & 0x7F; // completeness (1), nal_unit_type (7)
......@@ -74,12 +77,16 @@ public final class HevcConfig {
System.arraycopy(
data.getData(), data.getPosition(), buffer, bufferPosition, nalUnitLength);
if (nalUnitType == SPS_NAL_UNIT_TYPE && j == 0) {
ParsableNalUnitBitArray bitArray =
new ParsableNalUnitBitArray(
buffer,
/* offset= */ bufferPosition,
/* limit= */ bufferPosition + nalUnitLength);
codecs = CodecSpecificDataUtil.buildHevcCodecStringFromSps(bitArray);
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnitPayload(
buffer, bufferPosition + 1, bufferPosition + nalUnitLength);
width = spsData.picWidthInLumaSamples;
height = spsData.picHeightInLumaSamples;
pixelWidthAspectRatio = spsData.pixelWidthHeightRatio;
codecs = CodecSpecificDataUtil.buildHevcCodecString(spsData.generalProfileSpace,
spsData.generalTierFlag, spsData.generalProfileIdc,
spsData.generalProfileCompatibilityFlags, spsData.constraintBytes,
spsData.generalLevelIdc);
}
bufferPosition += nalUnitLength;
data.skipBytes(nalUnitLength);
......@@ -88,7 +95,13 @@ public final class HevcConfig {
@Nullable
List<byte[]> initializationData = csdLength == 0 ? null : Collections.singletonList(buffer);
return new HevcConfig(initializationData, lengthSizeMinusOne + 1, codecs);
return new HevcConfig(
initializationData,
lengthSizeMinusOne + 1,
width,
height,
pixelWidthAspectRatio,
codecs);
} catch (ArrayIndexOutOfBoundsException e) {
throw ParserException.createForMalformedContainer("Error parsing HEVC config", e);
}
......@@ -105,6 +118,9 @@ public final class HevcConfig {
@Nullable public final List<byte[]> initializationData;
/** The length of the NAL unit length field in the bitstream's container, in bytes. */
public final int nalUnitLengthFieldLength;
public final int width;
public final int height;
public final float pixelWidthHeightRatio;
/**
* An RFC 6381 codecs string representing the video format, or {@code null} if not known.
*
......@@ -115,9 +131,15 @@ public final class HevcConfig {
private HevcConfig(
@Nullable List<byte[]> initializationData,
int nalUnitLengthFieldLength,
int width,
int height,
float pixelWidthAspectRatio,
@Nullable String codecs) {
this.initializationData = initializationData;
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
this.width = width;
this.height = height;
this.pixelWidthHeightRatio = pixelWidthAspectRatio;
this.codecs = codecs;
}
}
......@@ -183,8 +183,8 @@ import com.google.common.collect.ImmutableMap;
// Process SPS (Sequence Parameter Set).
byte[] spsNalDataWithStartCode = initializationData.get(0);
NalUnitUtil.SpsData spsData =
NalUnitUtil.parseSpsNalUnit(
spsNalDataWithStartCode, NAL_START_CODE.length, spsNalDataWithStartCode.length);
NalUnitUtil.parseSpsNalUnitPayload(
spsNalDataWithStartCode, NAL_START_CODE.length + 1, spsNalDataWithStartCode.length);
formatBuilder.setPixelWidthHeightRatio(spsData.pixelWidthAspectRatio);
formatBuilder.setHeight(spsData.height);
formatBuilder.setWidth(spsData.width);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment