Skip to content

Commit

Permalink
Fix SCTE-35 timestamp adjustment
Browse files Browse the repository at this point in the history
Issue:#4573

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=206737252
  • Loading branch information
AquilesCanta authored and ojw28 committed Aug 1, 2018
1 parent e2bf474 commit 9ec14d1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 43 deletions.
7 changes: 5 additions & 2 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@

* Add `AudioListener` for listening to changes in audio configuration during
playback ([#3994](https://github.com/google/ExoPlayer/issues/3994)).
* MPEG-TS: Support CEA-608/708 in H262
([#2565](https://github.com/google/ExoPlayer/issues/2565)).
* MPEG-TS:
* Support CEA-608/708 in H262
([#2565](https://github.com/google/ExoPlayer/issues/2565)).
* Fix bug preventing SCTE-35 cues from being output
([#4573](https://github.com/google/ExoPlayer/issues/4573)).
* MPEG-PS: Support reading duration from MPEG-PS Streams
([#4476](https://github.com/google/ExoPlayer/issues/4476)).
* MediaSession extension:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,22 @@ public final class TimestampAdjuster {
public static final long DO_NOT_OFFSET = Long.MAX_VALUE;

/**
* The value one greater than the largest representable (33 bit) MPEG-2 TS presentation timestamp.
* 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 lastSampleTimestamp;
private volatile long lastSampleTimestampUs;

/**
* @param firstSampleTimestampUs See {@link #setFirstSampleTimestampUs(long)}.
*/
public TimestampAdjuster(long firstSampleTimestampUs) {
lastSampleTimestamp = C.TIME_UNSET;
lastSampleTimestampUs = C.TIME_UNSET;
setFirstSampleTimestampUs(firstSampleTimestampUs);
}

Expand All @@ -56,30 +57,24 @@ public TimestampAdjuster(long firstSampleTimestampUs) {
* {@link #DO_NOT_OFFSET} if presentation timestamps should not be offset.
*/
public synchronized void setFirstSampleTimestampUs(long firstSampleTimestampUs) {
Assertions.checkState(lastSampleTimestamp == C.TIME_UNSET);
Assertions.checkState(lastSampleTimestampUs == C.TIME_UNSET);
this.firstSampleTimestampUs = firstSampleTimestampUs;
}

/**
* Returns the first adjusted sample timestamp in microseconds.
*
* @return The first adjusted sample timestamp in microseconds.
*/
/** Returns the last value passed to {@link #setFirstSampleTimestampUs(long)}. */
public long getFirstSampleTimestampUs() {
return firstSampleTimestampUs;
}

/**
* Returns the last adjusted timestamp. If no timestamp has been adjusted, returns
* {@code firstSampleTimestampUs} as provided to the constructor. If this value is
* {@link #DO_NOT_OFFSET}, returns {@link C#TIME_UNSET}.
*
* @return The last adjusted timestamp. If not present, {@code firstSampleTimestampUs} is
* returned unless equal to {@link #DO_NOT_OFFSET}, in which case {@link C#TIME_UNSET} is
* returned.
* 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 lastSampleTimestamp != C.TIME_UNSET ? lastSampleTimestamp
return lastSampleTimestampUs != C.TIME_UNSET
? (lastSampleTimestampUs + timestampOffsetUs)
: firstSampleTimestampUs != DO_NOT_OFFSET ? firstSampleTimestampUs : C.TIME_UNSET;
}

Expand All @@ -93,60 +88,63 @@ public long getLastAdjustedTimestampUs() {
* be offset.
*/
public long getTimestampOffsetUs() {
return firstSampleTimestampUs == DO_NOT_OFFSET ? 0
: lastSampleTimestamp == C.TIME_UNSET ? C.TIME_UNSET : timestampOffsetUs;
return firstSampleTimestampUs == DO_NOT_OFFSET
? 0
: lastSampleTimestampUs == C.TIME_UNSET ? C.TIME_UNSET : timestampOffsetUs;
}

/**
* Resets the instance to its initial state.
*/
public void reset() {
lastSampleTimestamp = C.TIME_UNSET;
lastSampleTimestampUs = C.TIME_UNSET;
}

/**
* Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound.
*
* @param pts The MPEG-2 TS presentation timestamp.
* @param pts90Khz A 90 kHz clock MPEG-2 TS presentation timestamp.
* @return The adjusted timestamp in microseconds.
*/
public long adjustTsTimestamp(long pts) {
if (pts == C.TIME_UNSET) {
public long adjustTsTimestamp(long pts90Khz) {
if (pts90Khz == C.TIME_UNSET) {
return C.TIME_UNSET;
}
if (lastSampleTimestamp != 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 lastSampleTimestamp.
long lastPts = usToPts(lastSampleTimestamp);
// 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 = 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;
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(pts));
return adjustSampleTimestamp(ptsToUs(pts90Khz));
}

/**
* Offsets a sample timestamp in microseconds.
* Offsets a timestamp in microseconds.
*
* @param timeUs The timestamp of a sample to adjust.
* @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 (lastSampleTimestamp != C.TIME_UNSET) {
lastSampleTimestamp = timeUs;
if (lastSampleTimestampUs != C.TIME_UNSET) {
lastSampleTimestampUs = timeUs;
} else {
if (firstSampleTimestampUs != DO_NOT_OFFSET) {
// Calculate the timestamp offset.
timestampOffsetUs = firstSampleTimestampUs - timeUs;
}
synchronized (this) {
lastSampleTimestamp = timeUs;
lastSampleTimestampUs = timeUs;
// Notify threads waiting for this adjuster to be initialized.
notifyAll();
}
Expand All @@ -160,26 +158,26 @@ public long adjustSampleTimestamp(long timeUs) {
* @throws InterruptedException If the thread was interrupted.
*/
public synchronized void waitUntilInitialized() throws InterruptedException {
while (lastSampleTimestamp == C.TIME_UNSET) {
while (lastSampleTimestampUs == C.TIME_UNSET) {
wait();
}
}

/**
* Converts a value in MPEG-2 timestamp units to the corresponding value in microseconds.
* Converts a 90 kHz clock timestamp to a timestamp in microseconds.
*
* @param pts A value in MPEG-2 timestamp units.
* @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 value in microseconds to the corresponding values in MPEG-2 timestamp units.
* Converts a timestamp in microseconds to a 90 kHz clock timestamp.
*
* @param us A value in microseconds.
* @return The corresponding value in MPEG-2 timestamp units.
* @return The corresponding value as a 90 kHz clock timestamp.
*/
public static long usToPts(long us) {
return (us * 90000) / C.MICROS_PER_SECOND;
Expand Down

0 comments on commit 9ec14d1

Please sign in to comment.