Fix SCTE-35 timestamp adjustment

Issue:#4573

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=206737252
This commit is contained in:
aquilescanta 2018-07-31 04:06:22 -07:00 committed by Oliver Woodman
parent e2bf474c1e
commit 9ec14d1ada
2 changed files with 44 additions and 43 deletions

View File

@ -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:

View File

@ -30,7 +30,8 @@ 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;
@ -38,13 +39,13 @@ public final class TimestampAdjuster {
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);
}
@ -56,30 +57,24 @@ public final class TimestampAdjuster {
* {@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;
}
@ -93,44 +88,47 @@ public final class TimestampAdjuster {
* 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) {
@ -138,15 +136,15 @@ public final class TimestampAdjuster {
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();
}
@ -160,15 +158,15 @@ public final class TimestampAdjuster {
* @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) {
@ -176,10 +174,10 @@ public final class TimestampAdjuster {
}
/**
* 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;