MpegAudioChunkHandler cleanup and Tests

This commit is contained in:
Dustin 2022-02-05 09:32:19 -07:00
parent 3a9f1f9a34
commit 247b57692e
3 changed files with 134 additions and 13 deletions

View file

@ -51,7 +51,12 @@ public class MpegAudioChunkHandler extends ChunkHandler {
syncTime();
return true;
}
chunkRemaining = size;
this.size = chunkRemaining = size;
return resume(input);
}
@Override
boolean resume(@NonNull ExtractorInput input) throws IOException {
if (process(input)) {
// 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.
@ -59,17 +64,8 @@ public class MpegAudioChunkHandler extends ChunkHandler {
scratch.setPosition(0);
trackOutput.sampleData(scratch, size);
scratch.reset(0);
done(size);
}
clock.advance();
return true;
}
return false;
}
@Override
boolean resume(@NonNull ExtractorInput input) throws IOException {
if (process(input)) {
clock.advance();
return true;
}
return false;
@ -101,7 +97,6 @@ public class MpegAudioChunkHandler extends ChunkHandler {
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);
@ -127,7 +122,7 @@ public class MpegAudioChunkHandler extends ChunkHandler {
trackOutput.sampleData(scratch, scratchBytes);
frameRemaining = header.frameSize - scratchBytes;
} else {
return chunkRemaining == 0;
return true;
}
}
final int bytes = trackOutput.sampleData(input, Math.min(frameRemaining, chunkRemaining), false);
@ -151,4 +146,14 @@ public class MpegAudioChunkHandler extends ChunkHandler {
timeUs = clock.getUs();
frameRemaining = 0;
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
long getTimeUs() {
return timeUs;
}
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
int getFrameRemaining() {
return frameRemaining;
}
}

View file

@ -0,0 +1,116 @@
package com.google.android.exoplayer2.extractor.avi;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.audio.MpegAudioUtil;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.FakeTrackOutput;
import com.google.android.exoplayer2.testutil.TestUtil;
import com.google.android.exoplayer2.util.MimeTypes;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class MpegAudioChunkHandlerTest {
private static final int FPS = 24;
private Format MP3_FORMAT = new Format.Builder().setChannelCount(2).
setSampleMimeType(MimeTypes.AUDIO_MPEG).setSampleRate(44100).build();
private static final long CHUNK_MS = C.MICROS_PER_SECOND / FPS;
private final MpegAudioUtil.Header header = new MpegAudioUtil.Header();
private FakeTrackOutput fakeTrackOutput;
private MpegAudioChunkHandler mpegAudioChunkHandler;
private byte[] mp3Frame;
private long frameUs;
@Before
public void before() throws IOException {
fakeTrackOutput = new FakeTrackOutput(false);
fakeTrackOutput.format(MP3_FORMAT);
mpegAudioChunkHandler = new MpegAudioChunkHandler(0, fakeTrackOutput,
new ChunkClock(C.MICROS_PER_SECOND, FPS), MP3_FORMAT.sampleRate);
if (mp3Frame == null) {
final Context context = ApplicationProvider.getApplicationContext();
mp3Frame = TestUtil.getByteArray(context,"extractordumps/avi/frame.mp3.dump");
header.setForHeaderData(ByteBuffer.wrap(mp3Frame).getInt());
//About 26ms
frameUs = header.samplesPerFrame * C.MICROS_PER_SECOND / header.sampleRate;
}
}
@Test
public void newChunk_givenNonMpegData() throws IOException {
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(new byte[1024]).
build();
mpegAudioChunkHandler.newChunk((int)input.getLength(), input);
Assert.assertEquals(1024, fakeTrackOutput.getSampleData(0).length);
Assert.assertEquals(CHUNK_MS, mpegAudioChunkHandler.getClock().getUs());
}
@Test
public void newChunk_givenEmptyChunk() throws IOException {
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(new byte[0]).
build();
mpegAudioChunkHandler.newChunk((int)input.getLength(), input);
Assert.assertEquals(C.MICROS_PER_SECOND / 24, mpegAudioChunkHandler.getClock().getUs());
}
@Test
public void setIndex_given12frames() {
mpegAudioChunkHandler.setIndex(12);
Assert.assertEquals(500_000L, mpegAudioChunkHandler.getTimeUs());
}
@Test
public void newChunk_givenSingleFrame() throws IOException {
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(mp3Frame).build();
mpegAudioChunkHandler.newChunk(mp3Frame.length, input);
Assert.assertArrayEquals(mp3Frame, fakeTrackOutput.getSampleData(0));
Assert.assertEquals(frameUs, mpegAudioChunkHandler.getTimeUs());
}
@Test
public void newChunk_givenSeekAndFragmentedFrames() throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(mp3Frame.length * 2);
byteBuffer.put(mp3Frame, mp3Frame.length / 2, mp3Frame.length / 2);
byteBuffer.put(mp3Frame);
final int remainder = byteBuffer.remaining();
byteBuffer.put(mp3Frame, 0, remainder);
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).
build();
mpegAudioChunkHandler.setIndex(1); //Seek
Assert.assertFalse(mpegAudioChunkHandler.newChunk(byteBuffer.capacity(), input));
Assert.assertArrayEquals(mp3Frame, fakeTrackOutput.getSampleData(0));
Assert.assertEquals(frameUs + CHUNK_MS, mpegAudioChunkHandler.getTimeUs());
Assert.assertTrue(mpegAudioChunkHandler.resume(input));
Assert.assertEquals(header.frameSize - remainder, mpegAudioChunkHandler.getFrameRemaining());
}
@Test
public void newChunk_givenTwoFrames() throws IOException {
ByteBuffer byteBuffer = ByteBuffer.allocate(mp3Frame.length * 2);
byteBuffer.put(mp3Frame);
byteBuffer.put(mp3Frame);
final FakeExtractorInput input = new FakeExtractorInput.Builder().setData(byteBuffer.array()).
build();
Assert.assertFalse(mpegAudioChunkHandler.newChunk(byteBuffer.capacity(), input));
Assert.assertEquals(1, fakeTrackOutput.getSampleCount());
Assert.assertEquals(0L, fakeTrackOutput.getSampleTimeUs(0));
Assert.assertTrue(mpegAudioChunkHandler.resume(input));
Assert.assertEquals(2, fakeTrackOutput.getSampleCount());
Assert.assertEquals(frameUs, fakeTrackOutput.getSampleTimeUs(1));
}
}

Binary file not shown.