mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Optimize AvcChunkHandler to use normal ChunkClock if no B Frames.
This commit is contained in:
parent
5b952294f6
commit
ba0b991d76
3 changed files with 54 additions and 25 deletions
|
|
@ -16,6 +16,7 @@
|
||||||
package com.google.android.exoplayer2.extractor.avi;
|
package com.google.android.exoplayer2.extractor.avi;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||||
|
|
@ -43,28 +44,36 @@ public class AvcChunkHandler extends NalChunkPeeker {
|
||||||
|
|
||||||
public AvcChunkHandler(int id, @NonNull TrackOutput trackOutput,
|
public AvcChunkHandler(int id, @NonNull TrackOutput trackOutput,
|
||||||
@NonNull ChunkClock clock, Format.Builder formatBuilder) {
|
@NonNull ChunkClock clock, Format.Builder formatBuilder) {
|
||||||
super(id, trackOutput, new PicCountClock(clock.durationUs, clock.chunks), 16);
|
super(id, trackOutput, clock, 16);
|
||||||
this.formatBuilder = formatBuilder;
|
this.formatBuilder = formatBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@Nullable
|
||||||
@Override
|
@VisibleForTesting
|
||||||
public PicCountClock getClock() {
|
PicCountClock getPicCountClock() {
|
||||||
return (PicCountClock) clock;
|
if (clock instanceof PicCountClock) {
|
||||||
|
return (PicCountClock)clock;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
boolean skip(byte nalType) {
|
boolean skip(byte nalType) {
|
||||||
return false;
|
if (clock instanceof PicCountClock) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
//If the clock is regular clock, skip "normal" frames
|
||||||
|
return nalType >= 0 && nalType <= NAL_TYPE_IDR;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Greatly simplified way to calculate the picOrder
|
* Greatly simplified way to calculate the picOrder
|
||||||
* Full logic is here
|
* Full logic is here
|
||||||
* https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/video/h264_poc.cc
|
* https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/video/h264_poc.cc
|
||||||
* @param nalTypeOffset
|
|
||||||
*/
|
*/
|
||||||
void updatePicCountClock(final int nalTypeOffset) {
|
void updatePicCountClock(final int nalTypeOffset, final PicCountClock picCountClock) {
|
||||||
final ParsableNalUnitBitArray in = new ParsableNalUnitBitArray(buffer, nalTypeOffset + 1, buffer.length);
|
final ParsableNalUnitBitArray in = new ParsableNalUnitBitArray(buffer, nalTypeOffset + 1, buffer.length);
|
||||||
//slide_header()
|
//slide_header()
|
||||||
in.readUnsignedExpGolombCodedInt(); //first_mb_in_slice
|
in.readUnsignedExpGolombCodedInt(); //first_mb_in_slice
|
||||||
|
|
@ -84,10 +93,10 @@ public class AvcChunkHandler extends NalChunkPeeker {
|
||||||
if (spsData.picOrderCountType == 0) {
|
if (spsData.picOrderCountType == 0) {
|
||||||
int picOrderCountLsb = in.readBits(spsData.picOrderCntLsbLength);
|
int picOrderCountLsb = in.readBits(spsData.picOrderCntLsbLength);
|
||||||
//Log.d("Test", "FrameNum: " + frame + " cnt=" + picOrderCountLsb);
|
//Log.d("Test", "FrameNum: " + frame + " cnt=" + picOrderCountLsb);
|
||||||
getClock().setPicCount(picOrderCountLsb);
|
picCountClock.setPicCount(picOrderCountLsb);
|
||||||
return;
|
return;
|
||||||
} else if (spsData.picOrderCountType == 2) {
|
} else if (spsData.picOrderCountType == 2) {
|
||||||
getClock().setPicCount(frameNum);
|
picCountClock.setPicCount(frameNum);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
clock.setIndex(clock.getIndex());
|
clock.setIndex(clock.getIndex());
|
||||||
|
|
@ -98,11 +107,20 @@ public class AvcChunkHandler extends NalChunkPeeker {
|
||||||
final int spsStart = nalTypeOffset + 1;
|
final int spsStart = nalTypeOffset + 1;
|
||||||
nalTypeOffset = seekNextNal(input, spsStart);
|
nalTypeOffset = seekNextNal(input, spsStart);
|
||||||
spsData = NalUnitUtil.parseSpsNalUnitPayload(buffer, spsStart, pos);
|
spsData = NalUnitUtil.parseSpsNalUnitPayload(buffer, spsStart, pos);
|
||||||
if (spsData.picOrderCountType == 0) {
|
//If we have B Frames, upgrade to PicCountClock
|
||||||
getClock().setMaxPicCount(1 << spsData.picOrderCntLsbLength, 2);
|
final PicCountClock picCountClock;
|
||||||
} else if (spsData.picOrderCountType == 2) {
|
if (spsData.maxNumRefFrames > 1 && !(clock instanceof PicCountClock)) {
|
||||||
//Plus one because we double the frame number
|
clock = picCountClock = new PicCountClock(clock.durationUs, clock.chunks);
|
||||||
getClock().setMaxPicCount(1 << spsData.frameNumLength, 1);
|
} else {
|
||||||
|
picCountClock = getPicCountClock();
|
||||||
|
}
|
||||||
|
if (picCountClock != null) {
|
||||||
|
if (spsData.picOrderCountType == 0) {
|
||||||
|
picCountClock.setMaxPicCount(1 << spsData.picOrderCntLsbLength, 2);
|
||||||
|
} else if (spsData.picOrderCountType == 2) {
|
||||||
|
//Plus one because we double the frame number
|
||||||
|
picCountClock.setMaxPicCount(1 << spsData.frameNumLength, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (spsData.pixelWidthHeightRatio != pixelWidthHeightRatio) {
|
if (spsData.pixelWidthHeightRatio != pixelWidthHeightRatio) {
|
||||||
pixelWidthHeightRatio = spsData.pixelWidthHeightRatio;
|
pixelWidthHeightRatio = spsData.pixelWidthHeightRatio;
|
||||||
|
|
@ -121,11 +139,17 @@ public class AvcChunkHandler extends NalChunkPeeker {
|
||||||
case 2:
|
case 2:
|
||||||
case 3:
|
case 3:
|
||||||
case 4:
|
case 4:
|
||||||
updatePicCountClock(nalTypeOffset);
|
if (clock instanceof PicCountClock) {
|
||||||
|
updatePicCountClock(nalTypeOffset, (PicCountClock)clock);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
case NAL_TYPE_IDR:
|
case NAL_TYPE_IDR: {
|
||||||
getClock().syncIndexes();
|
final PicCountClock picCountClock = getPicCountClock();
|
||||||
|
if (picCountClock != null) {
|
||||||
|
picCountClock.syncIndexes();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
case NAL_TYPE_AUD:
|
case NAL_TYPE_AUD:
|
||||||
case NAL_TYPE_SEI:
|
case NAL_TYPE_SEI:
|
||||||
case NAL_TYPE_PPS: {
|
case NAL_TYPE_PPS: {
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ public final class NalUnitUtil {
|
||||||
public final int constraintsFlagsAndReservedZero2Bits;
|
public final int constraintsFlagsAndReservedZero2Bits;
|
||||||
public final int levelIdc;
|
public final int levelIdc;
|
||||||
public final int seqParameterSetId;
|
public final int seqParameterSetId;
|
||||||
|
public final int maxNumRefFrames;
|
||||||
public final int width;
|
public final int width;
|
||||||
public final int height;
|
public final int height;
|
||||||
public final float pixelWidthHeightRatio;
|
public final float pixelWidthHeightRatio;
|
||||||
|
|
@ -48,6 +49,7 @@ public final class NalUnitUtil {
|
||||||
int constraintsFlagsAndReservedZero2Bits,
|
int constraintsFlagsAndReservedZero2Bits,
|
||||||
int levelIdc,
|
int levelIdc,
|
||||||
int seqParameterSetId,
|
int seqParameterSetId,
|
||||||
|
int maxNumRefFrames,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
float pixelWidthHeightRatio,
|
float pixelWidthHeightRatio,
|
||||||
|
|
@ -61,6 +63,7 @@ public final class NalUnitUtil {
|
||||||
this.constraintsFlagsAndReservedZero2Bits = constraintsFlagsAndReservedZero2Bits;
|
this.constraintsFlagsAndReservedZero2Bits = constraintsFlagsAndReservedZero2Bits;
|
||||||
this.levelIdc = levelIdc;
|
this.levelIdc = levelIdc;
|
||||||
this.seqParameterSetId = seqParameterSetId;
|
this.seqParameterSetId = seqParameterSetId;
|
||||||
|
this.maxNumRefFrames = maxNumRefFrames;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
|
|
@ -367,7 +370,7 @@ public final class NalUnitUtil {
|
||||||
data.readUnsignedExpGolombCodedInt(); // offset_for_ref_frame[i]
|
data.readUnsignedExpGolombCodedInt(); // offset_for_ref_frame[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.readUnsignedExpGolombCodedInt(); // max_num_ref_frames
|
int maxNumRefFrames = data.readUnsignedExpGolombCodedInt(); // max_num_ref_frames
|
||||||
data.skipBit(); // gaps_in_frame_num_value_allowed_flag
|
data.skipBit(); // gaps_in_frame_num_value_allowed_flag
|
||||||
|
|
||||||
int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1;
|
int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1;
|
||||||
|
|
@ -427,6 +430,7 @@ public final class NalUnitUtil {
|
||||||
constraintsFlagsAndReservedZero2Bits,
|
constraintsFlagsAndReservedZero2Bits,
|
||||||
levelIdc,
|
levelIdc,
|
||||||
seqParameterSetId,
|
seqParameterSetId,
|
||||||
|
maxNumRefFrames,
|
||||||
frameWidth,
|
frameWidth,
|
||||||
frameHeight,
|
frameHeight,
|
||||||
pixelWidthHeightRatio,
|
pixelWidthHeightRatio,
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ public class AvcChunkPeekerTest {
|
||||||
setSampleMimeType(MimeTypes.VIDEO_H264).
|
setSampleMimeType(MimeTypes.VIDEO_H264).
|
||||||
setWidth(1280).setHeight(720).setFrameRate(24000f/1001f);
|
setWidth(1280).setHeight(720).setFrameRate(24000f/1001f);
|
||||||
|
|
||||||
private static final byte[] P_SLICE = {00,00,00,01,0x41,(byte)0x9A,0x13,0x36,0x21,0x3A,0x5F,
|
private static final byte[] P_SLICE = {0,0,0,1,0x41,(byte)0x9A,0x13,0x36,0x21,0x3A,0x5F,
|
||||||
(byte)0xFE,(byte)0x9E,0x10,00,00};
|
(byte)0xFE,(byte)0x9E,0x10,0,0};
|
||||||
|
|
||||||
private FakeTrackOutput fakeTrackOutput;
|
private FakeTrackOutput fakeTrackOutput;
|
||||||
private AvcChunkHandler avcChunkPeeker;
|
private AvcChunkHandler avcChunkPeeker;
|
||||||
|
|
@ -61,19 +61,20 @@ public class AvcChunkPeekerTest {
|
||||||
@Test
|
@Test
|
||||||
public void peek_givenStreamHeader() throws IOException {
|
public void peek_givenStreamHeader() throws IOException {
|
||||||
peekStreamHeader();
|
peekStreamHeader();
|
||||||
final PicCountClock picCountClock = avcChunkPeeker.getClock();
|
final PicCountClock picCountClock = avcChunkPeeker.getPicCountClock();
|
||||||
|
Assert.assertNotNull(picCountClock);
|
||||||
Assert.assertEquals(64, picCountClock.getMaxPicCount());
|
Assert.assertEquals(64, picCountClock.getMaxPicCount());
|
||||||
Assert.assertEquals(0, avcChunkPeeker.getSpsData().picOrderCountType);
|
Assert.assertEquals(0, avcChunkPeeker.getSpsData().picOrderCountType);
|
||||||
Assert.assertEquals(1.18f, fakeTrackOutput.lastFormat.pixelWidthHeightRatio, 0.01f);
|
Assert.assertEquals(1.18f, fakeTrackOutput.lastFormat.pixelWidthHeightRatio, 0.01f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void peek_givenStreamHeaderAndPSlice() throws IOException {
|
public void newChunk_givenStreamHeaderAndPSlice() throws IOException {
|
||||||
peekStreamHeader();
|
peekStreamHeader();
|
||||||
final PicCountClock picCountClock = avcChunkPeeker.getClock();
|
final PicCountClock picCountClock = avcChunkPeeker.getPicCountClock();
|
||||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(P_SLICE).build();
|
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(P_SLICE).build();
|
||||||
|
|
||||||
avcChunkPeeker.peek(input, P_SLICE.length);
|
avcChunkPeeker.newChunk(0, P_SLICE.length, input);
|
||||||
|
|
||||||
Assert.assertEquals(12, picCountClock.getLastPicCount());
|
Assert.assertEquals(12, picCountClock.getLastPicCount());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue