mirror of
https://github.com/samsonjs/media.git
synced 2026-04-24 14:37:45 +00:00
Improved comments, improved naming consistency.
This commit is contained in:
parent
14c842e503
commit
3a9f1f9a34
14 changed files with 147 additions and 124 deletions
|
|
@ -29,7 +29,7 @@ import java.io.IOException;
|
|||
* Corrects the time and PAR for H264 streams
|
||||
* AVC is very rare in AVI due to the rise of the mp4 container
|
||||
*/
|
||||
public class AvcChunkHandler extends NalChunkPeeker {
|
||||
public class AvcChunkHandler extends NalChunkHandler {
|
||||
private static final int NAL_TYPE_MASK = 0x1f;
|
||||
private static final int NAL_TYPE_IDR = 5; //I Frame
|
||||
private static final int NAL_TYPE_SEI = 6;
|
||||
|
|
@ -63,7 +63,7 @@ public class AvcChunkHandler extends NalChunkPeeker {
|
|||
if (clock instanceof PicCountClock) {
|
||||
return false;
|
||||
} else {
|
||||
//If the clock is regular clock, skip "normal" frames
|
||||
//If the clock is ChunkClock, skip "normal" frames
|
||||
return nalType >= 0 && nalType <= NAL_TYPE_IDR;
|
||||
}
|
||||
}
|
||||
|
|
@ -107,10 +107,12 @@ public class AvcChunkHandler extends NalChunkPeeker {
|
|||
final int spsStart = nalTypeOffset + 1;
|
||||
nalTypeOffset = seekNextNal(input, spsStart);
|
||||
spsData = NalUnitUtil.parseSpsNalUnitPayload(buffer, spsStart, pos);
|
||||
//If we have B Frames, upgrade to PicCountClock
|
||||
//If we can have B Frames, upgrade to PicCountClock
|
||||
final PicCountClock picCountClock;
|
||||
if (spsData.maxNumRefFrames > 1 && !(clock instanceof PicCountClock)) {
|
||||
clock = picCountClock = new PicCountClock(clock.durationUs, clock.chunks);
|
||||
picCountClock = new PicCountClock(clock.durationUs, clock.chunks);
|
||||
picCountClock.setIndex(clock.getIndex());
|
||||
clock = picCountClock;
|
||||
} else {
|
||||
picCountClock = getPicCountClock();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -291,7 +291,7 @@ public class AviExtractor implements Extractor {
|
|||
}
|
||||
trackOutput.format(builder.build());
|
||||
if (MimeTypes.AUDIO_MPEG.equals(mimeType)) {
|
||||
chunkHandler = new Mp3ChunkHandler(streamId, trackOutput, clock,
|
||||
chunkHandler = new MpegAudioChunkHandler(streamId, trackOutput, clock,
|
||||
audioFormat.getSamplesPerSecond());
|
||||
} else {
|
||||
chunkHandler = new ChunkHandler(streamId, ChunkHandler.TYPE_AUDIO,
|
||||
|
|
@ -367,7 +367,7 @@ public class AviExtractor implements Extractor {
|
|||
}
|
||||
|
||||
void fixTimings(final int[] keyFrameCounts, final long videoDuration) {
|
||||
for (final ChunkHandler chunkHandler : chunkHandlers) {
|
||||
for (@Nullable final ChunkHandler chunkHandler : chunkHandlers) {
|
||||
if (chunkHandler != null) {
|
||||
if (chunkHandler.isAudio()) {
|
||||
final long durationUs = chunkHandler.getClock().durationUs;
|
||||
|
|
@ -452,7 +452,7 @@ public class AviExtractor implements Extractor {
|
|||
int indexSize = seekIndexes[videoId].getSize();
|
||||
if (indexSize == 0 || chunkHandler.chunks - seekIndexes[videoId].get(indexSize - 1) >= chunksPerKeyFrame) {
|
||||
keyFrameOffsetsDiv2.add(offset / 2);
|
||||
for (ChunkHandler seekTrack : chunkHandlers) {
|
||||
for (@Nullable ChunkHandler seekTrack : chunkHandlers) {
|
||||
if (seekTrack != null) {
|
||||
seekIndexes[seekTrack.getId()].add(seekTrack.chunks);
|
||||
}
|
||||
|
|
@ -487,7 +487,7 @@ public class AviExtractor implements Extractor {
|
|||
@Nullable
|
||||
@VisibleForTesting
|
||||
ChunkHandler getChunkHandler(int chunkId) {
|
||||
for (ChunkHandler chunkHandler : chunkHandlers) {
|
||||
for (@Nullable ChunkHandler chunkHandler : chunkHandlers) {
|
||||
if (chunkHandler != null && chunkHandler.handlesChunkId(chunkId)) {
|
||||
return chunkHandler;
|
||||
}
|
||||
|
|
@ -536,7 +536,7 @@ public class AviExtractor implements Extractor {
|
|||
+ " size=" + size + " moviEnd=" + moviEnd);
|
||||
return RESULT_CONTINUE;
|
||||
}
|
||||
if (chunkHandler.newChunk(chunkId, size, input)) {
|
||||
if (chunkHandler.newChunk(size, input)) {
|
||||
alignInput(input);
|
||||
} else {
|
||||
this.chunkHandler = chunkHandler;
|
||||
|
|
@ -587,20 +587,20 @@ public class AviExtractor implements Extractor {
|
|||
chunkHandler = null;
|
||||
if (position <= 0) {
|
||||
if (moviOffset != 0) {
|
||||
resetClocks();
|
||||
setIndexes(new int[chunkHandlers.length]);
|
||||
state = STATE_SEEK_START;
|
||||
}
|
||||
} else {
|
||||
if (aviSeekMap != null) {
|
||||
aviSeekMap.setFrames(position, timeUs, chunkHandlers);
|
||||
setIndexes(aviSeekMap.getIndexes(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resetClocks() {
|
||||
private void setIndexes(@NonNull int[] indexes) {
|
||||
for (@Nullable ChunkHandler chunkHandler : chunkHandlers) {
|
||||
if (chunkHandler != null) {
|
||||
chunkHandler.getClock().setIndex(0);
|
||||
chunkHandler.setIndex(indexes[chunkHandler.getId()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,18 +99,23 @@ public class AviSeekMap implements SeekMap {
|
|||
//Log.d(AviExtractor.TAG, "SeekPoint: us=" + outUs + " pos=" + position);
|
||||
}
|
||||
|
||||
public void setFrames(final long position, final long timeUs, final ChunkHandler[] chunkHandlers) {
|
||||
/**
|
||||
* Get the ChunkClock indexes by stream id
|
||||
* @param position seek position in the file
|
||||
*/
|
||||
@NonNull
|
||||
public int[] getIndexes(final long position) {
|
||||
final int index = Arrays.binarySearch(keyFrameOffsetsDiv2, (int)((position - seekOffset) / 2));
|
||||
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Position: " + position);
|
||||
}
|
||||
for (int i=0;i<chunkHandlers.length;i++) {
|
||||
final ChunkHandler chunkHandler = chunkHandlers[i];
|
||||
if (chunkHandler != null) {
|
||||
// Log.d(AviExtractor.TAG, "Frame: " + (chunkHandler.isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " frame=" + clock.getIndex() + " key=" + chunkHandler.isKeyFrame());
|
||||
chunkHandler.setIndex(seekIndexes[i][index]);
|
||||
final int[] indexes = new int[seekIndexes.length];
|
||||
for (int i=0;i<indexes.length;i++) {
|
||||
if (seekIndexes[i].length > index) {
|
||||
indexes[i] = seekIndexes[i][index];
|
||||
}
|
||||
}
|
||||
return indexes;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,11 +25,16 @@ import java.io.IOException;
|
|||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Collection of info about a track.
|
||||
* This acts a bridge between AVI and ExoPlayer structures
|
||||
* Handles chunk data from a given stream.
|
||||
* This acts a bridge between AVI and ExoPlayer
|
||||
*/
|
||||
public class ChunkHandler {
|
||||
|
||||
/**
|
||||
* Constant meaning all frames are considered key frames
|
||||
*/
|
||||
public static final int[] ALL_KEY_FRAMES = new int[0];
|
||||
|
||||
public static int TYPE_VIDEO = ('d' << 16) | ('c' << 24);
|
||||
public static int TYPE_AUDIO = ('w' << 16) | ('b' << 24);
|
||||
|
||||
|
|
@ -39,10 +44,24 @@ public class ChunkHandler {
|
|||
@NonNull
|
||||
final TrackOutput trackOutput;
|
||||
|
||||
/**
|
||||
* The chunk id as it appears in the index and the movi
|
||||
*/
|
||||
final int chunkId;
|
||||
|
||||
/**
|
||||
* Secondary chunk id. Bad muxers sometimes use uncompressed for key frames
|
||||
*/
|
||||
final int chunkIdAlt;
|
||||
|
||||
/**
|
||||
* Number of chunks as calculated by the index
|
||||
*/
|
||||
int chunks;
|
||||
|
||||
/**
|
||||
* Size total size of the stream in bytes calculated by the index
|
||||
*/
|
||||
int size;
|
||||
|
||||
/**
|
||||
|
|
@ -50,9 +69,18 @@ public class ChunkHandler {
|
|||
*/
|
||||
int[] keyFrames = new int[0];
|
||||
|
||||
/**
|
||||
* Size of the current chunk in bytes
|
||||
*/
|
||||
transient int chunkSize;
|
||||
/**
|
||||
* Bytes remaining in the chunk to be processed
|
||||
*/
|
||||
transient int chunkRemaining;
|
||||
|
||||
/**
|
||||
* Get stream id in ASCII
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static int getChunkIdLower(int id) {
|
||||
int tens = id / 10;
|
||||
|
|
@ -71,6 +99,10 @@ public class ChunkHandler {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return true if this can handle the chunkId
|
||||
*/
|
||||
public boolean handlesChunkId(int chunkId) {
|
||||
return this.chunkId == chunkId || chunkIdAlt == chunkId;
|
||||
}
|
||||
|
|
@ -80,13 +112,9 @@ public class ChunkHandler {
|
|||
return clock;
|
||||
}
|
||||
|
||||
public void setClock(@NonNull ChunkClock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param keyFrames null means all key frames
|
||||
* Sets the list of key frames
|
||||
* @param keyFrames list of frame indexes or {@link #ALL_KEY_FRAMES}
|
||||
*/
|
||||
void setKeyFrames(@NonNull final int[] keyFrames) {
|
||||
this.keyFrames = keyFrames;
|
||||
|
|
@ -104,23 +132,27 @@ public class ChunkHandler {
|
|||
return (chunkId & TYPE_AUDIO) == TYPE_AUDIO;
|
||||
}
|
||||
|
||||
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
|
||||
final int remaining = size - trackOutput.sampleData(input, size, false);
|
||||
if (remaining == 0) {
|
||||
/**
|
||||
* Process a new chunk
|
||||
* @param size total size of the chunk
|
||||
* @return True if the chunk has been completely processed. False implies {@link #resume}
|
||||
* will be called
|
||||
*/
|
||||
public boolean newChunk(int size, @NonNull ExtractorInput input) throws IOException {
|
||||
final int sampled = trackOutput.sampleData(input, size, false);
|
||||
if (sampled == size) {
|
||||
done(size);
|
||||
return true;
|
||||
} else {
|
||||
chunkSize = size;
|
||||
chunkRemaining = remaining;
|
||||
chunkRemaining = size - sampled;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a partial read of a chunk
|
||||
* @param input
|
||||
* @return
|
||||
* @throws IOException
|
||||
* May be called multiple times
|
||||
*/
|
||||
boolean resume(ExtractorInput input) throws IOException {
|
||||
chunkRemaining -= trackOutput.sampleData(input, chunkRemaining, false);
|
||||
|
|
@ -133,25 +165,29 @@ public class ChunkHandler {
|
|||
}
|
||||
|
||||
/**
|
||||
* Done reading a chunk
|
||||
* @param size
|
||||
* Done reading a chunk. Send the timing info and advance the clock
|
||||
* @param size the amount of data passed to the trackOutput
|
||||
*/
|
||||
void done(final int size) {
|
||||
if (size > 0) {
|
||||
trackOutput.sampleMetadata(
|
||||
clock.getUs(), (isKeyFrame() ? C.BUFFER_FLAG_KEY_FRAME : 0), size, 0, null);
|
||||
}
|
||||
final ChunkClock clock = getClock();
|
||||
//Log.d(AviExtractor.TAG, "Frame: " + (isVideo()? 'V' : 'A') + " us=" + clock.getUs() + " size=" + size + " frame=" + clock.getIndex() + " key=" + isKeyFrame());
|
||||
clock.advance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the streamId.
|
||||
* @return The unique stream id for this file
|
||||
*/
|
||||
public int getId() {
|
||||
return ((chunkId >> 8) & 0xf) + ( chunkId & 0xf) * 10;
|
||||
return ((chunkId >> 8) & 0xf) + (chunkId & 0xf) * 10;
|
||||
}
|
||||
|
||||
/**
|
||||
* A seek occurred
|
||||
* @param index of the chunk
|
||||
*/
|
||||
public void setIndex(int index) {
|
||||
getClock().setIndex(index);
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import java.io.IOException;
|
|||
/**
|
||||
* Peeks an MP4V stream looking for pixelWidthHeightRatio data
|
||||
*/
|
||||
public class Mp4vChunkHandler extends NalChunkPeeker {
|
||||
public class Mp4vChunkHandler extends NalChunkHandler {
|
||||
@VisibleForTesting
|
||||
static final byte SEQUENCE_START_CODE = (byte)0xb0;
|
||||
@VisibleForTesting
|
||||
|
|
|
|||
|
|
@ -16,37 +16,45 @@
|
|||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
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 {
|
||||
/**
|
||||
* Resolves several issues with Mpeg Audio
|
||||
* 1. That muxers don't always mux MPEG audio on the frame boundary
|
||||
* 2. That some codecs can't handle multiple or partial frames (Pixels)
|
||||
*/
|
||||
public class MpegAudioChunkHandler extends ChunkHandler {
|
||||
private final MpegAudioUtil.Header header = new MpegAudioUtil.Header();
|
||||
private final ParsableByteArray scratch = new ParsableByteArray(0);
|
||||
private final int fps;
|
||||
private final ParsableByteArray scratch = new ParsableByteArray(8);
|
||||
private final int samplesPerSecond;
|
||||
//Bytes remaining in the Mpeg Audio frame
|
||||
private int frameRemaining;
|
||||
private long us = 0L;
|
||||
private long timeUs = 0L;
|
||||
|
||||
Mp3ChunkHandler(int id, @NonNull TrackOutput trackOutput, @NonNull ChunkClock clock, int fps) {
|
||||
MpegAudioChunkHandler(int id, @NonNull TrackOutput trackOutput, @NonNull ChunkClock clock,
|
||||
int samplesPerSecond) {
|
||||
super(id, TYPE_AUDIO, trackOutput, clock);
|
||||
this.fps = fps;
|
||||
this.samplesPerSecond = samplesPerSecond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
|
||||
public boolean newChunk(int size, @NonNull ExtractorInput input) throws IOException {
|
||||
if (size == 0) {
|
||||
//Empty frame, advance the clock and sync
|
||||
clock.advance();
|
||||
syncUs();
|
||||
//Log.d(AviExtractor.TAG, "Blank Frame: us=" + us);
|
||||
syncTime();
|
||||
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
|
||||
// Fail Over: If the scratch is the entire chunk, we didn't find a MP3 header.
|
||||
// Dump the chunk as is and hope the decoder can handle it.
|
||||
if (scratch.limit() == size) {
|
||||
scratch.setPosition(0);
|
||||
trackOutput.sampleData(scratch, size);
|
||||
|
|
@ -59,7 +67,7 @@ public class Mp3ChunkHandler extends ChunkHandler {
|
|||
}
|
||||
|
||||
@Override
|
||||
boolean resume(ExtractorInput input) throws IOException {
|
||||
boolean resume(@NonNull ExtractorInput input) throws IOException {
|
||||
if (process(input)) {
|
||||
clock.advance();
|
||||
return true;
|
||||
|
|
@ -67,6 +75,11 @@ public class Mp3ChunkHandler extends ChunkHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read from input to scratch
|
||||
* @param bytes to attempt to read
|
||||
* @return {@link C#RESULT_END_OF_INPUT} or number of bytes read.
|
||||
*/
|
||||
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);
|
||||
|
|
@ -78,7 +91,12 @@ public class Mp3ChunkHandler extends ChunkHandler {
|
|||
return read;
|
||||
}
|
||||
|
||||
private boolean findFrame(ExtractorInput input) throws IOException {
|
||||
/**
|
||||
* Attempt to find a frame header in the input
|
||||
* @return true if a frame header was found
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean findFrame(ExtractorInput input) throws IOException {
|
||||
scratch.reset(0);
|
||||
scratch.ensureCapacity(scratch.limit() + chunkRemaining);
|
||||
int toRead = 4;
|
||||
|
|
@ -96,8 +114,14 @@ public class Mp3ChunkHandler extends ChunkHandler {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean process(ExtractorInput input) throws IOException {
|
||||
/**
|
||||
* Process the chunk by breaking it in Mpeg audio frames
|
||||
* @return true if the chunk has been completely processed
|
||||
*/
|
||||
@VisibleForTesting
|
||||
boolean process(ExtractorInput input) throws IOException {
|
||||
if (frameRemaining == 0) {
|
||||
//Find the next frame
|
||||
if (findFrame(input)) {
|
||||
final int scratchBytes = scratch.bytesLeft();
|
||||
trackOutput.sampleData(scratch, scratchBytes);
|
||||
|
|
@ -107,14 +131,11 @@ public class Mp3ChunkHandler extends ChunkHandler {
|
|||
}
|
||||
}
|
||||
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);
|
||||
trackOutput.sampleMetadata(timeUs, C.BUFFER_FLAG_KEY_FRAME, header.frameSize, 0, null);
|
||||
//Log.d(AviExtractor.TAG, "MP3: us=" + us);
|
||||
us += header.samplesPerFrame * C.MICROS_PER_SECOND / fps;
|
||||
timeUs += header.samplesPerFrame * C.MICROS_PER_SECOND / samplesPerSecond;
|
||||
}
|
||||
chunkRemaining -= bytes;
|
||||
return chunkRemaining == 0;
|
||||
|
|
@ -123,11 +144,11 @@ public class Mp3ChunkHandler extends ChunkHandler {
|
|||
@Override
|
||||
public void setIndex(int index) {
|
||||
super.setIndex(index);
|
||||
syncUs();
|
||||
syncTime();
|
||||
}
|
||||
|
||||
private void syncUs() {
|
||||
us = clock.getUs();
|
||||
private void syncTime() {
|
||||
timeUs = clock.getUs();
|
||||
frameRemaining = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ import java.util.Arrays;
|
|||
* Generic base class for NAL (0x00 0x00 0x01) chunk headers
|
||||
* Theses are used by AVC and MP4V (XVID)
|
||||
*/
|
||||
public abstract class NalChunkPeeker extends ChunkHandler {
|
||||
public abstract class NalChunkHandler extends ChunkHandler {
|
||||
private static final int SEEK_PEEK_SIZE = 256;
|
||||
private final int peekSize;
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ public abstract class NalChunkPeeker extends ChunkHandler {
|
|||
transient byte[] buffer;
|
||||
transient int pos;
|
||||
|
||||
NalChunkPeeker(int id, @NonNull TrackOutput trackOutput,
|
||||
NalChunkHandler(int id, @NonNull TrackOutput trackOutput,
|
||||
@NonNull ChunkClock clock, int peakSize) {
|
||||
super(id, TYPE_VIDEO, trackOutput, clock);
|
||||
if (peakSize < 5) {
|
||||
|
|
@ -113,9 +113,9 @@ public abstract class NalChunkPeeker extends ChunkHandler {
|
|||
|
||||
abstract boolean skip(byte nalType);
|
||||
|
||||
public boolean newChunk(int tag, int size, ExtractorInput input) throws IOException {
|
||||
public boolean newChunk(int size, ExtractorInput input) throws IOException {
|
||||
peek(input, size);
|
||||
return super.newChunk(tag, size, input);
|
||||
return super.newChunk(size, input);
|
||||
}
|
||||
|
||||
public void peek(ExtractorInput input, final int size) throws IOException {
|
||||
|
|
@ -16,13 +16,6 @@
|
|||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||
import com.google.android.exoplayer2.util.Log;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
|
|
@ -39,7 +32,6 @@ public class ResidentBox extends Box {
|
|||
|
||||
/**
|
||||
* Returns shallow copy of this ByteBuffer with the position at 0
|
||||
* @return
|
||||
*/
|
||||
@NonNull
|
||||
public ByteBuffer getByteBuffer() {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package com.google.android.exoplayer2.extractor.avi;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Human readable stream name
|
||||
* Box containing a human readable stream name
|
||||
*/
|
||||
public class StreamNameBox extends ResidentBox {
|
||||
public static final int STRN = 's' | ('t' << 8) | ('r' << 16) | ('n' << 24);
|
||||
|
|
|
|||
|
|
@ -39,12 +39,12 @@ public class AvcChunkPeekerTest {
|
|||
(byte)0xFE,(byte)0x9E,0x10,0,0};
|
||||
|
||||
private FakeTrackOutput fakeTrackOutput;
|
||||
private AvcChunkHandler avcChunkPeeker;
|
||||
private AvcChunkHandler avcChunkHandler;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
fakeTrackOutput = new FakeTrackOutput(false);
|
||||
avcChunkPeeker = new AvcChunkHandler(0, fakeTrackOutput,
|
||||
avcChunkHandler = new AvcChunkHandler(0, fakeTrackOutput,
|
||||
new ChunkClock(10_000_000L, 24 * 10), FORMAT_BUILDER_AVC);
|
||||
}
|
||||
|
||||
|
|
@ -55,26 +55,26 @@ public class AvcChunkPeekerTest {
|
|||
|
||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(bytes).build();
|
||||
|
||||
avcChunkPeeker.peek(input, bytes.length);
|
||||
avcChunkHandler.peek(input, bytes.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void peek_givenStreamHeader() throws IOException {
|
||||
peekStreamHeader();
|
||||
final PicCountClock picCountClock = avcChunkPeeker.getPicCountClock();
|
||||
final PicCountClock picCountClock = avcChunkHandler.getPicCountClock();
|
||||
Assert.assertNotNull(picCountClock);
|
||||
Assert.assertEquals(64, picCountClock.getMaxPicCount());
|
||||
Assert.assertEquals(0, avcChunkPeeker.getSpsData().picOrderCountType);
|
||||
Assert.assertEquals(0, avcChunkHandler.getSpsData().picOrderCountType);
|
||||
Assert.assertEquals(1.18f, fakeTrackOutput.lastFormat.pixelWidthHeightRatio, 0.01f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newChunk_givenStreamHeaderAndPSlice() throws IOException {
|
||||
peekStreamHeader();
|
||||
final PicCountClock picCountClock = avcChunkPeeker.getPicCountClock();
|
||||
final PicCountClock picCountClock = avcChunkHandler.getPicCountClock();
|
||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(P_SLICE).build();
|
||||
|
||||
avcChunkPeeker.newChunk(0, P_SLICE.length, input);
|
||||
avcChunkHandler.newChunk(P_SLICE.length, input);
|
||||
|
||||
Assert.assertEquals(12, picCountClock.getLastPicCount());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.extractor.SeekMap;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
|
@ -23,26 +22,25 @@ import org.junit.Test;
|
|||
public class AviSeekMapTest {
|
||||
|
||||
@Test
|
||||
public void setFrames_givenExactSeekPointMatch() {
|
||||
public void getFrames_givenExactSeekPointMatch() {
|
||||
final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap();
|
||||
final long position = aviSeekMap.keyFrameOffsetsDiv2[1] * 2L + aviSeekMap.seekOffset;
|
||||
final int secs = 4;
|
||||
final ChunkHandler[] chunkHandlers = new ChunkHandler[]{DataHelper.getVideoChunkHandler(secs),
|
||||
DataHelper.getAudioChunkHandler(secs)};
|
||||
|
||||
aviSeekMap.setFrames(position, C.MICROS_PER_SECOND, chunkHandlers);
|
||||
int[] indexes = aviSeekMap.getIndexes(position);
|
||||
for (int i=0;i<chunkHandlers.length;i++) {
|
||||
Assert.assertEquals(aviSeekMap.seekIndexes[i][1], chunkHandlers[i].getClock().getIndex());
|
||||
Assert.assertEquals(aviSeekMap.seekIndexes[i][1], indexes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setFrames_givenBadPosition() {
|
||||
final AviSeekMap aviSeekMap = DataHelper.getAviSeekMap();
|
||||
final ChunkHandler[] chunkHandlers = new ChunkHandler[2];
|
||||
|
||||
try {
|
||||
aviSeekMap.setFrames(1L, C.MICROS_PER_SECOND, chunkHandlers);
|
||||
aviSeekMap.getIndexes(1L);
|
||||
Assert.fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
//Intentionally blank
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* 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 org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ChunkHandlerTest {
|
||||
@Test
|
||||
public void setClock_givenLinearClock() {
|
||||
final ChunkClock linearClock = new ChunkClock(1_000_000L, 30);
|
||||
final ChunkHandler chunkHandler = DataHelper.getVideoChunkHandler(1);
|
||||
chunkHandler.setClock(linearClock);
|
||||
|
||||
Assert.assertSame(linearClock, chunkHandler.getClock());
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,9 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
|
|||
import com.google.android.exoplayer2.testutil.FakeTrackOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MockNalChunkPeeker extends NalChunkPeeker {
|
||||
public class MockNalChunkHandler extends NalChunkHandler {
|
||||
private boolean skip;
|
||||
public MockNalChunkPeeker(int peakSize, boolean skip) {
|
||||
public MockNalChunkHandler(int peakSize, boolean skip) {
|
||||
super(0, new FakeTrackOutput(false), new ChunkClock(1_000_000L, 24), peakSize);
|
||||
this.skip = skip;
|
||||
}
|
||||
|
|
@ -16,7 +16,6 @@
|
|||
package com.google.android.exoplayer2.extractor.avi;
|
||||
|
||||
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.junit.Assert;
|
||||
|
|
@ -26,7 +25,7 @@ public class NalChunkPeekerTest {
|
|||
@Test
|
||||
public void construct_givenTooSmallPeekSize() {
|
||||
try {
|
||||
new MockNalChunkPeeker(4, false);
|
||||
new MockNalChunkHandler(4, false);
|
||||
Assert.fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
//Intentionally blank
|
||||
|
|
@ -36,7 +35,7 @@ public class NalChunkPeekerTest {
|
|||
@Test
|
||||
public void peek_givenNoData() {
|
||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().build();
|
||||
final MockNalChunkPeeker peeker = new MockNalChunkPeeker(5, false);
|
||||
final MockNalChunkHandler peeker = new MockNalChunkHandler(5, false);
|
||||
try {
|
||||
peeker.peek(input, 10);
|
||||
} catch (IOException e) {
|
||||
|
|
@ -46,7 +45,7 @@ public class NalChunkPeekerTest {
|
|||
@Test
|
||||
public void peek_givenNoNal() {
|
||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(new byte[10]).build();
|
||||
final MockNalChunkPeeker peeker = new MockNalChunkPeeker(5, false);
|
||||
final MockNalChunkHandler peeker = new MockNalChunkHandler(5, false);
|
||||
try {
|
||||
peeker.peek(input, 10);
|
||||
} catch (IOException e) {
|
||||
|
|
@ -59,7 +58,7 @@ public class NalChunkPeekerTest {
|
|||
DataHelper.appendNal(byteBuffer, (byte)32);
|
||||
|
||||
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).build();
|
||||
final MockNalChunkPeeker peeker = new MockNalChunkPeeker(5, true);
|
||||
final MockNalChunkHandler peeker = new MockNalChunkHandler(5, true);
|
||||
try {
|
||||
peeker.peek(input, 10);
|
||||
Assert.assertEquals(0, input.getPeekPosition());
|
||||
|
|
|
|||
Loading…
Reference in a new issue