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 index 025fdfd209..a18202f4e2 100644 --- 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 @@ -20,10 +20,12 @@ 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; @@ -47,19 +49,20 @@ public final class FlacBinarySearchSeekerTest { 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); - + decoderJni, + outputFrameHolder); SeekMap seekMap = seeker.getSeekMap(); + assertThat(seekMap).isNotNull(); assertThat(seekMap.getDurationUs()).isEqualTo(DURATION_US); assertThat(seekMap.isSeekable()).isTrue(); @@ -70,18 +73,20 @@ public final class FlacBinarySearchSeekerTest { 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); - + decoderJni, + outputFrameHolder); seeker.setSeekTargetUs(/* timeUs= */ 1000); + assertThat(seeker.isSeeking()).isTrue(); } } diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeeker.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeeker.java index 08f179152e..74c3e73791 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeeker.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacBinarySearchSeeker.java @@ -31,16 +31,33 @@ import java.nio.ByteBuffer; */ /* package */ final class FlacBinarySearchSeeker extends BinarySearchSeeker { + /** + * Holds a frame extracted from a stream, together with the time stamp of the frame in + * microseconds. + */ + public static final class OutputFrameHolder { + + public final ByteBuffer byteBuffer; + public long timeUs; + + /** Constructs an instance, wrapping the given byte buffer. */ + public OutputFrameHolder(ByteBuffer outputByteBuffer) { + this.timeUs = 0; + this.byteBuffer = outputByteBuffer; + } + } + private final FlacDecoderJni decoderJni; public FlacBinarySearchSeeker( FlacStreamMetadata streamMetadata, long firstFramePosition, long inputLength, - FlacDecoderJni decoderJni) { + FlacDecoderJni decoderJni, + OutputFrameHolder outputFrameHolder) { super( new FlacSeekTimestampConverter(streamMetadata), - new FlacTimestampSeeker(decoderJni), + new FlacTimestampSeeker(decoderJni, outputFrameHolder), streamMetadata.getDurationUs(), /* floorTimePosition= */ 0, /* ceilingTimePosition= */ streamMetadata.totalSamples, @@ -63,14 +80,15 @@ import java.nio.ByteBuffer; private static final class FlacTimestampSeeker implements TimestampSeeker { private final FlacDecoderJni decoderJni; + private final OutputFrameHolder outputFrameHolder; - private FlacTimestampSeeker(FlacDecoderJni decoderJni) { + private FlacTimestampSeeker(FlacDecoderJni decoderJni, OutputFrameHolder outputFrameHolder) { this.decoderJni = decoderJni; + this.outputFrameHolder = outputFrameHolder; } @Override - public TimestampSearchResult searchForTimestamp( - ExtractorInput input, long targetSampleIndex, OutputFrameHolder outputFrameHolder) + public TimestampSearchResult searchForTimestamp(ExtractorInput input, long targetSampleIndex) throws IOException, InterruptedException { ByteBuffer outputBuffer = outputFrameHolder.byteBuffer; long searchPosition = input.getPosition(); diff --git a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java index 6ea099064e..7c69a93fc9 100644 --- a/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java +++ b/extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/FlacExtractor.java @@ -21,7 +21,7 @@ import androidx.annotation.IntDef; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.extractor.BinarySearchSeeker.OutputFrameHolder; +import com.google.android.exoplayer2.ext.flac.FlacBinarySearchSeeker.OutputFrameHolder; import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorOutput; @@ -190,11 +190,12 @@ public final class FlacExtractor implements Extractor { return; } + FlacDecoderJni flacDecoderJni = decoderJni; FlacStreamMetadata streamMetadata; try { - streamMetadata = decoderJni.decodeStreamMetadata(); + streamMetadata = flacDecoderJni.decodeStreamMetadata(); } catch (IOException e) { - decoderJni.reset(/* newPosition= */ 0); + flacDecoderJni.reset(/* newPosition= */ 0); input.setRetryPosition(/* position= */ 0, e); throw e; } @@ -202,12 +203,17 @@ public final class FlacExtractor implements Extractor { streamMetadataDecoded = true; if (this.streamMetadata == null) { this.streamMetadata = streamMetadata; - binarySearchSeeker = - outputSeekMap(decoderJni, streamMetadata, input.getLength(), extractorOutput); - Metadata metadata = streamMetadata.getMetadataCopyWithAppendedEntriesFrom(id3Metadata); - outputFormat(streamMetadata, metadata, trackOutput); outputBuffer.reset(streamMetadata.getMaxDecodedFrameSize()); outputFrameHolder = new OutputFrameHolder(ByteBuffer.wrap(outputBuffer.data)); + binarySearchSeeker = + outputSeekMap( + flacDecoderJni, + streamMetadata, + input.getLength(), + extractorOutput, + outputFrameHolder); + Metadata metadata = streamMetadata.getMetadataCopyWithAppendedEntriesFrom(id3Metadata); + outputFormat(streamMetadata, metadata, trackOutput); } } @@ -219,7 +225,7 @@ public final class FlacExtractor implements Extractor { OutputFrameHolder outputFrameHolder, TrackOutput trackOutput) throws InterruptedException, IOException { - int seekResult = binarySearchSeeker.handlePendingSeek(input, seekPosition, outputFrameHolder); + int seekResult = binarySearchSeeker.handlePendingSeek(input, seekPosition); ByteBuffer outputByteBuffer = outputFrameHolder.byteBuffer; if (seekResult == RESULT_CONTINUE && outputByteBuffer.limit() > 0) { outputSample(outputBuffer, outputByteBuffer.limit(), outputFrameHolder.timeUs, trackOutput); @@ -236,7 +242,8 @@ public final class FlacExtractor implements Extractor { FlacDecoderJni decoderJni, FlacStreamMetadata streamMetadata, long streamLength, - ExtractorOutput output) { + ExtractorOutput output, + OutputFrameHolder outputFrameHolder) { boolean haveSeekTable = decoderJni.getSeekPoints(/* timeUs= */ 0) != null; FlacBinarySearchSeeker binarySearchSeeker = null; SeekMap seekMap; @@ -245,7 +252,8 @@ public final class FlacExtractor implements Extractor { } else if (streamLength != C.LENGTH_UNSET) { long firstFramePosition = decoderJni.getDecodePosition(); binarySearchSeeker = - new FlacBinarySearchSeeker(streamMetadata, firstFramePosition, streamLength, decoderJni); + new FlacBinarySearchSeeker( + streamMetadata, firstFramePosition, streamLength, decoderJni, outputFrameHolder); seekMap = binarySearchSeeker.getSeekMap(); } else { seekMap = new SeekMap.Unseekable(streamMetadata.getDurationUs()); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/BinarySearchSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/BinarySearchSeeker.java index 06d3ed603e..0d823fa31d 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/BinarySearchSeeker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/BinarySearchSeeker.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.nio.ByteBuffer; /** * A seeker that supports seeking within a stream by searching for the target frame using binary @@ -48,38 +47,17 @@ public abstract class BinarySearchSeeker { * * @param input The {@link ExtractorInput} from which data should be peeked. * @param targetTimestamp The target timestamp. - * @param outputFrameHolder If {@link TimestampSearchResult#TYPE_TARGET_TIMESTAMP_FOUND} is - * returned, this holder may be updated to hold the extracted frame that contains the target - * frame/sample associated with the target timestamp. * @return A {@link TimestampSearchResult} that describes the result of the search. * @throws IOException If an error occurred reading from the input. * @throws InterruptedException If the thread was interrupted. */ - TimestampSearchResult searchForTimestamp( - ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder) + TimestampSearchResult searchForTimestamp(ExtractorInput input, long targetTimestamp) throws IOException, InterruptedException; /** Called when a seek operation finishes. */ default void onSeekFinished() {} } - /** - * Holds a frame extracted from a stream, together with the time stamp of the frame in - * microseconds. - */ - public static final class OutputFrameHolder { - - public final ByteBuffer byteBuffer; - - public long timeUs; - - /** Constructs an instance, wrapping the given byte buffer. */ - public OutputFrameHolder(ByteBuffer outputByteBuffer) { - this.timeUs = 0; - this.byteBuffer = outputByteBuffer; - } - } - /** * A {@link SeekTimestampConverter} implementation that returns the seek time itself as the * timestamp for a seek time position. @@ -189,15 +167,11 @@ public abstract class BinarySearchSeeker { * @param input The {@link ExtractorInput} from which data should be read. * @param seekPositionHolder If {@link Extractor#RESULT_SEEK} is returned, this holder is updated * to hold the position of the required seek. - * @param outputFrameHolder If {@link Extractor#RESULT_CONTINUE} is returned, this holder may be - * updated to hold the extracted frame that contains the target sample. The caller needs to - * check the byte buffer limit to see if an extracted frame is available. * @return One of the {@code RESULT_} values defined in {@link Extractor}. * @throws IOException If an error occurred reading from the input. * @throws InterruptedException If the thread was interrupted. */ - public int handlePendingSeek( - ExtractorInput input, PositionHolder seekPositionHolder, OutputFrameHolder outputFrameHolder) + public int handlePendingSeek(ExtractorInput input, PositionHolder seekPositionHolder) throws InterruptedException, IOException { TimestampSeeker timestampSeeker = Assertions.checkNotNull(this.timestampSeeker); while (true) { @@ -217,8 +191,7 @@ public abstract class BinarySearchSeeker { input.resetPeekPosition(); TimestampSearchResult timestampSearchResult = - timestampSeeker.searchForTimestamp( - input, seekOperationParams.getTargetTimePosition(), outputFrameHolder); + timestampSeeker.searchForTimestamp(input, seekOperationParams.getTargetTimePosition()); switch (timestampSearchResult.type) { case TimestampSearchResult.TYPE_POSITION_OVERESTIMATED: @@ -419,7 +392,7 @@ public abstract class BinarySearchSeeker { /** * Represents possible search results for {@link - * TimestampSeeker#searchForTimestamp(ExtractorInput, long, OutputFrameHolder)}. + * TimestampSeeker#searchForTimestamp(ExtractorInput, long)}. */ public static final class TimestampSearchResult { @@ -495,10 +468,6 @@ public abstract class BinarySearchSeeker { /** * Returns a result to signal that the target timestamp has been found at {@code * resultBytePosition}, and the seek operation can stop. - * - *

Note that when this value is returned from {@link - * TimestampSeeker#searchForTimestamp(ExtractorInput, long, OutputFrameHolder)}, the {@link - * OutputFrameHolder} may be updated to hold the target frame as an optimization. */ public static TimestampSearchResult targetFoundResult(long resultBytePosition) { return new TimestampSearchResult( diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsBinarySearchSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsBinarySearchSeeker.java index 4efd38b7eb..c4f53ba176 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsBinarySearchSeeker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsBinarySearchSeeker.java @@ -69,8 +69,7 @@ import java.io.IOException; } @Override - public TimestampSearchResult searchForTimestamp( - ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder) + public TimestampSearchResult searchForTimestamp(ExtractorInput input, long targetTimestamp) throws IOException, InterruptedException { long inputPosition = input.getPosition(); int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java index f453a9cc43..fec108fd5f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java @@ -168,8 +168,7 @@ public final class PsExtractor implements Extractor { } maybeOutputSeekMap(inputLength); if (psBinarySearchSeeker != null && psBinarySearchSeeker.isSeeking()) { - return psBinarySearchSeeker.handlePendingSeek( - input, seekPosition, /* outputFrameHolder= */ null); + return psBinarySearchSeeker.handlePendingSeek(input, seekPosition); } input.resetPeekPosition(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsBinarySearchSeeker.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsBinarySearchSeeker.java index ea2519d2e9..a627c00ba2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsBinarySearchSeeker.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsBinarySearchSeeker.java @@ -73,8 +73,7 @@ import java.io.IOException; } @Override - public TimestampSearchResult searchForTimestamp( - ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder) + public TimestampSearchResult searchForTimestamp(ExtractorInput input, long targetTimestamp) throws IOException, InterruptedException { long inputPosition = input.getPosition(); int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index 04dd7df385..2cd7398d7c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -268,8 +268,7 @@ public final class TsExtractor implements Extractor { } if (tsBinarySearchSeeker != null && tsBinarySearchSeeker.isSeeking()) { - return tsBinarySearchSeeker.handlePendingSeek( - input, seekPosition, /* outputFrameHolder= */ null); + return tsBinarySearchSeeker.handlePendingSeek(input, seekPosition); } }