Commit b7710914 by olly Committed by Oliver Woodman

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
parent 5e6174fe
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
* DASH: Fix detecting the end of live events * DASH: Fix detecting the end of live events
([#4780](https://github.com/google/ExoPlayer/issues/4780)). ([#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. * Support for playing spherical videos on Daydream.
* Improve decoder re-use between playbacks. TODO: Write and link a blog post * Improve decoder re-use between playbacks. TODO: Write and link a blog post
here ([#2826](https://github.com/google/ExoPlayer/issues/2826)). here ([#2826](https://github.com/google/ExoPlayer/issues/2826)).
......
...@@ -58,6 +58,9 @@ public abstract class BinarySearchSeeker { ...@@ -58,6 +58,9 @@ public abstract class BinarySearchSeeker {
TimestampSearchResult searchForTimestamp( TimestampSearchResult searchForTimestamp(
ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder) ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder)
throws IOException, InterruptedException; throws IOException, InterruptedException;
/** Called when a seek operation finishes. */
default void onSeekFinished() {}
} }
/** /**
...@@ -256,6 +259,7 @@ public abstract class BinarySearchSeeker { ...@@ -256,6 +259,7 @@ public abstract class BinarySearchSeeker {
protected final void markSeekOperationFinished(boolean foundTargetFrame, long resultPosition) { protected final void markSeekOperationFinished(boolean foundTargetFrame, long resultPosition) {
seekOperationParams = null; seekOperationParams = null;
timestampSeeker.onSeekFinished();
onSeekOperationFinished(foundTargetFrame, resultPosition); onSeekOperationFinished(foundTargetFrame, resultPosition);
} }
......
...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker; ...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
/** /**
...@@ -64,7 +65,7 @@ import java.io.IOException; ...@@ -64,7 +65,7 @@ import java.io.IOException;
private PsScrSeeker(TimestampAdjuster scrTimestampAdjuster) { private PsScrSeeker(TimestampAdjuster scrTimestampAdjuster) {
this.scrTimestampAdjuster = scrTimestampAdjuster; this.scrTimestampAdjuster = scrTimestampAdjuster;
packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); packetBuffer = new ParsableByteArray();
} }
@Override @Override
...@@ -74,12 +75,17 @@ import java.io.IOException; ...@@ -74,12 +75,17 @@ import java.io.IOException;
long inputPosition = input.getPosition(); long inputPosition = input.getPosition();
int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition);
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
packetBuffer.reset(bytesToSearch); packetBuffer.reset(bytesToSearch);
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
return searchForScrValueInBuffer(packetBuffer, targetTimestamp, inputPosition); return searchForScrValueInBuffer(packetBuffer, targetTimestamp, inputPosition);
} }
@Override
public void onSeekFinished() {
packetBuffer.reset(Util.EMPTY_BYTE_ARRAY);
}
private TimestampSearchResult searchForScrValueInBuffer( private TimestampSearchResult searchForScrValueInBuffer(
ParsableByteArray packetBuffer, long targetScrTimeUs, long bufferStartOffset) { ParsableByteArray packetBuffer, long targetScrTimeUs, long bufferStartOffset) {
int startOfLastPacketPosition = C.POSITION_UNSET; int startOfLastPacketPosition = C.POSITION_UNSET;
......
...@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput; ...@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
/** /**
...@@ -56,7 +57,7 @@ import java.io.IOException; ...@@ -56,7 +57,7 @@ import java.io.IOException;
firstScrValue = C.TIME_UNSET; firstScrValue = C.TIME_UNSET;
lastScrValue = C.TIME_UNSET; lastScrValue = C.TIME_UNSET;
durationUs = 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. */ /** Returns true if a PS duration has been read. */
...@@ -129,6 +130,7 @@ import java.io.IOException; ...@@ -129,6 +130,7 @@ import java.io.IOException;
} }
private int finishReadDuration(ExtractorInput input) { private int finishReadDuration(ExtractorInput input) {
packetBuffer.reset(Util.EMPTY_BYTE_ARRAY);
isDurationRead = true; isDurationRead = true;
input.resetPeekPosition(); input.resetPeekPosition();
return Extractor.RESULT_CONTINUE; return Extractor.RESULT_CONTINUE;
...@@ -143,9 +145,9 @@ import java.io.IOException; ...@@ -143,9 +145,9 @@ import java.io.IOException;
return Extractor.RESULT_SEEK; return Extractor.RESULT_SEEK;
} }
packetBuffer.reset(bytesToSearch);
input.resetPeekPosition(); input.resetPeekPosition();
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
packetBuffer.reset(bytesToSearch);
firstScrValue = readFirstScrValueFromBuffer(packetBuffer); firstScrValue = readFirstScrValueFromBuffer(packetBuffer);
isFirstScrValueRead = true; isFirstScrValueRead = true;
...@@ -180,9 +182,9 @@ import java.io.IOException; ...@@ -180,9 +182,9 @@ import java.io.IOException;
return Extractor.RESULT_SEEK; return Extractor.RESULT_SEEK;
} }
packetBuffer.reset(bytesToSearch);
input.resetPeekPosition(); input.resetPeekPosition();
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
packetBuffer.reset(bytesToSearch);
lastScrValue = readLastScrValueFromBuffer(packetBuffer); lastScrValue = readLastScrValueFromBuffer(packetBuffer);
isLastScrValueRead = true; isLastScrValueRead = true;
......
...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker; ...@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker;
import com.google.android.exoplayer2.extractor.ExtractorInput; import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
/** /**
...@@ -34,7 +35,7 @@ import java.io.IOException; ...@@ -34,7 +35,7 @@ import java.io.IOException;
private static final long SEEK_TOLERANCE_US = 100_000; 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 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( public TsBinarySearchSeeker(
TimestampAdjuster pcrTimestampAdjuster, long streamDurationUs, long inputLength, int pcrPid) { TimestampAdjuster pcrTimestampAdjuster, long streamDurationUs, long inputLength, int pcrPid) {
...@@ -68,7 +69,7 @@ import java.io.IOException; ...@@ -68,7 +69,7 @@ import java.io.IOException;
public TsPcrSeeker(int pcrPid, TimestampAdjuster pcrTimestampAdjuster) { public TsPcrSeeker(int pcrPid, TimestampAdjuster pcrTimestampAdjuster) {
this.pcrPid = pcrPid; this.pcrPid = pcrPid;
this.pcrTimestampAdjuster = pcrTimestampAdjuster; this.pcrTimestampAdjuster = pcrTimestampAdjuster;
packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES); packetBuffer = new ParsableByteArray();
} }
@Override @Override
...@@ -78,8 +79,8 @@ import java.io.IOException; ...@@ -78,8 +79,8 @@ import java.io.IOException;
long inputPosition = input.getPosition(); long inputPosition = input.getPosition();
int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition); int bytesToSearch = (int) Math.min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition);
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
packetBuffer.reset(bytesToSearch); packetBuffer.reset(bytesToSearch);
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
return searchForPcrValueInBuffer(packetBuffer, targetTimestamp, inputPosition); return searchForPcrValueInBuffer(packetBuffer, targetTimestamp, inputPosition);
} }
...@@ -131,5 +132,10 @@ import java.io.IOException; ...@@ -131,5 +132,10 @@ import java.io.IOException;
return TimestampSearchResult.NO_TIMESTAMP_IN_RANGE_RESULT; return TimestampSearchResult.NO_TIMESTAMP_IN_RANGE_RESULT;
} }
} }
@Override
public void onSeekFinished() {
packetBuffer.reset(Util.EMPTY_BYTE_ARRAY);
}
} }
} }
...@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput; ...@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.util.ParsableByteArray; import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.TimestampAdjuster; import com.google.android.exoplayer2.util.TimestampAdjuster;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
/** /**
...@@ -35,7 +36,7 @@ import java.io.IOException; ...@@ -35,7 +36,7 @@ import java.io.IOException;
*/ */
/* package */ final class TsDurationReader { /* 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 TimestampAdjuster pcrTimestampAdjuster;
private final ParsableByteArray packetBuffer; private final ParsableByteArray packetBuffer;
...@@ -53,7 +54,7 @@ import java.io.IOException; ...@@ -53,7 +54,7 @@ import java.io.IOException;
firstPcrValue = C.TIME_UNSET; firstPcrValue = C.TIME_UNSET;
lastPcrValue = C.TIME_UNSET; lastPcrValue = C.TIME_UNSET;
durationUs = 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. */ /** Returns true if a TS duration has been read. */
...@@ -116,6 +117,7 @@ import java.io.IOException; ...@@ -116,6 +117,7 @@ import java.io.IOException;
} }
private int finishReadDuration(ExtractorInput input) { private int finishReadDuration(ExtractorInput input) {
packetBuffer.reset(Util.EMPTY_BYTE_ARRAY);
isDurationRead = true; isDurationRead = true;
input.resetPeekPosition(); input.resetPeekPosition();
return Extractor.RESULT_CONTINUE; return Extractor.RESULT_CONTINUE;
...@@ -130,9 +132,9 @@ import java.io.IOException; ...@@ -130,9 +132,9 @@ import java.io.IOException;
return Extractor.RESULT_SEEK; return Extractor.RESULT_SEEK;
} }
packetBuffer.reset(bytesToSearch);
input.resetPeekPosition(); input.resetPeekPosition();
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
packetBuffer.reset(bytesToSearch);
firstPcrValue = readFirstPcrValueFromBuffer(packetBuffer, pcrPid); firstPcrValue = readFirstPcrValueFromBuffer(packetBuffer, pcrPid);
isFirstPcrValueRead = true; isFirstPcrValueRead = true;
...@@ -166,9 +168,9 @@ import java.io.IOException; ...@@ -166,9 +168,9 @@ import java.io.IOException;
return Extractor.RESULT_SEEK; return Extractor.RESULT_SEEK;
} }
packetBuffer.reset(bytesToSearch);
input.resetPeekPosition(); input.resetPeekPosition();
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch); input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
packetBuffer.reset(bytesToSearch);
lastPcrValue = readLastPcrValueFromBuffer(packetBuffer, pcrPid); lastPcrValue = readLastPcrValueFromBuffer(packetBuffer, pcrPid);
isLastPcrValueRead = true; isLastPcrValueRead = true;
......
...@@ -67,6 +67,12 @@ public final class ParsableByteArray { ...@@ -67,6 +67,12 @@ public final class ParsableByteArray {
this.limit = 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 * 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. * capacity, {@code data} is replaced with a new array of sufficient size.
...@@ -78,6 +84,16 @@ public final class ParsableByteArray { ...@@ -78,6 +84,16 @@ public final class ParsableByteArray {
} }
/** /**
* 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. * Updates the instance to wrap {@code data}, and resets the position to zero.
* *
* @param data The array to wrap. * @param data The array to wrap.
...@@ -90,14 +106,6 @@ public final class ParsableByteArray { ...@@ -90,14 +106,6 @@ public final class ParsableByteArray {
} }
/** /**
* Sets the position and limit to zero.
*/
public void reset() {
position = 0;
limit = 0;
}
/**
* Returns the number of bytes yet to be read. * Returns the number of bytes yet to be read.
*/ */
public int bytesLeft() { public int bytesLeft() {
......
...@@ -26,10 +26,14 @@ track 256: ...@@ -26,10 +26,14 @@ track 256:
drmInitData = - drmInitData = -
initializationData: initializationData:
data = length 22, hash CE183139 data = length 22, hash CE183139
total output bytes = 24315 total output bytes = 45026
sample count = 1 sample count = 2
sample 0: sample 0:
time = 55611 time = 55610
flags = 1
data = length 20711, hash 34341E8
sample 1:
time = 88977
flags = 0 flags = 0
data = length 18112, hash EC44B35B data = length 18112, hash EC44B35B
track 257: track 257:
...@@ -57,19 +61,19 @@ track 257: ...@@ -57,19 +61,19 @@ track 257:
total output bytes = 5015 total output bytes = 5015
sample count = 4 sample count = 4
sample 0: sample 0:
time = 11333 time = 44699
flags = 1 flags = 1
data = length 1253, hash 727FD1C6 data = length 1253, hash 727FD1C6
sample 1: sample 1:
time = 37455 time = 70821
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
sample 2: sample 2:
time = 63578 time = 96944
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
sample 3: sample 3:
time = 89700 time = 123066
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
track 8448: track 8448:
......
...@@ -26,10 +26,14 @@ track 256: ...@@ -26,10 +26,14 @@ track 256:
drmInitData = - drmInitData = -
initializationData: initializationData:
data = length 22, hash CE183139 data = length 22, hash CE183139
total output bytes = 24315 total output bytes = 45026
sample count = 1 sample count = 2
sample 0: sample 0:
time = 77855 time = 77854
flags = 1
data = length 20711, hash 34341E8
sample 1:
time = 111221
flags = 0 flags = 0
data = length 18112, hash EC44B35B data = length 18112, hash EC44B35B
track 257: track 257:
...@@ -57,19 +61,19 @@ track 257: ...@@ -57,19 +61,19 @@ track 257:
total output bytes = 5015 total output bytes = 5015
sample count = 4 sample count = 4
sample 0: sample 0:
time = 33577 time = 66943
flags = 1 flags = 1
data = length 1253, hash 727FD1C6 data = length 1253, hash 727FD1C6
sample 1: sample 1:
time = 59699 time = 93065
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
sample 2: sample 2:
time = 85822 time = 119188
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
sample 3: sample 3:
time = 111944 time = 145310
flags = 1 flags = 1
data = length 1254, hash 73FB07B8 data = length 1254, hash 73FB07B8
track 8448: track 8448:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment