mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +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();
|
syncTime();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
chunkRemaining = size;
|
this.size = chunkRemaining = size;
|
||||||
|
return resume(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean resume(@NonNull ExtractorInput input) throws IOException {
|
||||||
if (process(input)) {
|
if (process(input)) {
|
||||||
// Fail Over: If the scratch is the entire chunk, we didn't find a MP3 header.
|
// 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.
|
// Dump the chunk as is and hope the decoder can handle it.
|
||||||
|
|
@ -59,17 +64,8 @@ public class MpegAudioChunkHandler extends ChunkHandler {
|
||||||
scratch.setPosition(0);
|
scratch.setPosition(0);
|
||||||
trackOutput.sampleData(scratch, size);
|
trackOutput.sampleData(scratch, size);
|
||||||
scratch.reset(0);
|
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 true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -101,7 +97,6 @@ public class MpegAudioChunkHandler extends ChunkHandler {
|
||||||
scratch.ensureCapacity(scratch.limit() + chunkRemaining);
|
scratch.ensureCapacity(scratch.limit() + chunkRemaining);
|
||||||
int toRead = 4;
|
int toRead = 4;
|
||||||
while (chunkRemaining > 0 && readScratch(input, toRead) != C.RESULT_END_OF_INPUT) {
|
while (chunkRemaining > 0 && readScratch(input, toRead) != C.RESULT_END_OF_INPUT) {
|
||||||
readScratch(input, toRead);
|
|
||||||
while (scratch.bytesLeft() >= 4) {
|
while (scratch.bytesLeft() >= 4) {
|
||||||
if (header.setForHeaderData(scratch.readInt())) {
|
if (header.setForHeaderData(scratch.readInt())) {
|
||||||
scratch.skipBytes(-4);
|
scratch.skipBytes(-4);
|
||||||
|
|
@ -127,7 +122,7 @@ public class MpegAudioChunkHandler extends ChunkHandler {
|
||||||
trackOutput.sampleData(scratch, scratchBytes);
|
trackOutput.sampleData(scratch, scratchBytes);
|
||||||
frameRemaining = header.frameSize - scratchBytes;
|
frameRemaining = header.frameSize - scratchBytes;
|
||||||
} else {
|
} else {
|
||||||
return chunkRemaining == 0;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final int bytes = trackOutput.sampleData(input, Math.min(frameRemaining, chunkRemaining), false);
|
final int bytes = trackOutput.sampleData(input, Math.min(frameRemaining, chunkRemaining), false);
|
||||||
|
|
@ -151,4 +146,14 @@ public class MpegAudioChunkHandler extends ChunkHandler {
|
||||||
timeUs = clock.getUs();
|
timeUs = clock.getUs();
|
||||||
frameRemaining = 0;
|
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