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 022b5b4c754..e9842611455 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 @@ -42,31 +42,16 @@ public abstract class BinarySearchSeeker { protected interface TimestampSeeker { /** - * Searches for a given timestamp from the input. + * Searches a limited window of the provided input for a target timestamp. The size of the + * window is implementation specific, but should be small enough such that it's reasonable for + * multiple such reads to occur during a seek operation. * - *

Given a target timestamp and an input stream, this seeker will try to read up to a range - * of {@code searchRangeBytes} bytes from that input, look for all available timestamps from all - * frames in that range, compare those with the target timestamp, and return one of the {@link - * TimestampSearchResult}. - * - * @param input The {@link ExtractorInput} from which data should be read. - * @param targetTimestamp The target timestamp that we are looking for. - * @param outputFrameHolder If {@link TimestampSearchResult#RESULT_TARGET_TIMESTAMP_FOUND} is + * @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 includes a {@link TimestampSearchResult#result} - * value, and other necessary info: - *

- * + * @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. */ @@ -232,22 +217,22 @@ public int handlePendingSeek( timestampSeeker.searchForTimestamp( input, seekOperationParams.getTargetTimePosition(), outputFrameHolder); - switch (timestampSearchResult.result) { - case TimestampSearchResult.RESULT_POSITION_OVERESTIMATED: + switch (timestampSearchResult.type) { + case TimestampSearchResult.TYPE_POSITION_OVERESTIMATED: seekOperationParams.updateSeekCeiling( timestampSearchResult.timestampToUpdate, timestampSearchResult.bytePositionToUpdate); break; - case TimestampSearchResult.RESULT_POSITION_UNDERESTIMATED: + case TimestampSearchResult.TYPE_POSITION_UNDERESTIMATED: seekOperationParams.updateSeekFloor( timestampSearchResult.timestampToUpdate, timestampSearchResult.bytePositionToUpdate); break; - case TimestampSearchResult.RESULT_TARGET_TIMESTAMP_FOUND: + case TimestampSearchResult.TYPE_TARGET_TIMESTAMP_FOUND: markSeekOperationFinished( /* foundTargetFrame= */ true, timestampSearchResult.bytePositionToUpdate); skipInputUntilPosition(input, timestampSearchResult.bytePositionToUpdate); return seekToPosition( input, timestampSearchResult.bytePositionToUpdate, seekPositionHolder); - case TimestampSearchResult.RESULT_NO_TIMESTAMP: + case TimestampSearchResult.TYPE_NO_TIMESTAMP: // We can't find any timestamp in the search range from the search position. // Give up, and just continue reading from the last search position in this case. markSeekOperationFinished(/* foundTargetFrame= */ false, searchPosition); @@ -434,45 +419,49 @@ private void updateNextSearchBytePosition() { */ public static final class TimestampSearchResult { - public static final int RESULT_TARGET_TIMESTAMP_FOUND = 0; - public static final int RESULT_POSITION_OVERESTIMATED = -1; - public static final int RESULT_POSITION_UNDERESTIMATED = -2; - public static final int RESULT_NO_TIMESTAMP = -3; + /** The search found a timestamp that it deems close enough to the given target. */ + public static final int TYPE_TARGET_TIMESTAMP_FOUND = 0; + /** The search found only timestamps larger than the target timestamp. */ + public static final int TYPE_POSITION_OVERESTIMATED = -1; + /** The search found only timestamps smaller than the target timestamp. */ + public static final int TYPE_POSITION_UNDERESTIMATED = -2; + /** The search didn't find any timestamps. */ + public static final int TYPE_NO_TIMESTAMP = -3; @Documented @Retention(RetentionPolicy.SOURCE) @IntDef({ - RESULT_TARGET_TIMESTAMP_FOUND, - RESULT_POSITION_OVERESTIMATED, - RESULT_POSITION_UNDERESTIMATED, - RESULT_NO_TIMESTAMP + TYPE_TARGET_TIMESTAMP_FOUND, + TYPE_POSITION_OVERESTIMATED, + TYPE_POSITION_UNDERESTIMATED, + TYPE_NO_TIMESTAMP }) - @interface SearchResult {} + @interface Type {} public static final TimestampSearchResult NO_TIMESTAMP_IN_RANGE_RESULT = - new TimestampSearchResult(RESULT_NO_TIMESTAMP, C.TIME_UNSET, C.POSITION_UNSET); + new TimestampSearchResult(TYPE_NO_TIMESTAMP, C.TIME_UNSET, C.POSITION_UNSET); - /** @see TimestampSeeker */ - private final @SearchResult int result; + /** The type of the result. */ + @Type private final int type; /** - * When {@code result} is {@link #RESULT_POSITION_OVERESTIMATED}, the {@link - * SeekOperationParams#ceilingTimePosition} should be updated with this value. When {@code - * result} is {@link #RESULT_POSITION_UNDERESTIMATED}, the {@link + * When {@link #type} is {@link #TYPE_POSITION_OVERESTIMATED}, the {@link + * SeekOperationParams#ceilingTimePosition} should be updated with this value. When {@link + * #type} is {@link #TYPE_POSITION_UNDERESTIMATED}, the {@link * SeekOperationParams#floorTimePosition} should be updated with this value. */ private final long timestampToUpdate; /** - * When {@code result} is {@link #RESULT_POSITION_OVERESTIMATED}, the {@link - * SeekOperationParams#ceilingBytePosition} should be updated with this value. When {@code - * result} is {@link #RESULT_POSITION_UNDERESTIMATED}, the {@link + * When {@link #type} is {@link #TYPE_POSITION_OVERESTIMATED}, the {@link + * SeekOperationParams#ceilingBytePosition} should be updated with this value. When {@link + * #type} is {@link #TYPE_POSITION_UNDERESTIMATED}, the {@link * SeekOperationParams#floorBytePosition} should be updated with this value. */ private final long bytePositionToUpdate; private TimestampSearchResult( - @SearchResult int result, long timestampToUpdate, long bytePositionToUpdate) { - this.result = result; + @Type int type, long timestampToUpdate, long bytePositionToUpdate) { + this.type = type; this.timestampToUpdate = timestampToUpdate; this.bytePositionToUpdate = bytePositionToUpdate; } @@ -485,7 +474,7 @@ private TimestampSearchResult( public static TimestampSearchResult overestimatedResult( long newCeilingTimestamp, long newCeilingBytePosition) { return new TimestampSearchResult( - RESULT_POSITION_OVERESTIMATED, newCeilingTimestamp, newCeilingBytePosition); + TYPE_POSITION_OVERESTIMATED, newCeilingTimestamp, newCeilingBytePosition); } /** @@ -496,11 +485,11 @@ public static TimestampSearchResult overestimatedResult( public static TimestampSearchResult underestimatedResult( long newFloorTimestamp, long newCeilingBytePosition) { return new TimestampSearchResult( - RESULT_POSITION_UNDERESTIMATED, newFloorTimestamp, newCeilingBytePosition); + TYPE_POSITION_UNDERESTIMATED, newFloorTimestamp, newCeilingBytePosition); } /** - * Returns a result to signal that the target timestamp has been found at the {@code + * 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 @@ -509,7 +498,7 @@ public static TimestampSearchResult underestimatedResult( */ public static TimestampSearchResult targetFoundResult(long resultBytePosition) { return new TimestampSearchResult( - RESULT_TARGET_TIMESTAMP_FOUND, C.TIME_UNSET, resultBytePosition); + TYPE_TARGET_TIMESTAMP_FOUND, C.TIME_UNSET, resultBytePosition); } } 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 e8c207f75d7..1180dd486e9 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 @@ -53,10 +53,9 @@ public PsBinarySearchSeeker( /** * A seeker that looks for a given SCR timestamp at a given position in a PS stream. * - *

Given a SCR timestamp, and a position within a PS stream, this seeker will try to read a - * range of up to {@link #TIMESTAMP_SEARCH_BYTES} bytes from that stream position, look for all - * packs in that range, and then compare the SCR timestamps (if available) of these packets vs the - * target timestamp. + *

Given a SCR timestamp, and a position within a PS stream, this seeker will peek up to {@link + * #TIMESTAMP_SEARCH_BYTES} bytes from that stream position, look for all packs in that range, and + * then compare the SCR timestamps (if available) of these packets to the target timestamp. */ private static final class PsScrSeeker implements TimestampSeeker { @@ -73,10 +72,10 @@ public TimestampSearchResult searchForTimestamp( ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder) throws IOException, InterruptedException { long inputPosition = input.getPosition(); - int bytesToRead = - (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - input.getPosition()); - packetBuffer.reset(bytesToRead); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToRead); + int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); + + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); + packetBuffer.reset(bytesToSearch); return searchForScrValueInBuffer(packetBuffer, targetTimestamp, inputPosition); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsDurationReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsDurationReader.java index 3b522062358..077a35f4a73 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsDurationReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsDurationReader.java @@ -38,7 +38,7 @@ */ /* package */ final class PsDurationReader { - private static final int DURATION_READ_BYTES = 20000; + private static final int TIMESTAMP_SEARCH_BYTES = 20000; private final TimestampAdjuster scrTimestampAdjuster; private final ParsableByteArray packetBuffer; @@ -56,7 +56,7 @@ firstScrValue = C.TIME_UNSET; lastScrValue = C.TIME_UNSET; durationUs = C.TIME_UNSET; - packetBuffer = new ParsableByteArray(DURATION_READ_BYTES); + packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); } /** Returns true if a PS duration has been read. */ @@ -136,16 +136,16 @@ private int finishReadDuration(ExtractorInput input) { private int readFirstScrValue(ExtractorInput input, PositionHolder seekPositionHolder) throws IOException, InterruptedException { - if (input.getPosition() != 0) { - seekPositionHolder.position = 0; + int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength()); + int searchStartPosition = 0; + if (input.getPosition() != searchStartPosition) { + seekPositionHolder.position = searchStartPosition; return Extractor.RESULT_SEEK; } - int bytesToRead = (int) Math.min(DURATION_READ_BYTES, input.getLength()); input.resetPeekPosition(); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToRead); - packetBuffer.setPosition(0); - packetBuffer.setLimit(bytesToRead); + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); + packetBuffer.reset(bytesToSearch); firstScrValue = readFirstScrValueFromBuffer(packetBuffer); isFirstScrValueRead = true; @@ -172,17 +172,17 @@ private long readFirstScrValueFromBuffer(ParsableByteArray packetBuffer) { private int readLastScrValue(ExtractorInput input, PositionHolder seekPositionHolder) throws IOException, InterruptedException { - int bytesToRead = (int) Math.min(DURATION_READ_BYTES, input.getLength()); - long bufferStartStreamPosition = input.getLength() - bytesToRead; - if (input.getPosition() != bufferStartStreamPosition) { - seekPositionHolder.position = bufferStartStreamPosition; + long inputLength = input.getLength(); + int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, inputLength); + long searchStartPosition = inputLength - bytesToSearch; + if (input.getPosition() != searchStartPosition) { + seekPositionHolder.position = searchStartPosition; return Extractor.RESULT_SEEK; } input.resetPeekPosition(); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToRead); - packetBuffer.setPosition(0); - packetBuffer.setLimit(bytesToRead); + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); + packetBuffer.reset(bytesToSearch); lastScrValue = readLastScrValueFromBuffer(packetBuffer); isLastScrValueRead = true; 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 29aa0d55d27..e9b051592de 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 @@ -33,10 +33,8 @@ /* package */ final class TsBinarySearchSeeker extends BinarySearchSeeker { private static final long SEEK_TOLERANCE_US = 100_000; - private static final int MINIMUM_SEARCH_RANGE_BYTES = TsExtractor.TS_PACKET_SIZE * 5; - private static final int TIMESTAMP_SEARCH_PACKETS = 200; - private static final int TIMESTAMP_SEARCH_BYTES = - TsExtractor.TS_PACKET_SIZE * TIMESTAMP_SEARCH_PACKETS; + private static final int MINIMUM_SEARCH_RANGE_BYTES = 5 * TsExtractor.TS_PACKET_SIZE; + private static final int TIMESTAMP_SEARCH_BYTES = 200 * TsExtractor.TS_PACKET_SIZE; public TsBinarySearchSeeker( TimestampAdjuster pcrTimestampAdjuster, long streamDurationUs, long inputLength, int pcrPid) { @@ -56,10 +54,10 @@ public TsBinarySearchSeeker( * A {@link TimestampSeeker} implementation that looks for a given PCR timestamp at a given * position in a TS stream. * - *

Given a PCR timestamp, and a position within a TS stream, this seeker will try to read up to - * {@link #TIMESTAMP_SEARCH_PACKETS} TS packets from that stream position, look for all packet - * with PID equals to PCR_PID, and then compare the PCR timestamps (if available) of these packets - * vs the target timestamp. + *

Given a PCR timestamp, and a position within a TS stream, this seeker will peek up to {@link + * #TIMESTAMP_SEARCH_BYTES} from that stream position, look for all packets with PID equal to + * PCR_PID, and then compare the PCR timestamps (if available) of these packets to the target + * timestamp. */ private static final class TsPcrSeeker implements TimestampSeeker { @@ -78,10 +76,10 @@ public TimestampSearchResult searchForTimestamp( ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder) throws IOException, InterruptedException { long inputPosition = input.getPosition(); - int bytesToRead = - (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - input.getPosition()); - packetBuffer.reset(bytesToRead); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToRead); + int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); + + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); + packetBuffer.reset(bytesToSearch); return searchForPcrValueInBuffer(packetBuffer, targetTimestamp, inputPosition); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java index 350337cc860..44010833245 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java @@ -35,8 +35,7 @@ */ /* package */ final class TsDurationReader { - private static final int DURATION_READ_PACKETS = 200; - private static final int DURATION_READ_BYTES = TsExtractor.TS_PACKET_SIZE * DURATION_READ_PACKETS; + private static final int TIMESTAMP_SEARCH_BYTES = 200 * TsExtractor.TS_PACKET_SIZE; private final TimestampAdjuster pcrTimestampAdjuster; private final ParsableByteArray packetBuffer; @@ -54,7 +53,7 @@ firstPcrValue = C.TIME_UNSET; lastPcrValue = C.TIME_UNSET; durationUs = C.TIME_UNSET; - packetBuffer = new ParsableByteArray(DURATION_READ_BYTES); + packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); } /** Returns true if a TS duration has been read. */ @@ -124,16 +123,16 @@ private int finishReadDuration(ExtractorInput input) { private int readFirstPcrValue(ExtractorInput input, PositionHolder seekPositionHolder, int pcrPid) throws IOException, InterruptedException { - if (input.getPosition() != 0) { - seekPositionHolder.position = 0; + int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength()); + int searchStartPosition = 0; + if (input.getPosition() != searchStartPosition) { + seekPositionHolder.position = searchStartPosition; return Extractor.RESULT_SEEK; } - int bytesToRead = (int) Math.min(DURATION_READ_BYTES, input.getLength()); input.resetPeekPosition(); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToRead); - packetBuffer.setPosition(0); - packetBuffer.setLimit(bytesToRead); + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); + packetBuffer.reset(bytesToSearch); firstPcrValue = readFirstPcrValueFromBuffer(packetBuffer, pcrPid); isFirstPcrValueRead = true; @@ -159,17 +158,17 @@ private long readFirstPcrValueFromBuffer(ParsableByteArray packetBuffer, int pcr private int readLastPcrValue(ExtractorInput input, PositionHolder seekPositionHolder, int pcrPid) throws IOException, InterruptedException { - int bytesToRead = (int) Math.min(DURATION_READ_BYTES, input.getLength()); - long bufferStartStreamPosition = input.getLength() - bytesToRead; - if (input.getPosition() != bufferStartStreamPosition) { - seekPositionHolder.position = bufferStartStreamPosition; + long inputLength = input.getLength(); + int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, inputLength); + long searchStartPosition = inputLength - bytesToSearch; + if (input.getPosition() != searchStartPosition) { + seekPositionHolder.position = searchStartPosition; return Extractor.RESULT_SEEK; } input.resetPeekPosition(); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToRead); - packetBuffer.setPosition(0); - packetBuffer.setLimit(bytesToRead); + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); + packetBuffer.reset(bytesToSearch); lastPcrValue = readLastPcrValueFromBuffer(packetBuffer, pcrPid); isLastPcrValueRead = true;