From b77109148e500b1d0271aa97dd99fe808fd5474d Mon Sep 17 00:00:00 2001 From: olly Date: Wed, 14 Nov 2018 08:40:42 -0800 Subject: [PATCH] Workaround for TS seeking - Increase the search window size to fix TS seeking for problematic media we've had provided to us. - As per my comments on the issue, we should look at doing more here to better fix the problem. This will solve the worst of the immediate problem, however. - The memory usage is non-trivial, particularly with the increased search window size. I've made the allocations only live whilst determining duration and seeking to address this. I've done the same for PS just for consistency. Issue: #5097 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=221449988 --- RELEASENOTES.md | 2 ++ .../extractor/BinarySearchSeeker.java | 4 ++++ .../extractor/ts/PsBinarySearchSeeker.java | 10 ++++++-- .../extractor/ts/PsDurationReader.java | 8 ++++--- .../extractor/ts/TsBinarySearchSeeker.java | 12 +++++++--- .../extractor/ts/TsDurationReader.java | 10 ++++---- .../exoplayer2/util/ParsableByteArray.java | 24 ++++++++++++------- .../core/src/test/assets/ts/sample.ts.1.dump | 18 ++++++++------ .../core/src/test/assets/ts/sample.ts.2.dump | 18 ++++++++------ 9 files changed, 72 insertions(+), 34 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b39c3ca30d9..f54fa796059 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -4,6 +4,8 @@ * DASH: Fix detecting the end of live events ([#4780](https://github.com/google/ExoPlayer/issues/4780)). +* Support seeking for a wider range of MPEG-TS streams + ([#5097](https://github.com/google/ExoPlayer/issues/5097)). * Support for playing spherical videos on Daydream. * Improve decoder re-use between playbacks. TODO: Write and link a blog post here ([#2826](https://github.com/google/ExoPlayer/issues/2826)). 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 e9842611455..af40206ede5 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() {} } /** @@ -256,6 +259,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: