mirror of
https://github.com/androidx/media.git
synced 2025-05-13 18:50:02 +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;
|
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;
|
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
|
||||||
private PositionHolder positionHolder;
|
.createDataSource();
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
||||||
|
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
long firstSeekTimeUs = 1_234_000;
|
||||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
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);
|
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;
|
||||||
|
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;
|
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(
|
assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHandlePendingSeek_handlesSeekingForward_extractsCorrectFrame()
|
public void seeking_binarySearch_handlesSeekToEoF() throws IOException, InterruptedException {
|
||||||
throws IOException, InterruptedException {
|
String fileName = TEST_FILE_BINARY_SEARCH;
|
||||||
FlacExtractor extractor = new FlacExtractor();
|
Uri fileUri = TestUtil.buildAssetUri(fileName);
|
||||||
|
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
|
||||||
|
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
long targetSeekTimeUs = seekMap.getDurationUs();
|
||||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
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);
|
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(
|
assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
fileName, trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHandlePendingSeek_handlesRandomSeeks_extractsCorrectFrame()
|
public void flacExtractorReads_unseekable_returnUnseekableSeekMap()
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
FlacExtractor extractor = new FlacExtractor();
|
Uri fileUri = TestUtil.buildAssetUri(TEST_FILE_UNSEEKABLE);
|
||||||
|
|
||||||
FakeExtractorOutput extractorOutput = new FakeExtractorOutput();
|
SeekMap seekMap = TestUtil.extractSeekMap(extractor, extractorOutput, dataSource, fileUri);
|
||||||
SeekMap seekMap = extractSeekMap(extractor, extractorOutput);
|
|
||||||
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
|
||||||
|
|
||||||
long numSeek = 100;
|
assertThat(seekMap).isNotNull();
|
||||||
for (long i = 0; i < numSeek; i++) {
|
assertThat(seekMap.getDurationUs()).isEqualTo(C.TIME_UNSET);
|
||||||
long targetSeekTimeUs = RANDOM.nextInt(DURATION_US + 1);
|
assertThat(seekMap.isSeekable()).isFalse();
|
||||||
int extractedFrameIndex = seekToTimeUs(extractor, seekMap, targetSeekTimeUs, trackOutput);
|
|
||||||
|
|
||||||
assertThat(extractedFrameIndex).isNotEqualTo(-1);
|
|
||||||
assertFirstFrameAfterSeekContainTargetSeekTime(
|
|
||||||
trackOutput, targetSeekTimeUs, extractedFrameIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal methods
|
private static void assertFirstFrameAfterSeekContainsTargetSeekTime(
|
||||||
|
String fileName,
|
||||||
private long readInputLength() throws IOException {
|
FakeTrackOutput trackOutput,
|
||||||
DataSpec dataSpec = new DataSpec(FILE_URI, /* position= */ 0, C.LENGTH_UNSET);
|
long targetSeekTimeUs,
|
||||||
long totalInputLength = dataSource.open(dataSpec);
|
int firstFrameIndexAfterSeek)
|
||||||
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)
|
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
int numSampleBeforeSeek = trackOutput.getSampleCount();
|
FakeTrackOutput expectedTrackOutput = getExpectedTrackOutput(fileName);
|
||||||
SeekMap.SeekPoints seekPoints = seekMap.getSeekPoints(seekTimeUs);
|
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(
|
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;
|
|
||||||
|
assertThat(frameFound).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExtractorInput getExtractorInputFromPosition(long position) throws IOException {
|
private static FakeTrackOutput getExpectedTrackOutput(String fileName)
|
||||||
DataSpec dataSpec = new DataSpec(FILE_URI, position, totalInputLength);
|
|
||||||
dataSource.open(dataSpec);
|
|
||||||
return new DefaultExtractorInput(dataSource, position, totalInputLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void extractAllSamplesFromFileToExpectedOutput(Context context, String fileName)
|
|
||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
byte[] data = TestUtil.getByteArray(context, fileName);
|
return TestUtil.extractAllSamplesFromFile(
|
||||||
|
new FlacExtractor(), ApplicationProvider.getApplicationContext(), fileName)
|
||||||
|
.trackOutputs
|
||||||
|
.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
FlacExtractor extractor = new FlacExtractor();
|
private static int getFrameIndex(FakeTrackOutput expectedTrackOutput, long targetSeekTimeUs) {
|
||||||
extractor.init(expectedOutput);
|
List<Long> frameTimes = expectedTrackOutput.getSampleTimesUs();
|
||||||
FakeExtractorInput input = new FakeExtractorInput.Builder().setData(data).build();
|
return Util.binarySearchFloor(
|
||||||
|
frameTimes, targetSeekTimeUs, /* inclusive= */ true, /* stayInBounds= */ false);
|
||||||
while (extractor.read(input, new PositionHolder()) != Extractor.RESULT_END_OF_INPUT) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
|||||||
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 =
|
||||||
|
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
|
||||||
@Before
|
.createDataSource();
|
||||||
public void setUp() throws Exception {
|
|
||||||
extractor = new FlacExtractor();
|
|
||||||
extractorOutput = new FakeExtractorOutput();
|
|
||||||
dataSource =
|
|
||||||
new DefaultDataSourceFactory(ApplicationProvider.getApplicationContext(), "UserAgent")
|
|
||||||
.createDataSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void flacExtractorReads_seekTable_returnSeekableSeekMap()
|
public void flacExtractorReads_seekTable_returnSeekableSeekMap()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user