Merge pull request #9421 from MarcusWichelmann:hevc-sps-parsing
PiperOrigin-RevId: 398749045
This commit is contained in:
commit
9108dc5b0e
@ -27,6 +27,9 @@
|
|||||||
* `SubtitleView` no longer implements `TextOutput`. `SubtitleView`
|
* `SubtitleView` no longer implements `TextOutput`. `SubtitleView`
|
||||||
implements `Player.Listener`, so can be registered to a player with
|
implements `Player.Listener`, so can be registered to a player with
|
||||||
`Player.addListener`.
|
`Player.addListener`.
|
||||||
|
* Extractors:
|
||||||
|
* MP4: Correctly handle HEVC tracks with pixel aspect ratios other than 1.
|
||||||
|
* TS: Correctly handle HEVC tracks with pixel aspect ratios other than 1.
|
||||||
* Downloads and caching:
|
* Downloads and caching:
|
||||||
* Modify `DownloadService` behavior when `DownloadService.getScheduler`
|
* Modify `DownloadService` behavior when `DownloadService.getScheduler`
|
||||||
returns `null`, or returns a `Scheduler` that does not support the
|
returns `null`, or returns a `Scheduler` that does not support the
|
||||||
|
@ -85,29 +85,14 @@ public final class CodecSpecificDataUtil {
|
|||||||
"avc1.%02X%02X%02X", profileIdc, constraintsFlagsAndReservedZero2Bits, levelIdc);
|
"avc1.%02X%02X%02X", profileIdc, constraintsFlagsAndReservedZero2Bits, levelIdc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Builds an RFC 6381 HEVC codec string using the provided parameters. */
|
||||||
* Returns an RFC 6381 HEVC codec string based on the SPS NAL unit read from the provided bit
|
public static String buildHevcCodecString(
|
||||||
* array. The position of the bit array must be the start of an SPS NALU (nal_unit_header), and
|
int generalProfileSpace,
|
||||||
* the position may be modified by this method.
|
boolean generalTierFlag,
|
||||||
*/
|
int generalProfileIdc,
|
||||||
public static String buildHevcCodecStringFromSps(ParsableNalUnitBitArray bitArray) {
|
int generalProfileCompatibilityFlags,
|
||||||
// Skip nal_unit_header, sps_video_parameter_set_id, sps_max_sub_layers_minus1 and
|
int[] constraintBytes,
|
||||||
// sps_temporal_id_nesting_flag.
|
int generalLevelIdc) {
|
||||||
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);
|
|
||||||
StringBuilder builder =
|
StringBuilder builder =
|
||||||
new StringBuilder(
|
new StringBuilder(
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.util;
|
package com.google.android.exoplayer2.util;
|
||||||
|
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -24,7 +26,7 @@ public final class NalUnitUtil {
|
|||||||
|
|
||||||
private static final String TAG = "NalUnitUtil";
|
private static final String TAG = "NalUnitUtil";
|
||||||
|
|
||||||
/** Holds data parsed from a sequence parameter set NAL unit. */
|
/** Holds data parsed from a H.264 sequence parameter set NAL unit. */
|
||||||
public static final class SpsData {
|
public static final class SpsData {
|
||||||
|
|
||||||
public final int profileIdc;
|
public final int profileIdc;
|
||||||
@ -33,7 +35,7 @@ public final class NalUnitUtil {
|
|||||||
public final int seqParameterSetId;
|
public final int seqParameterSetId;
|
||||||
public final int width;
|
public final int width;
|
||||||
public final int height;
|
public final int height;
|
||||||
public final float pixelWidthAspectRatio;
|
public final float pixelWidthHeightRatio;
|
||||||
public final boolean separateColorPlaneFlag;
|
public final boolean separateColorPlaneFlag;
|
||||||
public final boolean frameMbsOnlyFlag;
|
public final boolean frameMbsOnlyFlag;
|
||||||
public final int frameNumLength;
|
public final int frameNumLength;
|
||||||
@ -48,7 +50,7 @@ public final class NalUnitUtil {
|
|||||||
int seqParameterSetId,
|
int seqParameterSetId,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
float pixelWidthAspectRatio,
|
float pixelWidthHeightRatio,
|
||||||
boolean separateColorPlaneFlag,
|
boolean separateColorPlaneFlag,
|
||||||
boolean frameMbsOnlyFlag,
|
boolean frameMbsOnlyFlag,
|
||||||
int frameNumLength,
|
int frameNumLength,
|
||||||
@ -61,7 +63,7 @@ public final class NalUnitUtil {
|
|||||||
this.seqParameterSetId = seqParameterSetId;
|
this.seqParameterSetId = seqParameterSetId;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
this.separateColorPlaneFlag = separateColorPlaneFlag;
|
this.separateColorPlaneFlag = separateColorPlaneFlag;
|
||||||
this.frameMbsOnlyFlag = frameMbsOnlyFlag;
|
this.frameMbsOnlyFlag = frameMbsOnlyFlag;
|
||||||
this.frameNumLength = frameNumLength;
|
this.frameNumLength = frameNumLength;
|
||||||
@ -71,6 +73,44 @@ public final class NalUnitUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Holds data parsed from a H.265 sequence parameter set NAL unit. */
|
||||||
|
public static final class H265SpsData {
|
||||||
|
|
||||||
|
public final int generalProfileSpace;
|
||||||
|
public final boolean generalTierFlag;
|
||||||
|
public final int generalProfileIdc;
|
||||||
|
public final int generalProfileCompatibilityFlags;
|
||||||
|
public final int[] constraintBytes;
|
||||||
|
public final int generalLevelIdc;
|
||||||
|
public final int seqParameterSetId;
|
||||||
|
public final int width;
|
||||||
|
public final int height;
|
||||||
|
public final float pixelWidthHeightRatio;
|
||||||
|
|
||||||
|
public H265SpsData(
|
||||||
|
int generalProfileSpace,
|
||||||
|
boolean generalTierFlag,
|
||||||
|
int generalProfileIdc,
|
||||||
|
int generalProfileCompatibilityFlags,
|
||||||
|
int[] constraintBytes,
|
||||||
|
int generalLevelIdc,
|
||||||
|
int seqParameterSetId,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
float pixelWidthHeightRatio) {
|
||||||
|
this.generalProfileSpace = generalProfileSpace;
|
||||||
|
this.generalTierFlag = generalTierFlag;
|
||||||
|
this.generalProfileIdc = generalProfileIdc;
|
||||||
|
this.generalProfileCompatibilityFlags = generalProfileCompatibilityFlags;
|
||||||
|
this.constraintBytes = constraintBytes;
|
||||||
|
this.generalLevelIdc = generalLevelIdc;
|
||||||
|
this.seqParameterSetId = seqParameterSetId;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Holds data parsed from a picture parameter set NAL unit. */
|
/** Holds data parsed from a picture parameter set NAL unit. */
|
||||||
public static final class PpsData {
|
public static final class PpsData {
|
||||||
|
|
||||||
@ -252,7 +292,7 @@ public final class NalUnitUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an SPS NAL unit using the syntax defined in ITU-T Recommendation H.264 (2013) subsection
|
* Parses a SPS NAL unit using the syntax defined in ITU-T Recommendation H.264 (2013) subsection
|
||||||
* 7.3.2.1.1.
|
* 7.3.2.1.1.
|
||||||
*
|
*
|
||||||
* @param nalData A buffer containing escaped SPS data.
|
* @param nalData A buffer containing escaped SPS data.
|
||||||
@ -261,8 +301,20 @@ public final class NalUnitUtil {
|
|||||||
* @return A parsed representation of the SPS data.
|
* @return A parsed representation of the SPS data.
|
||||||
*/
|
*/
|
||||||
public static SpsData parseSpsNalUnit(byte[] nalData, int nalOffset, int nalLimit) {
|
public static SpsData parseSpsNalUnit(byte[] nalData, int nalOffset, int nalLimit) {
|
||||||
|
return parseSpsNalUnitPayload(nalData, nalOffset + 1, nalLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a SPS NAL unit payload (excluding the NAL unit header) using the syntax defined in ITU-T
|
||||||
|
* Recommendation H.264 (2013) subsection 7.3.2.1.1.
|
||||||
|
*
|
||||||
|
* @param nalData A buffer containing escaped SPS data.
|
||||||
|
* @param nalOffset The offset of the NAL unit payload in {@code nalData}.
|
||||||
|
* @param nalLimit The limit of the NAL unit in {@code nalData}.
|
||||||
|
* @return A parsed representation of the SPS data.
|
||||||
|
*/
|
||||||
|
public static SpsData parseSpsNalUnitPayload(byte[] nalData, int nalOffset, int nalLimit) {
|
||||||
ParsableNalUnitBitArray data = new ParsableNalUnitBitArray(nalData, nalOffset, nalLimit);
|
ParsableNalUnitBitArray data = new ParsableNalUnitBitArray(nalData, nalOffset, nalLimit);
|
||||||
data.skipBits(8); // nal_unit
|
|
||||||
int profileIdc = data.readBits(8);
|
int profileIdc = data.readBits(8);
|
||||||
int constraintsFlagsAndReservedZero2Bits = data.readBits(8);
|
int constraintsFlagsAndReservedZero2Bits = data.readBits(8);
|
||||||
int levelIdc = data.readBits(8);
|
int levelIdc = data.readBits(8);
|
||||||
@ -386,6 +438,168 @@ public final class NalUnitUtil {
|
|||||||
deltaPicOrderAlwaysZeroFlag);
|
deltaPicOrderAlwaysZeroFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a H.265 SPS NAL unit using the syntax defined in ITU-T Recommendation H.265 (2019)
|
||||||
|
* subsection 7.3.2.2.1.
|
||||||
|
*
|
||||||
|
* @param nalData A buffer containing escaped SPS data.
|
||||||
|
* @param nalOffset The offset of the NAL unit header in {@code nalData}.
|
||||||
|
* @param nalLimit The limit of the NAL unit in {@code nalData}.
|
||||||
|
* @return A parsed representation of the SPS data.
|
||||||
|
*/
|
||||||
|
public static H265SpsData parseH265SpsNalUnit(byte[] nalData, int nalOffset, int nalLimit) {
|
||||||
|
return parseH265SpsNalUnitPayload(nalData, nalOffset + 2, nalLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a H.265 SPS NAL unit payload (excluding the NAL unit header) using the syntax defined in
|
||||||
|
* ITU-T Recommendation H.265 (2019) subsection 7.3.2.2.1.
|
||||||
|
*
|
||||||
|
* @param nalData A buffer containing escaped SPS data.
|
||||||
|
* @param nalOffset The offset of the NAL unit payload in {@code nalData}.
|
||||||
|
* @param nalLimit The limit of the NAL unit in {@code nalData}.
|
||||||
|
* @return A parsed representation of the SPS data.
|
||||||
|
*/
|
||||||
|
public static H265SpsData parseH265SpsNalUnitPayload(
|
||||||
|
byte[] nalData, int nalOffset, int nalLimit) {
|
||||||
|
ParsableNalUnitBitArray data = new ParsableNalUnitBitArray(nalData, nalOffset, nalLimit);
|
||||||
|
data.skipBits(4); // sps_video_parameter_set_id
|
||||||
|
int maxSubLayersMinus1 = data.readBits(3);
|
||||||
|
data.skipBit(); // sps_temporal_id_nesting_flag
|
||||||
|
int generalProfileSpace = data.readBits(2);
|
||||||
|
boolean generalTierFlag = data.readBit();
|
||||||
|
int generalProfileIdc = data.readBits(5);
|
||||||
|
int generalProfileCompatibilityFlags = 0;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (data.readBit()) {
|
||||||
|
generalProfileCompatibilityFlags |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int[] constraintBytes = new int[6];
|
||||||
|
for (int i = 0; i < constraintBytes.length; ++i) {
|
||||||
|
constraintBytes[i] = data.readBits(8);
|
||||||
|
}
|
||||||
|
int generalLevelIdc = data.readBits(8);
|
||||||
|
int toSkip = 0;
|
||||||
|
for (int i = 0; i < maxSubLayersMinus1; i++) {
|
||||||
|
if (data.readBit()) { // sub_layer_profile_present_flag[i]
|
||||||
|
toSkip += 89;
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // sub_layer_level_present_flag[i]
|
||||||
|
toSkip += 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.skipBits(toSkip);
|
||||||
|
if (maxSubLayersMinus1 > 0) {
|
||||||
|
data.skipBits(2 * (8 - maxSubLayersMinus1));
|
||||||
|
}
|
||||||
|
int seqParameterSetId = data.readUnsignedExpGolombCodedInt();
|
||||||
|
int chromaFormatIdc = data.readUnsignedExpGolombCodedInt();
|
||||||
|
if (chromaFormatIdc == 3) {
|
||||||
|
data.skipBit(); // separate_colour_plane_flag
|
||||||
|
}
|
||||||
|
int frameWidth = data.readUnsignedExpGolombCodedInt();
|
||||||
|
int frameHeight = data.readUnsignedExpGolombCodedInt();
|
||||||
|
if (data.readBit()) { // conformance_window_flag
|
||||||
|
int confWinLeftOffset = data.readUnsignedExpGolombCodedInt();
|
||||||
|
int confWinRightOffset = data.readUnsignedExpGolombCodedInt();
|
||||||
|
int confWinTopOffset = data.readUnsignedExpGolombCodedInt();
|
||||||
|
int confWinBottomOffset = data.readUnsignedExpGolombCodedInt();
|
||||||
|
// H.265/HEVC (2014) Table 6-1
|
||||||
|
int subWidthC = chromaFormatIdc == 1 || chromaFormatIdc == 2 ? 2 : 1;
|
||||||
|
int subHeightC = chromaFormatIdc == 1 ? 2 : 1;
|
||||||
|
frameWidth -= subWidthC * (confWinLeftOffset + confWinRightOffset);
|
||||||
|
frameHeight -= subHeightC * (confWinTopOffset + confWinBottomOffset);
|
||||||
|
}
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // bit_depth_luma_minus8
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // bit_depth_chroma_minus8
|
||||||
|
int log2MaxPicOrderCntLsbMinus4 = data.readUnsignedExpGolombCodedInt();
|
||||||
|
// for (i = sps_sub_layer_ordering_info_present_flag ? 0 : sps_max_sub_layers_minus1; ...)
|
||||||
|
for (int i = data.readBit() ? 0 : maxSubLayersMinus1; i <= maxSubLayersMinus1; i++) {
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // sps_max_dec_pic_buffering_minus1[i]
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // sps_max_num_reorder_pics[i]
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // sps_max_latency_increase_plus1[i]
|
||||||
|
}
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // log2_min_luma_coding_block_size_minus3
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // log2_diff_max_min_luma_coding_block_size
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // log2_min_luma_transform_block_size_minus2
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // log2_diff_max_min_luma_transform_block_size
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // max_transform_hierarchy_depth_inter
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // max_transform_hierarchy_depth_intra
|
||||||
|
// if (scaling_list_enabled_flag) { if (sps_scaling_list_data_present_flag) {...}}
|
||||||
|
boolean scalingListEnabled = data.readBit();
|
||||||
|
if (scalingListEnabled && data.readBit()) {
|
||||||
|
skipH265ScalingList(data);
|
||||||
|
}
|
||||||
|
data.skipBits(2); // amp_enabled_flag (1), sample_adaptive_offset_enabled_flag (1)
|
||||||
|
if (data.readBit()) { // pcm_enabled_flag
|
||||||
|
// pcm_sample_bit_depth_luma_minus1 (4), pcm_sample_bit_depth_chroma_minus1 (4)
|
||||||
|
data.skipBits(8);
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // log2_min_pcm_luma_coding_block_size_minus3
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // log2_diff_max_min_pcm_luma_coding_block_size
|
||||||
|
data.skipBit(); // pcm_loop_filter_disabled_flag
|
||||||
|
}
|
||||||
|
skipShortTermReferencePictureSets(data);
|
||||||
|
if (data.readBit()) { // long_term_ref_pics_present_flag
|
||||||
|
// num_long_term_ref_pics_sps
|
||||||
|
for (int i = 0; i < data.readUnsignedExpGolombCodedInt(); i++) {
|
||||||
|
int ltRefPicPocLsbSpsLength = log2MaxPicOrderCntLsbMinus4 + 4;
|
||||||
|
// lt_ref_pic_poc_lsb_sps[i], used_by_curr_pic_lt_sps_flag[i]
|
||||||
|
data.skipBits(ltRefPicPocLsbSpsLength + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.skipBits(2); // sps_temporal_mvp_enabled_flag, strong_intra_smoothing_enabled_flag
|
||||||
|
float pixelWidthHeightRatio = 1;
|
||||||
|
if (data.readBit()) { // vui_parameters_present_flag
|
||||||
|
if (data.readBit()) { // aspect_ratio_info_present_flag
|
||||||
|
int aspectRatioIdc = data.readBits(8);
|
||||||
|
if (aspectRatioIdc == NalUnitUtil.EXTENDED_SAR) {
|
||||||
|
int sarWidth = data.readBits(16);
|
||||||
|
int sarHeight = data.readBits(16);
|
||||||
|
if (sarWidth != 0 && sarHeight != 0) {
|
||||||
|
pixelWidthHeightRatio = (float) sarWidth / sarHeight;
|
||||||
|
}
|
||||||
|
} else if (aspectRatioIdc < NalUnitUtil.ASPECT_RATIO_IDC_VALUES.length) {
|
||||||
|
pixelWidthHeightRatio = NalUnitUtil.ASPECT_RATIO_IDC_VALUES[aspectRatioIdc];
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // overscan_info_present_flag
|
||||||
|
data.skipBit(); // overscan_appropriate_flag
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // video_signal_type_present_flag
|
||||||
|
data.skipBits(4); // video_format, video_full_range_flag
|
||||||
|
if (data.readBit()) { // colour_description_present_flag
|
||||||
|
// colour_primaries, transfer_characteristics, matrix_coeffs
|
||||||
|
data.skipBits(24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.readBit()) { // chroma_loc_info_present_flag
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // chroma_sample_loc_type_top_field
|
||||||
|
data.readUnsignedExpGolombCodedInt(); // chroma_sample_loc_type_bottom_field
|
||||||
|
}
|
||||||
|
data.skipBit(); // neutral_chroma_indication_flag
|
||||||
|
if (data.readBit()) { // field_seq_flag
|
||||||
|
// field_seq_flag equal to 1 indicates that the coded video sequence conveys pictures that
|
||||||
|
// represent fields, which means that frame height is double the picture height.
|
||||||
|
frameHeight *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new H265SpsData(
|
||||||
|
generalProfileSpace,
|
||||||
|
generalTierFlag,
|
||||||
|
generalProfileIdc,
|
||||||
|
generalProfileCompatibilityFlags,
|
||||||
|
constraintBytes,
|
||||||
|
generalLevelIdc,
|
||||||
|
seqParameterSetId,
|
||||||
|
frameWidth,
|
||||||
|
frameHeight,
|
||||||
|
pixelWidthHeightRatio);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a PPS NAL unit using the syntax defined in ITU-T Recommendation H.264 (2013) subsection
|
* Parses a PPS NAL unit using the syntax defined in ITU-T Recommendation H.264 (2013) subsection
|
||||||
* 7.3.2.2.
|
* 7.3.2.2.
|
||||||
@ -396,8 +610,20 @@ public final class NalUnitUtil {
|
|||||||
* @return A parsed representation of the PPS data.
|
* @return A parsed representation of the PPS data.
|
||||||
*/
|
*/
|
||||||
public static PpsData parsePpsNalUnit(byte[] nalData, int nalOffset, int nalLimit) {
|
public static PpsData parsePpsNalUnit(byte[] nalData, int nalOffset, int nalLimit) {
|
||||||
|
return parsePpsNalUnitPayload(nalData, nalOffset + 1, nalLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a PPS NAL unit payload (excluding the NAL unit header) using the syntax defined in ITU-T
|
||||||
|
* Recommendation H.264 (2013) subsection 7.3.2.2.
|
||||||
|
*
|
||||||
|
* @param nalData A buffer containing escaped PPS data.
|
||||||
|
* @param nalOffset The offset of the NAL unit payload in {@code nalData}.
|
||||||
|
* @param nalLimit The limit of the NAL unit in {@code nalData}.
|
||||||
|
* @return A parsed representation of the PPS data.
|
||||||
|
*/
|
||||||
|
public static PpsData parsePpsNalUnitPayload(byte[] nalData, int nalOffset, int nalLimit) {
|
||||||
ParsableNalUnitBitArray data = new ParsableNalUnitBitArray(nalData, nalOffset, nalLimit);
|
ParsableNalUnitBitArray data = new ParsableNalUnitBitArray(nalData, nalOffset, nalLimit);
|
||||||
data.skipBits(8); // nal_unit
|
|
||||||
int picParameterSetId = data.readUnsignedExpGolombCodedInt();
|
int picParameterSetId = data.readUnsignedExpGolombCodedInt();
|
||||||
int seqParameterSetId = data.readUnsignedExpGolombCodedInt();
|
int seqParameterSetId = data.readUnsignedExpGolombCodedInt();
|
||||||
data.skipBit(); // entropy_coding_mode_flag
|
data.skipBit(); // entropy_coding_mode_flag
|
||||||
@ -516,6 +742,63 @@ public final class NalUnitUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void skipH265ScalingList(ParsableNalUnitBitArray bitArray) {
|
||||||
|
for (int sizeId = 0; sizeId < 4; sizeId++) {
|
||||||
|
for (int matrixId = 0; matrixId < 6; matrixId += sizeId == 3 ? 3 : 1) {
|
||||||
|
if (!bitArray.readBit()) { // scaling_list_pred_mode_flag[sizeId][matrixId]
|
||||||
|
// scaling_list_pred_matrix_id_delta[sizeId][matrixId]
|
||||||
|
bitArray.readUnsignedExpGolombCodedInt();
|
||||||
|
} else {
|
||||||
|
int coefNum = min(64, 1 << (4 + (sizeId << 1)));
|
||||||
|
if (sizeId > 1) {
|
||||||
|
// scaling_list_dc_coef_minus8[sizeId - 2][matrixId]
|
||||||
|
bitArray.readSignedExpGolombCodedInt();
|
||||||
|
}
|
||||||
|
for (int i = 0; i < coefNum; i++) {
|
||||||
|
bitArray.readSignedExpGolombCodedInt(); // scaling_list_delta_coef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void skipShortTermReferencePictureSets(ParsableNalUnitBitArray bitArray) {
|
||||||
|
int numShortTermRefPicSets = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
|
boolean interRefPicSetPredictionFlag = false;
|
||||||
|
int numNegativePics;
|
||||||
|
int numPositivePics;
|
||||||
|
// As this method applies in a SPS, the only element of NumDeltaPocs accessed is the previous
|
||||||
|
// one, so we just keep track of that rather than storing the whole array.
|
||||||
|
// RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1) and delta_idx_minus1 is always zero in SPS.
|
||||||
|
int previousNumDeltaPocs = 0;
|
||||||
|
for (int stRpsIdx = 0; stRpsIdx < numShortTermRefPicSets; stRpsIdx++) {
|
||||||
|
if (stRpsIdx != 0) {
|
||||||
|
interRefPicSetPredictionFlag = bitArray.readBit();
|
||||||
|
}
|
||||||
|
if (interRefPicSetPredictionFlag) {
|
||||||
|
bitArray.skipBit(); // delta_rps_sign
|
||||||
|
bitArray.readUnsignedExpGolombCodedInt(); // abs_delta_rps_minus1
|
||||||
|
for (int j = 0; j <= previousNumDeltaPocs; j++) {
|
||||||
|
if (bitArray.readBit()) { // used_by_curr_pic_flag[j]
|
||||||
|
bitArray.skipBit(); // use_delta_flag[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
numNegativePics = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
|
numPositivePics = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
|
previousNumDeltaPocs = numNegativePics + numPositivePics;
|
||||||
|
for (int i = 0; i < numNegativePics; i++) {
|
||||||
|
bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s0_minus1[i]
|
||||||
|
bitArray.skipBit(); // used_by_curr_pic_s0_flag[i]
|
||||||
|
}
|
||||||
|
for (int i = 0; i < numPositivePics; i++) {
|
||||||
|
bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s1_minus1[i]
|
||||||
|
bitArray.skipBit(); // used_by_curr_pic_s1_flag[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private NalUnitUtil() {
|
private NalUnitUtil() {
|
||||||
// Prevent instantiation.
|
// Prevent instantiation.
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,7 @@ public final class NalUnitUtilTest {
|
|||||||
assertThat(data.frameNumLength).isEqualTo(4);
|
assertThat(data.frameNumLength).isEqualTo(4);
|
||||||
assertThat(data.picOrderCntLsbLength).isEqualTo(6);
|
assertThat(data.picOrderCntLsbLength).isEqualTo(6);
|
||||||
assertThat(data.seqParameterSetId).isEqualTo(0);
|
assertThat(data.seqParameterSetId).isEqualTo(0);
|
||||||
assertThat(data.pixelWidthAspectRatio).isEqualTo(1.0f);
|
assertThat(data.pixelWidthHeightRatio).isEqualTo(1.0f);
|
||||||
assertThat(data.picOrderCountType).isEqualTo(0);
|
assertThat(data.picOrderCountType).isEqualTo(0);
|
||||||
assertThat(data.separateColorPlaneFlag).isFalse();
|
assertThat(data.separateColorPlaneFlag).isFalse();
|
||||||
}
|
}
|
||||||
|
@ -92,7 +92,7 @@ import com.google.android.exoplayer2.video.AvcConfig;
|
|||||||
.setCodecs(avcConfig.codecs)
|
.setCodecs(avcConfig.codecs)
|
||||||
.setWidth(avcConfig.width)
|
.setWidth(avcConfig.width)
|
||||||
.setHeight(avcConfig.height)
|
.setHeight(avcConfig.height)
|
||||||
.setPixelWidthHeightRatio(avcConfig.pixelWidthAspectRatio)
|
.setPixelWidthHeightRatio(avcConfig.pixelWidthHeightRatio)
|
||||||
.setInitializationData(avcConfig.initializationData)
|
.setInitializationData(avcConfig.initializationData)
|
||||||
.build();
|
.build();
|
||||||
output.format(format);
|
output.format(format);
|
||||||
|
@ -1130,7 +1130,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
initializationData = avcConfig.initializationData;
|
initializationData = avcConfig.initializationData;
|
||||||
out.nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
|
out.nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
|
||||||
if (!pixelWidthHeightRatioFromPasp) {
|
if (!pixelWidthHeightRatioFromPasp) {
|
||||||
pixelWidthHeightRatio = avcConfig.pixelWidthAspectRatio;
|
pixelWidthHeightRatio = avcConfig.pixelWidthHeightRatio;
|
||||||
}
|
}
|
||||||
codecs = avcConfig.codecs;
|
codecs = avcConfig.codecs;
|
||||||
} else if (childAtomType == Atom.TYPE_hvcC) {
|
} else if (childAtomType == Atom.TYPE_hvcC) {
|
||||||
@ -1140,6 +1140,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
HevcConfig hevcConfig = HevcConfig.parse(parent);
|
HevcConfig hevcConfig = HevcConfig.parse(parent);
|
||||||
initializationData = hevcConfig.initializationData;
|
initializationData = hevcConfig.initializationData;
|
||||||
out.nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
|
out.nalUnitLengthFieldLength = hevcConfig.nalUnitLengthFieldLength;
|
||||||
|
if (!pixelWidthHeightRatioFromPasp) {
|
||||||
|
pixelWidthHeightRatio = hevcConfig.pixelWidthHeightRatio;
|
||||||
|
}
|
||||||
codecs = hevcConfig.codecs;
|
codecs = hevcConfig.codecs;
|
||||||
} else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
|
} else if (childAtomType == Atom.TYPE_dvcC || childAtomType == Atom.TYPE_dvvC) {
|
||||||
@Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
|
@Nullable DolbyVisionConfig dolbyVisionConfig = DolbyVisionConfig.parse(parent);
|
||||||
|
@ -218,7 +218,7 @@ public final class H264Reader implements ElementaryStreamReader {
|
|||||||
.setCodecs(codecs)
|
.setCodecs(codecs)
|
||||||
.setWidth(spsData.width)
|
.setWidth(spsData.width)
|
||||||
.setHeight(spsData.height)
|
.setHeight(spsData.height)
|
||||||
.setPixelWidthHeightRatio(spsData.pixelWidthAspectRatio)
|
.setPixelWidthHeightRatio(spsData.pixelWidthHeightRatio)
|
||||||
.setInitializationData(initializationData)
|
.setInitializationData(initializationData)
|
||||||
.build());
|
.build());
|
||||||
hasOutputFormat = true;
|
hasOutputFormat = true;
|
||||||
|
@ -247,10 +247,20 @@ public final class H265Reader implements ElementaryStreamReader {
|
|||||||
bitArray.skipBits(40 + 4); // NAL header, sps_video_parameter_set_id
|
bitArray.skipBits(40 + 4); // NAL header, sps_video_parameter_set_id
|
||||||
int maxSubLayersMinus1 = bitArray.readBits(3);
|
int maxSubLayersMinus1 = bitArray.readBits(3);
|
||||||
bitArray.skipBit(); // sps_temporal_id_nesting_flag
|
bitArray.skipBit(); // sps_temporal_id_nesting_flag
|
||||||
|
int generalProfileSpace = bitArray.readBits(2);
|
||||||
// profile_tier_level(1, sps_max_sub_layers_minus1)
|
boolean generalTierFlag = bitArray.readBit();
|
||||||
bitArray.skipBits(88); // if (profilePresentFlag) {...}
|
int generalProfileIdc = bitArray.readBits(5);
|
||||||
bitArray.skipBits(8); // general_level_idc
|
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;
|
int toSkip = 0;
|
||||||
for (int i = 0; i < maxSubLayersMinus1; i++) {
|
for (int i = 0; i < maxSubLayersMinus1; i++) {
|
||||||
if (bitArray.readBit()) { // sub_layer_profile_present_flag[i]
|
if (bitArray.readBit()) { // sub_layer_profile_present_flag[i]
|
||||||
@ -360,10 +370,14 @@ public final class H265Reader implements ElementaryStreamReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the SPS to derive an RFC 6381 codecs string.
|
String codecs =
|
||||||
bitArray.reset(sps.nalData, 0, sps.nalLength);
|
CodecSpecificDataUtil.buildHevcCodecString(
|
||||||
bitArray.skipBits(24); // Skip start code.
|
generalProfileSpace,
|
||||||
String codecs = CodecSpecificDataUtil.buildHevcCodecStringFromSps(bitArray);
|
generalTierFlag,
|
||||||
|
generalProfileIdc,
|
||||||
|
generalProfileCompatibilityFlags,
|
||||||
|
constraintBytes,
|
||||||
|
generalLevelIdc);
|
||||||
|
|
||||||
return new Format.Builder()
|
return new Format.Builder()
|
||||||
.setId(formatId)
|
.setId(formatId)
|
||||||
|
@ -28,13 +28,6 @@ import java.util.List;
|
|||||||
/** AVC configuration data. */
|
/** AVC configuration data. */
|
||||||
public final class AvcConfig {
|
public final class AvcConfig {
|
||||||
|
|
||||||
public final List<byte[]> initializationData;
|
|
||||||
public final int nalUnitLengthFieldLength;
|
|
||||||
public final int width;
|
|
||||||
public final int height;
|
|
||||||
public final float pixelWidthAspectRatio;
|
|
||||||
@Nullable public final String codecs;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses AVC configuration data.
|
* Parses AVC configuration data.
|
||||||
*
|
*
|
||||||
@ -62,7 +55,7 @@ public final class AvcConfig {
|
|||||||
|
|
||||||
int width = Format.NO_VALUE;
|
int width = Format.NO_VALUE;
|
||||||
int height = Format.NO_VALUE;
|
int height = Format.NO_VALUE;
|
||||||
float pixelWidthAspectRatio = 1;
|
float pixelWidthHeightRatio = 1;
|
||||||
@Nullable String codecs = null;
|
@Nullable String codecs = null;
|
||||||
if (numSequenceParameterSets > 0) {
|
if (numSequenceParameterSets > 0) {
|
||||||
byte[] sps = initializationData.get(0);
|
byte[] sps = initializationData.get(0);
|
||||||
@ -71,7 +64,7 @@ public final class AvcConfig {
|
|||||||
initializationData.get(0), nalUnitLengthFieldLength, sps.length);
|
initializationData.get(0), nalUnitLengthFieldLength, sps.length);
|
||||||
width = spsData.width;
|
width = spsData.width;
|
||||||
height = spsData.height;
|
height = spsData.height;
|
||||||
pixelWidthAspectRatio = spsData.pixelWidthAspectRatio;
|
pixelWidthHeightRatio = spsData.pixelWidthHeightRatio;
|
||||||
codecs =
|
codecs =
|
||||||
CodecSpecificDataUtil.buildAvcCodecString(
|
CodecSpecificDataUtil.buildAvcCodecString(
|
||||||
spsData.profileIdc, spsData.constraintsFlagsAndReservedZero2Bits, spsData.levelIdc);
|
spsData.profileIdc, spsData.constraintsFlagsAndReservedZero2Bits, spsData.levelIdc);
|
||||||
@ -82,25 +75,51 @@ public final class AvcConfig {
|
|||||||
nalUnitLengthFieldLength,
|
nalUnitLengthFieldLength,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
pixelWidthAspectRatio,
|
pixelWidthHeightRatio,
|
||||||
codecs);
|
codecs);
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
throw ParserException.createForMalformedContainer("Error parsing AVC config", e);
|
throw ParserException.createForMalformedContainer("Error parsing AVC config", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of buffers containing the codec-specific data to be provided to the decoder.
|
||||||
|
*
|
||||||
|
* @see com.google.android.exoplayer2.Format#initializationData
|
||||||
|
*/
|
||||||
|
public final List<byte[]> initializationData;
|
||||||
|
|
||||||
|
/** The length of the NAL unit length field in the bitstream's container, in bytes. */
|
||||||
|
public final int nalUnitLengthFieldLength;
|
||||||
|
|
||||||
|
/** The width of each decoded frame, or {@link Format#NO_VALUE} if unknown. */
|
||||||
|
public final int width;
|
||||||
|
|
||||||
|
/** The height of each decoded frame, or {@link Format#NO_VALUE} if unknown. */
|
||||||
|
public final int height;
|
||||||
|
|
||||||
|
/** The pixel width to height ratio. */
|
||||||
|
public final float pixelWidthHeightRatio;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An RFC 6381 codecs string representing the video format, or {@code null} if not known.
|
||||||
|
*
|
||||||
|
* @see com.google.android.exoplayer2.Format#codecs
|
||||||
|
*/
|
||||||
|
@Nullable public final String codecs;
|
||||||
|
|
||||||
private AvcConfig(
|
private AvcConfig(
|
||||||
List<byte[]> initializationData,
|
List<byte[]> initializationData,
|
||||||
int nalUnitLengthFieldLength,
|
int nalUnitLengthFieldLength,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
float pixelWidthAspectRatio,
|
float pixelWidthHeightRatio,
|
||||||
@Nullable String codecs) {
|
@Nullable String codecs) {
|
||||||
this.initializationData = initializationData;
|
this.initializationData = initializationData;
|
||||||
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
|
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
this.codecs = codecs;
|
this.codecs = codecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@
|
|||||||
package com.google.android.exoplayer2.video;
|
package com.google.android.exoplayer2.video;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.ParserException;
|
import com.google.android.exoplayer2.ParserException;
|
||||||
import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
|
import com.google.android.exoplayer2.util.CodecSpecificDataUtil;
|
||||||
import com.google.android.exoplayer2.util.NalUnitUtil;
|
import com.google.android.exoplayer2.util.NalUnitUtil;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
import com.google.android.exoplayer2.util.ParsableNalUnitBitArray;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -58,6 +58,9 @@ public final class HevcConfig {
|
|||||||
data.setPosition(csdStartPosition);
|
data.setPosition(csdStartPosition);
|
||||||
byte[] buffer = new byte[csdLength];
|
byte[] buffer = new byte[csdLength];
|
||||||
int bufferPosition = 0;
|
int bufferPosition = 0;
|
||||||
|
int width = Format.NO_VALUE;
|
||||||
|
int height = Format.NO_VALUE;
|
||||||
|
float pixelWidthHeightRatio = 1;
|
||||||
@Nullable String codecs = null;
|
@Nullable String codecs = null;
|
||||||
for (int i = 0; i < numberOfArrays; i++) {
|
for (int i = 0; i < numberOfArrays; i++) {
|
||||||
int nalUnitType = data.readUnsignedByte() & 0x7F; // completeness (1), nal_unit_type (7)
|
int nalUnitType = data.readUnsignedByte() & 0x7F; // completeness (1), nal_unit_type (7)
|
||||||
@ -74,21 +77,30 @@ public final class HevcConfig {
|
|||||||
System.arraycopy(
|
System.arraycopy(
|
||||||
data.getData(), data.getPosition(), buffer, bufferPosition, nalUnitLength);
|
data.getData(), data.getPosition(), buffer, bufferPosition, nalUnitLength);
|
||||||
if (nalUnitType == SPS_NAL_UNIT_TYPE && j == 0) {
|
if (nalUnitType == SPS_NAL_UNIT_TYPE && j == 0) {
|
||||||
ParsableNalUnitBitArray bitArray =
|
NalUnitUtil.H265SpsData spsData =
|
||||||
new ParsableNalUnitBitArray(
|
NalUnitUtil.parseH265SpsNalUnit(
|
||||||
buffer,
|
buffer, bufferPosition, bufferPosition + nalUnitLength);
|
||||||
/* offset= */ bufferPosition,
|
width = spsData.width;
|
||||||
/* limit= */ bufferPosition + nalUnitLength);
|
height = spsData.height;
|
||||||
codecs = CodecSpecificDataUtil.buildHevcCodecStringFromSps(bitArray);
|
pixelWidthHeightRatio = spsData.pixelWidthHeightRatio;
|
||||||
|
codecs =
|
||||||
|
CodecSpecificDataUtil.buildHevcCodecString(
|
||||||
|
spsData.generalProfileSpace,
|
||||||
|
spsData.generalTierFlag,
|
||||||
|
spsData.generalProfileIdc,
|
||||||
|
spsData.generalProfileCompatibilityFlags,
|
||||||
|
spsData.constraintBytes,
|
||||||
|
spsData.generalLevelIdc);
|
||||||
}
|
}
|
||||||
bufferPosition += nalUnitLength;
|
bufferPosition += nalUnitLength;
|
||||||
data.skipBytes(nalUnitLength);
|
data.skipBytes(nalUnitLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
List<byte[]> initializationData =
|
||||||
List<byte[]> initializationData = csdLength == 0 ? null : Collections.singletonList(buffer);
|
csdLength == 0 ? Collections.emptyList() : Collections.singletonList(buffer);
|
||||||
return new HevcConfig(initializationData, lengthSizeMinusOne + 1, codecs);
|
return new HevcConfig(
|
||||||
|
initializationData, lengthSizeMinusOne + 1, width, height, pixelWidthHeightRatio, codecs);
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
throw ParserException.createForMalformedContainer("Error parsing HEVC config", e);
|
throw ParserException.createForMalformedContainer("Error parsing HEVC config", e);
|
||||||
}
|
}
|
||||||
@ -97,14 +109,24 @@ public final class HevcConfig {
|
|||||||
private static final int SPS_NAL_UNIT_TYPE = 33;
|
private static final int SPS_NAL_UNIT_TYPE = 33;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of buffers containing the codec-specific data to be provided to the decoder, or {@code
|
* List of buffers containing the codec-specific data to be provided to the decoder.
|
||||||
* null} if not known.
|
|
||||||
*
|
*
|
||||||
* @see com.google.android.exoplayer2.Format#initializationData
|
* @see com.google.android.exoplayer2.Format#initializationData
|
||||||
*/
|
*/
|
||||||
@Nullable public final List<byte[]> initializationData;
|
public final List<byte[]> initializationData;
|
||||||
|
|
||||||
/** The length of the NAL unit length field in the bitstream's container, in bytes. */
|
/** The length of the NAL unit length field in the bitstream's container, in bytes. */
|
||||||
public final int nalUnitLengthFieldLength;
|
public final int nalUnitLengthFieldLength;
|
||||||
|
|
||||||
|
/** The width of each decoded frame, or {@link Format#NO_VALUE} if unknown. */
|
||||||
|
public final int width;
|
||||||
|
|
||||||
|
/** The height of each decoded frame, or {@link Format#NO_VALUE} if unknown. */
|
||||||
|
public final int height;
|
||||||
|
|
||||||
|
/** The pixel width to height ratio. */
|
||||||
|
public final float pixelWidthHeightRatio;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An RFC 6381 codecs string representing the video format, or {@code null} if not known.
|
* An RFC 6381 codecs string representing the video format, or {@code null} if not known.
|
||||||
*
|
*
|
||||||
@ -113,11 +135,17 @@ public final class HevcConfig {
|
|||||||
@Nullable public final String codecs;
|
@Nullable public final String codecs;
|
||||||
|
|
||||||
private HevcConfig(
|
private HevcConfig(
|
||||||
@Nullable List<byte[]> initializationData,
|
List<byte[]> initializationData,
|
||||||
int nalUnitLengthFieldLength,
|
int nalUnitLengthFieldLength,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
float pixelWidthHeightRatio,
|
||||||
@Nullable String codecs) {
|
@Nullable String codecs) {
|
||||||
this.initializationData = initializationData;
|
this.initializationData = initializationData;
|
||||||
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
|
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
this.codecs = codecs;
|
this.codecs = codecs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ import com.google.common.collect.ImmutableMap;
|
|||||||
NalUnitUtil.SpsData spsData =
|
NalUnitUtil.SpsData spsData =
|
||||||
NalUnitUtil.parseSpsNalUnit(
|
NalUnitUtil.parseSpsNalUnit(
|
||||||
spsNalDataWithStartCode, NAL_START_CODE.length, spsNalDataWithStartCode.length);
|
spsNalDataWithStartCode, NAL_START_CODE.length, spsNalDataWithStartCode.length);
|
||||||
formatBuilder.setPixelWidthHeightRatio(spsData.pixelWidthAspectRatio);
|
formatBuilder.setPixelWidthHeightRatio(spsData.pixelWidthHeightRatio);
|
||||||
formatBuilder.setHeight(spsData.height);
|
formatBuilder.setHeight(spsData.height);
|
||||||
formatBuilder.setWidth(spsData.width);
|
formatBuilder.setWidth(spsData.width);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user