mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Parse pixel aspect ratio from SPS for use when there's no pasp atom.
Issue: #697
This commit is contained in:
parent
69466adbe3
commit
f7ffeb75cf
@ -22,6 +22,7 @@ import com.google.android.exoplayer.util.Assertions;
|
|||||||
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.NalUnitUtil;
|
import com.google.android.exoplayer.util.NalUnitUtil;
|
||||||
|
import com.google.android.exoplayer.util.ParsableBitArray;
|
||||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||||
import com.google.android.exoplayer.util.Util;
|
import com.google.android.exoplayer.util.Util;
|
||||||
|
|
||||||
@ -370,6 +371,7 @@ import java.util.List;
|
|||||||
parent.skipBytes(24);
|
parent.skipBytes(24);
|
||||||
int width = parent.readUnsignedShort();
|
int width = parent.readUnsignedShort();
|
||||||
int height = parent.readUnsignedShort();
|
int height = parent.readUnsignedShort();
|
||||||
|
boolean pixelWidthHeightRatioFromPasp = false;
|
||||||
float pixelWidthHeightRatio = 1;
|
float pixelWidthHeightRatio = 1;
|
||||||
parent.skipBytes(50);
|
parent.skipBytes(50);
|
||||||
|
|
||||||
@ -389,9 +391,12 @@ import java.util.List;
|
|||||||
if (childAtomType == Atom.TYPE_avcC) {
|
if (childAtomType == Atom.TYPE_avcC) {
|
||||||
Assertions.checkState(mimeType == null);
|
Assertions.checkState(mimeType == null);
|
||||||
mimeType = MimeTypes.VIDEO_H264;
|
mimeType = MimeTypes.VIDEO_H264;
|
||||||
Pair<List<byte[]>, Integer> avcCData = parseAvcCFromParent(parent, childStartPosition);
|
AvcCData avcCData = parseAvcCFromParent(parent, childStartPosition);
|
||||||
initializationData = avcCData.first;
|
initializationData = avcCData.initializationData;
|
||||||
out.nalUnitLengthFieldLength = avcCData.second;
|
out.nalUnitLengthFieldLength = avcCData.nalUnitLengthFieldLength;
|
||||||
|
if (!pixelWidthHeightRatioFromPasp) {
|
||||||
|
pixelWidthHeightRatio = avcCData.pixelWidthAspectRatio;
|
||||||
|
}
|
||||||
} else if (childAtomType == Atom.TYPE_hvcC) {
|
} else if (childAtomType == Atom.TYPE_hvcC) {
|
||||||
Assertions.checkState(mimeType == null);
|
Assertions.checkState(mimeType == null);
|
||||||
mimeType = MimeTypes.VIDEO_H265;
|
mimeType = MimeTypes.VIDEO_H265;
|
||||||
@ -412,6 +417,7 @@ import java.util.List;
|
|||||||
parseSinfFromParent(parent, childStartPosition, childAtomSize);
|
parseSinfFromParent(parent, childStartPosition, childAtomSize);
|
||||||
} else if (childAtomType == Atom.TYPE_pasp) {
|
} else if (childAtomType == Atom.TYPE_pasp) {
|
||||||
pixelWidthHeightRatio = parsePaspFromParent(parent, childStartPosition);
|
pixelWidthHeightRatio = parsePaspFromParent(parent, childStartPosition);
|
||||||
|
pixelWidthHeightRatioFromPasp = true;
|
||||||
}
|
}
|
||||||
childPosition += childAtomSize;
|
childPosition += childAtomSize;
|
||||||
}
|
}
|
||||||
@ -425,8 +431,7 @@ import java.util.List;
|
|||||||
width, height, pixelWidthHeightRatio, initializationData);
|
width, height, pixelWidthHeightRatio, initializationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Pair<List<byte[]>, Integer> parseAvcCFromParent(ParsableByteArray parent,
|
private static AvcCData parseAvcCFromParent(ParsableByteArray parent, int position) {
|
||||||
int position) {
|
|
||||||
parent.setPosition(position + Atom.HEADER_SIZE + 4);
|
parent.setPosition(position + Atom.HEADER_SIZE + 4);
|
||||||
// Start of the AVCDecoderConfigurationRecord (defined in 14496-15)
|
// Start of the AVCDecoderConfigurationRecord (defined in 14496-15)
|
||||||
int nalUnitLengthFieldLength = (parent.readUnsignedByte() & 0x3) + 1;
|
int nalUnitLengthFieldLength = (parent.readUnsignedByte() & 0x3) + 1;
|
||||||
@ -434,8 +439,7 @@ import java.util.List;
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
List<byte[]> initializationData = new ArrayList<>();
|
List<byte[]> initializationData = new ArrayList<>();
|
||||||
// TODO: We should try and parse these using CodecSpecificDataUtil.parseSpsNalUnit, and
|
float pixelWidthAspectRatio = 1;
|
||||||
// expose the AVC profile and level somewhere useful; Most likely in MediaFormat.
|
|
||||||
int numSequenceParameterSets = parent.readUnsignedByte() & 0x1F;
|
int numSequenceParameterSets = parent.readUnsignedByte() & 0x1F;
|
||||||
for (int j = 0; j < numSequenceParameterSets; j++) {
|
for (int j = 0; j < numSequenceParameterSets; j++) {
|
||||||
initializationData.add(NalUnitUtil.parseChildNalUnit(parent));
|
initializationData.add(NalUnitUtil.parseChildNalUnit(parent));
|
||||||
@ -444,7 +448,17 @@ import java.util.List;
|
|||||||
for (int j = 0; j < numPictureParameterSets; j++) {
|
for (int j = 0; j < numPictureParameterSets; j++) {
|
||||||
initializationData.add(NalUnitUtil.parseChildNalUnit(parent));
|
initializationData.add(NalUnitUtil.parseChildNalUnit(parent));
|
||||||
}
|
}
|
||||||
return Pair.create(initializationData, nalUnitLengthFieldLength);
|
|
||||||
|
if (numSequenceParameterSets > 0) {
|
||||||
|
// Parse the first sequence parameter set to obtain pixelWidthAspectRatio.
|
||||||
|
ParsableBitArray spsDataBitArray = new ParsableBitArray(initializationData.get(0));
|
||||||
|
// Skip the NAL header consisting of the nalUnitLengthField and the type (1 byte).
|
||||||
|
spsDataBitArray.setPosition(8 * (nalUnitLengthFieldLength + 1));
|
||||||
|
pixelWidthAspectRatio = CodecSpecificDataUtil.parseSpsNalUnit(spsDataBitArray)
|
||||||
|
.pixelWidthAspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AvcCData(initializationData, nalUnitLengthFieldLength, pixelWidthAspectRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Pair<List<byte[]>, Integer> parseHvcCFromParent(ParsableByteArray parent,
|
private static Pair<List<byte[]>, Integer> parseHvcCFromParent(ParsableByteArray parent,
|
||||||
@ -705,4 +719,19 @@ import java.util.List;
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class AvcCData {
|
||||||
|
|
||||||
|
public final List<byte[]> initializationData;
|
||||||
|
public final int nalUnitLengthFieldLength;
|
||||||
|
public final float pixelWidthAspectRatio;
|
||||||
|
|
||||||
|
public AvcCData(List<byte[]> initializationData, int nalUnitLengthFieldLength,
|
||||||
|
float pixelWidthAspectRatio) {
|
||||||
|
this.initializationData = initializationData;
|
||||||
|
this.nalUnitLengthFieldLength = nalUnitLengthFieldLength;
|
||||||
|
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,13 @@ package com.google.android.exoplayer.extractor.ts;
|
|||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.MediaFormat;
|
import com.google.android.exoplayer.MediaFormat;
|
||||||
import com.google.android.exoplayer.extractor.TrackOutput;
|
import com.google.android.exoplayer.extractor.TrackOutput;
|
||||||
|
import com.google.android.exoplayer.util.CodecSpecificDataUtil;
|
||||||
|
import com.google.android.exoplayer.util.CodecSpecificDataUtil.SpsData;
|
||||||
import com.google.android.exoplayer.util.MimeTypes;
|
import com.google.android.exoplayer.util.MimeTypes;
|
||||||
import com.google.android.exoplayer.util.NalUnitUtil;
|
import com.google.android.exoplayer.util.NalUnitUtil;
|
||||||
import com.google.android.exoplayer.util.ParsableBitArray;
|
import com.google.android.exoplayer.util.ParsableBitArray;
|
||||||
import com.google.android.exoplayer.util.ParsableByteArray;
|
import com.google.android.exoplayer.util.ParsableByteArray;
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -34,8 +34,6 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
/* package */ final class H264Reader extends ElementaryStreamReader {
|
/* package */ final class H264Reader extends ElementaryStreamReader {
|
||||||
|
|
||||||
private static final String TAG = "H264Reader";
|
|
||||||
|
|
||||||
private static final int FRAME_TYPE_I = 2;
|
private static final int FRAME_TYPE_I = 2;
|
||||||
private static final int FRAME_TYPE_ALL_I = 7;
|
private static final int FRAME_TYPE_ALL_I = 7;
|
||||||
|
|
||||||
@ -205,121 +203,19 @@ import java.util.List;
|
|||||||
initializationData.add(spsData);
|
initializationData.add(spsData);
|
||||||
initializationData.add(ppsData);
|
initializationData.add(ppsData);
|
||||||
|
|
||||||
// Unescape and then parse the SPS unit.
|
// Unescape and parse the SPS unit.
|
||||||
NalUnitUtil.unescapeStream(sps.nalData, sps.nalLength);
|
NalUnitUtil.unescapeStream(sps.nalData, sps.nalLength);
|
||||||
ParsableBitArray bitArray = new ParsableBitArray(sps.nalData);
|
ParsableBitArray bitArray = new ParsableBitArray(sps.nalData);
|
||||||
bitArray.skipBits(32); // NAL header
|
bitArray.skipBits(32); // NAL header
|
||||||
int profileIdc = bitArray.readBits(8);
|
SpsData parsedSpsData = CodecSpecificDataUtil.parseSpsNalUnit(bitArray);
|
||||||
bitArray.skipBits(16); // constraint bits (6), reserved (2) and level_idc (8)
|
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // seq_parameter_set_id
|
|
||||||
|
|
||||||
int chromaFormatIdc = 1; // Default is 4:2:0
|
|
||||||
if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244
|
|
||||||
|| profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118
|
|
||||||
|| profileIdc == 128 || profileIdc == 138) {
|
|
||||||
chromaFormatIdc = bitArray.readUnsignedExpGolombCodedInt();
|
|
||||||
if (chromaFormatIdc == 3) {
|
|
||||||
bitArray.skipBits(1); // separate_colour_plane_flag
|
|
||||||
}
|
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // bit_depth_luma_minus8
|
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // bit_depth_chroma_minus8
|
|
||||||
bitArray.skipBits(1); // qpprime_y_zero_transform_bypass_flag
|
|
||||||
boolean seqScalingMatrixPresentFlag = bitArray.readBit();
|
|
||||||
if (seqScalingMatrixPresentFlag) {
|
|
||||||
int limit = (chromaFormatIdc != 3) ? 8 : 12;
|
|
||||||
for (int i = 0; i < limit; i++) {
|
|
||||||
boolean seqScalingListPresentFlag = bitArray.readBit();
|
|
||||||
if (seqScalingListPresentFlag) {
|
|
||||||
skipScalingList(bitArray, i < 6 ? 16 : 64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // log2_max_frame_num_minus4
|
|
||||||
long picOrderCntType = bitArray.readUnsignedExpGolombCodedInt();
|
|
||||||
if (picOrderCntType == 0) {
|
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // log2_max_pic_order_cnt_lsb_minus4
|
|
||||||
} else if (picOrderCntType == 1) {
|
|
||||||
bitArray.skipBits(1); // delta_pic_order_always_zero_flag
|
|
||||||
bitArray.readSignedExpGolombCodedInt(); // offset_for_non_ref_pic
|
|
||||||
bitArray.readSignedExpGolombCodedInt(); // offset_for_top_to_bottom_field
|
|
||||||
long numRefFramesInPicOrderCntCycle = bitArray.readUnsignedExpGolombCodedInt();
|
|
||||||
for (int i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
|
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // offset_for_ref_frame[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // max_num_ref_frames
|
|
||||||
bitArray.skipBits(1); // gaps_in_frame_num_value_allowed_flag
|
|
||||||
|
|
||||||
int picWidthInMbs = bitArray.readUnsignedExpGolombCodedInt() + 1;
|
|
||||||
int picHeightInMapUnits = bitArray.readUnsignedExpGolombCodedInt() + 1;
|
|
||||||
boolean frameMbsOnlyFlag = bitArray.readBit();
|
|
||||||
int frameHeightInMbs = (2 - (frameMbsOnlyFlag ? 1 : 0)) * picHeightInMapUnits;
|
|
||||||
if (!frameMbsOnlyFlag) {
|
|
||||||
bitArray.skipBits(1); // mb_adaptive_frame_field_flag
|
|
||||||
}
|
|
||||||
|
|
||||||
bitArray.skipBits(1); // direct_8x8_inference_flag
|
|
||||||
int frameWidth = picWidthInMbs * 16;
|
|
||||||
int frameHeight = frameHeightInMbs * 16;
|
|
||||||
boolean frameCroppingFlag = bitArray.readBit();
|
|
||||||
if (frameCroppingFlag) {
|
|
||||||
int frameCropLeftOffset = bitArray.readUnsignedExpGolombCodedInt();
|
|
||||||
int frameCropRightOffset = bitArray.readUnsignedExpGolombCodedInt();
|
|
||||||
int frameCropTopOffset = bitArray.readUnsignedExpGolombCodedInt();
|
|
||||||
int frameCropBottomOffset = bitArray.readUnsignedExpGolombCodedInt();
|
|
||||||
int cropUnitX, cropUnitY;
|
|
||||||
if (chromaFormatIdc == 0) {
|
|
||||||
cropUnitX = 1;
|
|
||||||
cropUnitY = 2 - (frameMbsOnlyFlag ? 1 : 0);
|
|
||||||
} else {
|
|
||||||
int subWidthC = (chromaFormatIdc == 3) ? 1 : 2;
|
|
||||||
int subHeightC = (chromaFormatIdc == 1) ? 2 : 1;
|
|
||||||
cropUnitX = subWidthC;
|
|
||||||
cropUnitY = subHeightC * (2 - (frameMbsOnlyFlag ? 1 : 0));
|
|
||||||
}
|
|
||||||
frameWidth -= (frameCropLeftOffset + frameCropRightOffset) * cropUnitX;
|
|
||||||
frameHeight -= (frameCropTopOffset + frameCropBottomOffset) * cropUnitY;
|
|
||||||
}
|
|
||||||
|
|
||||||
float pixelWidthHeightRatio = 1;
|
|
||||||
boolean vuiParametersPresentFlag = bitArray.readBit();
|
|
||||||
if (vuiParametersPresentFlag) {
|
|
||||||
boolean aspectRatioInfoPresentFlag = bitArray.readBit();
|
|
||||||
if (aspectRatioInfoPresentFlag) {
|
|
||||||
int aspectRatioIdc = bitArray.readBits(8);
|
|
||||||
if (aspectRatioIdc == NalUnitUtil.EXTENDED_SAR) {
|
|
||||||
int sarWidth = bitArray.readBits(16);
|
|
||||||
int sarHeight = bitArray.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Construct and output the format.
|
||||||
output.format(MediaFormat.createVideoFormat(MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE,
|
output.format(MediaFormat.createVideoFormat(MimeTypes.VIDEO_H264, MediaFormat.NO_VALUE,
|
||||||
C.UNKNOWN_TIME_US, frameWidth, frameHeight, pixelWidthHeightRatio, initializationData));
|
C.UNKNOWN_TIME_US, parsedSpsData.width, parsedSpsData.height,
|
||||||
|
parsedSpsData.pixelWidthAspectRatio, initializationData));
|
||||||
hasOutputFormat = true;
|
hasOutputFormat = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void skipScalingList(ParsableBitArray bitArray, int size) {
|
|
||||||
int lastScale = 8;
|
|
||||||
int nextScale = 8;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
if (nextScale != 0) {
|
|
||||||
int deltaScale = bitArray.readSignedExpGolombCodedInt();
|
|
||||||
nextScale = (lastScale + deltaScale + 256) % 256;
|
|
||||||
}
|
|
||||||
lastScale = (nextScale == 0) ? lastScale : nextScale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A buffer specifically for IFR units that can be used to parse the IFR's slice type.
|
* A buffer specifically for IFR units that can be used to parse the IFR's slice type.
|
||||||
*/
|
*/
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer.util;
|
package com.google.android.exoplayer.util;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.util.Log;
|
||||||
import android.media.MediaCodecInfo.CodecProfileLevel;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -27,6 +26,23 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public final class CodecSpecificDataUtil {
|
public final class CodecSpecificDataUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds data parsed from a sequence parameter set NAL unit.
|
||||||
|
*/
|
||||||
|
public static final class SpsData {
|
||||||
|
|
||||||
|
public final int width;
|
||||||
|
public final int height;
|
||||||
|
public final float pixelWidthAspectRatio;
|
||||||
|
|
||||||
|
public SpsData(int width, int height, float pixelWidthAspectRatio) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
|
private static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
|
||||||
|
|
||||||
private static final int[] AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE = new int[] {
|
private static final int[] AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE = new int[] {
|
||||||
@ -37,7 +53,7 @@ public final class CodecSpecificDataUtil {
|
|||||||
0, 1, 2, 3, 4, 5, 6, 8
|
0, 1, 2, 3, 4, 5, 6, 8
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int SPS_NAL_UNIT_TYPE = 7;
|
private static final String TAG = "CodecSpecificDataUtil";
|
||||||
|
|
||||||
private CodecSpecificDataUtil() {}
|
private CodecSpecificDataUtil() {}
|
||||||
|
|
||||||
@ -188,88 +204,117 @@ public final class CodecSpecificDataUtil {
|
|||||||
/**
|
/**
|
||||||
* Parses an SPS NAL unit.
|
* Parses an SPS NAL unit.
|
||||||
*
|
*
|
||||||
* @param spsNalUnit The NAL unit.
|
* @param bitArray A {@link ParsableBitArray} containing the SPS data. The position must to set
|
||||||
* @return A pair consisting of AVC profile and level constants, as defined in
|
* to the start of the data (i.e. the first bit of the profile_idc field).
|
||||||
* {@link CodecProfileLevel}. Null if the input data was not an SPS NAL unit.
|
* @return A parsed representation of the SPS data.
|
||||||
*/
|
*/
|
||||||
public static Pair<Integer, Integer> parseSpsNalUnit(byte[] spsNalUnit) {
|
public static SpsData parseSpsNalUnit(ParsableBitArray bitArray) {
|
||||||
// SPS NAL unit:
|
int profileIdc = bitArray.readBits(8);
|
||||||
// - Start prefix (4 bytes)
|
bitArray.skipBits(16); // constraint bits (6), reserved (2) and level_idc (8)
|
||||||
// - Forbidden zero bit (1 bit)
|
bitArray.readUnsignedExpGolombCodedInt(); // seq_parameter_set_id
|
||||||
// - NAL ref idx (2 bits)
|
|
||||||
// - NAL unit type (5 bits)
|
|
||||||
// - Profile idc (8 bits)
|
|
||||||
// - Constraint bits (3 bits)
|
|
||||||
// - Reserved bits (5 bits)
|
|
||||||
// - Level idx (8 bits)
|
|
||||||
if (isNalStartCode(spsNalUnit, 0) && spsNalUnit.length == 8
|
|
||||||
&& (spsNalUnit[5] & 0x1F) == SPS_NAL_UNIT_TYPE) {
|
|
||||||
return Pair.create(parseAvcProfile(spsNalUnit), parseAvcLevel(spsNalUnit));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
int chromaFormatIdc = 1; // Default is 4:2:0
|
||||||
private static int parseAvcProfile(byte[] data) {
|
if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244
|
||||||
int profileIdc = data[6] & 0xFF;
|
|| profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118
|
||||||
switch (profileIdc) {
|
|| profileIdc == 128 || profileIdc == 138) {
|
||||||
case 0x42:
|
chromaFormatIdc = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
return CodecProfileLevel.AVCProfileBaseline;
|
if (chromaFormatIdc == 3) {
|
||||||
case 0x4d:
|
bitArray.skipBits(1); // separate_colour_plane_flag
|
||||||
return CodecProfileLevel.AVCProfileMain;
|
}
|
||||||
case 0x58:
|
bitArray.readUnsignedExpGolombCodedInt(); // bit_depth_luma_minus8
|
||||||
return CodecProfileLevel.AVCProfileExtended;
|
bitArray.readUnsignedExpGolombCodedInt(); // bit_depth_chroma_minus8
|
||||||
case 0x64:
|
bitArray.skipBits(1); // qpprime_y_zero_transform_bypass_flag
|
||||||
return CodecProfileLevel.AVCProfileHigh;
|
boolean seqScalingMatrixPresentFlag = bitArray.readBit();
|
||||||
case 0x6e:
|
if (seqScalingMatrixPresentFlag) {
|
||||||
return CodecProfileLevel.AVCProfileHigh10;
|
int limit = (chromaFormatIdc != 3) ? 8 : 12;
|
||||||
case 0x7a:
|
for (int i = 0; i < limit; i++) {
|
||||||
return CodecProfileLevel.AVCProfileHigh422;
|
boolean seqScalingListPresentFlag = bitArray.readBit();
|
||||||
case 0xf4:
|
if (seqScalingListPresentFlag) {
|
||||||
return CodecProfileLevel.AVCProfileHigh444;
|
skipScalingList(bitArray, i < 6 ? 16 : 64);
|
||||||
default:
|
}
|
||||||
return 0;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
bitArray.readUnsignedExpGolombCodedInt(); // log2_max_frame_num_minus4
|
||||||
private static int parseAvcLevel(byte[] data) {
|
long picOrderCntType = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
int levelIdc = data[8] & 0xFF;
|
if (picOrderCntType == 0) {
|
||||||
switch (levelIdc) {
|
bitArray.readUnsignedExpGolombCodedInt(); // log2_max_pic_order_cnt_lsb_minus4
|
||||||
case 9:
|
} else if (picOrderCntType == 1) {
|
||||||
return CodecProfileLevel.AVCLevel1b;
|
bitArray.skipBits(1); // delta_pic_order_always_zero_flag
|
||||||
case 10:
|
bitArray.readSignedExpGolombCodedInt(); // offset_for_non_ref_pic
|
||||||
return CodecProfileLevel.AVCLevel1;
|
bitArray.readSignedExpGolombCodedInt(); // offset_for_top_to_bottom_field
|
||||||
case 11:
|
long numRefFramesInPicOrderCntCycle = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
return CodecProfileLevel.AVCLevel11;
|
for (int i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
|
||||||
case 12:
|
bitArray.readUnsignedExpGolombCodedInt(); // offset_for_ref_frame[i]
|
||||||
return CodecProfileLevel.AVCLevel12;
|
}
|
||||||
case 13:
|
}
|
||||||
return CodecProfileLevel.AVCLevel13;
|
bitArray.readUnsignedExpGolombCodedInt(); // max_num_ref_frames
|
||||||
case 20:
|
bitArray.skipBits(1); // gaps_in_frame_num_value_allowed_flag
|
||||||
return CodecProfileLevel.AVCLevel2;
|
|
||||||
case 21:
|
int picWidthInMbs = bitArray.readUnsignedExpGolombCodedInt() + 1;
|
||||||
return CodecProfileLevel.AVCLevel21;
|
int picHeightInMapUnits = bitArray.readUnsignedExpGolombCodedInt() + 1;
|
||||||
case 22:
|
boolean frameMbsOnlyFlag = bitArray.readBit();
|
||||||
return CodecProfileLevel.AVCLevel22;
|
int frameHeightInMbs = (2 - (frameMbsOnlyFlag ? 1 : 0)) * picHeightInMapUnits;
|
||||||
case 30:
|
if (!frameMbsOnlyFlag) {
|
||||||
return CodecProfileLevel.AVCLevel3;
|
bitArray.skipBits(1); // mb_adaptive_frame_field_flag
|
||||||
case 31:
|
}
|
||||||
return CodecProfileLevel.AVCLevel31;
|
|
||||||
case 32:
|
bitArray.skipBits(1); // direct_8x8_inference_flag
|
||||||
return CodecProfileLevel.AVCLevel32;
|
int frameWidth = picWidthInMbs * 16;
|
||||||
case 40:
|
int frameHeight = frameHeightInMbs * 16;
|
||||||
return CodecProfileLevel.AVCLevel4;
|
boolean frameCroppingFlag = bitArray.readBit();
|
||||||
case 41:
|
if (frameCroppingFlag) {
|
||||||
return CodecProfileLevel.AVCLevel41;
|
int frameCropLeftOffset = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
case 42:
|
int frameCropRightOffset = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
return CodecProfileLevel.AVCLevel42;
|
int frameCropTopOffset = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
case 50:
|
int frameCropBottomOffset = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
return CodecProfileLevel.AVCLevel5;
|
int cropUnitX, cropUnitY;
|
||||||
case 51:
|
if (chromaFormatIdc == 0) {
|
||||||
return CodecProfileLevel.AVCLevel51;
|
cropUnitX = 1;
|
||||||
default:
|
cropUnitY = 2 - (frameMbsOnlyFlag ? 1 : 0);
|
||||||
return 0;
|
} else {
|
||||||
|
int subWidthC = (chromaFormatIdc == 3) ? 1 : 2;
|
||||||
|
int subHeightC = (chromaFormatIdc == 1) ? 2 : 1;
|
||||||
|
cropUnitX = subWidthC;
|
||||||
|
cropUnitY = subHeightC * (2 - (frameMbsOnlyFlag ? 1 : 0));
|
||||||
|
}
|
||||||
|
frameWidth -= (frameCropLeftOffset + frameCropRightOffset) * cropUnitX;
|
||||||
|
frameHeight -= (frameCropTopOffset + frameCropBottomOffset) * cropUnitY;
|
||||||
|
}
|
||||||
|
|
||||||
|
float pixelWidthHeightRatio = 1;
|
||||||
|
boolean vuiParametersPresentFlag = bitArray.readBit();
|
||||||
|
if (vuiParametersPresentFlag) {
|
||||||
|
boolean aspectRatioInfoPresentFlag = bitArray.readBit();
|
||||||
|
if (aspectRatioInfoPresentFlag) {
|
||||||
|
int aspectRatioIdc = bitArray.readBits(8);
|
||||||
|
if (aspectRatioIdc == NalUnitUtil.EXTENDED_SAR) {
|
||||||
|
int sarWidth = bitArray.readBits(16);
|
||||||
|
int sarHeight = bitArray.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SpsData(frameWidth, frameHeight, pixelWidthHeightRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void skipScalingList(ParsableBitArray bitArray, int size) {
|
||||||
|
int lastScale = 8;
|
||||||
|
int nextScale = 8;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
if (nextScale != 0) {
|
||||||
|
int deltaScale = bitArray.readSignedExpGolombCodedInt();
|
||||||
|
nextScale = (lastScale + deltaScale + 256) % 256;
|
||||||
|
}
|
||||||
|
lastScale = (nextScale == 0) ? lastScale : nextScale;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user