Commit bf1a1565 by Dustin

Added support for AVC picOrderCountType = 2

parent 5e7679df
...@@ -18,6 +18,7 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -18,6 +18,7 @@ public class AvcChunkPeeker extends NalChunkPeeker {
private static final int NAL_TYPE_SEI = 6; private static final int NAL_TYPE_SEI = 6;
private static final int NAL_TYPE_SPS = 7; private static final int NAL_TYPE_SPS = 7;
private static final int NAL_TYPE_PPS = 8; private static final int NAL_TYPE_PPS = 8;
private static final int NAL_TYPE_AUD = 9;
private final PicCountClock picCountClock; private final PicCountClock picCountClock;
private final Format.Builder formatBuilder; private final Format.Builder formatBuilder;
...@@ -26,15 +27,14 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -26,15 +27,14 @@ public class AvcChunkPeeker extends NalChunkPeeker {
private float pixelWidthHeightRatio = 1f; private float pixelWidthHeightRatio = 1f;
private NalUnitUtil.SpsData spsData; private NalUnitUtil.SpsData spsData;
public AvcChunkPeeker(Format.Builder formatBuilder, TrackOutput trackOutput, long durationUs, public AvcChunkPeeker(Format.Builder formatBuilder, TrackOutput trackOutput, LinearClock clock) {
int length) {
super(16); super(16);
this.formatBuilder = formatBuilder; this.formatBuilder = formatBuilder;
this.trackOutput = trackOutput; this.trackOutput = trackOutput;
picCountClock = new PicCountClock(durationUs, length); picCountClock = new PicCountClock(clock.durationUs, clock.length);
} }
public PicCountClock getPicCountClock() { public LinearClock getClock() {
return picCountClock; return picCountClock;
} }
...@@ -58,7 +58,7 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -58,7 +58,7 @@ public class AvcChunkPeeker extends NalChunkPeeker {
if (spsData.separateColorPlaneFlag) { if (spsData.separateColorPlaneFlag) {
in.skipBits(2); //colour_plane_id in.skipBits(2); //colour_plane_id
} }
in.readBits(spsData.frameNumLength); //frame_num final int frameNum = in.readBits(spsData.frameNumLength); //frame_num
if (!spsData.frameMbsOnlyFlag) { if (!spsData.frameMbsOnlyFlag) {
boolean field_pic_flag = in.readBit(); // field_pic_flag boolean field_pic_flag = in.readBit(); // field_pic_flag
if (field_pic_flag) { if (field_pic_flag) {
...@@ -71,6 +71,9 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -71,6 +71,9 @@ public class AvcChunkPeeker extends NalChunkPeeker {
//Log.d("Test", "FrameNum: " + frame + " cnt=" + picOrderCountLsb); //Log.d("Test", "FrameNum: " + frame + " cnt=" + picOrderCountLsb);
picCountClock.setPicCount(picOrderCountLsb); picCountClock.setPicCount(picOrderCountLsb);
return; return;
} else if (spsData.picOrderCountType == 2) {
picCountClock.setPicCount(frameNum);
return;
} }
picCountClock.setIndex(picCountClock.getIndex()); picCountClock.setIndex(picCountClock.getIndex());
} }
...@@ -80,7 +83,12 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -80,7 +83,12 @@ public class AvcChunkPeeker 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);
picCountClock.setMaxPicCount(1 << (spsData.picOrderCntLsbLength)); 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;
formatBuilder.setPixelWidthHeightRatio(pixelWidthHeightRatio); formatBuilder.setPixelWidthHeightRatio(pixelWidthHeightRatio);
...@@ -103,6 +111,7 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -103,6 +111,7 @@ public class AvcChunkPeeker extends NalChunkPeeker {
case NAL_TYPE_IRD: case NAL_TYPE_IRD:
picCountClock.syncIndexes(); picCountClock.syncIndexes();
return; return;
case NAL_TYPE_AUD:
case NAL_TYPE_SEI: case NAL_TYPE_SEI:
case NAL_TYPE_PPS: { case NAL_TYPE_PPS: {
nalTypeOffset = seekNextNal(input, nalTypeOffset); nalTypeOffset = seekNextNal(input, nalTypeOffset);
......
...@@ -255,15 +255,14 @@ public class AviExtractor implements Extractor { ...@@ -255,15 +255,14 @@ public class AviExtractor implements Extractor {
builder.setFrameRate(streamHeader.getFrameRate()); builder.setFrameRate(streamHeader.getFrameRate());
builder.setSampleMimeType(mimeType); builder.setSampleMimeType(mimeType);
final LinearClock clock = new LinearClock(durationUs, length);
aviTrack = new AviTrack(streamId, C.TRACK_TYPE_VIDEO, clock, trackOutput);
if (MimeTypes.VIDEO_H264.equals(mimeType)) { if (MimeTypes.VIDEO_H264.equals(mimeType)) {
final AvcChunkPeeker avcChunkPeeker = new AvcChunkPeeker(builder, trackOutput, durationUs, final AvcChunkPeeker avcChunkPeeker = new AvcChunkPeeker(builder, trackOutput, clock);
length); aviTrack.setClock(avcChunkPeeker.getClock());
aviTrack = new AviTrack(streamId, C.TRACK_TYPE_VIDEO, avcChunkPeeker.getPicCountClock(),
trackOutput);
aviTrack.setChunkPeeker(avcChunkPeeker); aviTrack.setChunkPeeker(avcChunkPeeker);
} else { } else {
aviTrack = new AviTrack(streamId, C.TRACK_TYPE_VIDEO,
new LinearClock(durationUs, length), trackOutput);
if (MimeTypes.VIDEO_MP4V.equals(mimeType)) { if (MimeTypes.VIDEO_MP4V.equals(mimeType)) {
aviTrack.setChunkPeeker(new Mp4vChunkPeeker(builder, trackOutput)); aviTrack.setChunkPeeker(new Mp4vChunkPeeker(builder, trackOutput));
} }
...@@ -361,23 +360,31 @@ public class AviExtractor implements Extractor { ...@@ -361,23 +360,31 @@ public class AviExtractor implements Extractor {
return null; return null;
} }
void updateAudioTiming(final int[] keyFrameCounts, final long videoDuration) { void fixTimings(final int[] keyFrameCounts, final long videoDuration) {
for (final AviTrack aviTrack : aviTracks) { for (final AviTrack aviTrack : aviTracks) {
if (aviTrack != null && aviTrack.isAudio()) { if (aviTrack != null) {
final long durationUs = aviTrack.getClock().durationUs; if (aviTrack.isAudio()) {
i("Audio #" + aviTrack.id + " chunks: " + aviTrack.chunks + " us=" + durationUs + final long durationUs = aviTrack.getClock().durationUs;
" size=" + aviTrack.size); i("Audio #" + aviTrack.id + " chunks: " + aviTrack.chunks + " us=" + durationUs +
final LinearClock linearClock = aviTrack.getClock(); " size=" + aviTrack.size);
//If the audio track duration is off from the video by >5 % recalc using video final LinearClock linearClock = aviTrack.getClock();
if ((durationUs - videoDuration) / (float)videoDuration > .05f) { //If the audio track duration is off from the video by >5 % recalc using video
w("Audio #" + aviTrack.id + " duration is off using videoDuration"); if ((durationUs - videoDuration) / (float)videoDuration > .05f) {
linearClock.setDuration(videoDuration); w("Audio #" + aviTrack.id + " duration is off using videoDuration");
} linearClock.setDuration(videoDuration);
linearClock.setLength(aviTrack.chunks); }
if (aviTrack.chunks != keyFrameCounts[aviTrack.id]) { linearClock.setLength(aviTrack.chunks);
w("Audio is not all key frames chunks=" + aviTrack.chunks + " keyFrames=" + if (aviTrack.chunks != keyFrameCounts[aviTrack.id]) {
keyFrameCounts[aviTrack.id]); w("Audio is not all key frames chunks=" + aviTrack.chunks + " keyFrames=" +
} keyFrameCounts[aviTrack.id]);
}
} /* else if (aviTrack.isVideo()) {
final LinearClock clock = aviTrack.getClock();
if (clock.length != aviTrack.chunks) {
w("Video #" + aviTrack.id + " chunks != length changing FPS");
clock.setLength(aviTrack.chunks);
}
}*/
} }
} }
} }
...@@ -464,8 +471,7 @@ public class AviExtractor implements Extractor { ...@@ -464,8 +471,7 @@ public class AviExtractor implements Extractor {
i("Video chunks=" + videoTrack.chunks + " us=" + seekMap.getDurationUs()); i("Video chunks=" + videoTrack.chunks + " us=" + seekMap.getDurationUs());
//Needs to be called after the duration is updated fixTimings(keyFrameCounts, durationUs);
updateAudioTiming(keyFrameCounts, durationUs);
setSeekMap(seekMap); setSeekMap(seekMap);
} }
......
...@@ -20,7 +20,7 @@ public class AviTrack { ...@@ -20,7 +20,7 @@ public class AviTrack {
final @C.TrackType int trackType; final @C.TrackType int trackType;
@NonNull @NonNull
final LinearClock clock; LinearClock clock;
@NonNull @NonNull
final TrackOutput trackOutput; final TrackOutput trackOutput;
...@@ -56,7 +56,7 @@ public class AviTrack { ...@@ -56,7 +56,7 @@ public class AviTrack {
return getChunkIdLower(id) | ('w' << 16) | ('b' << 24); return getChunkIdLower(id) | ('w' << 16) | ('b' << 24);
} }
AviTrack(int id, final @C.TrackType int trackType, @NonNull LinearClock clock, AviTrack(int id, @C.TrackType int trackType, @NonNull LinearClock clock,
@NonNull TrackOutput trackOutput) { @NonNull TrackOutput trackOutput) {
this.id = id; this.id = id;
this.clock = clock; this.clock = clock;
...@@ -77,10 +77,15 @@ public class AviTrack { ...@@ -77,10 +77,15 @@ public class AviTrack {
return this.chunkId == chunkId || chunkIdAlt == chunkId; return this.chunkId == chunkId || chunkIdAlt == chunkId;
} }
@NonNull
public LinearClock getClock() { public LinearClock getClock() {
return clock; return clock;
} }
public void setClock(@NonNull LinearClock clock) {
this.clock = clock;
}
public void setChunkPeeker(ChunkPeeker chunkPeeker) { public void setChunkPeeker(ChunkPeeker chunkPeeker) {
this.chunkPeeker = chunkPeeker; this.chunkPeeker = chunkPeeker;
} }
...@@ -144,7 +149,7 @@ public class AviTrack { ...@@ -144,7 +149,7 @@ public class AviTrack {
trackOutput.sampleMetadata( trackOutput.sampleMetadata(
clock.getUs(), (isKeyFrame() ? C.BUFFER_FLAG_KEY_FRAME : 0), size, 0, null); clock.getUs(), (isKeyFrame() ? C.BUFFER_FLAG_KEY_FRAME : 0), size, 0, null);
final LinearClock clock = getClock(); final LinearClock clock = getClock();
// Log.d(AviExtractor.TAG, "Frame: " + (isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " size=" + size + " frame=" + clock.getIndex() + " key=" + isKeyFrame()); Log.d(AviExtractor.TAG, "Frame: " + (isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " size=" + size + " frame=" + clock.getIndex() + " key=" + isKeyFrame());
clock.advance(); clock.advance();
} }
} }
...@@ -4,14 +4,13 @@ package com.google.android.exoplayer2.extractor.avi; ...@@ -4,14 +4,13 @@ package com.google.android.exoplayer2.extractor.avi;
* Properly calculates the frame time for H264 frames using PicCount * Properly calculates the frame time for H264 frames using PicCount
*/ */
public class PicCountClock extends LinearClock { public class PicCountClock extends LinearClock {
//I believe this is 2 because there is a bottom pic order and a top pic order
private static final int STEP = 2;
//The frame as a calculated from the picCount //The frame as a calculated from the picCount
private int picIndex; private int picIndex;
private int lastPicCount; private int lastPicCount;
//Largest picFrame, used when we hit an I frame //Largest picFrame, used when we hit an I frame
private int maxPicIndex =-1; private int maxPicIndex =-1;
private int maxPicCount; private int maxPicCount;
private int step = 2;
private int posHalf; private int posHalf;
private int negHalf; private int negHalf;
...@@ -19,15 +18,15 @@ public class PicCountClock extends LinearClock { ...@@ -19,15 +18,15 @@ public class PicCountClock extends LinearClock {
super(durationUs, length); super(durationUs, length);
} }
public void setMaxPicCount(int maxPicCount) { public void setMaxPicCount(int maxPicCount, int step) {
this.maxPicCount = maxPicCount; this.maxPicCount = maxPicCount;
posHalf = maxPicCount / STEP; this.step = step;
posHalf = maxPicCount / step;
negHalf = -posHalf; negHalf = -posHalf;
} }
/** /**
* Done on seek. May cause sync issues if frame picCount != 0 (I frames are always 0) * Used primarily on seek. May cause issues if not a key frame
* @param index
*/ */
@Override @Override
public void setIndex(int index) { public void setIndex(int index) {
...@@ -42,7 +41,7 @@ public class PicCountClock extends LinearClock { ...@@ -42,7 +41,7 @@ public class PicCountClock extends LinearClock {
} else if (delta > posHalf) { } else if (delta > posHalf) {
delta -= maxPicCount; delta -= maxPicCount;
} }
picIndex += delta / STEP; picIndex += delta / step;
lastPicCount = picCount; lastPicCount = picCount;
if (maxPicIndex < picIndex) { if (maxPicIndex < picIndex) {
maxPicIndex = picIndex; maxPicIndex = picIndex;
......
...@@ -7,7 +7,7 @@ public class PicCountClockTest { ...@@ -7,7 +7,7 @@ public class PicCountClockTest {
@Test @Test
public void us_givenTwoStepsForward() { public void us_givenTwoStepsForward() {
final PicCountClock picCountClock = new PicCountClock(10_000L, 100); final PicCountClock picCountClock = new PicCountClock(10_000L, 100);
picCountClock.setMaxPicCount(16*2); picCountClock.setMaxPicCount(16*2, 2);
picCountClock.setPicCount(2*2); picCountClock.setPicCount(2*2);
Assert.assertEquals(2*100, picCountClock.getUs()); Assert.assertEquals(2*100, picCountClock.getUs());
} }
...@@ -15,7 +15,7 @@ public class PicCountClockTest { ...@@ -15,7 +15,7 @@ public class PicCountClockTest {
@Test @Test
public void us_givenThreeStepsBackwards() { public void us_givenThreeStepsBackwards() {
final PicCountClock picCountClock = new PicCountClock(10_000L, 100); final PicCountClock picCountClock = new PicCountClock(10_000L, 100);
picCountClock.setMaxPicCount(16*2); picCountClock.setMaxPicCount(16*2, 2);
picCountClock.setPicCount(4*2); // 400ms picCountClock.setPicCount(4*2); // 400ms
Assert.assertEquals(400, picCountClock.getUs()); Assert.assertEquals(400, picCountClock.getUs());
picCountClock.setPicCount(1*2); picCountClock.setPicCount(1*2);
...@@ -32,7 +32,7 @@ public class PicCountClockTest { ...@@ -32,7 +32,7 @@ public class PicCountClockTest {
@Test @Test
public void us_giveWrapBackwards() { public void us_giveWrapBackwards() {
final PicCountClock picCountClock = new PicCountClock(10_000L, 100); final PicCountClock picCountClock = new PicCountClock(10_000L, 100);
picCountClock.setMaxPicCount(16*2); picCountClock.setMaxPicCount(16*2, 2);
//Need to walk up no faster than maxPicCount / 2 //Need to walk up no faster than maxPicCount / 2
picCountClock.setPicCount(7*2); picCountClock.setPicCount(7*2);
picCountClock.setPicCount(11*2); picCountClock.setPicCount(11*2);
......
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