Commit cab02289 by aquilescanta Committed by Santiago Seifert

Generalize the PtsTimestampAdjuster

This allows the adjustment of timestamps in microseconds along with
TS timestamps. This is useful for containers that include the
timestamps in microseconds format, like fMP4 and WebVTT.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=132547521
parent 04c28c6d
......@@ -56,7 +56,7 @@ public final class PsExtractor implements Extractor {
public static final int VIDEO_STREAM = 0xE0;
public static final int VIDEO_STREAM_MASK = 0xF0;
private final PtsTimestampAdjuster ptsTimestampAdjuster;
private final TimestampAdjuster timestampAdjuster;
private final SparseArray<PesReader> psPayloadReaders; // Indexed by pid
private final ParsableByteArray psPacketBuffer;
private boolean foundAllTracks;
......@@ -67,11 +67,11 @@ public final class PsExtractor implements Extractor {
private ExtractorOutput output;
public PsExtractor() {
this(new PtsTimestampAdjuster(0));
this(new TimestampAdjuster(0));
}
public PsExtractor(PtsTimestampAdjuster ptsTimestampAdjuster) {
this.ptsTimestampAdjuster = ptsTimestampAdjuster;
public PsExtractor(TimestampAdjuster timestampAdjuster) {
this.timestampAdjuster = timestampAdjuster;
psPacketBuffer = new ParsableByteArray(4096);
psPayloadReaders = new SparseArray<>();
}
......@@ -125,7 +125,7 @@ public final class PsExtractor implements Extractor {
@Override
public void seek(long position) {
ptsTimestampAdjuster.reset();
timestampAdjuster.reset();
for (int i = 0; i < psPayloadReaders.size(); i++) {
psPayloadReaders.valueAt(i).seek();
}
......@@ -199,7 +199,7 @@ public final class PsExtractor implements Extractor {
foundVideoTrack = true;
}
if (elementaryStreamReader != null) {
payloadReader = new PesReader(elementaryStreamReader, ptsTimestampAdjuster);
payloadReader = new PesReader(elementaryStreamReader, timestampAdjuster);
psPayloadReaders.put(streamId, payloadReader);
}
}
......@@ -244,7 +244,7 @@ public final class PsExtractor implements Extractor {
private static final int PES_SCRATCH_SIZE = 64;
private final ElementaryStreamReader pesPayloadReader;
private final PtsTimestampAdjuster ptsTimestampAdjuster;
private final TimestampAdjuster timestampAdjuster;
private final ParsableBitArray pesScratch;
private boolean ptsFlag;
......@@ -254,9 +254,9 @@ public final class PsExtractor implements Extractor {
private long timeUs;
public PesReader(ElementaryStreamReader pesPayloadReader,
PtsTimestampAdjuster ptsTimestampAdjuster) {
TimestampAdjuster timestampAdjuster) {
this.pesPayloadReader = pesPayloadReader;
this.ptsTimestampAdjuster = ptsTimestampAdjuster;
this.timestampAdjuster = timestampAdjuster;
pesScratch = new ParsableBitArray(new byte[PES_SCRATCH_SIZE]);
}
......@@ -327,10 +327,10 @@ public final class PsExtractor implements Extractor {
// decode timestamp to the adjuster here so that in the case that this is the first to be
// fed, the adjuster will be able to compute an offset to apply such that the adjusted
// presentation timestamps of all future packets are non-negative.
ptsTimestampAdjuster.adjustTimestamp(dts);
timestampAdjuster.adjustTsTimestamp(dts);
seenFirstDts = true;
}
timeUs = ptsTimestampAdjuster.adjustTimestamp(pts);
timeUs = timestampAdjuster.adjustTsTimestamp(pts);
}
}
......
......@@ -18,10 +18,10 @@ package com.google.android.exoplayer2.extractor.ts;
import com.google.android.exoplayer2.C;
/**
* Scales and adjusts MPEG-2 TS presentation timestamps, taking into account an initial offset and
* timestamp rollover.
* Offsets timestamps according to an initial sample timestamp offset. MPEG-2 TS timestamps scaling
* and adjustment is supported, taking into account timestamp rollover.
*/
public final class PtsTimestampAdjuster {
public final class TimestampAdjuster {
/**
* A special {@code firstSampleTimestampUs} value indicating that presentation timestamps should
......@@ -30,7 +30,7 @@ public final class PtsTimestampAdjuster {
public static final long DO_NOT_OFFSET = Long.MAX_VALUE;
/**
* The value one greater than the largest representable (33 bit) presentation timestamp.
* The value one greater than the largest representable (33 bit) MPEG-2 TS presentation timestamp.
*/
private static final long MAX_PTS_PLUS_ONE = 0x200000000L;
......@@ -38,57 +38,66 @@ public final class PtsTimestampAdjuster {
private long timestampOffsetUs;
// Volatile to allow isInitialized to be called on a different thread to adjustTimestamp.
private volatile long lastPts;
// Volatile to allow isInitialized to be called on a different thread to adjustSampleTimestamp.
private volatile long lastSampleTimestamp;
/**
* @param firstSampleTimestampUs The desired result of the first call to
* {@link #adjustTimestamp(long)}, or {@link #DO_NOT_OFFSET} if presentation timestamps
* {@link #adjustSampleTimestamp(long)}, or {@link #DO_NOT_OFFSET} if presentation timestamps
* should not be offset.
*/
public PtsTimestampAdjuster(long firstSampleTimestampUs) {
public TimestampAdjuster(long firstSampleTimestampUs) {
this.firstSampleTimestampUs = firstSampleTimestampUs;
lastPts = Long.MIN_VALUE;
lastSampleTimestamp = C.TIME_UNSET;
}
/**
* Resets the instance to its initial state.
*/
public void reset() {
lastPts = Long.MIN_VALUE;
lastSampleTimestamp = C.TIME_UNSET;
}
/**
* Whether this adjuster has been initialized with a first MPEG-2 TS presentation timestamp.
*/
public boolean isInitialized() {
return lastPts != Long.MIN_VALUE;
return lastSampleTimestamp != C.TIME_UNSET;
}
/**
* Scales and offsets an MPEG-2 TS presentation timestamp.
* Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound.
*
* @param pts The MPEG-2 TS presentation timestamp.
* @return The adjusted timestamp in microseconds.
*/
public long adjustTimestamp(long pts) {
if (lastPts != Long.MIN_VALUE) {
public long adjustTsTimestamp(long pts) {
if (lastSampleTimestamp != C.TIME_UNSET) {
// The wrap count for the current PTS may be closestWrapCount or (closestWrapCount - 1),
// and we need to snap to the one closest to lastPts.
// and we need to snap to the one closest to lastSampleTimestamp.
long lastPts = usToPts(lastSampleTimestamp);
long closestWrapCount = (lastPts + (MAX_PTS_PLUS_ONE / 2)) / MAX_PTS_PLUS_ONE;
long ptsWrapBelow = pts + (MAX_PTS_PLUS_ONE * (closestWrapCount - 1));
long ptsWrapAbove = pts + (MAX_PTS_PLUS_ONE * closestWrapCount);
pts = Math.abs(ptsWrapBelow - lastPts) < Math.abs(ptsWrapAbove - lastPts)
? ptsWrapBelow : ptsWrapAbove;
}
// Calculate the corresponding timestamp.
long timeUs = ptsToUs(pts);
if (firstSampleTimestampUs != DO_NOT_OFFSET && lastPts == Long.MIN_VALUE) {
return adjustSampleTimestamp(ptsToUs(pts));
}
/**
* Offsets a sample timestamp in microseconds.
*
* @param timeUs The timestamp of a sample to adjust.
* @return The adjusted timestamp in microseconds.
*/
public long adjustSampleTimestamp(long timeUs) {
if (firstSampleTimestampUs != DO_NOT_OFFSET && lastSampleTimestamp == C.TIME_UNSET) {
// Calculate the timestamp offset.
timestampOffsetUs = firstSampleTimestampUs - timeUs;
}
// Record the adjusted PTS to adjust for wraparound next time.
lastPts = pts;
lastSampleTimestamp = timeUs;
return timeUs + timestampOffsetUs;
}
......
......@@ -81,7 +81,7 @@ public final class TsExtractor implements Extractor {
private static final int BUFFER_PACKET_COUNT = 5; // Should be at least 2
private static final int BUFFER_SIZE = TS_PACKET_SIZE * BUFFER_PACKET_COUNT;
private final PtsTimestampAdjuster ptsTimestampAdjuster;
private final TimestampAdjuster timestampAdjuster;
private final int workaroundFlags;
private final ParsableByteArray tsPacketBuffer;
private final ParsableBitArray tsScratch;
......@@ -94,15 +94,15 @@ public final class TsExtractor implements Extractor {
/* package */ Id3Reader id3Reader;
public TsExtractor() {
this(new PtsTimestampAdjuster(0));
this(new TimestampAdjuster(0));
}
public TsExtractor(PtsTimestampAdjuster ptsTimestampAdjuster) {
this(ptsTimestampAdjuster, 0);
public TsExtractor(TimestampAdjuster timestampAdjuster) {
this(timestampAdjuster, 0);
}
public TsExtractor(PtsTimestampAdjuster ptsTimestampAdjuster, int workaroundFlags) {
this.ptsTimestampAdjuster = ptsTimestampAdjuster;
public TsExtractor(TimestampAdjuster timestampAdjuster, int workaroundFlags) {
this.timestampAdjuster = timestampAdjuster;
this.workaroundFlags = workaroundFlags;
tsPacketBuffer = new ParsableByteArray(BUFFER_SIZE);
tsScratch = new ParsableBitArray(new byte[3]);
......@@ -140,7 +140,7 @@ public final class TsExtractor implements Extractor {
@Override
public void seek(long position) {
ptsTimestampAdjuster.reset();
timestampAdjuster.reset();
for (int i = 0; i < tsPayloadReaders.size(); i++) {
tsPayloadReaders.valueAt(i).seek();
}
......@@ -486,7 +486,7 @@ public final class TsExtractor implements Extractor {
if (pesPayloadReader != null) {
trackIds.put(trackId, true);
tsPayloadReaders.put(elementaryPid,
new PesReader(pesPayloadReader, ptsTimestampAdjuster));
new PesReader(pesPayloadReader, timestampAdjuster));
}
}
......@@ -554,7 +554,7 @@ public final class TsExtractor implements Extractor {
private static final int PES_SCRATCH_SIZE = 10; // max(HEADER_SIZE, MAX_HEADER_EXTENSION_SIZE)
private final ElementaryStreamReader pesPayloadReader;
private final PtsTimestampAdjuster ptsTimestampAdjuster;
private final TimestampAdjuster timestampAdjuster;
private final ParsableBitArray pesScratch;
private int state;
......@@ -569,9 +569,9 @@ public final class TsExtractor implements Extractor {
private long timeUs;
public PesReader(ElementaryStreamReader pesPayloadReader,
PtsTimestampAdjuster ptsTimestampAdjuster) {
TimestampAdjuster timestampAdjuster) {
this.pesPayloadReader = pesPayloadReader;
this.ptsTimestampAdjuster = ptsTimestampAdjuster;
this.timestampAdjuster = timestampAdjuster;
pesScratch = new ParsableBitArray(new byte[PES_SCRATCH_SIZE]);
state = STATE_FINDING_HEADER;
}
......@@ -734,10 +734,10 @@ public final class TsExtractor implements Extractor {
// decode timestamp to the adjuster here so that in the case that this is the first to be
// fed, the adjuster will be able to compute an offset to apply such that the adjusted
// presentation timestamps of all future packets are non-negative.
ptsTimestampAdjuster.adjustTimestamp(dts);
timestampAdjuster.adjustTsTimestamp(dts);
seenFirstDts = true;
}
timeUs = ptsTimestampAdjuster.adjustTimestamp(pts);
timeUs = timestampAdjuster.adjustTsTimestamp(pts);
}
}
......
......@@ -24,7 +24,7 @@ import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
import com.google.android.exoplayer2.extractor.ts.PtsTimestampAdjuster;
import com.google.android.exoplayer2.extractor.ts.TimestampAdjuster;
import com.google.android.exoplayer2.extractor.ts.TsExtractor;
import com.google.android.exoplayer2.source.BehindLiveWindowException;
import com.google.android.exoplayer2.source.TrackGroup;
......@@ -107,7 +107,7 @@ import java.util.Locale;
private final String baseUri;
private final DataSource dataSource;
private final HlsPlaylistParser playlistParser;
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
private final TimestampAdjusterProvider timestampAdjusterProvider;
private final HlsMasterPlaylist.HlsUrl[] variants;
private final HlsMediaPlaylist[] variantPlaylists;
private final TrackGroup trackGroup;
......@@ -132,12 +132,12 @@ import java.util.Locale;
* @param baseUri The playlist's base uri.
* @param variants The available variants.
* @param dataSource A {@link DataSource} suitable for loading the media data.
* @param timestampAdjusterProvider A provider of {@link PtsTimestampAdjuster} instances. If
* @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
* same provider.
*/
public HlsChunkSource(String baseUri, HlsMasterPlaylist.HlsUrl[] variants, DataSource dataSource,
PtsTimestampAdjusterProvider timestampAdjusterProvider) {
TimestampAdjusterProvider timestampAdjusterProvider) {
this.baseUri = baseUri;
this.variants = variants;
this.dataSource = dataSource;
......@@ -343,7 +343,7 @@ import java.util.Locale;
extractor = new Mp3Extractor(startTimeUs);
} else if (lastPathSegment.endsWith(WEBVTT_FILE_EXTENSION)
|| lastPathSegment.endsWith(VTT_FILE_EXTENSION)) {
PtsTimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(false,
TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(false,
segment.discontinuitySequenceNumber, startTimeUs);
if (timestampAdjuster == null) {
// The master source has yet to instantiate an adjuster for the discontinuity sequence.
......@@ -356,7 +356,7 @@ import java.util.Locale;
|| previous.discontinuitySequenceNumber != segment.discontinuitySequenceNumber
|| format != previous.trackFormat) {
// MPEG-2 TS segments, but we need a new extractor.
PtsTimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(true,
TimestampAdjuster timestampAdjuster = timestampAdjusterProvider.getAdjuster(true,
segment.discontinuitySequenceNumber, startTimeUs);
if (timestampAdjuster == null) {
// The master source has yet to instantiate an adjuster for the discontinuity sequence.
......
......@@ -57,7 +57,7 @@ import java.util.List;
private final Callback callback;
private final Allocator allocator;
private final IdentityHashMap<SampleStream, Integer> streamWrapperIndices;
private final PtsTimestampAdjusterProvider timestampAdjusterProvider;
private final TimestampAdjusterProvider timestampAdjusterProvider;
private final HlsPlaylistParser manifestParser;
private final Handler continueLoadingHandler;
private final Loader manifestFetcher;
......@@ -85,7 +85,7 @@ import java.util.List;
this.callback = callback;
this.allocator = allocator;
streamWrapperIndices = new IdentityHashMap<>();
timestampAdjusterProvider = new PtsTimestampAdjusterProvider();
timestampAdjusterProvider = new TimestampAdjusterProvider();
manifestParser = new HlsPlaylistParser();
continueLoadingHandler = new Handler();
manifestFetcher = new Loader("Loader:ManifestFetcher");
......
......@@ -16,23 +16,23 @@
package com.google.android.exoplayer2.source.hls;
import android.util.SparseArray;
import com.google.android.exoplayer2.extractor.ts.PtsTimestampAdjuster;
import com.google.android.exoplayer2.extractor.ts.TimestampAdjuster;
/**
* Provides {@link PtsTimestampAdjuster} instances for use during HLS playbacks.
* Provides {@link TimestampAdjuster} instances for use during HLS playbacks.
*/
public final class PtsTimestampAdjusterProvider {
public final class TimestampAdjusterProvider {
// TODO: Prevent this array from growing indefinitely large by removing adjusters that are no
// longer required.
private final SparseArray<PtsTimestampAdjuster> ptsTimestampAdjusters;
private final SparseArray<TimestampAdjuster> timestampAdjusters;
public PtsTimestampAdjusterProvider() {
ptsTimestampAdjusters = new SparseArray<>();
public TimestampAdjusterProvider() {
timestampAdjusters = new SparseArray<>();
}
/**
* Returns a {@link PtsTimestampAdjuster} suitable for adjusting the pts timestamps contained in
* Returns a {@link TimestampAdjuster} suitable for adjusting the pts timestamps contained in
* a chunk with a given discontinuity sequence.
* <p>
* This method may return null if the master source has yet to initialize a suitable adjuster.
......@@ -40,14 +40,14 @@ public final class PtsTimestampAdjusterProvider {
* @param isMasterSource True if the calling chunk source is the master.
* @param discontinuitySequence The chunk's discontinuity sequence.
* @param startTimeUs The chunk's start time.
* @return A {@link PtsTimestampAdjuster}.
* @return A {@link TimestampAdjuster}.
*/
public PtsTimestampAdjuster getAdjuster(boolean isMasterSource, int discontinuitySequence,
public TimestampAdjuster getAdjuster(boolean isMasterSource, int discontinuitySequence,
long startTimeUs) {
PtsTimestampAdjuster adjuster = ptsTimestampAdjusters.get(discontinuitySequence);
TimestampAdjuster adjuster = timestampAdjusters.get(discontinuitySequence);
if (isMasterSource && adjuster == null) {
adjuster = new PtsTimestampAdjuster(startTimeUs);
ptsTimestampAdjusters.put(discontinuitySequence, adjuster);
adjuster = new TimestampAdjuster(startTimeUs);
timestampAdjusters.put(discontinuitySequence, adjuster);
}
return isMasterSource || (adjuster != null && adjuster.isInitialized()) ? adjuster : null;
}
......@@ -56,7 +56,7 @@ public final class PtsTimestampAdjusterProvider {
* Resets the provider.
*/
public void reset() {
ptsTimestampAdjusters.clear();
timestampAdjusters.clear();
}
}
......@@ -25,7 +25,7 @@ import com.google.android.exoplayer2.extractor.ExtractorOutput;
import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.extractor.TrackOutput;
import com.google.android.exoplayer2.extractor.ts.PtsTimestampAdjuster;
import com.google.android.exoplayer2.extractor.ts.TimestampAdjuster;
import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.text.webvtt.WebvttParserUtil;
import com.google.android.exoplayer2.util.MimeTypes;
......@@ -49,7 +49,7 @@ import java.util.regex.Pattern;
private static final Pattern MEDIA_TIMESTAMP = Pattern.compile("MPEGTS:(\\d+)");
private final String language;
private final PtsTimestampAdjuster ptsTimestampAdjuster;
private final TimestampAdjuster timestampAdjuster;
private final ParsableByteArray sampleDataWrapper;
private ExtractorOutput output;
......@@ -57,9 +57,9 @@ import java.util.regex.Pattern;
private byte[] sampleData;
private int sampleSize;
public WebvttExtractor(String language, PtsTimestampAdjuster ptsTimestampAdjuster) {
public WebvttExtractor(String language, TimestampAdjuster timestampAdjuster) {
this.language = language;
this.ptsTimestampAdjuster = ptsTimestampAdjuster;
this.timestampAdjuster = timestampAdjuster;
this.sampleDataWrapper = new ParsableByteArray();
sampleData = new byte[1024];
}
......@@ -141,7 +141,7 @@ import java.util.regex.Pattern;
throw new ParserException("X-TIMESTAMP-MAP doesn't contain media timestamp: " + line);
}
vttTimestampUs = WebvttParserUtil.parseTimestampUs(localTimestampMatcher.group(1));
tsTimestampUs = PtsTimestampAdjuster.ptsToUs(
tsTimestampUs = TimestampAdjuster.ptsToUs(
Long.parseLong(mediaTimestampMatcher.group(1)));
}
}
......@@ -155,8 +155,8 @@ import java.util.regex.Pattern;
}
long firstCueTimeUs = WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(1));
long sampleTimeUs = ptsTimestampAdjuster.adjustTimestamp(
PtsTimestampAdjuster.usToPts(firstCueTimeUs + tsTimestampUs - vttTimestampUs));
long sampleTimeUs = timestampAdjuster.adjustSampleTimestamp(
firstCueTimeUs + tsTimestampUs - vttTimestampUs);
long subsampleOffsetUs = sampleTimeUs - firstCueTimeUs;
// Output the track.
TrackOutput trackOutput = buildTrackOutput(subsampleOffsetUs);
......
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