diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java index 849dc90cf2..ff86be4165 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java @@ -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 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); } } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PtsTimestampAdjuster.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TimestampAdjuster.java similarity index 69% rename from library/src/main/java/com/google/android/exoplayer2/extractor/ts/PtsTimestampAdjuster.java rename to library/src/main/java/com/google/android/exoplayer2/extractor/ts/TimestampAdjuster.java index aaed6907d3..9aa35847c6 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/PtsTimestampAdjuster.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TimestampAdjuster.java @@ -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; } diff --git a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java index be63658a13..77d4168558 100644 --- a/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java @@ -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); } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java index 476917d8e7..ada93c07a0 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java @@ -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. diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java index 78e85843ea..2530a18629 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java @@ -57,7 +57,7 @@ import java.util.List; private final Callback callback; private final Allocator allocator; private final IdentityHashMap 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"); diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/PtsTimestampAdjusterProvider.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/TimestampAdjusterProvider.java similarity index 63% rename from library/src/main/java/com/google/android/exoplayer2/source/hls/PtsTimestampAdjusterProvider.java rename to library/src/main/java/com/google/android/exoplayer2/source/hls/TimestampAdjusterProvider.java index 3196d79e03..248c8ed0e7 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/PtsTimestampAdjusterProvider.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/TimestampAdjusterProvider.java @@ -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 ptsTimestampAdjusters; + private final SparseArray 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. *

* 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(); } } diff --git a/library/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java b/library/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java index 8ccb2b84ce..68b489c72b 100644 --- a/library/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java +++ b/library/src/main/java/com/google/android/exoplayer2/source/hls/WebvttExtractor.java @@ -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);