summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/TimestampAdjuster.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/TimestampAdjuster.java')
-rw-r--r--mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/TimestampAdjuster.java186
1 files changed, 186 insertions, 0 deletions
diff --git a/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/TimestampAdjuster.java b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/TimestampAdjuster.java
new file mode 100644
index 0000000000..e824251282
--- /dev/null
+++ b/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/util/TimestampAdjuster.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.mozilla.thirdparty.com.google.android.exoplayer2.util;
+
+import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
+
+/**
+ * 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 TimestampAdjuster {
+
+ /**
+ * A special {@code firstSampleTimestampUs} value indicating that presentation timestamps should
+ * not be offset.
+ */
+ public static final long DO_NOT_OFFSET = Long.MAX_VALUE;
+
+ /**
+ * The value one greater than the largest representable (33 bit) MPEG-2 TS 90 kHz clock
+ * presentation timestamp.
+ */
+ private static final long MAX_PTS_PLUS_ONE = 0x200000000L;
+
+ private long firstSampleTimestampUs;
+ private long timestampOffsetUs;
+
+ // Volatile to allow isInitialized to be called on a different thread to adjustSampleTimestamp.
+ private volatile long lastSampleTimestampUs;
+
+ /**
+ * @param firstSampleTimestampUs See {@link #setFirstSampleTimestampUs(long)}.
+ */
+ public TimestampAdjuster(long firstSampleTimestampUs) {
+ lastSampleTimestampUs = C.TIME_UNSET;
+ setFirstSampleTimestampUs(firstSampleTimestampUs);
+ }
+
+ /**
+ * Sets the desired result of the first call to {@link #adjustSampleTimestamp(long)}. Can only be
+ * called before any timestamps have been adjusted.
+ *
+ * @param firstSampleTimestampUs The first adjusted sample timestamp in microseconds, or
+ * {@link #DO_NOT_OFFSET} if presentation timestamps should not be offset.
+ */
+ public synchronized void setFirstSampleTimestampUs(long firstSampleTimestampUs) {
+ Assertions.checkState(lastSampleTimestampUs == C.TIME_UNSET);
+ this.firstSampleTimestampUs = firstSampleTimestampUs;
+ }
+
+ /** Returns the last value passed to {@link #setFirstSampleTimestampUs(long)}. */
+ public long getFirstSampleTimestampUs() {
+ return firstSampleTimestampUs;
+ }
+
+ /**
+ * Returns the last value obtained from {@link #adjustSampleTimestamp}. If {@link
+ * #adjustSampleTimestamp} has not been called, returns the result of calling {@link
+ * #getFirstSampleTimestampUs()}. If this value is {@link #DO_NOT_OFFSET}, returns {@link
+ * C#TIME_UNSET}.
+ */
+ public long getLastAdjustedTimestampUs() {
+ return lastSampleTimestampUs != C.TIME_UNSET
+ ? (lastSampleTimestampUs + timestampOffsetUs)
+ : firstSampleTimestampUs != DO_NOT_OFFSET ? firstSampleTimestampUs : C.TIME_UNSET;
+ }
+
+ /**
+ * Returns the offset between the input of {@link #adjustSampleTimestamp(long)} and its output.
+ * If {@link #DO_NOT_OFFSET} was provided to the constructor, 0 is returned. If the timestamp
+ * adjuster is yet not initialized, {@link C#TIME_UNSET} is returned.
+ *
+ * @return The offset between {@link #adjustSampleTimestamp(long)}'s input and output.
+ * {@link C#TIME_UNSET} if the adjuster is not yet initialized and 0 if timestamps should not
+ * be offset.
+ */
+ public long getTimestampOffsetUs() {
+ return firstSampleTimestampUs == DO_NOT_OFFSET
+ ? 0
+ : lastSampleTimestampUs == C.TIME_UNSET ? C.TIME_UNSET : timestampOffsetUs;
+ }
+
+ /**
+ * Resets the instance to its initial state.
+ */
+ public void reset() {
+ lastSampleTimestampUs = C.TIME_UNSET;
+ }
+
+ /**
+ * Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound.
+ *
+ * @param pts90Khz A 90 kHz clock MPEG-2 TS presentation timestamp.
+ * @return The adjusted timestamp in microseconds.
+ */
+ public long adjustTsTimestamp(long pts90Khz) {
+ if (pts90Khz == C.TIME_UNSET) {
+ return C.TIME_UNSET;
+ }
+ if (lastSampleTimestampUs != 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 lastSampleTimestampUs.
+ long lastPts = usToPts(lastSampleTimestampUs);
+ long closestWrapCount = (lastPts + (MAX_PTS_PLUS_ONE / 2)) / MAX_PTS_PLUS_ONE;
+ long ptsWrapBelow = pts90Khz + (MAX_PTS_PLUS_ONE * (closestWrapCount - 1));
+ long ptsWrapAbove = pts90Khz + (MAX_PTS_PLUS_ONE * closestWrapCount);
+ pts90Khz =
+ Math.abs(ptsWrapBelow - lastPts) < Math.abs(ptsWrapAbove - lastPts)
+ ? ptsWrapBelow
+ : ptsWrapAbove;
+ }
+ return adjustSampleTimestamp(ptsToUs(pts90Khz));
+ }
+
+ /**
+ * Offsets a timestamp in microseconds.
+ *
+ * @param timeUs The timestamp to adjust in microseconds.
+ * @return The adjusted timestamp in microseconds.
+ */
+ public long adjustSampleTimestamp(long timeUs) {
+ if (timeUs == C.TIME_UNSET) {
+ return C.TIME_UNSET;
+ }
+ // Record the adjusted PTS to adjust for wraparound next time.
+ if (lastSampleTimestampUs != C.TIME_UNSET) {
+ lastSampleTimestampUs = timeUs;
+ } else {
+ if (firstSampleTimestampUs != DO_NOT_OFFSET) {
+ // Calculate the timestamp offset.
+ timestampOffsetUs = firstSampleTimestampUs - timeUs;
+ }
+ synchronized (this) {
+ lastSampleTimestampUs = timeUs;
+ // Notify threads waiting for this adjuster to be initialized.
+ notifyAll();
+ }
+ }
+ return timeUs + timestampOffsetUs;
+ }
+
+ /**
+ * Blocks the calling thread until this adjuster is initialized.
+ *
+ * @throws InterruptedException If the thread was interrupted.
+ */
+ public synchronized void waitUntilInitialized() throws InterruptedException {
+ while (lastSampleTimestampUs == C.TIME_UNSET) {
+ wait();
+ }
+ }
+
+ /**
+ * Converts a 90 kHz clock timestamp to a timestamp in microseconds.
+ *
+ * @param pts A 90 kHz clock timestamp.
+ * @return The corresponding value in microseconds.
+ */
+ public static long ptsToUs(long pts) {
+ return (pts * C.MICROS_PER_SECOND) / 90000;
+ }
+
+ /**
+ * Converts a timestamp in microseconds to a 90 kHz clock timestamp.
+ *
+ * @param us A value in microseconds.
+ * @return The corresponding value as a 90 kHz clock timestamp.
+ */
+ public static long usToPts(long us) {
+ return (us * 90000) / C.MICROS_PER_SECOND;
+ }
+
+}