Commit 8353463b by andrewlewis Committed by Oliver Woodman

Detect sample boundaries in H.264 TSs without AUDs.

Issue: #1263
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=117813106
parent fc716b57
...@@ -269,11 +269,11 @@ public final class VorbisBitArrayTest extends TestCase { ...@@ -269,11 +269,11 @@ public final class VorbisBitArrayTest extends TestCase {
assertEquals(10, bitArray.bitsLeft()); assertEquals(10, bitArray.bitsLeft());
assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft());
bitArray.readBit(); bitArray.skipBits(1);
assertEquals(9, bitArray.bitsLeft()); assertEquals(9, bitArray.bitsLeft());
assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft());
bitArray.readBits(1); bitArray.skipBits(1);
assertEquals(8, bitArray.bitsLeft()); assertEquals(8, bitArray.bitsLeft());
assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft()); assertEquals(bitArray.limit(), bitArray.getPosition() + bitArray.bitsLeft());
......
...@@ -20,7 +20,6 @@ import com.google.android.exoplayer.Format; ...@@ -20,7 +20,6 @@ import com.google.android.exoplayer.Format;
import com.google.android.exoplayer.ParserException; import com.google.android.exoplayer.ParserException;
import com.google.android.exoplayer.extractor.TrackOutput; import com.google.android.exoplayer.extractor.TrackOutput;
import com.google.android.exoplayer.util.Assertions; import com.google.android.exoplayer.util.Assertions;
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.ParsableBitArray;
...@@ -165,7 +164,7 @@ import java.util.List; ...@@ -165,7 +164,7 @@ import java.util.List;
ParsableBitArray spsDataBitArray = new ParsableBitArray(initializationData.get(0)); ParsableBitArray spsDataBitArray = new ParsableBitArray(initializationData.get(0));
// Skip the NAL header consisting of the nalUnitLengthField and the type (1 byte). // Skip the NAL header consisting of the nalUnitLengthField and the type (1 byte).
spsDataBitArray.setPosition(8 * (nalUnitLengthFieldLength + 1)); spsDataBitArray.setPosition(8 * (nalUnitLengthFieldLength + 1));
CodecSpecificDataUtil.SpsData sps = CodecSpecificDataUtil.parseSpsNalUnit(spsDataBitArray); NalUnitUtil.SpsData sps = NalUnitUtil.parseSpsNalUnit(spsDataBitArray);
width = sps.width; width = sps.width;
height = sps.height; height = sps.height;
pixelWidthAspectRatio = sps.pixelWidthAspectRatio; pixelWidthAspectRatio = sps.pixelWidthAspectRatio;
......
...@@ -669,8 +669,7 @@ import java.util.List; ...@@ -669,8 +669,7 @@ import java.util.List;
ParsableBitArray spsDataBitArray = new ParsableBitArray(initializationData.get(0)); ParsableBitArray spsDataBitArray = new ParsableBitArray(initializationData.get(0));
// Skip the NAL header consisting of the nalUnitLengthField and the type (1 byte). // Skip the NAL header consisting of the nalUnitLengthField and the type (1 byte).
spsDataBitArray.setPosition(8 * (nalUnitLengthFieldLength + 1)); spsDataBitArray.setPosition(8 * (nalUnitLengthFieldLength + 1));
pixelWidthAspectRatio = CodecSpecificDataUtil.parseSpsNalUnit(spsDataBitArray) pixelWidthAspectRatio = NalUnitUtil.parseSpsNalUnit(spsDataBitArray).pixelWidthAspectRatio;
.pixelWidthAspectRatio;
} }
return new AvcCData(initializationData, nalUnitLengthFieldLength, pixelWidthAspectRatio); return new AvcCData(initializationData, nalUnitLengthFieldLength, pixelWidthAspectRatio);
......
...@@ -64,6 +64,10 @@ import java.util.Collections; ...@@ -64,6 +64,10 @@ import java.util.Collections;
// Scratch variables to avoid allocations. // Scratch variables to avoid allocations.
private final ParsableByteArray seiWrapper; private final ParsableByteArray seiWrapper;
/**
* @param output A {@link TrackOutput} to which H.265 samples should be written.
* @param seiReader A reader for EIA-608 samples in SEI NAL units.
*/
public H265Reader(TrackOutput output, SeiReader seiReader) { public H265Reader(TrackOutput output, SeiReader seiReader) {
super(output); super(output);
this.seiReader = seiReader; this.seiReader = seiReader;
...@@ -130,7 +134,7 @@ import java.util.Collections; ...@@ -130,7 +134,7 @@ import java.util.Collections;
// Indicate the end of the previous NAL unit. If the length to the start of the next unit // Indicate the end of the previous NAL unit. If the length to the start of the next unit
// is negative then we wrote too many bytes to the NAL buffers. Discard the excess bytes // is negative then we wrote too many bytes to the NAL buffers. Discard the excess bytes
// when notifying that the unit has ended. // when notifying that the unit has ended.
nalUnitEnd(absolutePosition, bytesWrittenPastPosition, endNalUnit(absolutePosition, bytesWrittenPastPosition,
lengthToNalUnit < 0 ? -lengthToNalUnit : 0, pesTimeUs); lengthToNalUnit < 0 ? -lengthToNalUnit : 0, pesTimeUs);
// Indicate the start of the next NAL unit. // Indicate the start of the next NAL unit.
startNalUnit(absolutePosition, bytesWrittenPastPosition, nalUnitType, pesTimeUs); startNalUnit(absolutePosition, bytesWrittenPastPosition, nalUnitType, pesTimeUs);
...@@ -168,7 +172,7 @@ import java.util.Collections; ...@@ -168,7 +172,7 @@ import java.util.Collections;
suffixSei.appendToNalUnit(dataArray, offset, limit); suffixSei.appendToNalUnit(dataArray, offset, limit);
} }
private void nalUnitEnd(long position, int offset, int discardPadding, long pesTimeUs) { private void endNalUnit(long position, int offset, int discardPadding, long pesTimeUs) {
if (hasOutputFormat) { if (hasOutputFormat) {
sampleReader.endNalUnit(position, offset); sampleReader.endNalUnit(position, offset);
} else { } else {
...@@ -218,10 +222,10 @@ import java.util.Collections; ...@@ -218,10 +222,10 @@ import java.util.Collections;
bitArray.skipBits(8); // general_level_idc bitArray.skipBits(8); // general_level_idc
int toSkip = 0; int toSkip = 0;
for (int i = 0; i < maxSubLayersMinus1; i++) { for (int i = 0; i < maxSubLayersMinus1; i++) {
if (bitArray.readBits(1) == 1) { // sub_layer_profile_present_flag[i] if (bitArray.readBit()) { // sub_layer_profile_present_flag[i]
toSkip += 89; toSkip += 89;
} }
if (bitArray.readBits(1) == 1) { // sub_layer_level_present_flag[i] if (bitArray.readBit()) { // sub_layer_level_present_flag[i]
toSkip += 8; toSkip += 8;
} }
} }
...@@ -309,7 +313,9 @@ import java.util.Collections; ...@@ -309,7 +313,9 @@ import java.util.Collections;
Collections.singletonList(csd), Format.NO_VALUE, pixelWidthHeightRatio); Collections.singletonList(csd), Format.NO_VALUE, pixelWidthHeightRatio);
} }
/** Skips scaling_list_data(). See H.265/HEVC (2014) 7.3.4. */ /**
* Skips scaling_list_data(). See H.265/HEVC (2014) 7.3.4.
*/
private static void skipScalingList(ParsableBitArray bitArray) { private static void skipScalingList(ParsableBitArray bitArray) {
for (int sizeId = 0; sizeId < 4; sizeId++) { for (int sizeId = 0; sizeId < 4; sizeId++) {
for (int matrixId = 0; matrixId < 6; matrixId += sizeId == 3 ? 3 : 1) { for (int matrixId = 0; matrixId < 6; matrixId += sizeId == 3 ? 3 : 1) {
......
...@@ -40,6 +40,7 @@ public final class TsExtractor implements Extractor { ...@@ -40,6 +40,7 @@ public final class TsExtractor implements Extractor {
public static final int WORKAROUND_ALLOW_NON_IDR_KEYFRAMES = 1; public static final int WORKAROUND_ALLOW_NON_IDR_KEYFRAMES = 1;
public static final int WORKAROUND_IGNORE_AAC_STREAM = 2; public static final int WORKAROUND_IGNORE_AAC_STREAM = 2;
public static final int WORKAROUND_IGNORE_H264_STREAM = 4; public static final int WORKAROUND_IGNORE_H264_STREAM = 4;
public static final int WORKAROUND_DETECT_ACCESS_UNITS = 8;
private static final String TAG = "TsExtractor"; private static final String TAG = "TsExtractor";
...@@ -362,7 +363,8 @@ public final class TsExtractor implements Extractor { ...@@ -362,7 +363,8 @@ public final class TsExtractor implements Extractor {
pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_H264_STREAM) != 0 ? null pesPayloadReader = (workaroundFlags & WORKAROUND_IGNORE_H264_STREAM) != 0 ? null
: new H264Reader(output.track(TS_STREAM_TYPE_H264), : new H264Reader(output.track(TS_STREAM_TYPE_H264),
new SeiReader(output.track(TS_STREAM_TYPE_EIA608)), new SeiReader(output.track(TS_STREAM_TYPE_EIA608)),
(workaroundFlags & WORKAROUND_ALLOW_NON_IDR_KEYFRAMES) != 0); (workaroundFlags & WORKAROUND_ALLOW_NON_IDR_KEYFRAMES) != 0,
(workaroundFlags & WORKAROUND_DETECT_ACCESS_UNITS) != 0);
break; break;
case TS_STREAM_TYPE_H265: case TS_STREAM_TYPE_H265:
pesPayloadReader = new H265Reader(output.track(TS_STREAM_TYPE_H265), pesPayloadReader = new H265Reader(output.track(TS_STREAM_TYPE_H265),
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
*/ */
package com.google.android.exoplayer.util; package com.google.android.exoplayer.util;
import android.util.Log;
import android.util.Pair; import android.util.Pair;
import java.util.ArrayList; import java.util.ArrayList;
...@@ -26,23 +25,6 @@ import java.util.List; ...@@ -26,23 +25,6 @@ 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_FREQUENCY_INDEX_ARBITRARY = 0xF; private static final int AUDIO_SPECIFIC_CONFIG_FREQUENCY_INDEX_ARBITRARY = 0xF;
...@@ -94,8 +76,6 @@ public final class CodecSpecificDataUtil { ...@@ -94,8 +76,6 @@ public final class CodecSpecificDataUtil {
// Parametric Stereo. // Parametric Stereo.
private static final int AUDIO_OBJECT_TYPE_PS = 29; private static final int AUDIO_OBJECT_TYPE_PS = 29;
private static final String TAG = "CodecSpecificDataUtil";
private CodecSpecificDataUtil() {} private CodecSpecificDataUtil() {}
/** /**
...@@ -267,121 +247,4 @@ public final class CodecSpecificDataUtil { ...@@ -267,121 +247,4 @@ public final class CodecSpecificDataUtil {
return true; return true;
} }
/**
* Parses an SPS NAL unit.
*
* @param bitArray A {@link ParsableBitArray} containing the SPS data. The position must to set
* to the start of the data (i.e. the first bit of the profile_idc field).
* @return A parsed representation of the SPS data.
*/
public static SpsData parseSpsNalUnit(ParsableBitArray bitArray) {
int profileIdc = bitArray.readBits(8);
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);
}
}
}
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;
}
}
} }
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
*/ */
package com.google.android.exoplayer.util; package com.google.android.exoplayer.util;
import android.util.Log;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays; import java.util.Arrays;
...@@ -23,6 +25,59 @@ import java.util.Arrays; ...@@ -23,6 +25,59 @@ import java.util.Arrays;
*/ */
public final class NalUnitUtil { public final class NalUnitUtil {
private static final String TAG = "NalUnitUtil";
/**
* Holds data parsed from a sequence parameter set NAL unit.
*/
public static final class SpsData {
public final int seqParameterSetId;
public final int width;
public final int height;
public final float pixelWidthAspectRatio;
public final boolean separateColorPlaneFlag;
public final boolean frameMbsOnlyFlag;
public final int frameNumLength;
public final int picOrderCountType;
public final int picOrderCntLsbLength;
public final boolean deltaPicOrderAlwaysZeroFlag;
public SpsData(int seqParameterSetId, int width, int height, float pixelWidthAspectRatio,
boolean separateColorPlaneFlag, boolean frameMbsOnlyFlag, int frameNumLength,
int picOrderCountType, int picOrderCntLsbLength, boolean deltaPicOrderAlwaysZeroFlag) {
this.seqParameterSetId = seqParameterSetId;
this.width = width;
this.height = height;
this.pixelWidthAspectRatio = pixelWidthAspectRatio;
this.separateColorPlaneFlag = separateColorPlaneFlag;
this.frameMbsOnlyFlag = frameMbsOnlyFlag;
this.frameNumLength = frameNumLength;
this.picOrderCountType = picOrderCountType;
this.picOrderCntLsbLength = picOrderCntLsbLength;
this.deltaPicOrderAlwaysZeroFlag = deltaPicOrderAlwaysZeroFlag;
}
}
/**
* Holds data parsed from a picture parameter set NAL unit.
*/
public static final class PpsData {
public final int picParameterSetId;
public final int seqParameterSetId;
public final boolean bottomFieldPicOrderInFramePresentFlag;
public PpsData(int picParameterSetId, int seqParameterSetId,
boolean bottomFieldPicOrderInFramePresentFlag) {
this.picParameterSetId = picParameterSetId;
this.seqParameterSetId = seqParameterSetId;
this.bottomFieldPicOrderInFramePresentFlag = bottomFieldPicOrderInFramePresentFlag;
}
}
/** Four initial bytes that must prefix NAL units for decoding. */ /** Four initial bytes that must prefix NAL units for decoding. */
public static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1}; public static final byte[] NAL_START_CODE = new byte[] {0, 0, 0, 1};
...@@ -178,6 +233,134 @@ public final class NalUnitUtil { ...@@ -178,6 +233,134 @@ public final class NalUnitUtil {
} }
/** /**
* Parses an SPS NAL unit using the syntax defined in ITU-T Recommendation H.264 (2013) subsection
* 7.3.2.1.1.
*
* @param data A {@link ParsableBitArray} containing the SPS data. The position must to set to the
* start of the data (i.e. the first bit of the profile_idc field).
* @return A parsed representation of the SPS data.
*/
public static SpsData parseSpsNalUnit(ParsableBitArray data) {
int profileIdc = data.readBits(8);
data.skipBits(16); // constraint bits (6), reserved (2) and level_idc (8)
int seqParameterSetId = data.readUnsignedExpGolombCodedInt();
int chromaFormatIdc = 1; // Default is 4:2:0
boolean separateColorPlaneFlag = false;
if (profileIdc == 100 || profileIdc == 110 || profileIdc == 122 || profileIdc == 244
|| profileIdc == 44 || profileIdc == 83 || profileIdc == 86 || profileIdc == 118
|| profileIdc == 128 || profileIdc == 138) {
chromaFormatIdc = data.readUnsignedExpGolombCodedInt();
if (chromaFormatIdc == 3) {
separateColorPlaneFlag = data.readBit();
}
data.readUnsignedExpGolombCodedInt(); // bit_depth_luma_minus8
data.readUnsignedExpGolombCodedInt(); // bit_depth_chroma_minus8
data.skipBits(1); // qpprime_y_zero_transform_bypass_flag
boolean seqScalingMatrixPresentFlag = data.readBit();
if (seqScalingMatrixPresentFlag) {
int limit = (chromaFormatIdc != 3) ? 8 : 12;
for (int i = 0; i < limit; i++) {
boolean seqScalingListPresentFlag = data.readBit();
if (seqScalingListPresentFlag) {
skipScalingList(data, i < 6 ? 16 : 64);
}
}
}
}
int frameNumLength = data.readUnsignedExpGolombCodedInt() + 4; // log2_max_frame_num_minus4 + 4
int picOrderCntType = data.readUnsignedExpGolombCodedInt();
int picOrderCntLsbLength = 0;
boolean deltaPicOrderAlwaysZeroFlag = false;
if (picOrderCntType == 0) {
// log2_max_pic_order_cnt_lsb_minus4 + 4
picOrderCntLsbLength = data.readUnsignedExpGolombCodedInt() + 4;
} else if (picOrderCntType == 1) {
deltaPicOrderAlwaysZeroFlag = data.readBit(); // delta_pic_order_always_zero_flag
data.readSignedExpGolombCodedInt(); // offset_for_non_ref_pic
data.readSignedExpGolombCodedInt(); // offset_for_top_to_bottom_field
long numRefFramesInPicOrderCntCycle = data.readUnsignedExpGolombCodedInt();
for (int i = 0; i < numRefFramesInPicOrderCntCycle; i++) {
data.readUnsignedExpGolombCodedInt(); // offset_for_ref_frame[i]
}
}
data.readUnsignedExpGolombCodedInt(); // max_num_ref_frames
data.skipBits(1); // gaps_in_frame_num_value_allowed_flag
int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1;
int picHeightInMapUnits = data.readUnsignedExpGolombCodedInt() + 1;
boolean frameMbsOnlyFlag = data.readBit();
int frameHeightInMbs = (2 - (frameMbsOnlyFlag ? 1 : 0)) * picHeightInMapUnits;
if (!frameMbsOnlyFlag) {
data.skipBits(1); // mb_adaptive_frame_field_flag
}
data.skipBits(1); // direct_8x8_inference_flag
int frameWidth = picWidthInMbs * 16;
int frameHeight = frameHeightInMbs * 16;
boolean frameCroppingFlag = data.readBit();
if (frameCroppingFlag) {
int frameCropLeftOffset = data.readUnsignedExpGolombCodedInt();
int frameCropRightOffset = data.readUnsignedExpGolombCodedInt();
int frameCropTopOffset = data.readUnsignedExpGolombCodedInt();
int frameCropBottomOffset = data.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 = data.readBit();
if (vuiParametersPresentFlag) {
boolean aspectRatioInfoPresentFlag = data.readBit();
if (aspectRatioInfoPresentFlag) {
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);
}
}
}
return new SpsData(seqParameterSetId, frameWidth, frameHeight, pixelWidthHeightRatio,
separateColorPlaneFlag, frameMbsOnlyFlag, frameNumLength, picOrderCntType,
picOrderCntLsbLength, deltaPicOrderAlwaysZeroFlag);
}
/**
* Parses a PPS NAL unit using the syntax defined in ITU-T Recommendation H.264 (2013) subsection
* 7.3.2.2.
*
* @param data A {@link ParsableBitArray} containing the PPS data. The position must to set to the
* start of the data (i.e. the first bit of the pic_parameter_set_id field).
* @return A parsed representation of the PPS data.
*/
public static PpsData parsePpsNalUnit(ParsableBitArray data) {
int picParameterSetId = data.readUnsignedExpGolombCodedInt();
int seqParameterSetId = data.readUnsignedExpGolombCodedInt();
data.skipBits(1); // entropy_coding_mode_flag
boolean bottomFieldPicOrderInFramePresentFlag = data.readBit();
return new PpsData(picParameterSetId, seqParameterSetId, bottomFieldPicOrderInFramePresentFlag);
}
/**
* Finds the first NAL unit in {@code data}. * Finds the first NAL unit in {@code data}.
* <p> * <p>
* If {@code prefixFlags} is null then the first three bytes of a NAL unit must be entirely * If {@code prefixFlags} is null then the first three bytes of a NAL unit must be entirely
...@@ -276,6 +459,18 @@ public final class NalUnitUtil { ...@@ -276,6 +459,18 @@ public final class NalUnitUtil {
return limit; return limit;
} }
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;
}
}
private NalUnitUtil() { private NalUnitUtil() {
// Prevent instantiation. // Prevent instantiation.
} }
......
...@@ -178,12 +178,12 @@ public final class ParsableBitArray { ...@@ -178,12 +178,12 @@ public final class ParsableBitArray {
} }
/** /**
* Peeks the length of an Exp-Golomb-coded integer (signed or unsigned) starting from the current * Returns whether it is possible to read an Exp-Golomb-coded integer starting from the current
* offset, returning the length or -1 if the limit is reached. * offset. The offset is not modified.
* *
* @return The length of the Exp-Golob-coded integer, or -1. * @return Whether it is possible to read an Exp-Golomb-coded integer.
*/ */
public int peekExpGolombCodedNumLength() { public boolean canReadExpGolombCodedNum() {
int initialByteOffset = byteOffset; int initialByteOffset = byteOffset;
int initialBitOffset = bitOffset; int initialBitOffset = bitOffset;
int leadingZeros = 0; int leadingZeros = 0;
...@@ -193,7 +193,7 @@ public final class ParsableBitArray { ...@@ -193,7 +193,7 @@ public final class ParsableBitArray {
boolean hitLimit = byteOffset == byteLimit; boolean hitLimit = byteOffset == byteLimit;
byteOffset = initialByteOffset; byteOffset = initialByteOffset;
bitOffset = initialBitOffset; bitOffset = initialBitOffset;
return hitLimit ? -1 : leadingZeros * 2 + 1; return !hitLimit && bitsLeft() >= leadingZeros * 2 + 1;
} }
/** /**
......
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