Commit f5025bfc by kimvde Committed by Oliver Woodman

Share seek tests between FLAC extractors

PiperOrigin-RevId: 294893303
parent ebce903a
/*
* Copyright (C) 2018 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.ext.flac;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.ext.flac.FlacBinarySearchSeeker.OutputFrameHolder;
import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/** Unit test for {@link FlacBinarySearchSeeker}. */
@RunWith(AndroidJUnit4.class)
public final class FlacBinarySearchSeekerTest {
private static final String NOSEEKTABLE_FLAC = "flac/bear_one_metadata_block.flac";
private static final int DURATION_US = 2_741_000;
@Before
public void setUp() {
if (!FlacLibrary.isAvailable()) {
fail("Flac library not available.");
}
}
@Test
public void testGetSeekMap_returnsSeekMapWithCorrectDuration()
throws IOException, FlacDecoderException, InterruptedException {
byte[] data =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), NOSEEKTABLE_FLAC);
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).build();
FlacDecoderJni decoderJni = new FlacDecoderJni();
decoderJni.setData(input);
OutputFrameHolder outputFrameHolder = new OutputFrameHolder(ByteBuffer.allocateDirect(0));
FlacBinarySearchSeeker seeker =
new FlacBinarySearchSeeker(
decoderJni.decodeStreamMetadata(),
/* firstFramePosition= */ 0,
data.length,
decoderJni,
outputFrameHolder);
SeekMap seekMap = seeker.getSeekMap();
assertThat(seekMap).isNotNull();
assertThat(seekMap.getDurationUs()).isEqualTo(DURATION_US);
assertThat(seekMap.isSeekable()).isTrue();
}
@Test
public void testSetSeekTargetUs_returnsSeekPending()
throws IOException, FlacDecoderException, InterruptedException {
byte[] data =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), NOSEEKTABLE_FLAC);
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).build();
FlacDecoderJni decoderJni = new FlacDecoderJni();
decoderJni.setData(input);
OutputFrameHolder outputFrameHolder = new OutputFrameHolder(ByteBuffer.allocateDirect(0));
FlacBinarySearchSeeker seeker =
new FlacBinarySearchSeeker(
decoderJni.decodeStreamMetadata(),
/* firstFramePosition= */ 0,
data.length,
decoderJni,
outputFrameHolder);
seeker.setSeekTargetUs(/* timeUs= */ 1000);
assertThat(seeker.isSeeking()).isTrue();
}
}
...@@ -16,73 +16,44 @@ ...@@ -16,73 +16,44 @@
package com.google.android.exoplayer2.ext.flac; package com.google.android.exoplayer2.ext.flac;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import android.content.Context;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.Nullable;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
import com.google.android.exoplayer2.testutil.FakeExtractorOutput; import com.google.android.exoplayer2.testutil.FakeExtractorOutput;
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 com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.DefaultDataSource; import com.google.android.exoplayer2.upstream.DefaultDataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Random;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
/** Seeking tests for {@link FlacExtractor} when the FLAC stream does not have a SEEKTABLE. */ /** Seeking tests for {@link FlacExtractor}. */
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public final class FlacExtractorSeekTest { public final class FlacExtractorSeekTest {
private static final String NO_SEEKTABLE_FLAC = "flac/bear_one_metadata_block.flac"; private static final String TEST_FILE_SEEK_TABLE = "flac/bear.flac";
private static final String TEST_FILE_BINARY_SEARCH = "flac/bear_one_metadata_block.flac";
private static final String TEST_FILE_UNSEEKABLE = "flac/bear_no_seek_table_no_num_samples.flac";
private static final int DURATION_US = 2_741_000; private static final int DURATION_US = 2_741_000;
private static final Uri FILE_URI = Uri.parse("file:///android_asset/" + NO_SEEKTABLE_FLAC);
private static final Random RANDOM = new Random(1234L);
private FakeExtractorOutput expectedOutput; private FlacExtractor extractor = new FlacExtractor();
private FakeTrackOutput expectedTrackOutput; private FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
private DefaultDataSource dataSource =
private DefaultDataSource dataSource;
private PositionHolder positionHolder;
private long totalInputLength;
@Before
public void setUp() throws Exception {
if (!FlacLibrary.isAvailable()) {
fail("Flac library not available.");
}
expectedOutput = new FakeExtractorOutput();
extractAllSamplesFromFileToExpectedOutput(
ApplicationProvider.getApplicationContext(), NO_SEEKTABLE_FLAC);
expectedTrackOutput = expectedOutput.trackOutputs.get(0);
dataSource =
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent") new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
.createDataSource(); .createDataSource();
totalInputLength = readInputLength();
positionHolder = new PositionHolder();
}
@Test @Test
public void testFlacExtractorReads_nonSeekTableFile_returnSeekableSeekMap() public void flacExtractorReads_seekTable_returnSeekableSeekMap()
throws IOException, InterruptedException { throws IOException, InterruptedException {
FlacExtractor extractor = new FlacExtractor(); Uri fileUri = TestUtil.buildAssetUri(TEST_FILE_SEEK_TABLE);
SeekMap seekMap = extractSeekMap(extractor, new FakeExtractorOutput()); SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
assertThat(seekMap).isNotNull(); assertThat(seekMap).isNotNull();
assertThat(seekMap.getDurationUs()).isEqualTo(DURATION_US); assertThat(seekMap.getDurationUs()).isEqualTo(DURATION_US);
...@@ -90,205 +61,232 @@ public final class FlacExtractorSeekTest { ...@@ -90,205 +61,232 @@ public final class FlacExtractorSeekTest {
} }
@Test @Test
public void testHandlePendingSeek_handlesSeekingToPositionInFile_extractsCorrectFrame() public void seeking_seekTable_handlesSeekToZero() throws IOException, InterruptedException {
throws IOException, InterruptedException { String fileName = TEST_FILE_SEEK_TABLE;
FlacExtractor extractor = new FlacExtractor(); Uri fileUri = TestUtil.buildAssetUri(fileName);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0); FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
long targetSeekTimeUs = 987_000; long targetSeekTimeUs = 0;
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput); int extractedFrameIndex =
TestUtil.seekToTimeUs(
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
assertThat(extractedFrameIndex).isNotEqualTo(-1); assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
assertFirstFrameAfterSeekContainTargetSeekTime( assertFirstFrameAfterSeekPrecedesTargetSeekTime(
trackOutput, targetSeekTimeUs, extractedFrameIndex); fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
} }
@Test @Test
public void testHandlePendingSeek_handlesSeekToEoF_extractsLastFrame() public void seeking_seekTable_handlesSeekToEoF() throws IOException, InterruptedException {
throws IOException, InterruptedException { String fileName = TEST_FILE_SEEK_TABLE;
FlacExtractor extractor = new FlacExtractor(); Uri fileUri = TestUtil.buildAssetUri(fileName);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0); FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
long targetSeekTimeUs = seekMap.getDurationUs(); long targetSeekTimeUs = seekMap.getDurationUs();
int extractedFrameIndex =
TestUtil.seekToTimeUs(
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput); assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
assertFirstFrameAfterSeekPrecedesTargetSeekTime(
assertThat(extractedFrameIndex).isNotEqualTo(-1); fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
assertFirstFrameAfterSeekContainTargetSeekTime(
trackOutput, targetSeekTimeUs, extractedFrameIndex);
} }
@Test @Test
public void testHandlePendingSeek_handlesSeekingBackward_extractsCorrectFrame() public void seeking_seekTable_handlesSeekingBackward() throws IOException, InterruptedException {
throws IOException, InterruptedException { String fileName = TEST_FILE_SEEK_TABLE;
FlacExtractor extractor = new FlacExtractor(); Uri fileUri = TestUtil.buildAssetUri(fileName);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0); FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
long firstSeekTimeUs = 987_000; long firstSeekTimeUs = 1_234_000;
seekToTimeUs(extractor, seekMap, firstSeekTimeUs, trackOutput); TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
long targetSeekTimeUs = 987_000;
long targetSeekTimeUs = 0; int extractedFrameIndex =
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput); TestUtil.seekToTimeUs(
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
assertThat(extractedFrameIndex).isNotEqualTo(-1); assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
assertFirstFrameAfterSeekContainTargetSeekTime( assertFirstFrameAfterSeekPrecedesTargetSeekTime(
trackOutput, targetSeekTimeUs, extractedFrameIndex); fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
} }
@Test @Test
public void testHandlePendingSeek_handlesSeekingForward_extractsCorrectFrame() public void seeking_seekTable_handlesSeekingForward() throws IOException, InterruptedException {
throws IOException, InterruptedException { String fileName = TEST_FILE_SEEK_TABLE;
FlacExtractor extractor = new FlacExtractor(); Uri fileUri = TestUtil.buildAssetUri(fileName);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0); FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
long firstSeekTimeUs = 987_000; long firstSeekTimeUs = 987_000;
seekToTimeUs(extractor, seekMap, firstSeekTimeUs, trackOutput); TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
long targetSeekTimeUs = 1_234_000; long targetSeekTimeUs = 1_234_000;
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput); int extractedFrameIndex =
TestUtil.seekToTimeUs(
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
assertThat(extractedFrameIndex).isNotEqualTo(-1); assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
assertFirstFrameAfterSeekContainTargetSeekTime( assertFirstFrameAfterSeekPrecedesTargetSeekTime(
trackOutput, targetSeekTimeUs, extractedFrameIndex); fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
} }
@Test @Test
public void testHandlePendingSeek_handlesRandomSeeks_extractsCorrectFrame() public void flacExtractorReads_binarySearch_returnSeekableSeekMap()
throws IOException, InterruptedException { throws IOException, InterruptedException {
FlacExtractor extractor = new FlacExtractor(); Uri fileUri = TestUtil.buildAssetUri(TEST_FILE_BINARY_SEARCH);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
assertThat(seekMap).isNotNull();
assertThat(seekMap.getDurationUs()).isEqualTo(DURATION_US);
assertThat(seekMap.isSeekable()).isTrue();
}
FakeExtractorOutput extractorOutput = new FakeExtractorOutput(); @Test
SeekMap seekMap = extractSeekMap(extractor, extractorOutput); public void seeking_binarySearch_handlesSeekToZero() throws IOException, InterruptedException {
String fileName = TEST_FILE_BINARY_SEARCH;
Uri fileUri = TestUtil.buildAssetUri(fileName);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0); FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
long numSeek = 100; long targetSeekTimeUs = 0;
for (long i = 0; i < numSeek; i++) { int extractedFrameIndex =
long targetSeekTimeUs = RANDOM.nextInt(DURATION_US + 1); TestUtil.seekToTimeUs(
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput); extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
assertThat(extractedFrameIndex).isNotEqualTo(-1); assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
assertFirstFrameAfterSeekContainTargetSeekTime( assertFirstFrameAfterSeekContainsTargetSeekTime(
trackOutput, targetSeekTimeUs, extractedFrameIndex); fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
}
} }
// Internal methods @Test
public void seeking_binarySearch_handlesSeekToEoF() throws IOException, InterruptedException {
String fileName = TEST_FILE_BINARY_SEARCH;
Uri fileUri = TestUtil.buildAssetUri(fileName);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
long targetSeekTimeUs = seekMap.getDurationUs();
int extractedFrameIndex =
TestUtil.seekToTimeUs(
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
private long readInputLength() throws IOException { assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
DataSpec dataSpec = new DataSpec(FILE_URI, /* position= */ 0, C.LENGTH_UNSET); assertFirstFrameAfterSeekContainsTargetSeekTime(
long totalInputLength = dataSource.open(dataSpec); fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
Util.closeQuietly(dataSource);
return totalInputLength;
} }
/** @Test
* Seeks to the given seek time and keeps reading from input until we can extract at least one public void seeking_binarySearch_handlesSeekingBackward()
* frame from the seek position, or until end-of-input is reached.
*
* @return The index of the first extracted frame written to the given {@code trackOutput} after
* the seek is completed, or -1 if the seek is completed without any extracted frame.
*/
private int seekToTimeUs(
FlacExtractor flacExtractor, SeekMap seekMap, long seekTimeUs, FakeTrackOutput trackOutput)
throws IOException, InterruptedException { throws IOException, InterruptedException {
int numSampleBeforeSeek = trackOutput.getSampleCount(); String fileName = TEST_FILE_BINARY_SEARCH;
SeekMap.SeekPoints seekPoints = seekMap.getSeekPoints(seekTimeUs); Uri fileUri = TestUtil.buildAssetUri(fileName);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
long initialSeekLoadPosition = seekPoints.first.position; FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
flacExtractor.seek(initialSeekLoadPosition, seekTimeUs);
positionHolder.position = C.POSITION_UNSET;
ExtractorInput extractorInput = getExtractorInputFromPosition(initialSeekLoadPosition);
int extractorReadResult = Extractor.RESULT_CONTINUE;
while (true) {
try {
// Keep reading until we can read at least one frame after seek
while (extractorReadResult == Extractor.RESULT_CONTINUE
&& trackOutput.getSampleCount() == numSampleBeforeSeek) {
extractorReadResult = flacExtractor.read(extractorInput, positionHolder);
}
} finally {
Util.closeQuietly(dataSource);
}
if (extractorReadResult == Extractor.RESULT_SEEK) { long firstSeekTimeUs = 1_234_000;
extractorInput = getExtractorInputFromPosition(positionHolder.position); TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
extractorReadResult = Extractor.RESULT_CONTINUE; long targetSeekTimeUs = 987_00;
} else if (extractorReadResult == Extractor.RESULT_END_OF_INPUT) { int extractedFrameIndex =
return -1; TestUtil.seekToTimeUs(
} else if (trackOutput.getSampleCount() > numSampleBeforeSeek) { extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
// First index after seek = num sample before seek.
return numSampleBeforeSeek; assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
} assertFirstFrameAfterSeekContainsTargetSeekTime(
} fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
} }
@Nullable @Test
private SeekMap extractSeekMap(FlacExtractor extractor, FakeExtractorOutput output) public void seeking_binarySearch_handlesSeekingForward()
throws IOException, InterruptedException { throws IOException, InterruptedException {
try { String fileName = TEST_FILE_BINARY_SEARCH;
ExtractorInput input = getExtractorInputFromPosition(0); Uri fileUri = TestUtil.buildAssetUri(fileName);
extractor.init(output); SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
while (output.seekMap == null) { FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
extractor.read(input, positionHolder);
} long firstSeekTimeUs = 987_000;
} finally { TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
Util.closeQuietly(dataSource); long targetSeekTimeUs = 1_234_000;
int extractedFrameIndex =
TestUtil.seekToTimeUs(
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
assertFirstFrameAfterSeekContainsTargetSeekTime(
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
} }
return output.seekMap;
@Test
public void flacExtractorReads_unseekable_returnUnseekableSeekMap()
throws IOException, InterruptedException {
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE_UNSEEKABLE);
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
assertThat(seekMap).isNotNull();
assertThat(seekMap.getDurationUs()).isEqualTo(C.TIME_UNSET);
assertThat(seekMap.isSeekable()).isFalse();
} }
private void assertFirstFrameAfterSeekContainTargetSeekTime( private static void assertFirstFrameAfterSeekContainsTargetSeekTime(
FakeTrackOutput trackOutput, long seekTimeUs, int firstFrameIndexAfterSeek) { String fileName,
int expectedSampleIndex = findTargetFrameInExpectedOutput(seekTimeUs); FakeTrackOutput trackOutput,
// Assert that after seeking, the first sample frame written to output contains the sample long targetSeekTimeUs,
// at seek time. int firstFrameIndexAfterSeek)
throws IOException, InterruptedException {
FakeTrackOutput expectedTrackOutput = getExpectedTrackOutput(fileName);
int expectedFrameIndex = getFrameIndex(expectedTrackOutput, targetSeekTimeUs);
trackOutput.assertSample( trackOutput.assertSample(
firstFrameIndexAfterSeek, firstFrameIndexAfterSeek,
expectedTrackOutput.getSampleData(expectedSampleIndex), expectedTrackOutput.getSampleData(expectedFrameIndex),
expectedTrackOutput.getSampleTimeUs(expectedSampleIndex), expectedTrackOutput.getSampleTimeUs(expectedFrameIndex),
expectedTrackOutput.getSampleFlags(expectedSampleIndex), expectedTrackOutput.getSampleFlags(expectedFrameIndex),
expectedTrackOutput.getSampleCryptoData(expectedSampleIndex)); expectedTrackOutput.getSampleCryptoData(expectedFrameIndex));
} }
private int findTargetFrameInExpectedOutput(long seekTimeUs) { private static void assertFirstFrameAfterSeekPrecedesTargetSeekTime(
List<Long> sampleTimes = expectedTrackOutput.getSampleTimesUs(); String fileName,
for (int i = 0; i < sampleTimes.size() - 1; i++) { FakeTrackOutput trackOutput,
long currentSampleTime = sampleTimes.get(i); long targetSeekTimeUs,
long nextSampleTime = sampleTimes.get(i + 1); int firstFrameIndexAfterSeek)
if (currentSampleTime <= seekTimeUs && nextSampleTime > seekTimeUs) { throws IOException, InterruptedException {
return i; FakeTrackOutput expectedTrackOutput = getExpectedTrackOutput(fileName);
} int maxFrameIndex = getFrameIndex(expectedTrackOutput, targetSeekTimeUs);
long firstFrameAfterSeekTimeUs = trackOutput.getSampleTimeUs(firstFrameIndexAfterSeek);
assertThat(firstFrameAfterSeekTimeUs).isAtMost(targetSeekTimeUs);
boolean frameFound = false;
for (int i = maxFrameIndex; i >= 0; i--) {
if (firstFrameAfterSeekTimeUs == expectedTrackOutput.getSampleTimeUs(i)) {
trackOutput.assertSample(
firstFrameIndexAfterSeek,
expectedTrackOutput.getSampleData(i),
expectedTrackOutput.getSampleTimeUs(i),
expectedTrackOutput.getSampleFlags(i),
expectedTrackOutput.getSampleCryptoData(i));
frameFound = true;
break;
} }
return sampleTimes.size() - 1;
} }
private ExtractorInput getExtractorInputFromPosition(long position) throws IOException { assertThat(frameFound).isTrue();
DataSpec dataSpec = new DataSpec(FILE_URI, position, totalInputLength);
dataSource.open(dataSpec);
return new DefaultExtractorInput(dataSource, position, totalInputLength);
} }
private void extractAllSamplesFromFileToExpectedOutput(Context context, String fileName) private static FakeTrackOutput getExpectedTrackOutput(String fileName)
throws IOException, InterruptedException { throws IOException, InterruptedException {
byte[] data = TestUtil.getByteArray(context, fileName); return TestUtil.extractAllSamplesFromFile(
new FlacExtractor(), ApplicationProvider.getApplicationContext(), fileName)
FlacExtractor extractor = new FlacExtractor(); .trackOutputs
extractor.init(expectedOutput); .get(0);
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).build(); }
while (extractor.read(input, new PositionHolder()) != Extractor.RESULT_END_OF_INPUT) {} private static int getFrameIndex(FakeTrackOutput expectedTrackOutput, long targetSeekTimeUs) {
List<Long> frameTimes = expectedTrackOutput.getSampleTimesUs();
return Util.binarySearchFloor(
frameTimes, targetSeekTimeUs, /* inclusive= */ true, /* stayInBounds= */ false);
} }
} }
...@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; ...@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
...@@ -43,18 +42,11 @@ public class FlacExtractorSeekTest { ...@@ -43,18 +42,11 @@ public class FlacExtractorSeekTest {
private static final String TEST_FILE_UNSEEKABLE = "flac/bear_no_seek_table_no_num_samples.flac"; private static final String TEST_FILE_UNSEEKABLE = "flac/bear_no_seek_table_no_num_samples.flac";
private static final int DURATION_US = 2_741_000; private static final int DURATION_US = 2_741_000;
private FlacExtractor extractor; private FlacExtractor extractor = new FlacExtractor();
private FakeExtractorOutput extractorOutput; private FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
private DefaultDataSource dataSource; private DefaultDataSource dataSource =
@Before
public void setUp() throws Exception {
extractor = new FlacExtractor();
extractorOutput = new FakeExtractorOutput();
dataSource =
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent") new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
.createDataSource(); .createDataSource();
}
@Test @Test
public void flacExtractorReads_seekTable_returnSeekableSeekMap() public void flacExtractorReads_seekTable_returnSeekableSeekMap()
......
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