From f5025bfce4edaefc90c1981da3b64c9782e13315 Mon Sep 17 00:00:00 2001 From: kimvde Date: Thu, 13 Feb 2020 13:30:26 +0000 Subject: [PATCH] Share seek tests between FLAC extractors PiperOrigin-RevId: 294893303 --- .../ext/flac/FlacBinarySearchSeekerTest.java | 92 ----- .../ext/flac/FlacExtractorSeekTest.java | 382 +++++++++--------- .../extractor/flac/FlacExtractorSeekTest.java | 18 +- 3 files changed, 195 insertions(+), 297 deletions(-) delete mode 100644 extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeekerTest.java diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeekerTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeekerTest.java deleted file mode 100644 index 7a9bdfed4e..0000000000 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeekerTest.java +++ /dev/null @@ -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(); - } -} diff --git a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorSeekTest.java b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorSeekTest.java index 49254c0f17..5ae2b6a196 100644 --- a/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorSeekTest.java +++ b/extensions/flac/src/androidTest/java/com/google/android/exoplayer2/ext/flac/FlacExtractorSeekTest.java @@ -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 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 frameTimes = expectedTrackOutput.getSampleTimesUs(); + return Util.binarySearchFloor( + frameTimes, targetSeekTimeUs, /* inclusive= */ true, /* stayInBounds= */ false); } } diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorSeekTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorSeekTest.java index 5b53c2f578..0df490b208 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorSeekTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/flac/FlacExtractorSeekTest.java @@ -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()