/*
* Copyright (C) 2014 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 com.google.android.exoplayer.extractor.ts;
import com.google.android.exoplayer.C;
/**
* Scales and adjusts MPEG-2 TS presentation timestamps, taking into account an initial offset and
* timestamp rollover.
*/
public final class PtsTimestampAdjuster {
/**
* The value one greater than the largest representable (33 bit) presentation timestamp.
*/
private static final long MAX_PTS_PLUS_ONE = 0x200000000L;
private final long firstSampleTimestampUs;
private long timestampOffsetUs;
private long lastPts;
/**
* @param firstSampleTimestampUs The desired result of the first call to
* {@link #adjustTimestamp(long)}.
*/
public PtsTimestampAdjuster(long firstSampleTimestampUs) {
this.firstSampleTimestampUs = firstSampleTimestampUs;
lastPts = Long.MIN_VALUE;
}
/**
* Resets the instance to its initial state.
*/
public void reset() {
lastPts = Long.MIN_VALUE;
}
/**
* Scales and adjusts an MPEG-2 TS presentation timestamp.
*
* @param pts The unscaled MPEG-2 TS presentation timestamp.
* @return The adjusted timestamp in microseconds.
*/
public long adjustTimestamp(long pts) {
if (lastPts != Long.MIN_VALUE) {
// The wrap count for the current PTS may be closestWrapCount or (closestWrapCount - 1),
// and we need to snap to the one closest to lastPts.
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 = (pts * C.MICROS_PER_SECOND) / 90000;
// If we haven't done the initial timestamp adjustment, do it now.
if (lastPts == Long.MIN_VALUE) {
timestampOffsetUs = firstSampleTimestampUs - timeUs;
}
// Record the adjusted PTS to adjust for wraparound next time.
lastPts = pts;
return timeUs + timestampOffsetUs;
}
}