mirror of
https://github.com/androidx/media.git
synced 2025-05-08 08:00:49 +08:00
Share seek tests between FLAC extractors
PiperOrigin-RevId: 294893303
This commit is contained in:
parent
ebce903aaf
commit
f5025bfce4
@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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 @@
|
||||
package com.google.android.exoplayer2.ext.flac;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
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.testutil.FakeExtractorInput;
|
||||
import com.google.android.exoplayer2.testutil.FakeExtractorOutput;
|
||||
import com.google.android.exoplayer2.testutil.FakeTrackOutput;
|
||||
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.DefaultDataSourceFactory;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
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)
|
||||
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 Uri FILE_URI = Uri.parse("file:///android_asset/" + NO_SEEKTABLE_FLAC);
|
||||
private static final Random RANDOM = new Random(1234L);
|
||||
|
||||
private FakeExtractorOutput expectedOutput;
|
||||
private FakeTrackOutput expectedTrackOutput;
|
||||
|
||||
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")
|
||||
.createDataSource();
|
||||
totalInputLength = readInputLength();
|
||||
positionHolder = new PositionHolder();
|
||||
}
|
||||
private FlacExtractor extractor = new FlacExtractor();
|
||||
private FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
||||
private DefaultDataSource dataSource =
|
||||
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
|
||||
.createDataSource();
|
||||
|
||||
@Test
|
||||
public void testFlacExtractorReads_nonSeekTableFile_returnSeekableSeekMap()
|
||||
public void flacExtractorReads_seekTable_returnSeekableSeekMap()
|
||||
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.getDurationUs()).isEqualTo(DURATION_US);
|
||||
@ -90,205 +61,232 @@ public final class FlacExtractorSeekTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePendingSeek_handlesSeekingToPositionInFile_extractsCorrectFrame()
|
||||
throws IOException, InterruptedException {
|
||||
FlacExtractor extractor = new FlacExtractor();
|
||||
|
||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
||||
public void seeking_seekTable_handlesSeekToZero() throws IOException, InterruptedException {
|
||||
String fileName = TEST_FILE_SEEK_TABLE;
|
||||
Uri fileUri = TestUtil.buildAssetUri(fileName);
|
||||
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
|
||||
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||
|
||||
long targetSeekTimeUs = 987_000;
|
||||
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput);
|
||||
long targetSeekTimeUs = 0;
|
||||
int extractedFrameIndex =
|
||||
TestUtil.seekToTimeUs(
|
||||
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(-1);
|
||||
assertFirstFrameAfterSeekContainTargetSeekTime(
|
||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekPrecedesTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePendingSeek_handlesSeekToEoF_extractsLastFrame()
|
||||
throws IOException, InterruptedException {
|
||||
FlacExtractor extractor = new FlacExtractor();
|
||||
|
||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
||||
public void seeking_seekTable_handlesSeekToEoF() throws IOException, InterruptedException {
|
||||
String fileName = TEST_FILE_SEEK_TABLE;
|
||||
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);
|
||||
|
||||
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(-1);
|
||||
assertFirstFrameAfterSeekContainTargetSeekTime(
|
||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekPrecedesTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePendingSeek_handlesSeekingBackward_extractsCorrectFrame()
|
||||
throws IOException, InterruptedException {
|
||||
FlacExtractor extractor = new FlacExtractor();
|
||||
public void seeking_seekTable_handlesSeekingBackward() throws IOException, InterruptedException {
|
||||
String fileName = TEST_FILE_SEEK_TABLE;
|
||||
Uri fileUri = TestUtil.buildAssetUri(fileName);
|
||||
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
|
||||
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||
|
||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
||||
long firstSeekTimeUs = 1_234_000;
|
||||
TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
long targetSeekTimeUs = 987_000;
|
||||
int extractedFrameIndex =
|
||||
TestUtil.seekToTimeUs(
|
||||
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekPrecedesTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seeking_seekTable_handlesSeekingForward() throws IOException, InterruptedException {
|
||||
String fileName = TEST_FILE_SEEK_TABLE;
|
||||
Uri fileUri = TestUtil.buildAssetUri(fileName);
|
||||
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
|
||||
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||
|
||||
long firstSeekTimeUs = 987_000;
|
||||
seekToTimeUs(extractor, seekMap, firstSeekTimeUs, trackOutput);
|
||||
TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
long targetSeekTimeUs = 1_234_000;
|
||||
int extractedFrameIndex =
|
||||
TestUtil.seekToTimeUs(
|
||||
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekPrecedesTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void flacExtractorReads_binarySearch_returnSeekableSeekMap()
|
||||
throws IOException, InterruptedException {
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
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);
|
||||
|
||||
long targetSeekTimeUs = 0;
|
||||
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput);
|
||||
int extractedFrameIndex =
|
||||
TestUtil.seekToTimeUs(
|
||||
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(-1);
|
||||
assertFirstFrameAfterSeekContainTargetSeekTime(
|
||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePendingSeek_handlesSeekingForward_extractsCorrectFrame()
|
||||
throws IOException, InterruptedException {
|
||||
FlacExtractor extractor = new FlacExtractor();
|
||||
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);
|
||||
|
||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
||||
long targetSeekTimeUs = seekMap.getDurationUs();
|
||||
int extractedFrameIndex =
|
||||
TestUtil.seekToTimeUs(
|
||||
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seeking_binarySearch_handlesSeekingBackward()
|
||||
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 firstSeekTimeUs = 1_234_000;
|
||||
TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
long targetSeekTimeUs = 987_00;
|
||||
int extractedFrameIndex =
|
||||
TestUtil.seekToTimeUs(
|
||||
extractor, seekMap, targetSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void seeking_binarySearch_handlesSeekingForward()
|
||||
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 firstSeekTimeUs = 987_000;
|
||||
seekToTimeUs(extractor, seekMap, firstSeekTimeUs, trackOutput);
|
||||
|
||||
TestUtil.seekToTimeUs(extractor, seekMap, firstSeekTimeUs, dataSource, trackOutput, fileUri);
|
||||
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);
|
||||
assertFirstFrameAfterSeekContainTargetSeekTime(
|
||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(C.INDEX_UNSET);
|
||||
assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePendingSeek_handlesRandomSeeks_extractsCorrectFrame()
|
||||
public void flacExtractorReads_unseekable_returnUnseekableSeekMap()
|
||||
throws IOException, InterruptedException {
|
||||
FlacExtractor extractor = new FlacExtractor();
|
||||
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE_UNSEEKABLE);
|
||||
|
||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
||||
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
|
||||
|
||||
long numSeek = 100;
|
||||
for (long i = 0; i < numSeek; i++) {
|
||||
long targetSeekTimeUs = RANDOM.nextInt(DURATION_US + 1);
|
||||
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput);
|
||||
|
||||
assertThat(extractedFrameIndex).isNotEqualTo(-1);
|
||||
assertFirstFrameAfterSeekContainTargetSeekTime(
|
||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||
}
|
||||
assertThat(seekMap).isNotNull();
|
||||
assertThat(seekMap.getDurationUs()).isEqualTo(C.TIME_UNSET);
|
||||
assertThat(seekMap.isSeekable()).isFalse();
|
||||
}
|
||||
|
||||
// Internal methods
|
||||
|
||||
private long readInputLength() throws IOException {
|
||||
DataSpec dataSpec = new DataSpec(FILE_URI, /* position= */ 0, C.LENGTH_UNSET);
|
||||
long totalInputLength = dataSource.open(dataSpec);
|
||||
Util.closeQuietly(dataSource);
|
||||
return totalInputLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seeks to the given seek time and keeps reading from input until we can extract at least one
|
||||
* 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)
|
||||
private static void assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||
String fileName,
|
||||
FakeTrackOutput trackOutput,
|
||||
long targetSeekTimeUs,
|
||||
int firstFrameIndexAfterSeek)
|
||||
throws IOException, InterruptedException {
|
||||
int numSampleBeforeSeek = trackOutput.getSampleCount();
|
||||
SeekMap.SeekPoints seekPoints = seekMap.getSeekPoints(seekTimeUs);
|
||||
FakeTrackOutput expectedTrackOutput = getExpectedTrackOutput(fileName);
|
||||
int expectedFrameIndex = getFrameIndex(expectedTrackOutput, targetSeekTimeUs);
|
||||
|
||||
long initialSeekLoadPosition = seekPoints.first.position;
|
||||
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) {
|
||||
extractorInput = getExtractorInputFromPosition(positionHolder.position);
|
||||
extractorReadResult = Extractor.RESULT_CONTINUE;
|
||||
} else if (extractorReadResult == Extractor.RESULT_END_OF_INPUT) {
|
||||
return -1;
|
||||
} else if (trackOutput.getSampleCount() > numSampleBeforeSeek) {
|
||||
// First index after seek = num sample before seek.
|
||||
return numSampleBeforeSeek;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SeekMap extractSeekMap(FlacExtractor extractor, FakeExtractorOutput output)
|
||||
throws IOException, InterruptedException {
|
||||
try {
|
||||
ExtractorInput input = getExtractorInputFromPosition(0);
|
||||
extractor.init(output);
|
||||
while (output.seekMap == null) {
|
||||
extractor.read(input, positionHolder);
|
||||
}
|
||||
} finally {
|
||||
Util.closeQuietly(dataSource);
|
||||
}
|
||||
return output.seekMap;
|
||||
}
|
||||
|
||||
private void assertFirstFrameAfterSeekContainTargetSeekTime(
|
||||
FakeTrackOutput trackOutput, long seekTimeUs, int firstFrameIndexAfterSeek) {
|
||||
int expectedSampleIndex = findTargetFrameInExpectedOutput(seekTimeUs);
|
||||
// Assert that after seeking, the first sample frame written to output contains the sample
|
||||
// at seek time.
|
||||
trackOutput.assertSample(
|
||||
firstFrameIndexAfterSeek,
|
||||
expectedTrackOutput.getSampleData(expectedSampleIndex),
|
||||
expectedTrackOutput.getSampleTimeUs(expectedSampleIndex),
|
||||
expectedTrackOutput.getSampleFlags(expectedSampleIndex),
|
||||
expectedTrackOutput.getSampleCryptoData(expectedSampleIndex));
|
||||
expectedTrackOutput.getSampleData(expectedFrameIndex),
|
||||
expectedTrackOutput.getSampleTimeUs(expectedFrameIndex),
|
||||
expectedTrackOutput.getSampleFlags(expectedFrameIndex),
|
||||
expectedTrackOutput.getSampleCryptoData(expectedFrameIndex));
|
||||
}
|
||||
|
||||
private int findTargetFrameInExpectedOutput(long seekTimeUs) {
|
||||
List<Long> sampleTimes = expectedTrackOutput.getSampleTimesUs();
|
||||
for (int i = 0; i < sampleTimes.size() - 1; i++) {
|
||||
long currentSampleTime = sampleTimes.get(i);
|
||||
long nextSampleTime = sampleTimes.get(i + 1);
|
||||
if (currentSampleTime <= seekTimeUs && nextSampleTime > seekTimeUs) {
|
||||
return i;
|
||||
private static void assertFirstFrameAfterSeekPrecedesTargetSeekTime(
|
||||
String fileName,
|
||||
FakeTrackOutput trackOutput,
|
||||
long targetSeekTimeUs,
|
||||
int firstFrameIndexAfterSeek)
|
||||
throws IOException, InterruptedException {
|
||||
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;
|
||||
|
||||
assertThat(frameFound).isTrue();
|
||||
}
|
||||
|
||||
private ExtractorInput getExtractorInputFromPosition(long position) throws IOException {
|
||||
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 {
|
||||
byte[] data = TestUtil.getByteArray(context, fileName);
|
||||
return TestUtil.extractAllSamplesFromFile(
|
||||
new FlacExtractor(), ApplicationProvider.getApplicationContext(), fileName)
|
||||
.trackOutputs
|
||||
.get(0);
|
||||
}
|
||||
|
||||
FlacExtractor extractor = new FlacExtractor();
|
||||
extractor.init(expectedOutput);
|
||||
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;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@ -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 int DURATION_US = 2_741_000;
|
||||
|
||||
private FlacExtractor extractor;
|
||||
private FakeExtractorOutput extractorOutput;
|
||||
private DefaultDataSource dataSource;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
extractor = new FlacExtractor();
|
||||
extractorOutput = new FakeExtractorOutput();
|
||||
dataSource =
|
||||
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
|
||||
.createDataSource();
|
||||
}
|
||||
private FlacExtractor extractor = new FlacExtractor();
|
||||
private FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
||||
private DefaultDataSource dataSource =
|
||||
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
|
||||
.createDataSource();
|
||||
|
||||
@Test
|
||||
public void flacExtractorReads_seekTable_returnSeekableSeekMap()
|
||||
|
Loading…
x
Reference in New Issue
Block a user