diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 27644c609c8..35e51076e5d 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -2,6 +2,8 @@ ### 2.9.2 ### +* Support seeking for a wider range of MPEG-TS streams + ([#5097](https://github.com/google/ExoPlayer/issues/5097)). * DASH: Fix detecting the end of live events ([#4780](https://github.com/google/ExoPlayer/issues/4780)). * Include channel count in audio capabilities check 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 efdeddbdce9..e8b6e736ba1 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 @@ -58,6 +58,9 @@ protected interface TimestampSeeker { TimestampSearchResult searchForTimestamp( ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder) throws IOException, InterruptedException; + + /** Called when a seek operation finishes. */ + default void onSeekFinished() {} } /** @@ -255,6 +258,7 @@ protected SeekOperationParams createSeekParamsForTargetTimeUs(long timeUs) { protected final void markSeekOperationFinished(boolean foundTargetFrame, long resultPosition) { seekOperationParams = null; + timestampSeeker.onSeekFinished(); onSeekOperationFinished(foundTargetFrame, resultPosition); } 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 1180dd486e9..4efd38b7eb7 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 @@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.TimestampAdjuster; +import com.google.android.exoplayer2.util.Util; import java.io.IOException; /** @@ -64,7 +65,7 @@ private static final class PsScrSeeker implements TimestampSeeker { private PsScrSeeker(TimestampAdjuster scrTimestampAdjuster) { this.scrTimestampAdjuster = scrTimestampAdjuster; - packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); + packetBuffer = new ParsableByteArray(); } @Override @@ -74,12 +75,17 @@ public TimestampSearchResult searchForTimestamp( long inputPosition = input.getPosition(); int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); packetBuffer.reset(bytesToSearch); + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); return searchForScrValueInBuffer(packetBuffer, targetTimestamp, inputPosition); } + @Override + public void onSeekFinished() { + packetBuffer.reset(Util.EMPTY_BYTE_ARRAY); + } + private TimestampSearchResult searchForScrValueInBuffer( ParsableByteArray packetBuffer, long targetScrTimeUs, long bufferStartOffset) { int startOfLastPacketPosition = C.POSITION_UNSET; 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 077a35f4a73..b0cdf7eb79a 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 @@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.TimestampAdjuster; +import com.google.android.exoplayer2.util.Util; import java.io.IOException; /** @@ -56,7 +57,7 @@ firstScrValue = C.TIME_UNSET; lastScrValue = C.TIME_UNSET; durationUs = C.TIME_UNSET; - packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); + packetBuffer = new ParsableByteArray(); } /** Returns true if a PS duration has been read. */ @@ -129,6 +130,7 @@ public static long readScrValueFromPack(ParsableByteArray packetBuffer) { } private int finishReadDuration(ExtractorInput input) { + packetBuffer.reset(Util.EMPTY_BYTE_ARRAY); isDurationRead = true; input.resetPeekPosition(); return Extractor.RESULT_CONTINUE; @@ -143,9 +145,9 @@ private int readFirstScrValue(ExtractorInput input, PositionHolder seekPositionH return Extractor.RESULT_SEEK; } + packetBuffer.reset(bytesToSearch); input.resetPeekPosition(); input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); - packetBuffer.reset(bytesToSearch); firstScrValue = readFirstScrValueFromBuffer(packetBuffer); isFirstScrValueRead = true; @@ -180,9 +182,9 @@ private int readLastScrValue(ExtractorInput input, PositionHolder seekPositionHo return Extractor.RESULT_SEEK; } + packetBuffer.reset(bytesToSearch); input.resetPeekPosition(); 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 e9b051592de..ea2519d2e9e 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 @@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.TimestampAdjuster; +import com.google.android.exoplayer2.util.Util; import java.io.IOException; /** @@ -34,7 +35,7 @@ private static final long SEEK_TOLERANCE_US = 100_000; 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; + private static final int TIMESTAMP_SEARCH_BYTES = 600 * TsExtractor.TS_PACKET_SIZE; public TsBinarySearchSeeker( TimestampAdjuster pcrTimestampAdjuster, long streamDurationUs, long inputLength, int pcrPid) { @@ -68,7 +69,7 @@ private static final class TsPcrSeeker implements TimestampSeeker { public TsPcrSeeker(int pcrPid, TimestampAdjuster pcrTimestampAdjuster) { this.pcrPid = pcrPid; this.pcrTimestampAdjuster = pcrTimestampAdjuster; - packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); + packetBuffer = new ParsableByteArray(); } @Override @@ -78,8 +79,8 @@ public TimestampSearchResult searchForTimestamp( long inputPosition = input.getPosition(); int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); - input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); packetBuffer.reset(bytesToSearch); + input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); return searchForPcrValueInBuffer(packetBuffer, targetTimestamp, inputPosition); } @@ -131,5 +132,10 @@ private TimestampSearchResult searchForPcrValueInBuffer( return TimestampSearchResult.NO_TIMESTAMP_IN_RANGE_RESULT; } } + + @Override + public void onSeekFinished() { + packetBuffer.reset(Util.EMPTY_BYTE_ARRAY); + } } } 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 44010833245..804a6434144 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 @@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.TimestampAdjuster; +import com.google.android.exoplayer2.util.Util; import java.io.IOException; /** @@ -35,7 +36,7 @@ */ /* package */ final class TsDurationReader { - private static final int TIMESTAMP_SEARCH_BYTES = 200 * TsExtractor.TS_PACKET_SIZE; + private static final int TIMESTAMP_SEARCH_BYTES = 600 * TsExtractor.TS_PACKET_SIZE; private final TimestampAdjuster pcrTimestampAdjuster; private final ParsableByteArray packetBuffer; @@ -53,7 +54,7 @@ firstPcrValue = C.TIME_UNSET; lastPcrValue = C.TIME_UNSET; durationUs = C.TIME_UNSET; - packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); + packetBuffer = new ParsableByteArray(); } /** Returns true if a TS duration has been read. */ @@ -116,6 +117,7 @@ public TimestampAdjuster getPcrTimestampAdjuster() { } private int finishReadDuration(ExtractorInput input) { + packetBuffer.reset(Util.EMPTY_BYTE_ARRAY); isDurationRead = true; input.resetPeekPosition(); return Extractor.RESULT_CONTINUE; @@ -130,9 +132,9 @@ private int readFirstPcrValue(ExtractorInput input, PositionHolder seekPositionH return Extractor.RESULT_SEEK; } + packetBuffer.reset(bytesToSearch); input.resetPeekPosition(); input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); - packetBuffer.reset(bytesToSearch); firstPcrValue = readFirstPcrValueFromBuffer(packetBuffer, pcrPid); isFirstPcrValueRead = true; @@ -166,9 +168,9 @@ private int readLastPcrValue(ExtractorInput input, PositionHolder seekPositionHo return Extractor.RESULT_SEEK; } + packetBuffer.reset(bytesToSearch); input.resetPeekPosition(); input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); - packetBuffer.reset(bytesToSearch); lastPcrValue = readLastPcrValueFromBuffer(packetBuffer, pcrPid); isLastPcrValueRead = true; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java index 464061153f9..b928ffc02b8 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java @@ -67,6 +67,12 @@ public ParsableByteArray(byte[] data, int limit) { this.limit = limit; } + /** Sets the position and limit to zero. */ + public void reset() { + position = 0; + limit = 0; + } + /** * Resets the position to zero and the limit to the specified value. If the limit exceeds the * capacity, {@code data} is replaced with a new array of sufficient size. @@ -77,6 +83,16 @@ public void reset(int limit) { reset(capacity() < limit ? new byte[limit] : data, limit); } + /** + * Updates the instance to wrap {@code data}, and resets the position to zero and the limit to + * {@code data.length}. + * + * @param data The array to wrap. + */ + public void reset(byte[] data) { + reset(data, data.length); + } + /** * Updates the instance to wrap {@code data}, and resets the position to zero. * @@ -89,14 +105,6 @@ public void reset(byte[] data, int limit) { position = 0; } - /** - * Sets the position and limit to zero. - */ - public void reset() { - position = 0; - limit = 0; - } - /** * Returns the number of bytes yet to be read. */ diff --git a/library/core/src/test/assets/ts/sample.ts.1.dump b/library/core/src/test/assets/ts/sample.ts.1.dump index 7454a021414..5c361e12460 100644 --- a/library/core/src/test/assets/ts/sample.ts.1.dump +++ b/library/core/src/test/assets/ts/sample.ts.1.dump @@ -26,10 +26,14 @@ track 256: drmInitData = - initializationData: data = length 22, hash CE183139 - total output bytes = 24315 - sample count = 1 + total output bytes = 45026 + sample count = 2 sample 0: - time = 55611 + time = 55610 + flags = 1 + data = length 20711, hash 34341E8 + sample 1: + time = 88977 flags = 0 data = length 18112, hash EC44B35B track 257: @@ -57,19 +61,19 @@ track 257: total output bytes = 5015 sample count = 4 sample 0: - time = 11333 + time = 44699 flags = 1 data = length 1253, hash 727FD1C6 sample 1: - time = 37455 + time = 70821 flags = 1 data = length 1254, hash 73FB07B8 sample 2: - time = 63578 + time = 96944 flags = 1 data = length 1254, hash 73FB07B8 sample 3: - time = 89700 + time = 123066 flags = 1 data = length 1254, hash 73FB07B8 track 8448: diff --git a/library/core/src/test/assets/ts/sample.ts.2.dump b/library/core/src/test/assets/ts/sample.ts.2.dump index c7cef05b935..cec91ae2b91 100644 --- a/library/core/src/test/assets/ts/sample.ts.2.dump +++ b/library/core/src/test/assets/ts/sample.ts.2.dump @@ -26,10 +26,14 @@ track 256: drmInitData = - initializationData: data = length 22, hash CE183139 - total output bytes = 24315 - sample count = 1 + total output bytes = 45026 + sample count = 2 sample 0: - time = 77855 + time = 77854 + flags = 1 + data = length 20711, hash 34341E8 + sample 1: + time = 111221 flags = 0 data = length 18112, hash EC44B35B track 257: @@ -57,19 +61,19 @@ track 257: total output bytes = 5015 sample count = 4 sample 0: - time = 33577 + time = 66943 flags = 1 data = length 1253, hash 727FD1C6 sample 1: - time = 59699 + time = 93065 flags = 1 data = length 1254, hash 73FB07B8 sample 2: - time = 85822 + time = 119188 flags = 1 data = length 1254, hash 73FB07B8 sample 3: - time = 111944 + time = 145310 flags = 1 data = length 1254, hash 73FB07B8 track 8448: