Skip to content

Commit

Permalink
Add search bytes parameter to TsExtractor
Browse files Browse the repository at this point in the history
Context: Issue: #7988
PiperOrigin-RevId: 335608610
  • Loading branch information
kim-vde authored and ojw28 committed Oct 17, 2020
1 parent 88abe26 commit 1cca9ff
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 24 deletions.
8 changes: 5 additions & 3 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@
([#7985](https://github.com/google/ExoPlayer/issues/7985)).
* Fix NPE in `TextRenderer` when playing content with a single subtitle
buffer ([#8017](https://github.com/google/ExoPlayer/issues/8017)).
* UI:
* Do not require subtitleButton in custom layouts of StyledPlayerView
([#7962](https://github.com/google/ExoPlayer/issues/7962)).
* Audio:
* Fix the default audio sink position not advancing correctly when using
`AudioTrack`-based speed adjustment
Expand All @@ -29,7 +26,12 @@
([#7949](https://github.com/google/ExoPlayer/issues/7949)).
* Fix regression for Ogg files with packets that span multiple pages
([#7992](https://github.com/google/ExoPlayer/issues/7992)).
* Add TS extractor parameter to configure the number of bytes in which
to search for a timestamp to determine the duration and to seek.
([#7988](https://github.com/google/ExoPlayer/issues/7988)).
* UI
* Do not require subtitleButton in custom layouts of StyledPlayerView
([#7962](https://github.com/google/ExoPlayer/issues/7962)).
* Add the option to sort tracks by `Format` in `TrackSelectionView` and
`TrackSelectionDialogBuilder`
([#7709](https://github.com/google/ExoPlayer/issues/7709)).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,11 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
@Mp3Extractor.Flags private int mp3Flags;
@TsExtractor.Mode private int tsMode;
@DefaultTsPayloadReaderFactory.Flags private int tsFlags;
private int tsTimestampSearchBytes;

public DefaultExtractorsFactory() {
tsMode = TsExtractor.MODE_SINGLE_PMT;
tsTimestampSearchBytes = TsExtractor.DEFAULT_TIMESTAMP_SEARCH_BYTES;
}

/**
Expand Down Expand Up @@ -246,7 +248,7 @@ public synchronized DefaultExtractorsFactory setMp3ExtractorFlags(@Mp3Extractor.
/**
* Sets the mode for {@link TsExtractor} instances created by the factory.
*
* @see TsExtractor#TsExtractor(int, TimestampAdjuster, TsPayloadReader.Factory)
* @see TsExtractor#TsExtractor(int, TimestampAdjuster, TsPayloadReader.Factory, int)
* @param mode The mode to use.
* @return The factory, for convenience.
*/
Expand All @@ -269,6 +271,20 @@ public synchronized DefaultExtractorsFactory setTsExtractorFlags(
return this;
}

/**
* Sets the number of bytes searched to find a timestamp for {@link TsExtractor} instances created
* by the factory.
*
* @see TsExtractor#TsExtractor(int, TimestampAdjuster, TsPayloadReader.Factory, int)
* @param timestampSearchBytes The number of search bytes to use.
* @return The factory, for convenience.
*/
public synchronized DefaultExtractorsFactory setTsExtractorTimestampSearchBytes(
int timestampSearchBytes) {
tsTimestampSearchBytes = timestampSearchBytes;
return this;
}

@Override
public synchronized Extractor[] createExtractors() {
return createExtractors(Uri.EMPTY, new HashMap<>());
Expand Down Expand Up @@ -361,7 +377,7 @@ private void addExtractorsForFileType(@FileTypes.Type int fileType, List<Extract
extractors.add(new PsExtractor());
break;
case FileTypes.TS:
extractors.add(new TsExtractor(tsMode, tsFlags));
extractors.add(new TsExtractor(tsMode, tsFlags, tsTimestampSearchBytes));
break;
case FileTypes.WAV:
extractors.add(new WavExtractor());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@

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 = 600 * TsExtractor.TS_PACKET_SIZE;

public TsBinarySearchSeeker(
TimestampAdjuster pcrTimestampAdjuster, long streamDurationUs, long inputLength, int pcrPid) {
TimestampAdjuster pcrTimestampAdjuster,
long streamDurationUs,
long inputLength,
int pcrPid,
int timestampSearchBytes) {
super(
new DefaultSeekTimestampConverter(),
new TsPcrSeeker(pcrPid, pcrTimestampAdjuster),
new TsPcrSeeker(pcrPid, pcrTimestampAdjuster, timestampSearchBytes),
streamDurationUs,
/* floorTimePosition= */ 0,
/* ceilingTimePosition= */ streamDurationUs + 1,
Expand All @@ -58,7 +61,7 @@ public TsBinarySearchSeeker(
* position in a TS stream.
*
* <p>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
* #timestampSearchBytes} 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.
*/
Expand All @@ -67,18 +70,21 @@ private static final class TsPcrSeeker implements TimestampSeeker {
private final TimestampAdjuster pcrTimestampAdjuster;
private final ParsableByteArray packetBuffer;
private final int pcrPid;
private final int timestampSearchBytes;

public TsPcrSeeker(int pcrPid, TimestampAdjuster pcrTimestampAdjuster) {
public TsPcrSeeker(
int pcrPid, TimestampAdjuster pcrTimestampAdjuster, int timestampSearchBytes) {
this.pcrPid = pcrPid;
this.pcrTimestampAdjuster = pcrTimestampAdjuster;
this.timestampSearchBytes = timestampSearchBytes;
packetBuffer = new ParsableByteArray();
}

@Override
public TimestampSearchResult searchForTimestamp(ExtractorInput input, long targetTimestamp)
throws IOException {
long inputPosition = input.getPosition();
int bytesToSearch = (int) min(TIMESTAMP_SEARCH_BYTES, input.getLength() - inputPosition);
int bytesToSearch = (int) min(timestampSearchBytes, input.getLength() - inputPosition);

packetBuffer.reset(bytesToSearch);
input.peekFully(packetBuffer.getData(), /* offset= */ 0, bytesToSearch);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@
*/
/* package */ final class TsDurationReader {

private static final int TIMESTAMP_SEARCH_BYTES = 600 * TsExtractor.TS_PACKET_SIZE;

private final int timestampSearchBytes;
private final TimestampAdjuster pcrTimestampAdjuster;
private final ParsableByteArray packetBuffer;

Expand All @@ -51,7 +50,8 @@
private long lastPcrValue;
private long durationUs;

/* package */ TsDurationReader() {
/* package */ TsDurationReader(int timestampSearchBytes) {
this.timestampSearchBytes = timestampSearchBytes;
pcrTimestampAdjuster = new TimestampAdjuster(/* firstSampleTimestampUs= */ 0);
firstPcrValue = C.TIME_UNSET;
lastPcrValue = C.TIME_UNSET;
Expand Down Expand Up @@ -125,7 +125,7 @@ private int finishReadDuration(ExtractorInput input) {

private int readFirstPcrValue(ExtractorInput input, PositionHolder seekPositionHolder, int pcrPid)
throws IOException {
int bytesToSearch = (int) min(TIMESTAMP_SEARCH_BYTES, input.getLength());
int bytesToSearch = (int) min(timestampSearchBytes, input.getLength());
int searchStartPosition = 0;
if (input.getPosition() != searchStartPosition) {
seekPositionHolder.position = searchStartPosition;
Expand Down Expand Up @@ -161,7 +161,7 @@ private long readFirstPcrValueFromBuffer(ParsableByteArray packetBuffer, int pcr
private int readLastPcrValue(ExtractorInput input, PositionHolder seekPositionHolder, int pcrPid)
throws IOException {
long inputLength = input.getLength();
int bytesToSearch = (int) min(TIMESTAMP_SEARCH_BYTES, inputLength);
int bytesToSearch = (int) min(timestampSearchBytes, inputLength);
long searchStartPosition = inputLength - bytesToSearch;
if (input.getPosition() != searchStartPosition) {
seekPositionHolder.position = searchStartPosition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ public final class TsExtractor implements Extractor {
*/
public static final int MODE_HLS = 2;

public static final int TS_PACKET_SIZE = 188;
public static final int DEFAULT_TIMESTAMP_SEARCH_BYTES = 600 * TS_PACKET_SIZE;

public static final int TS_STREAM_TYPE_MPA = 0x03;
public static final int TS_STREAM_TYPE_MPA_LSF = 0x04;
public static final int TS_STREAM_TYPE_AAC_ADTS = 0x0F;
Expand All @@ -100,7 +103,6 @@ public final class TsExtractor implements Extractor {
// Stream types that aren't defined by the MPEG-2 TS specification.
public static final int TS_STREAM_TYPE_AIT = 0x101;

public static final int TS_PACKET_SIZE = 188;
public static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet.

private static final int TS_PAT_PID = 0;
Expand All @@ -115,6 +117,7 @@ public final class TsExtractor implements Extractor {
private static final int SNIFF_TS_PACKET_COUNT = 5;

private final @Mode int mode;
private final int timestampSearchBytes;
private final List<TimestampAdjuster> timestampAdjusters;
private final ParsableByteArray tsPacketBuffer;
private final SparseIntArray continuityCounters;
Expand All @@ -136,28 +139,38 @@ public final class TsExtractor implements Extractor {
private int pcrPid;

public TsExtractor() {
this(0);
this(/* defaultTsPayloadReaderFlags= */ 0);
}

/**
* @param defaultTsPayloadReaderFlags A combination of {@link DefaultTsPayloadReaderFactory}
* {@code FLAG_*} values that control the behavior of the payload readers.
*/
public TsExtractor(@Flags int defaultTsPayloadReaderFlags) {
this(MODE_SINGLE_PMT, defaultTsPayloadReaderFlags);
this(MODE_SINGLE_PMT, defaultTsPayloadReaderFlags, DEFAULT_TIMESTAMP_SEARCH_BYTES);
}

/**
* @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT}
* and {@link #MODE_HLS}.
* @param defaultTsPayloadReaderFlags A combination of {@link DefaultTsPayloadReaderFactory}
* {@code FLAG_*} values that control the behavior of the payload readers.
* @param timestampSearchBytes The number of bytes searched from a given position in the stream to
* find a PCR timestamp. If this value is too small, the duration might be unknown and seeking
* might not be supported for high bitrate progressive streams. Setting a large value for this
* field might be inefficient though because the extractor stores a buffer of {@code
* timestampSearchBytes} bytes when determining the duration or when performing a seek
* operation. The default value is {@link #DEFAULT_TIMESTAMP_SEARCH_BYTES}. If the number of
* bytes left in the stream from the current position is less than {@code
* timestampSearchBytes}, the search is performed on the bytes left.
*/
public TsExtractor(@Mode int mode, @Flags int defaultTsPayloadReaderFlags) {
public TsExtractor(
@Mode int mode, @Flags int defaultTsPayloadReaderFlags, int timestampSearchBytes) {
this(
mode,
new TimestampAdjuster(0),
new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags));
new DefaultTsPayloadReaderFactory(defaultTsPayloadReaderFlags),
timestampSearchBytes);
}

/**
Expand All @@ -170,7 +183,30 @@ public TsExtractor(
@Mode int mode,
TimestampAdjuster timestampAdjuster,
TsPayloadReader.Factory payloadReaderFactory) {
this(mode, timestampAdjuster, payloadReaderFactory, DEFAULT_TIMESTAMP_SEARCH_BYTES);
}

/**
* @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT}
* and {@link #MODE_HLS}.
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
* @param payloadReaderFactory Factory for injecting a custom set of payload readers.
* @param timestampSearchBytes The number of bytes searched from a given position in the stream to
* find a PCR timestamp. If this value is too small, the duration might be unknown and seeking
* might not be supported for high bitrate progressive streams. Setting a large value for this
* field might be inefficient though because the extractor stores a buffer of {@code
* timestampSearchBytes} bytes when determining the duration or when performing a seek
* operation. The default value is {@link #DEFAULT_TIMESTAMP_SEARCH_BYTES}. If the number of
* bytes left in the stream from the current position is less than {@code
* timestampSearchBytes}, the search is performed on the bytes left.
*/
public TsExtractor(
@Mode int mode,
TimestampAdjuster timestampAdjuster,
TsPayloadReader.Factory payloadReaderFactory,
int timestampSearchBytes) {
this.payloadReaderFactory = Assertions.checkNotNull(payloadReaderFactory);
this.timestampSearchBytes = timestampSearchBytes;
this.mode = mode;
if (mode == MODE_SINGLE_PMT || mode == MODE_HLS) {
timestampAdjusters = Collections.singletonList(timestampAdjuster);
Expand All @@ -183,7 +219,7 @@ public TsExtractor(
trackPids = new SparseBooleanArray();
tsPayloadReaders = new SparseArray<>();
continuityCounters = new SparseIntArray();
durationReader = new TsDurationReader();
durationReader = new TsDurationReader(timestampSearchBytes);
pcrPid = -1;
resetPayloadReaders();
}
Expand Down Expand Up @@ -365,7 +401,8 @@ private void maybeOutputSeekMap(long inputLength) {
durationReader.getPcrTimestampAdjuster(),
durationReader.getDurationUs(),
inputLength,
pcrPid);
pcrPid,
timestampSearchBytes);
output.seekMap(tsBinarySearchSeeker.getSeekMap());
} else {
output.seekMap(new SeekMap.Unseekable(durationReader.getDurationUs()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public final class TsDurationReaderTest {

@Before
public void setUp() {
tsDurationReader = new TsDurationReader();
tsDurationReader = new TsDurationReader(TsExtractor.DEFAULT_TIMESTAMP_SEARCH_BYTES);
seekPositionHolder = new PositionHolder();
}

Expand Down

0 comments on commit 1cca9ff

Please sign in to comment.