mirror of
https://github.com/androidx/media.git
synced 2025-05-11 09:39:52 +08:00
Add search bytes parameter to TsExtractor
Context: Issue: #7988 PiperOrigin-RevId: 335608610
This commit is contained in:
parent
88abe26ec3
commit
1cca9ffd01
@ -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
|
||||
@ -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)).
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -246,7 +248,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@ -269,6 +271,20 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
||||
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<>());
|
||||
@ -361,7 +377,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
||||
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());
|
||||
|
@ -37,13 +37,16 @@ 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 = 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,
|
||||
@ -58,7 +61,7 @@ import java.io.IOException;
|
||||
* 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.
|
||||
*/
|
||||
@ -67,10 +70,13 @@ import java.io.IOException;
|
||||
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();
|
||||
}
|
||||
|
||||
@ -78,7 +84,7 @@ import java.io.IOException;
|
||||
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);
|
||||
|
@ -38,8 +38,7 @@ import java.io.IOException;
|
||||
*/
|
||||
/* 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;
|
||||
|
||||
@ -51,7 +50,8 @@ import java.io.IOException;
|
||||
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;
|
||||
@ -125,7 +125,7 @@ import java.io.IOException;
|
||||
|
||||
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;
|
||||
@ -161,7 +161,7 @@ import java.io.IOException;
|
||||
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;
|
||||
|
@ -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;
|
||||
@ -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;
|
||||
@ -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;
|
||||
@ -136,7 +139,7 @@ public final class TsExtractor implements Extractor {
|
||||
private int pcrPid;
|
||||
|
||||
public TsExtractor() {
|
||||
this(0);
|
||||
this(/* defaultTsPayloadReaderFlags= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,7 +147,7 @@ public final class TsExtractor implements Extractor {
|
||||
* {@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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,12 +155,22 @@ public final class TsExtractor implements Extractor {
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,7 +183,30 @@ public final class TsExtractor implements Extractor {
|
||||
@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);
|
||||
@ -183,7 +219,7 @@ public final class TsExtractor implements Extractor {
|
||||
trackPids = new SparseBooleanArray();
|
||||
tsPayloadReaders = new SparseArray<>();
|
||||
continuityCounters = new SparseIntArray();
|
||||
durationReader = new TsDurationReader();
|
||||
durationReader = new TsDurationReader(timestampSearchBytes);
|
||||
pcrPid = -1;
|
||||
resetPayloadReaders();
|
||||
}
|
||||
@ -365,7 +401,8 @@ public final class TsExtractor implements Extractor {
|
||||
durationReader.getPcrTimestampAdjuster(),
|
||||
durationReader.getDurationUs(),
|
||||
inputLength,
|
||||
pcrPid);
|
||||
pcrPid,
|
||||
timestampSearchBytes);
|
||||
output.seekMap(tsBinarySearchSeeker.getSeekMap());
|
||||
} else {
|
||||
output.seekMap(new SeekMap.Unseekable(durationReader.getDurationUs()));
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user