Add NAL unit parsing needed for stereo MV-HEVC playback.

Add the following NAL unit parsing utility functions that will be needed for the MV-HEVC support as proposed in Apple's HEVC stereo video interoperability profile:
- NAL unit header parsing to get the layer information needed for MV-HEVC support.
- VPS parsing, including vps_extension() needed for MV-HEVC support.
- SPS parsing modifications to support MV-HEVC.

PiperOrigin-RevId: 647329211
This commit is contained in:
Googler 2024-06-27 08:36:25 -07:00 committed by Copybara-Service
parent 304bcfc852
commit 3da63eeaa7
7 changed files with 1463 additions and 132 deletions

View File

@ -92,6 +92,13 @@ public final class ParsableNalUnitBitArray {
assertValidOffset();
}
/** Skips bits until at a byte alignment; if already byte aligned, then simply returns. */
public void byteAlign() {
if (bitOffset > 0) {
skipBits(8 - bitOffset);
}
}
/**
* Returns whether it's possible to read {@code n} bits starting from the current offset. The
* offset is not modified.

View File

@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.media3.common.util.Util;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.junit.Test;
@ -37,6 +38,58 @@ public final class NalUnitUtilTest {
0x91, 0x00, 0x00, 0x7E, 0xA0);
private static final int SPS_TEST_DATA_OFFSET = 3;
// Below are H.265 VPS and SPS samples obtained from the "24-bit big endian raw audio LPCM
// (MP4,H265,raw)" clip in the ExoPlayer sample.
private static final byte[] H265_VPS_TEST_DATA =
createByteArray(
0x40, 0x01, 0x0C, 0x01, 0xFF, 0xFF, 0x22, 0x20, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x99, 0x2C, 0x09);
private static final byte[] H265_SPS_TEST_DATA =
createByteArray(
0x42, 0x01, 0x01, 0x22, 0x20, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x99, 0xA0, 0x01, 0xE0, 0x20, 0x02, 0x1C, 0x4D, 0x94, 0xBB, 0xB4, 0xA3, 0x32,
0xAA, 0xC0, 0x5A, 0x84, 0x89, 0x04, 0x8A, 0x00, 0x00, 0x07, 0xD2, 0x00, 0x00, 0xBB, 0x80,
0xE4, 0x68, 0x7C, 0x9C, 0x00, 0x01, 0x2E, 0x1F, 0x80, 0x00, 0x21, 0xFD, 0x30, 0x00, 0x02,
0x5C, 0x3F, 0x00, 0x00, 0x43, 0xFA, 0x62);
// Below are MV-HEVC VPS and SPS samples obtained from the two sample clips in b/40937818.
private static final byte[] H265_VPS_TEST_DATA_2VIEWS =
createByteArray(
0x40, 0x01, 0x0C, 0x11, 0xFF, 0xFF, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x78, 0x15, 0xC1, 0x5B, 0x00, 0x20, 0x00, 0x28, 0x24, 0xC1,
0x97, 0x06, 0x02, 0x00, 0x00, 0x03, 0x00, 0xBF, 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x78,
0x8D, 0x07, 0x80, 0x04, 0x40, 0xA0, 0x1E, 0x5C, 0x52, 0xBF, 0x48);
private static final byte[] H265_SPS_TEST_DATA_2VIEWS_VIEW_0 =
createByteArray(
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x78, 0xA0, 0x03, 0xC0, 0x80, 0x11, 0x07, 0xCB, 0x88, 0x15, 0xEE, 0x45, 0x95,
0x4D, 0x40, 0x40, 0x40, 0x40, 0x20);
private static final byte[] H265_SPS_TEST_DATA_2VIEWS_VIEW_1 =
createByteArray(0x42, 0x09, 0x0E, 0x82, 0x2E, 0x45, 0x8A, 0xA0, 0x05, 0x01);
private static final byte[] H265_VPS_TEST_DATA_2VIEWS_HDR =
createByteArray(
0x40, 0x01, 0x0C, 0x11, 0xFF, 0xFF, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x00, 0x00,
0x03, 0x00, 0x00, 0x03, 0x00, 0x99, 0x98, 0xA3, 0x41, 0x5C, 0x00, 0x00, 0x0F, 0xA4, 0x00,
0x03, 0xA9, 0x83, 0xFF, 0x99, 0x20, 0x00, 0x21, 0x16, 0x93, 0x93, 0x11, 0x00, 0x00, 0x03,
0x00, 0x5E, 0xC4, 0x00, 0x00, 0x03, 0x00, 0x00, 0x4C, 0xC6, 0x87, 0x80, 0x04, 0x38, 0x52,
0x24, 0x31, 0x8A, 0x3B, 0xA4, 0x80);
private static final byte[] H265_SPS_TEST_DATA_2VIEWS_HDR_VIEW_0 =
createByteArray(
0x42, 0x01, 0x01, 0x02, 0x20, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x00, 0x00, 0x03, 0x00, 0x00,
0x03, 0x00, 0x99, 0xA0, 0x01, 0xE0, 0x20, 0x02, 0x1C, 0x4D, 0x94, 0x62, 0x8D, 0x92, 0x42,
0x97, 0x55, 0x58, 0x43, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x18, 0x82, 0x8D,
0x08, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x91, 0x88, 0x28, 0xD0, 0x87, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF9, 0x2B, 0xC0, 0x41, 0x40, 0x00, 0x00, 0xFA, 0x40,
0x00, 0x3A, 0x98, 0x3C, 0x24, 0x82, 0x4D, 0xC0, 0x00, 0x26, 0x25, 0xA0, 0x00, 0x13, 0x12,
0xDF, 0xC4, 0xC7, 0x8F, 0x40);
private static final byte[] H265_SPS_TEST_DATA_2VIEWS_HDR_VIEW_1 =
createByteArray(
0x42, 0x09, 0x0E, 0x85, 0x92, 0x42, 0x96, 0xAA, 0xAC, 0x21, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFC, 0x8C, 0x41, 0x46, 0x84, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xC8, 0xC4, 0x14, 0x68, 0x43, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x95, 0xA8,
0x18);
@Test
public void findNalUnit() {
byte[] data = buildTestData();
@ -143,6 +196,172 @@ public final class NalUnitUtilTest {
assertThat(data.maxNumReorderFrames).isEqualTo(1);
}
@Test
public void parseH265VpsAndSpsNalUnits() {
NalUnitUtil.H265VpsData vpsData =
NalUnitUtil.parseH265VpsNalUnit(
H265_VPS_TEST_DATA, /* nalOffset= */ 0, H265_VPS_TEST_DATA.length);
assertThat(vpsData.nalHeader.layerId).isEqualTo(0);
assertThat(vpsData.layerInfos).isEmpty();
ImmutableList<NalUnitUtil.H265ProfileTierLevel> profileTierLevels =
vpsData.profileTierLevelsAndIndices.profileTierLevels;
assertThat(profileTierLevels).hasSize(1);
assertThat(profileTierLevels.get(0).generalProfileIdc).isEqualTo(2);
assertThat(profileTierLevels.get(0).generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(profileTierLevels.get(0).generalLevelIdc).isEqualTo(153);
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnit(
H265_SPS_TEST_DATA, /* nalOffset= */ 0, H265_SPS_TEST_DATA.length, vpsData);
assertThat(spsData.nalHeader.layerId).isEqualTo(0);
assertThat(spsData.profileTierLevel.generalProfileIdc).isEqualTo(2);
assertThat(spsData.profileTierLevel.generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(spsData.profileTierLevel.generalLevelIdc).isEqualTo(153);
assertThat(spsData.chromaFormatIdc).isEqualTo(1);
assertThat(spsData.width).isEqualTo(3840);
assertThat(spsData.height).isEqualTo(2160);
assertThat(spsData.bitDepthLumaMinus8).isEqualTo(2);
assertThat(spsData.bitDepthChromaMinus8).isEqualTo(2);
assertThat(spsData.colorSpace).isEqualTo(6);
assertThat(spsData.colorRange).isEqualTo(2);
assertThat(spsData.colorTransfer).isEqualTo(7);
}
@Test
public void parseH265VpsAndSpsNalUnits2Views() {
NalUnitUtil.H265VpsData vpsData =
NalUnitUtil.parseH265VpsNalUnit(
H265_VPS_TEST_DATA_2VIEWS, /* nalOffset= */ 0, H265_VPS_TEST_DATA_2VIEWS.length);
assertThat(vpsData.nalHeader.layerId).isEqualTo(0);
ImmutableList<NalUnitUtil.H265LayerInfo> layerInfos = vpsData.layerInfos;
assertThat(layerInfos).hasSize(2);
assertThat(layerInfos.get(0).layerIdInVps).isEqualTo(0);
assertThat(layerInfos.get(0).viewId).isEqualTo(0);
assertThat(layerInfos.get(1).layerIdInVps).isEqualTo(1);
assertThat(layerInfos.get(1).viewId).isEqualTo(1);
ImmutableList<NalUnitUtil.H265ProfileTierLevel> profileTierLevels =
vpsData.profileTierLevelsAndIndices.profileTierLevels;
assertThat(profileTierLevels).hasSize(3);
assertThat(profileTierLevels.get(0).generalProfileIdc).isEqualTo(1);
assertThat(profileTierLevels.get(0).generalProfileCompatibilityFlags).isEqualTo(6);
assertThat(profileTierLevels.get(0).generalLevelIdc).isEqualTo(120);
assertThat(profileTierLevels.get(1).generalProfileIdc).isEqualTo(1);
assertThat(profileTierLevels.get(1).generalProfileCompatibilityFlags).isEqualTo(6);
assertThat(profileTierLevels.get(1).generalLevelIdc).isEqualTo(0);
assertThat(profileTierLevels.get(2).generalProfileIdc).isEqualTo(6);
assertThat(profileTierLevels.get(2).generalProfileCompatibilityFlags).isEqualTo(64);
assertThat(profileTierLevels.get(2).generalLevelIdc).isEqualTo(120);
ImmutableList<NalUnitUtil.H265RepFormat> repFormats = vpsData.repFormatsAndIndices.repFormats;
assertThat(repFormats).hasSize(1);
assertThat(repFormats.get(0).chromaFormatIdc).isEqualTo(1);
assertThat(repFormats.get(0).width).isEqualTo(1920);
assertThat(repFormats.get(0).height).isEqualTo(1080);
assertThat(repFormats.get(0).bitDepthLumaMinus8).isEqualTo(0);
assertThat(repFormats.get(0).bitDepthChromaMinus8).isEqualTo(0);
NalUnitUtil.H265SpsData spsDataView0 =
NalUnitUtil.parseH265SpsNalUnit(
H265_SPS_TEST_DATA_2VIEWS_VIEW_0,
/* nalOffset= */ 0,
H265_SPS_TEST_DATA_2VIEWS_VIEW_0.length,
vpsData);
assertThat(spsDataView0.nalHeader.layerId).isEqualTo(0);
assertThat(spsDataView0.profileTierLevel.generalProfileIdc).isEqualTo(1);
assertThat(spsDataView0.profileTierLevel.generalProfileCompatibilityFlags).isEqualTo(6);
assertThat(spsDataView0.profileTierLevel.generalLevelIdc).isEqualTo(120);
assertThat(spsDataView0.chromaFormatIdc).isEqualTo(1);
assertThat(spsDataView0.width).isEqualTo(1920);
assertThat(spsDataView0.height).isEqualTo(1080);
assertThat(spsDataView0.bitDepthLumaMinus8).isEqualTo(0);
assertThat(spsDataView0.bitDepthChromaMinus8).isEqualTo(0);
assertThat(spsDataView0.colorSpace).isEqualTo(1);
assertThat(spsDataView0.colorRange).isEqualTo(2);
assertThat(spsDataView0.colorTransfer).isEqualTo(3);
NalUnitUtil.H265SpsData spsDataView1 =
NalUnitUtil.parseH265SpsNalUnit(
H265_SPS_TEST_DATA_2VIEWS_VIEW_1,
/* nalOffset= */ 0,
H265_SPS_TEST_DATA_2VIEWS_VIEW_1.length,
vpsData);
assertThat(spsDataView1.nalHeader.layerId).isEqualTo(1);
assertThat(spsDataView1.profileTierLevel.generalProfileIdc).isEqualTo(6);
assertThat(spsDataView1.profileTierLevel.generalProfileCompatibilityFlags).isEqualTo(64);
assertThat(spsDataView1.profileTierLevel.generalLevelIdc).isEqualTo(120);
assertThat(spsDataView1.chromaFormatIdc).isEqualTo(1);
assertThat(spsDataView1.width).isEqualTo(1920);
assertThat(spsDataView1.height).isEqualTo(1080);
assertThat(spsDataView1.bitDepthLumaMinus8).isEqualTo(0);
assertThat(spsDataView1.bitDepthChromaMinus8).isEqualTo(0);
}
@Test
public void parseH265VpsAndSpsNalUnits2ViewsHdr() {
NalUnitUtil.H265VpsData vpsData =
NalUnitUtil.parseH265VpsNalUnit(
H265_VPS_TEST_DATA_2VIEWS_HDR,
/* nalOffset= */ 0,
H265_VPS_TEST_DATA_2VIEWS_HDR.length);
assertThat(vpsData.nalHeader.layerId).isEqualTo(0);
ImmutableList<NalUnitUtil.H265LayerInfo> layerInfos = vpsData.layerInfos;
assertThat(layerInfos).hasSize(2);
assertThat(layerInfos.get(0).layerIdInVps).isEqualTo(0);
assertThat(layerInfos.get(0).viewId).isEqualTo(0);
assertThat(layerInfos.get(1).layerIdInVps).isEqualTo(1);
assertThat(layerInfos.get(1).viewId).isEqualTo(1);
ImmutableList<NalUnitUtil.H265ProfileTierLevel> profileTierLevels =
vpsData.profileTierLevelsAndIndices.profileTierLevels;
assertThat(profileTierLevels).hasSize(3);
assertThat(profileTierLevels.get(0).generalProfileIdc).isEqualTo(2);
assertThat(profileTierLevels.get(0).generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(profileTierLevels.get(0).generalLevelIdc).isEqualTo(153);
assertThat(profileTierLevels.get(1).generalProfileIdc).isEqualTo(2);
assertThat(profileTierLevels.get(1).generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(profileTierLevels.get(1).generalLevelIdc).isEqualTo(153);
assertThat(profileTierLevels.get(2).generalProfileIdc).isEqualTo(6);
assertThat(profileTierLevels.get(2).generalProfileCompatibilityFlags).isEqualTo(68);
assertThat(profileTierLevels.get(2).generalLevelIdc).isEqualTo(153);
ImmutableList<NalUnitUtil.H265RepFormat> repFormats = vpsData.repFormatsAndIndices.repFormats;
assertThat(repFormats).hasSize(1);
assertThat(repFormats.get(0).chromaFormatIdc).isEqualTo(1);
assertThat(repFormats.get(0).width).isEqualTo(3840);
assertThat(repFormats.get(0).height).isEqualTo(2160);
assertThat(repFormats.get(0).bitDepthLumaMinus8).isEqualTo(2);
assertThat(repFormats.get(0).bitDepthChromaMinus8).isEqualTo(2);
NalUnitUtil.H265SpsData spsDataView0 =
NalUnitUtil.parseH265SpsNalUnit(
H265_SPS_TEST_DATA_2VIEWS_HDR_VIEW_0,
/* nalOffset= */ 0,
H265_SPS_TEST_DATA_2VIEWS_HDR_VIEW_0.length,
vpsData);
assertThat(spsDataView0.nalHeader.layerId).isEqualTo(0);
assertThat(spsDataView0.profileTierLevel.generalProfileIdc).isEqualTo(2);
assertThat(spsDataView0.profileTierLevel.generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(spsDataView0.profileTierLevel.generalLevelIdc).isEqualTo(153);
assertThat(spsDataView0.chromaFormatIdc).isEqualTo(1);
assertThat(spsDataView0.width).isEqualTo(3840);
assertThat(spsDataView0.height).isEqualTo(2160);
assertThat(spsDataView0.bitDepthLumaMinus8).isEqualTo(2);
assertThat(spsDataView0.bitDepthChromaMinus8).isEqualTo(2);
NalUnitUtil.H265SpsData spsDataView1 =
NalUnitUtil.parseH265SpsNalUnit(
H265_SPS_TEST_DATA_2VIEWS_HDR_VIEW_1,
/* nalOffset= */ 0,
H265_SPS_TEST_DATA_2VIEWS_HDR_VIEW_1.length,
vpsData);
assertThat(spsDataView1.nalHeader.layerId).isEqualTo(1);
assertThat(spsDataView1.profileTierLevel.generalProfileIdc).isEqualTo(6);
assertThat(spsDataView1.profileTierLevel.generalProfileCompatibilityFlags).isEqualTo(68);
assertThat(spsDataView1.profileTierLevel.generalLevelIdc).isEqualTo(153);
assertThat(spsDataView1.chromaFormatIdc).isEqualTo(1);
assertThat(spsDataView1.width).isEqualTo(3840);
assertThat(spsDataView1.height).isEqualTo(2160);
assertThat(spsDataView1.bitDepthLumaMinus8).isEqualTo(2);
assertThat(spsDataView1.bitDepthChromaMinus8).isEqualTo(2);
}
@Test
public void unescapeDoesNotModifyBuffersWithoutStartCodes() {
assertUnescapeDoesNotModify("");
@ -185,15 +404,18 @@ public final class NalUnitUtilTest {
-128, 28, 120, 1, -57, 0, 56, -15
};
NalUnitUtil.H265NalHeader nalHeader =
new NalUnitUtil.H265NalHeader(NalUnitUtil.H265_NAL_UNIT_TYPE_SPS, 0, 0);
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnitPayload(spsNalUnitPayload, 0, spsNalUnitPayload.length);
NalUnitUtil.parseH265SpsNalUnitPayload(
spsNalUnitPayload, 0, spsNalUnitPayload.length, nalHeader, null);
assertThat(spsData.constraintBytes).isEqualTo(new int[] {144, 0, 0, 0, 0, 0});
assertThat(spsData.generalLevelIdc).isEqualTo(150);
assertThat(spsData.generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(spsData.generalProfileIdc).isEqualTo(2);
assertThat(spsData.generalProfileSpace).isEqualTo(0);
assertThat(spsData.generalTierFlag).isFalse();
assertThat(spsData.profileTierLevel.constraintBytes).isEqualTo(new int[] {144, 0, 0, 0, 0, 0});
assertThat(spsData.profileTierLevel.generalLevelIdc).isEqualTo(150);
assertThat(spsData.profileTierLevel.generalProfileCompatibilityFlags).isEqualTo(4);
assertThat(spsData.profileTierLevel.generalProfileIdc).isEqualTo(2);
assertThat(spsData.profileTierLevel.generalProfileSpace).isEqualTo(0);
assertThat(spsData.profileTierLevel.generalTierFlag).isFalse();
assertThat(spsData.height).isEqualTo(2160);
assertThat(spsData.pixelWidthHeightRatio).isEqualTo(1);
assertThat(spsData.seqParameterSetId).isEqualTo(0);
@ -214,15 +436,18 @@ public final class NalUnitUtilTest {
8
};
NalUnitUtil.H265NalHeader nalHeader =
new NalUnitUtil.H265NalHeader(NalUnitUtil.H265_NAL_UNIT_TYPE_SPS, 0, 0);
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnitPayload(spsNalUnitPayload, 0, spsNalUnitPayload.length);
NalUnitUtil.parseH265SpsNalUnitPayload(
spsNalUnitPayload, 0, spsNalUnitPayload.length, nalHeader, null);
assertThat(spsData.constraintBytes).isEqualTo(new int[] {0, 0, 0, 0, 0, 0});
assertThat(spsData.generalLevelIdc).isEqualTo(150);
assertThat(spsData.generalProfileCompatibilityFlags).isEqualTo(6);
assertThat(spsData.generalProfileIdc).isEqualTo(2);
assertThat(spsData.generalProfileSpace).isEqualTo(0);
assertThat(spsData.generalTierFlag).isFalse();
assertThat(spsData.profileTierLevel.constraintBytes).isEqualTo(new int[] {0, 0, 0, 0, 0, 0});
assertThat(spsData.profileTierLevel.generalLevelIdc).isEqualTo(150);
assertThat(spsData.profileTierLevel.generalProfileCompatibilityFlags).isEqualTo(6);
assertThat(spsData.profileTierLevel.generalProfileIdc).isEqualTo(2);
assertThat(spsData.profileTierLevel.generalProfileSpace).isEqualTo(0);
assertThat(spsData.profileTierLevel.generalTierFlag).isFalse();
assertThat(spsData.width).isEqualTo(1080);
assertThat(spsData.height).isEqualTo(1920);
assertThat(spsData.pixelWidthHeightRatio).isEqualTo(1);

View File

@ -467,7 +467,10 @@ import com.google.common.collect.ImmutableMap;
byte[] spsNalDataWithStartCode = initializationData.get(1);
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnit(
spsNalDataWithStartCode, NAL_START_CODE.length, spsNalDataWithStartCode.length);
spsNalDataWithStartCode,
NAL_START_CODE.length,
spsNalDataWithStartCode.length,
/* vpsData= */ null);
formatBuilder.setPixelWidthHeightRatio(spsData.pixelWidthHeightRatio);
formatBuilder.setHeight(spsData.height).setWidth(spsData.width);
formatBuilder.setColorInfo(
@ -479,14 +482,16 @@ import com.google.common.collect.ImmutableMap;
.setChromaBitdepth(spsData.bitDepthChromaMinus8 + 8)
.build());
formatBuilder.setCodecs(
CodecSpecificDataUtil.buildHevcCodecString(
spsData.generalProfileSpace,
spsData.generalTierFlag,
spsData.generalProfileIdc,
spsData.generalProfileCompatibilityFlags,
spsData.constraintBytes,
spsData.generalLevelIdc));
if (spsData.profileTierLevel != null) {
formatBuilder.setCodecs(
CodecSpecificDataUtil.buildHevcCodecString(
spsData.profileTierLevel.generalProfileSpace,
spsData.profileTierLevel.generalTierFlag,
spsData.profileTierLevel.generalProfileIdc,
spsData.profileTierLevel.generalProfileCompatibilityFlags,
spsData.profileTierLevel.constraintBytes,
spsData.profileTierLevel.generalLevelIdc));
}
}
/**

View File

@ -89,7 +89,7 @@ public final class HevcConfig {
if (nalUnitType == SPS_NAL_UNIT_TYPE && j == 0) {
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnit(
buffer, bufferPosition, bufferPosition + nalUnitLength);
buffer, bufferPosition, bufferPosition + nalUnitLength, null);
width = spsData.width;
height = spsData.height;
bitdepthLuma = spsData.bitDepthLumaMinus8 + 8;
@ -100,14 +100,16 @@ public final class HevcConfig {
pixelWidthHeightRatio = spsData.pixelWidthHeightRatio;
maxNumReorderPics = spsData.maxNumReorderPics;
codecs =
CodecSpecificDataUtil.buildHevcCodecString(
spsData.generalProfileSpace,
spsData.generalTierFlag,
spsData.generalProfileIdc,
spsData.generalProfileCompatibilityFlags,
spsData.constraintBytes,
spsData.generalLevelIdc);
if (spsData.profileTierLevel != null) {
codecs =
CodecSpecificDataUtil.buildHevcCodecString(
spsData.profileTierLevel.generalProfileSpace,
spsData.profileTierLevel.generalTierFlag,
spsData.profileTierLevel.generalProfileIdc,
spsData.profileTierLevel.generalProfileCompatibilityFlags,
spsData.profileTierLevel.constraintBytes,
spsData.profileTierLevel.generalLevelIdc);
}
}
bufferPosition += nalUnitLength;
data.skipBytes(nalUnitLength);

View File

@ -246,17 +246,20 @@ public final class H265Reader implements ElementaryStreamReader {
// Skip the 3-byte NAL unit start code synthesised by the NalUnitTargetBuffer constructor.
NalUnitUtil.H265SpsData spsData =
NalUnitUtil.parseH265SpsNalUnit(sps.nalData, /* nalOffset= */ 3, sps.nalLength);
String codecs =
CodecSpecificDataUtil.buildHevcCodecString(
spsData.generalProfileSpace,
spsData.generalTierFlag,
spsData.generalProfileIdc,
spsData.generalProfileCompatibilityFlags,
spsData.constraintBytes,
spsData.generalLevelIdc);
NalUnitUtil.parseH265SpsNalUnit(
sps.nalData, /* nalOffset= */ 3, sps.nalLength, /* vpsData= */ null);
@Nullable String codecs = null;
if (spsData.profileTierLevel != null) {
codecs =
CodecSpecificDataUtil.buildHevcCodecString(
spsData.profileTierLevel.generalProfileSpace,
spsData.profileTierLevel.generalTierFlag,
spsData.profileTierLevel.generalProfileIdc,
spsData.profileTierLevel.generalProfileCompatibilityFlags,
spsData.profileTierLevel.constraintBytes,
spsData.profileTierLevel.generalLevelIdc);
}
return new Format.Builder()
.setId(formatId)
.setSampleMimeType(MimeTypes.VIDEO_H265)

View File

@ -1202,7 +1202,7 @@ import java.util.List;
NalUnitUtil.H265SpsData h265SpsData =
NalUnitUtil.parseH265SpsNalUnit(
spsArray, /* nalOffset= */ 0, /* nalLimit= */ spsArray.length);
spsArray, /* nalOffset= */ 0, /* nalLimit= */ spsArray.length, /* vpsData= */ null);
byte chromaFormat = (byte) (0xFC | h265SpsData.chromaFormatIdc); // First 6 bits reserved
byte bitDepthLumaMinus8 =