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
This commit is contained in:
parent
84fc24492f
commit
c08edc5e0a
@ -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
|
||||
|
@ -58,6 +58,9 @@ public abstract class BinarySearchSeeker {
|
||||
TimestampSearchResult searchForTimestamp(
|
||||
ExtractorInput input, long targetTimestamp, OutputFrameHolder outputFrameHolder)
|
||||
throws IOException, InterruptedException;
|
||||
|
||||
/** Called when a seek operation finishes. */
|
||||
default void onSeekFinished() {}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -255,6 +258,7 @@ public abstract class BinarySearchSeeker {
|
||||
|
||||
protected final void markSeekOperationFinished(boolean foundTargetFrame, long resultPosition) {
|
||||
seekOperationParams = null;
|
||||
timestampSeeker.onSeekFinished();
|
||||
onSeekOperationFinished(foundTargetFrame, resultPosition);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker;
|
||||
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 @@ import java.io.IOException;
|
||||
|
||||
private PsScrSeeker(TimestampAdjuster scrTimestampAdjuster) {
|
||||
this.scrTimestampAdjuster = scrTimestampAdjuster;
|
||||
packetBuffer = new ParsableByteArray(TIMESTAMP_SEARCH_BYTES);
|
||||
packetBuffer = new ParsableByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,12 +75,17 @@ import java.io.IOException;
|
||||
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;
|
||||
|
@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
|
||||
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 @@ import java.io.IOException;
|
||||
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 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
private int finishReadDuration(ExtractorInput input) {
|
||||
packetBuffer.reset(Util.EMPTY_BYTE_ARRAY);
|
||||
isDurationRead = true;
|
||||
input.resetPeekPosition();
|
||||
return Extractor.RESULT_CONTINUE;
|
||||
@ -143,9 +145,9 @@ import java.io.IOException;
|
||||
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 @@ import java.io.IOException;
|
||||
return Extractor.RESULT_SEEK;
|
||||
}
|
||||
|
||||
packetBuffer.reset(bytesToSearch);
|
||||
input.resetPeekPosition();
|
||||
input.peekFully(packetBuffer.data, /* offset= */ 0, bytesToSearch);
|
||||
packetBuffer.reset(bytesToSearch);
|
||||
|
||||
lastScrValue = readLastScrValueFromBuffer(packetBuffer);
|
||||
isLastScrValueRead = true;
|
||||
|
@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker;
|
||||
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 @@ import java.io.IOException;
|
||||
|
||||
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 @@ import java.io.IOException;
|
||||
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 @@ import java.io.IOException;
|
||||
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 @@ import java.io.IOException;
|
||||
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;
|
||||
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 @@ import java.io.IOException;
|
||||
*/
|
||||
/* 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 @@ import java.io.IOException;
|
||||
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 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
private int finishReadDuration(ExtractorInput input) {
|
||||
packetBuffer.reset(Util.EMPTY_BYTE_ARRAY);
|
||||
isDurationRead = true;
|
||||
input.resetPeekPosition();
|
||||
return Extractor.RESULT_CONTINUE;
|
||||
@ -130,9 +132,9 @@ import java.io.IOException;
|
||||
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 @@ import java.io.IOException;
|
||||
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;
|
||||
|
@ -67,6 +67,12 @@ public final class ParsableByteArray {
|
||||
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 final class ParsableByteArray {
|
||||
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 final class ParsableByteArray {
|
||||
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.
|
||||
*/
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user