Allow no output frame holder BinarySearchSeeker

PiperOrigin-RevId: 283544187
This commit is contained in:
kimvde 2019-12-03 15:51:44 +00:00 committed by bachinger
parent f28a17f9eb
commit 23b54a95d2
8 changed files with 60 additions and 64 deletions

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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());

View File

@ -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.
*
* <p>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(

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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);
}
}