Get frame rate from mkv. Get frame rate from AvcConfig.

This commit is contained in:
Will 2020-09-19 11:30:14 +08:00
parent 8bed008934
commit 989a97ebbd
6 changed files with 110 additions and 2 deletions

View File

@ -38,6 +38,7 @@ public final class NalUnitUtil {
public final int width; public final int width;
public final int height; public final int height;
public final float pixelWidthAspectRatio; public final float pixelWidthAspectRatio;
public final float frameRate;
public final boolean separateColorPlaneFlag; public final boolean separateColorPlaneFlag;
public final boolean frameMbsOnlyFlag; public final boolean frameMbsOnlyFlag;
public final int frameNumLength; public final int frameNumLength;
@ -53,6 +54,7 @@ public final class NalUnitUtil {
int width, int width,
int height, int height,
float pixelWidthAspectRatio, float pixelWidthAspectRatio,
float frameRate,
boolean separateColorPlaneFlag, boolean separateColorPlaneFlag,
boolean frameMbsOnlyFlag, boolean frameMbsOnlyFlag,
int frameNumLength, int frameNumLength,
@ -66,6 +68,7 @@ public final class NalUnitUtil {
this.width = width; this.width = width;
this.height = height; this.height = height;
this.pixelWidthAspectRatio = pixelWidthAspectRatio; this.pixelWidthAspectRatio = pixelWidthAspectRatio;
this.frameRate = frameRate;
this.separateColorPlaneFlag = separateColorPlaneFlag; this.separateColorPlaneFlag = separateColorPlaneFlag;
this.frameMbsOnlyFlag = frameMbsOnlyFlag; this.frameMbsOnlyFlag = frameMbsOnlyFlag;
this.frameNumLength = frameNumLength; this.frameNumLength = frameNumLength;
@ -349,6 +352,7 @@ public final class NalUnitUtil {
} }
float pixelWidthHeightRatio = 1; float pixelWidthHeightRatio = 1;
float frameRate = -1;
boolean vuiParametersPresentFlag = data.readBit(); boolean vuiParametersPresentFlag = data.readBit();
if (vuiParametersPresentFlag) { if (vuiParametersPresentFlag) {
boolean aspectRatioInfoPresentFlag = data.readBit(); boolean aspectRatioInfoPresentFlag = data.readBit();
@ -366,6 +370,68 @@ public final class NalUnitUtil {
Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc); Log.w(TAG, "Unexpected aspect_ratio_idc value: " + aspectRatioIdc);
} }
} }
// https://www.androidos.net.cn/android/9.0.0_r8/xref/external/mp4parser/isoparser/src/main/java/com/googlecode/mp4parser/h264/model/SeqParameterSet.java
boolean overscan_info_present_flag = data.readBit();
if (overscan_info_present_flag) {
int overscan_appropriate_flagu = data.readBits(1);
}
boolean video_signal_type_present_flag = data.readBit();
if (video_signal_type_present_flag) {
int video_format = data.readBits(3);
int video_full_range_flag = data.readBits(1);
boolean colour_description_present_flag = data.readBit();
if (colour_description_present_flag) {
int colour_primaries = data.readBits(8);
int transfer_characteristics = data.readBits(8);
int matrix_coefficients = data.readBits(8);
}
}
boolean chroma_loc_info_present_flag = data.readBit();
if (chroma_loc_info_present_flag) {
int chroma_sample_loc_type_top_field = data.readUnsignedExpGolombCodedInt();
int chroma_sample_loc_type_bottom_field = data.readUnsignedExpGolombCodedInt();
} else {
boolean timing_info_present_flag = data.readBit();
if (timing_info_present_flag) {
int num_units_in_tick = data.readBits(32);
int time_scale = data.readBits(32);
frameRate = (float) time_scale / (float) num_units_in_tick;
boolean fixed_frame_rate_flag = data.readBit();
if (fixed_frame_rate_flag) {
frameRate = frameRate / 2f;
} else if (frameRate > 40f) {
frameRate = frameRate / 2f;
}
}
boolean nal_hrd_parameters_present_flag = data.readBit();
if (nal_hrd_parameters_present_flag)
readHRDParameters(data); //nalHRDParams
boolean vcl_hrd_parameters_present_flag = data.readBit();
if (vcl_hrd_parameters_present_flag)
readHRDParameters(data); //vclHRDParams
if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) {
boolean low_delay_hrd_flag = data.readBit();
}
boolean pic_struct_present_flag = data.readBit();
boolean bitstream_restriction_flag = data.readBit();
if (bitstream_restriction_flag) {/*
vuip.bitstreamRestriction = new VUIParameters.BitstreamRestriction();
vuip.bitstreamRestriction.motion_vectors_over_pic_boundaries_flag = reader
.readBool("VUI: motion_vectors_over_pic_boundaries_flag");
vuip.bitstreamRestriction.max_bytes_per_pic_denom = reader
.readUE("VUI max_bytes_per_pic_denom");
vuip.bitstreamRestriction.max_bits_per_mb_denom = reader
.readUE("VUI max_bits_per_mb_denom");
vuip.bitstreamRestriction.log2_max_mv_length_horizontal = reader
.readUE("VUI log2_max_mv_length_horizontal");
vuip.bitstreamRestriction.log2_max_mv_length_vertical = reader
.readUE("VUI log2_max_mv_length_vertical");
vuip.bitstreamRestriction.num_reorder_frames = reader
.readUE("VUI num_reorder_frames");
vuip.bitstreamRestriction.max_dec_frame_buffering = reader
.readUE("VUI max_dec_frame_buffering");*/
}
}
} }
return new SpsData( return new SpsData(
@ -376,6 +442,7 @@ public final class NalUnitUtil {
frameWidth, frameWidth,
frameHeight, frameHeight,
pixelWidthHeightRatio, pixelWidthHeightRatio,
frameRate,
separateColorPlaneFlag, separateColorPlaneFlag,
frameMbsOnlyFlag, frameMbsOnlyFlag,
frameNumLength, frameNumLength,
@ -384,6 +451,29 @@ public final class NalUnitUtil {
deltaPicOrderAlwaysZeroFlag); deltaPicOrderAlwaysZeroFlag);
} }
private static void readHRDParameters(ParsableNalUnitBitArray data) {
// HRDParameters hrd = new HRDParameters();
// hrd.cpb_cnt_minus1 = reader.readUE("SPS: cpb_cnt_minus1");
int cpb_cnt_minus1 = data.readUnsignedExpGolombCodedInt();
// hrd.bit_rate_scale = (int) reader.readNBit(4, "HRD: bit_rate_scale");
int bit_rate_scale = data.readBits(4);
// hrd.cpb_size_scale = (int) reader.readNBit(4, "HRD: cpb_size_scale");
int cpb_size_scale = data.readBits(4);
int[] bit_rate_value_minus1 = new int[cpb_cnt_minus1 + 1];
int[] cpb_size_value_minus1 = new int[cpb_cnt_minus1 + 1];
boolean[] cbr_flag = new boolean[cpb_cnt_minus1 + 1];
for (int SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++) {
bit_rate_value_minus1[SchedSelIdx] = data.readUnsignedExpGolombCodedInt();
cpb_size_value_minus1[SchedSelIdx] = data.readUnsignedExpGolombCodedInt();
cbr_flag[SchedSelIdx] = data.readBit();
}
int initial_cpb_removal_delay_length_minus1 = (int) data.readBits(5);
int cpb_removal_delay_length_minus1 = data.readBits(5);
int dpb_output_delay_length_minus1 = data.readBits(5);
int time_offset_length = data.readBits(5);
}
/** /**
* 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.

View File

@ -34,6 +34,7 @@ public final class AvcConfig {
public final int width; public final int width;
public final int height; public final int height;
public final float pixelWidthAspectRatio; public final float pixelWidthAspectRatio;
public final float frameRate;
/** /**
* Parses AVC configuration data. * Parses AVC configuration data.
@ -63,6 +64,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 pixelWidthAspectRatio = 1;
float frameRate = -1;
if (numSequenceParameterSets > 0) { if (numSequenceParameterSets > 0) {
byte[] sps = initializationData.get(0); byte[] sps = initializationData.get(0);
SpsData spsData = NalUnitUtil.parseSpsNalUnit(initializationData.get(0), SpsData spsData = NalUnitUtil.parseSpsNalUnit(initializationData.get(0),
@ -70,21 +72,23 @@ public final class AvcConfig {
width = spsData.width; width = spsData.width;
height = spsData.height; height = spsData.height;
pixelWidthAspectRatio = spsData.pixelWidthAspectRatio; pixelWidthAspectRatio = spsData.pixelWidthAspectRatio;
frameRate = spsData.frameRate;
} }
return new AvcConfig(initializationData, nalUnitLengthFieldLength, width, height, return new AvcConfig(initializationData, nalUnitLengthFieldLength, width, height,
pixelWidthAspectRatio); pixelWidthAspectRatio, frameRate);
} catch (ArrayIndexOutOfBoundsException e) { } catch (ArrayIndexOutOfBoundsException e) {
throw new ParserException("Error parsing AVC config", e); throw new ParserException("Error parsing AVC config", e);
} }
} }
private AvcConfig(List<byte[]> initializationData, int nalUnitLengthFieldLength, private AvcConfig(List<byte[]> initializationData, int nalUnitLengthFieldLength,
int width, int height, float pixelWidthAspectRatio) { int width, int height, float pixelWidthAspectRatio, float frameRate) {
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.pixelWidthAspectRatio = pixelWidthAspectRatio;
this.frameRate = frameRate;
} }
private static byte[] buildNalUnitForChild(ParsableByteArray data) { private static byte[] buildNalUnitForChild(ParsableByteArray data) {

View File

@ -97,6 +97,7 @@ import com.google.android.exoplayer2.video.AvcConfig;
.setHeight(avcConfig.height) .setHeight(avcConfig.height)
.setPixelWidthHeightRatio(avcConfig.pixelWidthAspectRatio) .setPixelWidthHeightRatio(avcConfig.pixelWidthAspectRatio)
.setInitializationData(avcConfig.initializationData) .setInitializationData(avcConfig.initializationData)
.setFrameRate(avcConfig.frameRate < 0 ? Format.NO_VALUE : avcConfig.frameRate)
.build(); .build();
output.format(format); output.format(format);
hasOutputFormat = true; hasOutputFormat = true;

View File

@ -152,6 +152,7 @@ public class MatroskaExtractor implements Extractor {
private static final int ID_INFO = 0x1549A966; private static final int ID_INFO = 0x1549A966;
private static final int ID_TIMECODE_SCALE = 0x2AD7B1; private static final int ID_TIMECODE_SCALE = 0x2AD7B1;
private static final int ID_DURATION = 0x4489; private static final int ID_DURATION = 0x4489;
private static final int ID_FRAME_RATE = 0x2383E3;
private static final int ID_CLUSTER = 0x1F43B675; private static final int ID_CLUSTER = 0x1F43B675;
private static final int ID_TIME_CODE = 0xE7; private static final int ID_TIME_CODE = 0xE7;
private static final int ID_SIMPLE_BLOCK = 0xA3; private static final int ID_SIMPLE_BLOCK = 0xA3;
@ -589,6 +590,7 @@ public class MatroskaExtractor implements Extractor {
case ID_BLOCK_ADDITIONAL: case ID_BLOCK_ADDITIONAL:
return EbmlProcessor.ELEMENT_TYPE_BINARY; return EbmlProcessor.ELEMENT_TYPE_BINARY;
case ID_DURATION: case ID_DURATION:
case ID_FRAME_RATE:
case ID_SAMPLING_FREQUENCY: case ID_SAMPLING_FREQUENCY:
case ID_PRIMARY_R_CHROMATICITY_X: case ID_PRIMARY_R_CHROMATICITY_X:
case ID_PRIMARY_R_CHROMATICITY_Y: case ID_PRIMARY_R_CHROMATICITY_Y:
@ -828,6 +830,7 @@ public class MatroskaExtractor implements Extractor {
break; break;
case ID_DEFAULT_DURATION: case ID_DEFAULT_DURATION:
currentTrack.defaultSampleDurationNs = (int) value; currentTrack.defaultSampleDurationNs = (int) value;
currentTrack.frameRate = (float) 1E9 / value;
break; break;
case ID_MAX_BLOCK_ADDITION_ID: case ID_MAX_BLOCK_ADDITION_ID:
currentTrack.maxBlockAdditionId = (int) value; currentTrack.maxBlockAdditionId = (int) value;
@ -1008,6 +1011,9 @@ public class MatroskaExtractor implements Extractor {
case ID_DURATION: case ID_DURATION:
durationTimecode = (long) value; durationTimecode = (long) value;
break; break;
case ID_FRAME_RATE:
currentTrack.frameRate = (float) value;
break;
case ID_SAMPLING_FREQUENCY: case ID_SAMPLING_FREQUENCY:
currentTrack.sampleRate = (int) value; currentTrack.sampleRate = (int) value;
break; break;
@ -1932,6 +1938,7 @@ public class MatroskaExtractor implements Extractor {
public int displayWidth = Format.NO_VALUE; public int displayWidth = Format.NO_VALUE;
public int displayHeight = Format.NO_VALUE; public int displayHeight = Format.NO_VALUE;
public int displayUnit = DISPLAY_UNIT_PIXELS; public int displayUnit = DISPLAY_UNIT_PIXELS;
public float frameRate = -1;
@C.Projection public int projectionType = Format.NO_VALUE; @C.Projection public int projectionType = Format.NO_VALUE;
public float projectionPoseYaw = 0f; public float projectionPoseYaw = 0f;
public float projectionPosePitch = 0f; public float projectionPosePitch = 0f;
@ -2009,6 +2016,7 @@ public class MatroskaExtractor implements Extractor {
AvcConfig avcConfig = AvcConfig.parse(new ParsableByteArray(codecPrivate)); AvcConfig avcConfig = AvcConfig.parse(new ParsableByteArray(codecPrivate));
initializationData = avcConfig.initializationData; initializationData = avcConfig.initializationData;
nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength; nalUnitLengthFieldLength = avcConfig.nalUnitLengthFieldLength;
frameRate = avcConfig.frameRate;
break; break;
case CODEC_ID_H265: case CODEC_ID_H265:
mimeType = MimeTypes.VIDEO_H265; mimeType = MimeTypes.VIDEO_H265;
@ -2195,6 +2203,7 @@ public class MatroskaExtractor implements Extractor {
.setRotationDegrees(rotationDegrees) .setRotationDegrees(rotationDegrees)
.setProjectionData(projectionData) .setProjectionData(projectionData)
.setStereoMode(stereoMode) .setStereoMode(stereoMode)
.setFrameRate(frameRate < 0 ? Format.NO_VALUE : frameRate)
.setColorInfo(colorInfo); .setColorInfo(colorInfo);
} else if (MimeTypes.APPLICATION_SUBRIP.equals(mimeType)) { } else if (MimeTypes.APPLICATION_SUBRIP.equals(mimeType)) {
type = C.TRACK_TYPE_TEXT; type = C.TRACK_TYPE_TEXT;

View File

@ -968,6 +968,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
int height = parent.readUnsignedShort(); int height = parent.readUnsignedShort();
boolean pixelWidthHeightRatioFromPasp = false; boolean pixelWidthHeightRatioFromPasp = false;
float pixelWidthHeightRatio = 1; float pixelWidthHeightRatio = 1;
float frameRate = -1;
parent.skipBytes(50); parent.skipBytes(50);
int childPosition = parent.getPosition(); int childPosition = parent.getPosition();
@ -1014,6 +1015,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
if (!pixelWidthHeightRatioFromPasp) { if (!pixelWidthHeightRatioFromPasp) {
pixelWidthHeightRatio = avcConfig.pixelWidthAspectRatio; pixelWidthHeightRatio = avcConfig.pixelWidthAspectRatio;
} }
frameRate = avcConfig.frameRate;
} 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;
@ -1094,6 +1096,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
.setStereoMode(stereoMode) .setStereoMode(stereoMode)
.setInitializationData(initializationData) .setInitializationData(initializationData)
.setDrmInitData(drmInitData) .setDrmInitData(drmInitData)
.setFrameRate(frameRate < 0 ? Format.NO_VALUE : frameRate)
.build(); .build();
} }

View File

@ -215,6 +215,7 @@ public final class H264Reader implements ElementaryStreamReader {
.setHeight(spsData.height) .setHeight(spsData.height)
.setPixelWidthHeightRatio(spsData.pixelWidthAspectRatio) .setPixelWidthHeightRatio(spsData.pixelWidthAspectRatio)
.setInitializationData(initializationData) .setInitializationData(initializationData)
.setFrameRate(spsData.frameRate < 0 ? Format.NO_VALUE : spsData.frameRate)
.build()); .build());
hasOutputFormat = true; hasOutputFormat = true;
sampleReader.putSps(spsData); sampleReader.putSps(spsData);