mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Allow no output frame holder BinarySearchSeeker
PiperOrigin-RevId: 283544187
This commit is contained in:
parent
f28a17f9eb
commit
23b54a95d2
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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());
|
||||
|
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user