Handle AVI containers with no keyframes in index

If there is no keyframe in the index assume that the first chunk has a
keyframe.

PiperOrigin-RevId: 650915467
This commit is contained in:
andrewlewis 2024-07-10 01:48:05 -07:00 committed by Copybara-Service
parent b145a9b35e
commit dcc3e439e2
2 changed files with 24 additions and 17 deletions

View File

@ -411,10 +411,8 @@ public final class AviExtractor implements Extractor {
// We ignore unknown chunk IDs. // We ignore unknown chunk IDs.
continue; continue;
} }
if ((flags & AVIIF_KEYFRAME) == AVIIF_KEYFRAME) { chunkReader.appendIndexChunk(
chunkReader.appendKeyFrameToIndex(offset); offset, /* isKeyFrame= */ (flags & AVIIF_KEYFRAME) == AVIIF_KEYFRAME);
}
chunkReader.incrementIndexChunkCount();
} }
for (ChunkReader chunkReader : chunkReaders) { for (ChunkReader chunkReader : chunkReaders) {
chunkReader.compactIndex(); chunkReader.compactIndex();

View File

@ -70,17 +70,18 @@ import java.util.Arrays;
private int indexChunkCount; private int indexChunkCount;
private int indexSize; private int indexSize;
private long firstIndexChunkOffset;
private long[] keyFrameOffsets; private long[] keyFrameOffsets;
private int[] keyFrameIndices; private int[] keyFrameIndices;
public ChunkReader( public ChunkReader(
int id, int id,
@C.TrackType int trackType, @C.TrackType int trackType,
long durationnUs, long durationUs,
int streamHeaderChunkCount, int streamHeaderChunkCount,
TrackOutput trackOutput) { TrackOutput trackOutput) {
Assertions.checkArgument(trackType == C.TRACK_TYPE_AUDIO || trackType == C.TRACK_TYPE_VIDEO); Assertions.checkArgument(trackType == C.TRACK_TYPE_AUDIO || trackType == C.TRACK_TYPE_VIDEO);
this.durationUs = durationnUs; this.durationUs = durationUs;
this.streamHeaderChunkCount = streamHeaderChunkCount; this.streamHeaderChunkCount = streamHeaderChunkCount;
this.trackOutput = trackOutput; this.trackOutput = trackOutput;
@ChunkType @ChunkType
@ -89,18 +90,25 @@ import java.util.Arrays;
chunkId = getChunkIdFourCc(id, chunkType); chunkId = getChunkIdFourCc(id, chunkType);
alternativeChunkId = alternativeChunkId =
trackType == C.TRACK_TYPE_VIDEO ? getChunkIdFourCc(id, CHUNK_TYPE_VIDEO_UNCOMPRESSED) : -1; trackType == C.TRACK_TYPE_VIDEO ? getChunkIdFourCc(id, CHUNK_TYPE_VIDEO_UNCOMPRESSED) : -1;
firstIndexChunkOffset = C.INDEX_UNSET;
keyFrameOffsets = new long[INITIAL_INDEX_SIZE]; keyFrameOffsets = new long[INITIAL_INDEX_SIZE];
keyFrameIndices = new int[INITIAL_INDEX_SIZE]; keyFrameIndices = new int[INITIAL_INDEX_SIZE];
} }
public void appendKeyFrameToIndex(long offset) { public void appendIndexChunk(long offset, boolean isKeyFrame) {
if (indexSize == keyFrameIndices.length) { if (firstIndexChunkOffset == C.INDEX_UNSET) {
keyFrameOffsets = Arrays.copyOf(keyFrameOffsets, keyFrameOffsets.length * 3 / 2); firstIndexChunkOffset = offset;
keyFrameIndices = Arrays.copyOf(keyFrameIndices, keyFrameIndices.length * 3 / 2);
} }
keyFrameOffsets[indexSize] = offset; if (isKeyFrame) {
keyFrameIndices[indexSize] = indexChunkCount; if (indexSize == keyFrameIndices.length) {
indexSize++; keyFrameOffsets = Arrays.copyOf(keyFrameOffsets, keyFrameOffsets.length * 3 / 2);
keyFrameIndices = Arrays.copyOf(keyFrameIndices, keyFrameIndices.length * 3 / 2);
}
keyFrameOffsets[indexSize] = offset;
keyFrameIndices[indexSize] = indexChunkCount;
indexSize++;
}
indexChunkCount++;
} }
public void advanceCurrentChunk() { public void advanceCurrentChunk() {
@ -115,10 +123,6 @@ import java.util.Arrays;
return getChunkTimestampUs(/* chunkIndex= */ 1); return getChunkTimestampUs(/* chunkIndex= */ 1);
} }
public void incrementIndexChunkCount() {
indexChunkCount++;
}
public void compactIndex() { public void compactIndex() {
keyFrameOffsets = Arrays.copyOf(keyFrameOffsets, indexSize); keyFrameOffsets = Arrays.copyOf(keyFrameOffsets, indexSize);
keyFrameIndices = Arrays.copyOf(keyFrameIndices, indexSize); keyFrameIndices = Arrays.copyOf(keyFrameIndices, indexSize);
@ -180,6 +184,11 @@ import java.util.Arrays;
} }
public SeekMap.SeekPoints getSeekPoints(long timeUs) { public SeekMap.SeekPoints getSeekPoints(long timeUs) {
if (indexSize == 0) {
// Return the offset of the first chunk as there are no keyframes in the index.
return new SeekMap.SeekPoints(
new SeekPoint(/* timeUs= */ 0, /* position= */ firstIndexChunkOffset));
}
int targetFrameIndex = (int) (timeUs / getFrameDurationUs()); int targetFrameIndex = (int) (timeUs / getFrameDurationUs());
int keyFrameIndex = int keyFrameIndex =
Util.binarySearchFloor( Util.binarySearchFloor(