mirror of
https://github.com/samsonjs/media.git
synced 2026-04-02 10:45:51 +00:00
MpegAudioChunkHandler cleanup and Tests
This commit is contained in:
parent
3a9f1f9a34
commit
247b57692e
3 changed files with 134 additions and 13 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
BIN
testdata/src/test/assets/extractordumps/avi/frame.mp3.dump
vendored
Normal file
BIN
testdata/src/test/assets/extractordumps/avi/frame.mp3.dump
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue