Commit 5b952294 by Dustin

Refactor to ChunkHander, add Mp3ChunkHandler

parent aee15f6c
Showing with 298 additions and 167 deletions
...@@ -565,4 +565,22 @@ public final class ParsableByteArray { ...@@ -565,4 +565,22 @@ public final class ParsableByteArray {
position += length; position += length;
return value; return value;
} }
/**
* The data from the end of the buffer is copied to the front
* The limit() because the bytesLeft() and position is zero
*/
public void compact() {
if (bytesLeft() == 0) {
limit = 0;
} else {
final ByteBuffer byteBuffer = ByteBuffer.wrap(data);
byteBuffer.limit(limit);
byteBuffer.position(position);
byteBuffer.compact();
byteBuffer.flip();
limit = byteBuffer.limit();
}
position = 0;
}
} }
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.extractor.avi; package com.google.android.exoplayer2.extractor.avi;
import androidx.annotation.NonNull;
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;
...@@ -27,7 +28,7 @@ import java.io.IOException; ...@@ -27,7 +28,7 @@ import java.io.IOException;
* Corrects the time and PAR for H264 streams * Corrects the time and PAR for H264 streams
* AVC is very rare in AVI due to the rise of the mp4 container * AVC is very rare in AVI due to the rise of the mp4 container
*/ */
public class AvcChunkPeeker extends NalChunkPeeker { public class AvcChunkHandler extends NalChunkPeeker {
private static final int NAL_TYPE_MASK = 0x1f; private static final int NAL_TYPE_MASK = 0x1f;
private static final int NAL_TYPE_IDR = 5; //I Frame private static final int NAL_TYPE_IDR = 5; //I Frame
private static final int NAL_TYPE_SEI = 6; private static final int NAL_TYPE_SEI = 6;
...@@ -35,22 +36,21 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -35,22 +36,21 @@ public class AvcChunkPeeker extends NalChunkPeeker {
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 static final int NAL_TYPE_AUD = 9;
private final PicCountClock picCountClock;
private final Format.Builder formatBuilder; private final Format.Builder formatBuilder;
private final TrackOutput trackOutput;
private float pixelWidthHeightRatio = 1f; private float pixelWidthHeightRatio = 1f;
private NalUnitUtil.SpsData spsData; private NalUnitUtil.SpsData spsData;
public AvcChunkPeeker(Format.Builder formatBuilder, TrackOutput trackOutput, LinearClock clock) { public AvcChunkHandler(int id, @NonNull TrackOutput trackOutput,
super(16); @NonNull ChunkClock clock, Format.Builder formatBuilder) {
super(id, trackOutput, new PicCountClock(clock.durationUs, clock.chunks), 16);
this.formatBuilder = formatBuilder; this.formatBuilder = formatBuilder;
this.trackOutput = trackOutput;
picCountClock = new PicCountClock(clock.durationUs, clock.length);
} }
@NonNull
@Override
public PicCountClock getClock() { public PicCountClock getClock() {
return picCountClock; return (PicCountClock) clock;
} }
@Override @Override
...@@ -84,13 +84,13 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -84,13 +84,13 @@ public class AvcChunkPeeker 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);
picCountClock.setPicCount(picOrderCountLsb); getClock().setPicCount(picOrderCountLsb);
return; return;
} else if (spsData.picOrderCountType == 2) { } else if (spsData.picOrderCountType == 2) {
picCountClock.setPicCount(frameNum); getClock().setPicCount(frameNum);
return; return;
} }
picCountClock.setIndex(picCountClock.getIndex()); clock.setIndex(clock.getIndex());
} }
@VisibleForTesting @VisibleForTesting
...@@ -99,10 +99,10 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -99,10 +99,10 @@ public class AvcChunkPeeker extends NalChunkPeeker {
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 (spsData.picOrderCountType == 0) {
picCountClock.setMaxPicCount(1 << spsData.picOrderCntLsbLength, 2); getClock().setMaxPicCount(1 << spsData.picOrderCntLsbLength, 2);
} else if (spsData.picOrderCountType == 2) { } else if (spsData.picOrderCountType == 2) {
//Plus one because we double the frame number //Plus one because we double the frame number
picCountClock.setMaxPicCount(1 << spsData.frameNumLength, 1); getClock().setMaxPicCount(1 << spsData.frameNumLength, 1);
} }
if (spsData.pixelWidthHeightRatio != pixelWidthHeightRatio) { if (spsData.pixelWidthHeightRatio != pixelWidthHeightRatio) {
pixelWidthHeightRatio = spsData.pixelWidthHeightRatio; pixelWidthHeightRatio = spsData.pixelWidthHeightRatio;
...@@ -124,7 +124,7 @@ public class AvcChunkPeeker extends NalChunkPeeker { ...@@ -124,7 +124,7 @@ public class AvcChunkPeeker extends NalChunkPeeker {
updatePicCountClock(nalTypeOffset); updatePicCountClock(nalTypeOffset);
return; return;
case NAL_TYPE_IDR: case NAL_TYPE_IDR:
picCountClock.syncIndexes(); getClock().syncIndexes();
return; return;
case NAL_TYPE_AUD: case NAL_TYPE_AUD:
case NAL_TYPE_SEI: case NAL_TYPE_SEI:
......
...@@ -99,17 +99,18 @@ public class AviSeekMap implements SeekMap { ...@@ -99,17 +99,18 @@ public class AviSeekMap implements SeekMap {
//Log.d(AviExtractor.TAG, "SeekPoint: us=" + outUs + " pos=" + position); //Log.d(AviExtractor.TAG, "SeekPoint: us=" + outUs + " pos=" + position);
} }
public void setFrames(final long position, final long timeUs, final AviTrack[] aviTracks) { public void setFrames(final long position, final long timeUs, final ChunkHandler[] chunkHandlers) {
final int index = Arrays.binarySearch(keyFrameOffsetsDiv2, (int)((position - seekOffset) / 2)); final int index = Arrays.binarySearch(keyFrameOffsetsDiv2, (int)((position - seekOffset) / 2));
if (index < 0) { if (index < 0) {
throw new IllegalArgumentException("Position: " + position); throw new IllegalArgumentException("Position: " + position);
} }
for (int i=0;i<aviTracks.length;i++) { for (int i=0;i<chunkHandlers.length;i++) {
final AviTrack aviTrack = aviTracks[i]; final ChunkHandler chunkHandler = chunkHandlers[i];
final LinearClock clock = aviTrack.getClock(); if (chunkHandler != null) {
clock.setIndex(seekIndexes[i][index]); // Log.d(AviExtractor.TAG, "Frame: " + (chunkHandler.isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " frame=" + clock.getIndex() + " key=" + chunkHandler.isKeyFrame());
// Log.d(AviExtractor.TAG, "Frame: " + (aviTrack.isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " frame=" + clock.getIndex() + " key=" + aviTrack.isKeyFrame()); chunkHandler.setIndex(seekIndexes[i][index]);
}
} }
} }
} }
...@@ -18,23 +18,23 @@ package com.google.android.exoplayer2.extractor.avi; ...@@ -18,23 +18,23 @@ package com.google.android.exoplayer2.extractor.avi;
/** /**
* A clock that is linearly derived from the current chunk index of a given stream * A clock that is linearly derived from the current chunk index of a given stream
*/ */
public class LinearClock { public class ChunkClock {
long durationUs; long durationUs;
int length; int chunks;
int index; int index;
public LinearClock(long durationUs, int length) { public ChunkClock(long durationUs, int chunks) {
this.durationUs = durationUs; this.durationUs = durationUs;
this.length = length; this.chunks = chunks;
} }
public void setDuration(long durationUs) { public void setDuration(long durationUs) {
this.durationUs = durationUs; this.durationUs = durationUs;
} }
public void setLength(int length) { public void setChunks(int length) {
this.length = length; this.chunks = length;
} }
public int getIndex() { public int getIndex() {
...@@ -55,6 +55,6 @@ public class LinearClock { ...@@ -55,6 +55,6 @@ public class LinearClock {
long getUs(int index) { long getUs(int index) {
//Doing this the hard way lessens round errors //Doing this the hard way lessens round errors
return durationUs * index / length; return durationUs * index / chunks;
} }
} }
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
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.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.TrackOutput; import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.Log;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
...@@ -28,13 +28,13 @@ import java.util.Arrays; ...@@ -28,13 +28,13 @@ import java.util.Arrays;
* Collection of info about a track. * Collection of info about a track.
* This acts a bridge between AVI and ExoPlayer structures * This acts a bridge between AVI and ExoPlayer structures
*/ */
public class AviTrack { public class ChunkHandler {
public static final int[] ALL_KEY_FRAMES = new int[0]; public static final int[] ALL_KEY_FRAMES = new int[0];
public static int CHUNK_TYPE_VIDEO = ('d' << 16) | ('c' << 24); public static int TYPE_VIDEO = ('d' << 16) | ('c' << 24);
public static int CHUNK_TYPE_AUDIO = ('w' << 16) | ('b' << 24); public static int TYPE_AUDIO = ('w' << 16) | ('b' << 24);
@NonNull @NonNull
LinearClock clock; ChunkClock clock;
@NonNull @NonNull
final TrackOutput trackOutput; final TrackOutput trackOutput;
...@@ -42,9 +42,6 @@ public class AviTrack { ...@@ -42,9 +42,6 @@ public class AviTrack {
final int chunkId; final int chunkId;
final int chunkIdAlt; final int chunkIdAlt;
@Nullable
ChunkPeeker chunkPeeker;
int chunks; int chunks;
int size; int size;
...@@ -63,8 +60,7 @@ public class AviTrack { ...@@ -63,8 +60,7 @@ public class AviTrack {
return ('0' + tens) | (('0' + ones) << 8); return ('0' + tens) | (('0' + ones) << 8);
} }
AviTrack(int id, int chunkType, @NonNull LinearClock clock, ChunkHandler(int id, int chunkType, @NonNull TrackOutput trackOutput, @NonNull ChunkClock clock) {
@NonNull TrackOutput trackOutput) {
this.chunkId = getChunkIdLower(id) | chunkType; this.chunkId = getChunkIdLower(id) | chunkType;
this.clock = clock; this.clock = clock;
this.trackOutput = trackOutput; this.trackOutput = trackOutput;
...@@ -80,18 +76,14 @@ public class AviTrack { ...@@ -80,18 +76,14 @@ public class AviTrack {
} }
@NonNull @NonNull
public LinearClock getClock() { public ChunkClock getClock() {
return clock; return clock;
} }
public void setClock(@NonNull LinearClock clock) { public void setClock(@NonNull ChunkClock clock) {
this.clock = clock; this.clock = clock;
} }
public void setChunkPeeker(ChunkPeeker chunkPeeker) {
this.chunkPeeker = chunkPeeker;
}
/** /**
* *
* @param keyFrames null means all key frames * @param keyFrames null means all key frames
...@@ -105,17 +97,14 @@ public class AviTrack { ...@@ -105,17 +97,14 @@ public class AviTrack {
} }
public boolean isVideo() { public boolean isVideo() {
return (chunkId & CHUNK_TYPE_VIDEO) == CHUNK_TYPE_VIDEO; return (chunkId & TYPE_VIDEO) == TYPE_VIDEO;
} }
public boolean isAudio() { public boolean isAudio() {
return (chunkId & CHUNK_TYPE_AUDIO) == CHUNK_TYPE_AUDIO; return (chunkId & TYPE_AUDIO) == TYPE_AUDIO;
} }
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException { public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
if (chunkPeeker != null) {
chunkPeeker.peek(input, size);
}
final int remaining = size - trackOutput.sampleData(input, size, false); final int remaining = size - trackOutput.sampleData(input, size, false);
if (remaining == 0) { if (remaining == 0) {
done(size); done(size);
...@@ -152,7 +141,7 @@ public class AviTrack { ...@@ -152,7 +141,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 ChunkClock 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();
} }
...@@ -160,4 +149,11 @@ public class AviTrack { ...@@ -160,4 +149,11 @@ public class AviTrack {
public int getId() { public int getId() {
return ((chunkId >> 8) & 0xf) + ( chunkId & 0xf) * 10; return ((chunkId >> 8) & 0xf) + ( chunkId & 0xf) * 10;
} }
/**
* A seek occurred
*/
public void setIndex(int index) {
getClock().setIndex(index);
}
} }
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.avi;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import java.io.IOException;
/**
* Peeks for import data in the chunk stream.
*/
public interface ChunkPeeker {
void peek(ExtractorInput input, final int size) throws IOException;
}
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.extractor.avi;
import androidx.annotation.NonNull;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.audio.MpegAudioUtil;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.io.IOException;
public class Mp3ChunkHandler extends ChunkHandler {
private final MpegAudioUtil.Header header = new MpegAudioUtil.Header();
private final ParsableByteArray scratch = new ParsableByteArray(0);
private final int fps;
private int frameRemaining;
private long us = 0L;
Mp3ChunkHandler(int id, @NonNull TrackOutput trackOutput, @NonNull ChunkClock clock, int fps) {
super(id, TYPE_AUDIO, trackOutput, clock);
this.fps = fps;
}
@Override
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
if (size == 0) {
clock.advance();
syncUs();
//Log.d(AviExtractor.TAG, "Blank Frame: us=" + us);
return true;
}
chunkRemaining = size;
if (process(input)) {
//If we scratch is the entire buffer, we didn't find a MP3 header, so just dump the chunk
if (scratch.limit() == size) {
scratch.setPosition(0);
trackOutput.sampleData(scratch, size);
scratch.reset(0);
}
clock.advance();
return true;
}
return false;
}
@Override
boolean resume(ExtractorInput input) throws IOException {
if (process(input)) {
clock.advance();
return true;
}
return false;
}
int readScratch(ExtractorInput input, int bytes) throws IOException {
final int toRead = Math.min(bytes, chunkRemaining);
final int read = input.read(scratch.getData(), scratch.limit(), toRead);
if (read == C.RESULT_END_OF_INPUT) {
return read;
}
chunkRemaining -= read;
scratch.setLimit(scratch.limit() + read);
return read;
}
private boolean findFrame(ExtractorInput input) throws IOException {
scratch.reset(0);
scratch.ensureCapacity(scratch.limit() + chunkRemaining);
int toRead = 4;
while (chunkRemaining > 0 && readScratch(input, toRead) != C.RESULT_END_OF_INPUT) {
readScratch(input, toRead);
while (scratch.bytesLeft() >= 4) {
if (header.setForHeaderData(scratch.readInt())) {
scratch.skipBytes(-4);
return true;
}
scratch.skipBytes(-3);
}
toRead = Math.min(chunkRemaining, 128);
}
return false;
}
private boolean process(ExtractorInput input) throws IOException {
if (frameRemaining == 0) {
if (findFrame(input)) {
final int scratchBytes = scratch.bytesLeft();
trackOutput.sampleData(scratch, scratchBytes);
frameRemaining = header.frameSize - scratchBytes;
} else {
return chunkRemaining == 0;
}
}
final int bytes = trackOutput.sampleData(input, Math.min(frameRemaining, chunkRemaining), false);
if (bytes == C.RESULT_END_OF_INPUT) {
return true;
}
frameRemaining -= bytes;
if (frameRemaining == 0) {
trackOutput.sampleMetadata(us, C.BUFFER_FLAG_KEY_FRAME, header.frameSize, 0, null);
//Log.d(AviExtractor.TAG, "MP3: us=" + us);
us += header.samplesPerFrame * C.MICROS_PER_SECOND / fps;
}
chunkRemaining -= bytes;
return chunkRemaining == 0;
}
@Override
public void setIndex(int index) {
super.setIndex(index);
syncUs();
}
private void syncUs() {
us = clock.getUs();
frameRemaining = 0;
}
}
...@@ -26,7 +26,7 @@ import java.io.IOException; ...@@ -26,7 +26,7 @@ import java.io.IOException;
/** /**
* Peeks an MP4V stream looking for pixelWidthHeightRatio data * Peeks an MP4V stream looking for pixelWidthHeightRatio data
*/ */
public class Mp4vChunkPeeker extends NalChunkPeeker { public class Mp4vChunkHandler extends NalChunkPeeker {
@VisibleForTesting @VisibleForTesting
static final byte SEQUENCE_START_CODE = (byte)0xb0; static final byte SEQUENCE_START_CODE = (byte)0xb0;
@VisibleForTesting @VisibleForTesting
...@@ -36,15 +36,14 @@ public class Mp4vChunkPeeker extends NalChunkPeeker { ...@@ -36,15 +36,14 @@ public class Mp4vChunkPeeker extends NalChunkPeeker {
static final int Extended_PAR = 0xf; static final int Extended_PAR = 0xf;
private final Format.Builder formatBuilder; private final Format.Builder formatBuilder;
private final TrackOutput trackOutput;
@VisibleForTesting() @VisibleForTesting()
float pixelWidthHeightRatio = 1f; float pixelWidthHeightRatio = 1f;
public Mp4vChunkPeeker(@NonNull Format.Builder formatBuilder, @NonNull TrackOutput trackOutput) { public Mp4vChunkHandler(int id, @NonNull TrackOutput trackOutput,
super(5); @NonNull ChunkClock clock, @NonNull Format.Builder formatBuilder) {
super(id, trackOutput, clock, 5);
this.formatBuilder = formatBuilder; this.formatBuilder = formatBuilder;
this.trackOutput = trackOutput;
} }
@Override @Override
......
...@@ -15,7 +15,9 @@ ...@@ -15,7 +15,9 @@
*/ */
package com.google.android.exoplayer2.extractor.avi; package com.google.android.exoplayer2.extractor.avi;
import androidx.annotation.NonNull;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.TrackOutput;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
...@@ -23,7 +25,7 @@ import java.util.Arrays; ...@@ -23,7 +25,7 @@ import java.util.Arrays;
* Generic base class for NAL (0x00 0x00 0x01) chunk headers * Generic base class for NAL (0x00 0x00 0x01) chunk headers
* Theses are used by AVC and MP4V (XVID) * Theses are used by AVC and MP4V (XVID)
*/ */
public abstract class NalChunkPeeker implements ChunkPeeker { public abstract class NalChunkPeeker extends ChunkHandler {
private static final int SEEK_PEEK_SIZE = 256; private static final int SEEK_PEEK_SIZE = 256;
private final int peekSize; private final int peekSize;
...@@ -31,6 +33,15 @@ public abstract class NalChunkPeeker implements ChunkPeeker { ...@@ -31,6 +33,15 @@ public abstract class NalChunkPeeker implements ChunkPeeker {
transient byte[] buffer; transient byte[] buffer;
transient int pos; transient int pos;
NalChunkPeeker(int id, @NonNull TrackOutput trackOutput,
@NonNull ChunkClock clock, int peakSize) {
super(id, TYPE_VIDEO, trackOutput, clock);
if (peakSize < 5) {
throw new IllegalArgumentException("Peak size must at least be 5");
}
this.peekSize = peakSize;
}
abstract void processChunk(ExtractorInput input, int nalTypeOffset) throws IOException; abstract void processChunk(ExtractorInput input, int nalTypeOffset) throws IOException;
/** /**
...@@ -100,15 +111,13 @@ public abstract class NalChunkPeeker implements ChunkPeeker { ...@@ -100,15 +111,13 @@ public abstract class NalChunkPeeker implements ChunkPeeker {
return -1; return -1;
} }
public NalChunkPeeker(int peakSize) {
if (peakSize < 5) {
throw new IllegalArgumentException("Peak size must at least be 5");
}
this.peekSize = peakSize;
}
abstract boolean skip(byte nalType); abstract boolean skip(byte nalType);
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
peek(input, size);
return super.newChunk(tag, size, input);
}
public void peek(ExtractorInput input, final int size) throws IOException { public void peek(ExtractorInput input, final int size) throws IOException {
buffer = new byte[peekSize]; buffer = new byte[peekSize];
if (!input.peekFully(buffer, 0, peekSize, true)) { if (!input.peekFully(buffer, 0, peekSize, true)) {
......
...@@ -20,7 +20,7 @@ import androidx.annotation.VisibleForTesting; ...@@ -20,7 +20,7 @@ import androidx.annotation.VisibleForTesting;
/** /**
* 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 ChunkClock {
//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;
......
...@@ -39,13 +39,13 @@ public class AvcChunkPeekerTest { ...@@ -39,13 +39,13 @@ public class AvcChunkPeekerTest {
(byte)0xFE,(byte)0x9E,0x10,00,00}; (byte)0xFE,(byte)0x9E,0x10,00,00};
private FakeTrackOutput fakeTrackOutput; private FakeTrackOutput fakeTrackOutput;
private AvcChunkPeeker avcChunkPeeker; private AvcChunkHandler avcChunkPeeker;
@Before @Before
public void before() { public void before() {
fakeTrackOutput = new FakeTrackOutput(false); fakeTrackOutput = new FakeTrackOutput(false);
avcChunkPeeker = new AvcChunkPeeker(FORMAT_BUILDER_AVC, fakeTrackOutput, avcChunkPeeker = new AvcChunkHandler(0, fakeTrackOutput,
new LinearClock(10_000_000L, 24 * 10)); new ChunkClock(10_000_000L, 24 * 10), FORMAT_BUILDER_AVC);
} }
private void peekStreamHeader() throws IOException { private void peekStreamHeader() throws IOException {
......
...@@ -108,26 +108,26 @@ public class AviExtractorRoboTest { ...@@ -108,26 +108,26 @@ public class AviExtractorRoboTest {
Assert.assertEquals(AviExtractor.STATE_FIND_MOVI, aviExtractor.state); Assert.assertEquals(AviExtractor.STATE_FIND_MOVI, aviExtractor.state);
final AviTrack aviTrack = aviExtractor.getVideoTrack(); final ChunkHandler chunkHandler = aviExtractor.getVideoTrack();
Assert.assertEquals(aviTrack.getClock().durationUs, streamHeaderBox.getDurationUs()); Assert.assertEquals(chunkHandler.getClock().durationUs, streamHeaderBox.getDurationUs());
} }
@Test @Test
public void readSamples_fragmentedChunk() throws IOException { public void readSamples_fragmentedChunk() throws IOException {
AviExtractor aviExtractor = AviExtractorTest.setupVideoAviExtractor(); AviExtractor aviExtractor = AviExtractorTest.setupVideoAviExtractor();
final AviTrack aviTrack = aviExtractor.getVideoTrack(); final ChunkHandler chunkHandler = aviExtractor.getVideoTrack();
final int size = 24 + 16; final int size = 24 + 16;
final ByteBuffer byteBuffer = AviExtractor.allocate(size + 8); final ByteBuffer byteBuffer = AviExtractor.allocate(size + 8);
byteBuffer.putInt(aviTrack.chunkId); byteBuffer.putInt(chunkHandler.chunkId);
byteBuffer.putInt(size); byteBuffer.putInt(size);
final ExtractorInput chunk = new FakeExtractorInput.Builder().setData(byteBuffer.array()). final ExtractorInput chunk = new FakeExtractorInput.Builder().setData(byteBuffer.array()).
setSimulatePartialReads(true).build(); setSimulatePartialReads(true).build();
Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(chunk, new PositionHolder())); Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(chunk, new PositionHolder()));
Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, aviExtractor.read(chunk, new PositionHolder())); Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(chunk, new PositionHolder()));
final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput; final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) chunkHandler.trackOutput;
Assert.assertEquals(size, fakeTrackOutput.getSampleData(0).length); Assert.assertEquals(size, fakeTrackOutput.getSampleData(0).length);
} }
} }
...@@ -165,7 +165,7 @@ public class AviExtractorTest { ...@@ -165,7 +165,7 @@ public class AviExtractorTest {
Assert.assertEquals(1, AviExtractor.getStreamId('0' | ('1' << 8) | ('d' << 16) | ('c' << 24))); Assert.assertEquals(1, AviExtractor.getStreamId('0' | ('1' << 8) | ('d' << 16) | ('c' << 24)));
} }
private void assertIdx1(AviSeekMap aviSeekMap, AviTrack videoTrack, int keyFrames, private void assertIdx1(AviSeekMap aviSeekMap, ChunkHandler videoTrack, int keyFrames,
int keyFrameRate) { int keyFrameRate) {
Assert.assertEquals(keyFrames, videoTrack.keyFrames.length); Assert.assertEquals(keyFrames, videoTrack.keyFrames.length);
...@@ -192,9 +192,9 @@ public class AviExtractorTest { ...@@ -192,9 +192,9 @@ public class AviExtractorTest {
final int keyFrameRate = 3 * DataHelper.FPS; // Keyframe every 3 seconds final int keyFrameRate = 3 * DataHelper.FPS; // Keyframe every 3 seconds
final int keyFrames = secs * DataHelper.FPS / keyFrameRate; final int keyFrames = secs * DataHelper.FPS / keyFrameRate;
final ByteBuffer idx1 = DataHelper.getIndex(secs, keyFrameRate); final ByteBuffer idx1 = DataHelper.getIndex(secs, keyFrameRate);
final AviTrack videoTrack = DataHelper.getVideoAviTrack(secs); final ChunkHandler videoTrack = DataHelper.getVideoChunkHandler(secs);
final AviTrack audioTrack = DataHelper.getAudioAviTrack(secs); final ChunkHandler audioTrack = DataHelper.getAudioChunkHandler(secs);
aviExtractor.setAviTracks(new AviTrack[]{videoTrack, audioTrack}); aviExtractor.setChunkHandlers(new ChunkHandler[]{videoTrack, audioTrack});
aviExtractor.setAviHeader(DataHelper.createAviHeaderBox()); aviExtractor.setAviHeader(DataHelper.createAviHeaderBox());
aviExtractor.state = AviExtractor.STATE_READ_IDX1; aviExtractor.state = AviExtractor.STATE_READ_IDX1;
aviExtractor.setMovi(DataHelper.MOVI_OFFSET, 128*1024); aviExtractor.setMovi(DataHelper.MOVI_OFFSET, 128*1024);
...@@ -213,7 +213,7 @@ public class AviExtractorTest { ...@@ -213,7 +213,7 @@ public class AviExtractorTest {
final AviSeekMap aviSeekMap = aviExtractor.aviSeekMap; final AviSeekMap aviSeekMap = aviExtractor.aviSeekMap;
assertIdx1(aviSeekMap, videoTrack, keyFrames, keyFrameRate); assertIdx1(aviSeekMap, videoTrack, keyFrames, keyFrameRate);
Assert.assertEquals(AviExtractor.STATE_READ_SAMPLES, aviExtractor.state); Assert.assertEquals(AviExtractor.STATE_READ_CHUNKS, aviExtractor.state);
Assert.assertEquals(DataHelper.MOVI_OFFSET + 4, positionHolder.position); Assert.assertEquals(DataHelper.MOVI_OFFSET + 4, positionHolder.position);
} }
...@@ -225,8 +225,8 @@ public class AviExtractorTest { ...@@ -225,8 +225,8 @@ public class AviExtractorTest {
final int secs = 9; final int secs = 9;
final int keyFrameRate = 3 * DataHelper.FPS; // Keyframe every 3 seconds final int keyFrameRate = 3 * DataHelper.FPS; // Keyframe every 3 seconds
final ByteBuffer idx1 = DataHelper.getIndex(secs, keyFrameRate); final ByteBuffer idx1 = DataHelper.getIndex(secs, keyFrameRate);
final AviTrack audioTrack = DataHelper.getAudioAviTrack(secs); final ChunkHandler audioTrack = DataHelper.getAudioChunkHandler(secs);
aviExtractor.setAviTracks(new AviTrack[]{audioTrack}); aviExtractor.setChunkHandlers(new ChunkHandler[]{audioTrack});
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder() final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder()
.setData(idx1.array()).build(); .setData(idx1.array()).build();
...@@ -250,9 +250,9 @@ public class AviExtractorTest { ...@@ -250,9 +250,9 @@ public class AviExtractorTest {
junk.putInt(0); junk.putInt(0);
idx1.flip(); idx1.flip();
junk.put(idx1); junk.put(idx1);
final AviTrack videoTrack = DataHelper.getVideoAviTrack(secs); final ChunkHandler videoTrack = DataHelper.getVideoChunkHandler(secs);
final AviTrack audioTrack = DataHelper.getAudioAviTrack(secs); final ChunkHandler audioTrack = DataHelper.getAudioChunkHandler(secs);
aviExtractor.setAviTracks(new AviTrack[]{videoTrack, audioTrack}); aviExtractor.setChunkHandlers(new ChunkHandler[]{videoTrack, audioTrack});
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder(). final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().
setData(junk.array()).build(); setData(junk.array()).build();
...@@ -268,16 +268,16 @@ public class AviExtractorTest { ...@@ -268,16 +268,16 @@ public class AviExtractorTest {
aviExtractor.init(fakeExtractorOutput); aviExtractor.init(fakeExtractorOutput);
final int secs = 4; final int secs = 4;
final ByteBuffer idx1 = DataHelper.getIndex(secs, 1); final ByteBuffer idx1 = DataHelper.getIndex(secs, 1);
final AviTrack videoTrack = DataHelper.getVideoAviTrack(secs); final ChunkHandler videoTrack = DataHelper.getVideoChunkHandler(secs);
final AviTrack audioTrack = DataHelper.getAudioAviTrack(secs); final ChunkHandler audioTrack = DataHelper.getAudioChunkHandler(secs);
aviExtractor.setAviTracks(new AviTrack[]{videoTrack, audioTrack}); aviExtractor.setChunkHandlers(new ChunkHandler[]{videoTrack, audioTrack});
final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder(). final FakeExtractorInput fakeExtractorInput = new FakeExtractorInput.Builder().
setData(idx1.array()).build(); setData(idx1.array()).build();
aviExtractor.readIdx1(fakeExtractorInput, (int) fakeExtractorInput.getLength()); aviExtractor.readIdx1(fakeExtractorInput, (int) fakeExtractorInput.getLength());
//We should be throttled to 2 key frame per second //We should be throttled to 2 key frame per second
Assert.assertSame(AviTrack.ALL_KEY_FRAMES, videoTrack.keyFrames); Assert.assertSame(ChunkHandler.ALL_KEY_FRAMES, videoTrack.keyFrames);
} }
@Test @Test
...@@ -389,12 +389,12 @@ public class AviExtractorTest { ...@@ -389,12 +389,12 @@ public class AviExtractorTest {
final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput(); final FakeExtractorOutput fakeExtractorOutput = new FakeExtractorOutput();
aviExtractor.init(fakeExtractorOutput); aviExtractor.init(fakeExtractorOutput);
final AviTrack aviTrack = DataHelper.getVideoAviTrack(9); final ChunkHandler chunkHandler = DataHelper.getVideoChunkHandler(9);
aviExtractor.setAviTracks(new AviTrack[]{aviTrack}); aviExtractor.setChunkHandlers(new ChunkHandler[]{chunkHandler});
final Format format = new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_MP4V).build(); final Format format = new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_MP4V).build();
aviTrack.trackOutput.format(format); chunkHandler.trackOutput.format(format);
aviExtractor.state = AviExtractor.STATE_READ_SAMPLES; aviExtractor.state = AviExtractor.STATE_READ_CHUNKS;
aviExtractor.setMovi(DataHelper.MOVI_OFFSET, 128*1024); aviExtractor.setMovi(DataHelper.MOVI_OFFSET, 128*1024);
return aviExtractor; return aviExtractor;
} }
...@@ -403,9 +403,9 @@ public class AviExtractorTest { ...@@ -403,9 +403,9 @@ public class AviExtractorTest {
public void readSamples_givenAtEndOfInput() throws IOException { public void readSamples_givenAtEndOfInput() throws IOException {
AviExtractor aviExtractor = setupVideoAviExtractor(); AviExtractor aviExtractor = setupVideoAviExtractor();
aviExtractor.setMovi(0, 0); aviExtractor.setMovi(0, 0);
final AviTrack aviTrack = aviExtractor.getVideoTrack(); final ChunkHandler chunkHandler = aviExtractor.getVideoTrack();
final ByteBuffer byteBuffer = AviExtractor.allocate(32); final ByteBuffer byteBuffer = AviExtractor.allocate(32);
byteBuffer.putInt(aviTrack.chunkId); byteBuffer.putInt(chunkHandler.chunkId);
byteBuffer.putInt(24); byteBuffer.putInt(24);
final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).build(); final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).build();
...@@ -415,47 +415,47 @@ public class AviExtractorTest { ...@@ -415,47 +415,47 @@ public class AviExtractorTest {
@Test @Test
public void readSamples_completeChunk() throws IOException { public void readSamples_completeChunk() throws IOException {
AviExtractor aviExtractor = setupVideoAviExtractor(); AviExtractor aviExtractor = setupVideoAviExtractor();
final AviTrack aviTrack = aviExtractor.getVideoTrack(); final ChunkHandler chunkHandler = aviExtractor.getVideoTrack();
final ByteBuffer byteBuffer = AviExtractor.allocate(32); final ByteBuffer byteBuffer = AviExtractor.allocate(32);
byteBuffer.putInt(aviTrack.chunkId); byteBuffer.putInt(chunkHandler.chunkId);
byteBuffer.putInt(24); byteBuffer.putInt(24);
final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()) final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array())
.build(); .build();
Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, aviExtractor.read(input, new PositionHolder())); Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(input, new PositionHolder()));
final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput; final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) chunkHandler.trackOutput;
Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length); Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length);
} }
@Test @Test
public void readSamples_givenLeadingZeros() throws IOException { public void readSamples_givenLeadingZeros() throws IOException {
AviExtractor aviExtractor = setupVideoAviExtractor(); AviExtractor aviExtractor = setupVideoAviExtractor();
final AviTrack aviTrack = aviExtractor.getVideoTrack(); final ChunkHandler chunkHandler = aviExtractor.getVideoTrack();
final ByteBuffer byteBuffer = AviExtractor.allocate(48); final ByteBuffer byteBuffer = AviExtractor.allocate(48);
byteBuffer.position(16); byteBuffer.position(16);
byteBuffer.putInt(aviTrack.chunkId); byteBuffer.putInt(chunkHandler.chunkId);
byteBuffer.putInt(24); byteBuffer.putInt(24);
final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()) final ExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array())
.build(); .build();
Assert.assertEquals(Extractor.RESULT_END_OF_INPUT, aviExtractor.read(input, new PositionHolder())); Assert.assertEquals(Extractor.RESULT_CONTINUE, aviExtractor.read(input, new PositionHolder()));
final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) aviTrack.trackOutput; final FakeTrackOutput fakeTrackOutput = (FakeTrackOutput) chunkHandler.trackOutput;
Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length); Assert.assertEquals(24, fakeTrackOutput.getSampleData(0).length);
} }
@Test @Test
public void seek_givenPosition0() throws IOException { public void seek_givenPosition0() throws IOException {
final AviExtractor aviExtractor = setupVideoAviExtractor(); final AviExtractor aviExtractor = setupVideoAviExtractor();
final AviTrack aviTrack = aviExtractor.getVideoTrack(); final ChunkHandler chunkHandler = aviExtractor.getVideoTrack();
aviExtractor.setChunkHandler(aviTrack); aviExtractor.setChunkHandler(chunkHandler);
aviTrack.getClock().setIndex(10); chunkHandler.getClock().setIndex(10);
aviExtractor.seek(0L, 0L); aviExtractor.seek(0L, 0L);
Assert.assertNull(aviExtractor.getChunkHandler()); Assert.assertNull(aviExtractor.getChunkHandler());
Assert.assertEquals(0, aviTrack.getClock().getIndex()); Assert.assertEquals(0, chunkHandler.getClock().getIndex());
Assert.assertEquals(aviExtractor.state, AviExtractor.STATE_SEEK_START); Assert.assertEquals(aviExtractor.state, AviExtractor.STATE_SEEK_START);
...@@ -470,18 +470,18 @@ public class AviExtractorTest { ...@@ -470,18 +470,18 @@ public class AviExtractorTest {
final AviExtractor aviExtractor = setupVideoAviExtractor(); final AviExtractor aviExtractor = setupVideoAviExtractor();
final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap(); final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap();
aviExtractor.aviSeekMap = aviSeekMap; aviExtractor.aviSeekMap = aviSeekMap;
final AviTrack aviTrack = aviExtractor.getVideoTrack(); final ChunkHandler chunkHandler = aviExtractor.getVideoTrack();
final long position = DataHelper.MOVI_OFFSET + aviSeekMap.keyFrameOffsetsDiv2[1] * 2L; final long position = DataHelper.MOVI_OFFSET + aviSeekMap.keyFrameOffsetsDiv2[1] * 2L;
aviExtractor.seek(position, 0L); aviExtractor.seek(position, 0L);
Assert.assertEquals(aviSeekMap.seekIndexes[aviTrack.getId()][1], aviTrack.getClock().getIndex()); Assert.assertEquals(aviSeekMap.seekIndexes[chunkHandler.getId()][1], chunkHandler.getClock().getIndex());
} }
@Test @Test
public void getAviTrack_givenListWithNull() { public void getChunkHandler_givenListWithNull() {
final AviExtractor aviExtractor = new AviExtractor(); final AviExtractor aviExtractor = new AviExtractor();
final AviTrack aviTrack = DataHelper.getAudioAviTrack(9); final ChunkHandler chunkHandler = DataHelper.getAudioChunkHandler(9);
aviExtractor.setAviTracks(new AviTrack[]{null, aviTrack}); aviExtractor.setChunkHandlers(new ChunkHandler[]{null, chunkHandler});
Assert.assertSame(aviTrack, aviExtractor.getAviTrack(aviTrack.chunkId)); Assert.assertSame(chunkHandler, aviExtractor.getChunkHandler(chunkHandler.chunkId));
} }
@Test @Test
......
...@@ -27,22 +27,22 @@ public class AviSeekMapTest { ...@@ -27,22 +27,22 @@ public class AviSeekMapTest {
final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap(); final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap();
final long position = aviSeekMap.keyFrameOffsetsDiv2[1] * 2L + aviSeekMap.seekOffset; final long position = aviSeekMap.keyFrameOffsetsDiv2[1] * 2L + aviSeekMap.seekOffset;
final int secs = 4; final int secs = 4;
final AviTrack[] aviTracks = new AviTrack[]{DataHelper.getVideoAviTrack(secs), final ChunkHandler[] chunkHandlers = new ChunkHandler[]{DataHelper.getVideoChunkHandler(secs),
DataHelper.getAudioAviTrack(secs)}; DataHelper.getAudioChunkHandler(secs)};
aviSeekMap.setFrames(position, C.MICROS_PER_SECOND, aviTracks); aviSeekMap.setFrames(position, C.MICROS_PER_SECOND, chunkHandlers);
for (int i=0;i<aviTracks.length;i++) { for (int i=0;i<chunkHandlers.length;i++) {
Assert.assertEquals(aviSeekMap.seekIndexes[i][1], aviTracks[i].getClock().getIndex()); Assert.assertEquals(aviSeekMap.seekIndexes[i][1], chunkHandlers[i].getClock().getIndex());
} }
} }
@Test @Test
public void setFrames_givenBadPosition() { public void setFrames_givenBadPosition() {
final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap(); final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap();
final AviTrack[] aviTracks = new AviTrack[2]; final ChunkHandler[] chunkHandlers = new ChunkHandler[2];
try { try {
aviSeekMap.setFrames(1L, C.MICROS_PER_SECOND, aviTracks); aviSeekMap.setFrames(1L, C.MICROS_PER_SECOND, chunkHandlers);
Assert.fail(); Assert.fail();
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
//Intentionally blank //Intentionally blank
......
...@@ -18,13 +18,13 @@ package com.google.android.exoplayer2.extractor.avi; ...@@ -18,13 +18,13 @@ package com.google.android.exoplayer2.extractor.avi;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
public class AviTrackTest { public class ChunkHandlerTest {
@Test @Test
public void setClock_givenLinearClock() { public void setClock_givenLinearClock() {
final LinearClock linearClock = new LinearClock(1_000_000L, 30); final ChunkClock linearClock = new ChunkClock(1_000_000L, 30);
final AviTrack aviTrack = DataHelper.getVideoAviTrack(1); final ChunkHandler chunkHandler = DataHelper.getVideoChunkHandler(1);
aviTrack.setClock(linearClock); chunkHandler.setClock(linearClock);
Assert.assertSame(linearClock, aviTrack.getClock()); Assert.assertSame(linearClock, chunkHandler.getClock());
} }
} }
...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.extractor.avi; ...@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.extractor.avi;
import android.content.Context; import android.content.Context;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.testutil.FakeTrackOutput; import com.google.android.exoplayer2.testutil.FakeTrackOutput;
import com.google.android.exoplayer2.testutil.TestUtil; import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.IOException; import java.io.IOException;
...@@ -104,18 +103,16 @@ public class DataHelper { ...@@ -104,18 +103,16 @@ public class DataHelper {
return byteBuffer; return byteBuffer;
} }
public static AviTrack getVideoAviTrack(int sec) { public static ChunkHandler getVideoChunkHandler(int sec) {
final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false); final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false);
return new AviTrack(0, AviTrack.CHUNK_TYPE_VIDEO, return new ChunkHandler(0, ChunkHandler.TYPE_VIDEO, fakeTrackOutput,
new LinearClock(sec * 1_000_000L, sec * FPS), new ChunkClock(sec * 1_000_000L, sec * FPS));
fakeTrackOutput);
} }
public static AviTrack getAudioAviTrack(int sec) { public static ChunkHandler getAudioChunkHandler(int sec) {
final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false); final FakeTrackOutput fakeTrackOutput = new FakeTrackOutput(false);
return new AviTrack(AUDIO_ID, AviTrack.CHUNK_TYPE_AUDIO, return new ChunkHandler(AUDIO_ID, ChunkHandler.TYPE_AUDIO, fakeTrackOutput,
new LinearClock(sec * 1_000_000L, sec * FPS * AUDIO_PER_VIDEO), new ChunkClock(sec * 1_000_000L, sec * FPS * AUDIO_PER_VIDEO));
fakeTrackOutput);
} }
public static AviSeekMap getAviSeekMap() { public static AviSeekMap getAviSeekMap() {
...@@ -153,8 +150,8 @@ public class DataHelper { ...@@ -153,8 +150,8 @@ public class DataHelper {
*/ */
public static ByteBuffer getIndex(final int secs, final int keyFrameRate, int offset) { public static ByteBuffer getIndex(final int secs, final int keyFrameRate, int offset) {
final int videoFrames = secs * FPS; final int videoFrames = secs * FPS;
final int videoChunkId = AviTrack.CHUNK_TYPE_VIDEO | AviTrack.getChunkIdLower(0); final int videoChunkId = ChunkHandler.TYPE_VIDEO | ChunkHandler.getChunkIdLower(0);
final int audioChunkId = AviTrack.CHUNK_TYPE_AUDIO | AviTrack.getChunkIdLower(1); final int audioChunkId = ChunkHandler.TYPE_AUDIO | ChunkHandler.getChunkIdLower(1);
final ByteBuffer byteBuffer = AviExtractor.allocate((videoFrames + videoFrames*AUDIO_PER_VIDEO) * 16); final ByteBuffer byteBuffer = AviExtractor.allocate((videoFrames + videoFrames*AUDIO_PER_VIDEO) * 16);
for (int v=0;v<videoFrames;v++) { for (int v=0;v<videoFrames;v++) {
......
...@@ -24,7 +24,7 @@ import org.junit.Test; ...@@ -24,7 +24,7 @@ import org.junit.Test;
public class LinearClockTest { public class LinearClockTest {
@Test @Test
public void advance() { public void advance() {
final LinearClock linearClock = new LinearClock(1_000L, 10); final ChunkClock linearClock = new ChunkClock(1_000L, 10);
linearClock.setIndex(2); linearClock.setIndex(2);
Assert.assertEquals(200, linearClock.getUs()); Assert.assertEquals(200, linearClock.getUs());
linearClock.advance(); linearClock.advance();
......
...@@ -16,12 +16,13 @@ ...@@ -16,12 +16,13 @@
package com.google.android.exoplayer2.extractor.avi; package com.google.android.exoplayer2.extractor.avi;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.testutil.FakeTrackOutput;
import java.io.IOException; import java.io.IOException;
public class MockNalChunkPeeker extends NalChunkPeeker { public class MockNalChunkPeeker extends NalChunkPeeker {
private boolean skip; private boolean skip;
public MockNalChunkPeeker(int peakSize, boolean skip) { public MockNalChunkPeeker(int peakSize, boolean skip) {
super(peakSize); super(0, new FakeTrackOutput(false), new ChunkClock(1_000_000L, 24), peakSize);
this.skip = skip; this.skip = skip;
} }
......
...@@ -32,7 +32,7 @@ import org.junit.runner.RunWith; ...@@ -32,7 +32,7 @@ import org.junit.runner.RunWith;
public class Mp4vChunkPeekerTest { public class Mp4vChunkPeekerTest {
private ByteBuffer makeSequence() { private ByteBuffer makeSequence() {
return DataHelper.appendNal(AviExtractor.allocate(32),Mp4vChunkPeeker.SEQUENCE_START_CODE); return DataHelper.appendNal(AviExtractor.allocate(32), Mp4vChunkHandler.SEQUENCE_START_CODE);
} }
@Test @Test
...@@ -42,7 +42,8 @@ public class Mp4vChunkPeekerTest { ...@@ -42,7 +42,8 @@ public class Mp4vChunkPeekerTest {
final Format.Builder formatBuilder = new Format.Builder(); final Format.Builder formatBuilder = new Format.Builder();
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()) final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array())
.build(); .build();
final Mp4vChunkPeeker mp4vChunkPeeker = new Mp4vChunkPeeker(formatBuilder, fakeTrackOutput); final Mp4vChunkHandler mp4vChunkPeeker = new Mp4vChunkHandler(0, fakeTrackOutput,
new ChunkClock(1_000_000L, 24), formatBuilder);
mp4vChunkPeeker.peek(input, (int) input.getLength()); mp4vChunkPeeker.peek(input, (int) input.getLength());
Assert.assertEquals(1f, mp4vChunkPeeker.pixelWidthHeightRatio, 0.01); Assert.assertEquals(1f, mp4vChunkPeeker.pixelWidthHeightRatio, 0.01);
} }
...@@ -54,7 +55,8 @@ public class Mp4vChunkPeekerTest { ...@@ -54,7 +55,8 @@ public class Mp4vChunkPeekerTest {
final Context context = ApplicationProvider.getApplicationContext(); final Context context = ApplicationProvider.getApplicationContext();
final byte[] bytes = TestUtil.getByteArray(context, "extractordumps/avi/mp4v_sequence.dump"); final byte[] bytes = TestUtil.getByteArray(context, "extractordumps/avi/mp4v_sequence.dump");
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(bytes).build(); final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(bytes).build();
final Mp4vChunkPeeker mp4vChunkPeeker = new Mp4vChunkPeeker(formatBuilder, fakeTrackOutput); final Mp4vChunkHandler mp4vChunkPeeker = new Mp4vChunkHandler(0, fakeTrackOutput,
new ChunkClock(1_000_000L, 24), formatBuilder);
mp4vChunkPeeker.peek(input, (int) input.getLength()); mp4vChunkPeeker.peek(input, (int) input.getLength());
Assert.assertEquals(1.2121212, mp4vChunkPeeker.pixelWidthHeightRatio, 0.01); Assert.assertEquals(1.2121212, mp4vChunkPeeker.pixelWidthHeightRatio, 0.01);
...@@ -64,14 +66,14 @@ public class Mp4vChunkPeekerTest { ...@@ -64,14 +66,14 @@ public class Mp4vChunkPeekerTest {
public void peek_givenCustomAspectRatio() throws IOException { public void peek_givenCustomAspectRatio() throws IOException {
ByteBuffer byteBuffer = makeSequence(); ByteBuffer byteBuffer = makeSequence();
byteBuffer.putInt(0x5555); byteBuffer.putInt(0x5555);
DataHelper.appendNal(byteBuffer, (byte)Mp4vChunkPeeker.LAYER_START_CODE); DataHelper.appendNal(byteBuffer, (byte) Mp4vChunkHandler.LAYER_START_CODE);
BitBuffer bitBuffer = new BitBuffer(); BitBuffer bitBuffer = new BitBuffer();
bitBuffer.push(false); //random_accessible_vol bitBuffer.push(false); //random_accessible_vol
bitBuffer.push(8, 8); //video_object_type_indication bitBuffer.push(8, 8); //video_object_type_indication
bitBuffer.push(true); // is_object_layer_identifier bitBuffer.push(true); // is_object_layer_identifier
bitBuffer.push(7, 7); // video_object_layer_verid, video_object_layer_priority bitBuffer.push(7, 7); // video_object_layer_verid, video_object_layer_priority
bitBuffer.push(4, Mp4vChunkPeeker.Extended_PAR); bitBuffer.push(4, Mp4vChunkHandler.Extended_PAR);
bitBuffer.push(8, 16); bitBuffer.push(8, 16);
bitBuffer.push(8, 9); bitBuffer.push(8, 9);
final byte bytes[] = bitBuffer.getBytes(); final byte bytes[] = bitBuffer.getBytes();
...@@ -81,7 +83,8 @@ public class Mp4vChunkPeekerTest { ...@@ -81,7 +83,8 @@ public class Mp4vChunkPeekerTest {
final Format.Builder formatBuilder = new Format.Builder(); final Format.Builder formatBuilder = new Format.Builder();
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()) final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array())
.build(); .build();
final Mp4vChunkPeeker mp4vChunkPeeker = new Mp4vChunkPeeker(formatBuilder, fakeTrackOutput); final Mp4vChunkHandler mp4vChunkPeeker = new Mp4vChunkHandler(0, fakeTrackOutput,
new ChunkClock(1_000_000L, 24), formatBuilder);
mp4vChunkPeeker.peek(input, (int) input.getLength()); mp4vChunkPeeker.peek(input, (int) input.getLength());
Assert.assertEquals(16f/9f, mp4vChunkPeeker.pixelWidthHeightRatio, 0.01); Assert.assertEquals(16f/9f, mp4vChunkPeeker.pixelWidthHeightRatio, 0.01);
} }
......
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