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 * Add `AudioListener` for listening to changes in audio configuration during
playback ([#3994](https://github.com/google/ExoPlayer/issues/3994)). playback ([#3994](https://github.com/google/ExoPlayer/issues/3994)).
* MPEG-TS: Support CEA-608/708 in H262 * MPEG-TS:
* Support CEA-608/708 in H262
([#2565](https://github.com/google/ExoPlayer/issues/2565)). ([#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 * MPEG-PS: Support reading duration from MPEG-PS Streams
([#4476](https://github.com/google/ExoPlayer/issues/4476)). ([#4476](https://github.com/google/ExoPlayer/issues/4476)).
* MediaSession extension: * MediaSession extension:

View File

@ -30,7 +30,8 @@ public final class TimestampAdjuster {
public static final long DO_NOT_OFFSET = Long.MAX_VALUE; 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 static final long MAX_PTS_PLUS_ONE = 0x200000000L;
@ -38,13 +39,13 @@ public final class TimestampAdjuster {
private long timestampOffsetUs; private long timestampOffsetUs;
// Volatile to allow isInitialized to be called on a different thread to adjustSampleTimestamp. // 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)}. * @param firstSampleTimestampUs See {@link #setFirstSampleTimestampUs(long)}.
*/ */
public TimestampAdjuster(long firstSampleTimestampUs) { public TimestampAdjuster(long firstSampleTimestampUs) {
lastSampleTimestamp = C.TIME_UNSET; lastSampleTimestampUs = C.TIME_UNSET;
setFirstSampleTimestampUs(firstSampleTimestampUs); setFirstSampleTimestampUs(firstSampleTimestampUs);
} }
@ -56,30 +57,24 @@ public final class TimestampAdjuster {
* {@link #DO_NOT_OFFSET} if presentation timestamps should not be offset. * {@link #DO_NOT_OFFSET} if presentation timestamps should not be offset.
*/ */
public synchronized void setFirstSampleTimestampUs(long firstSampleTimestampUs) { public synchronized void setFirstSampleTimestampUs(long firstSampleTimestampUs) {
Assertions.checkState(lastSampleTimestamp == C.TIME_UNSET); Assertions.checkState(lastSampleTimestampUs == C.TIME_UNSET);
this.firstSampleTimestampUs = firstSampleTimestampUs; this.firstSampleTimestampUs = firstSampleTimestampUs;
} }
/** /** Returns the last value passed to {@link #setFirstSampleTimestampUs(long)}. */
* Returns the first adjusted sample timestamp in microseconds.
*
* @return The first adjusted sample timestamp in microseconds.
*/
public long getFirstSampleTimestampUs() { public long getFirstSampleTimestampUs() {
return firstSampleTimestampUs; return firstSampleTimestampUs;
} }
/** /**
* Returns the last adjusted timestamp. If no timestamp has been adjusted, returns * Returns the last value obtained from {@link #adjustSampleTimestamp}. If {@link
* {@code firstSampleTimestampUs} as provided to the constructor. If this value is * #adjustSampleTimestamp} has not been called, returns the result of calling {@link
* {@link #DO_NOT_OFFSET}, returns {@link C#TIME_UNSET}. * #getFirstSampleTimestampUs()}. 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.
*/ */
public long getLastAdjustedTimestampUs() { public long getLastAdjustedTimestampUs() {
return lastSampleTimestamp != C.TIME_UNSET ? lastSampleTimestamp return lastSampleTimestampUs != C.TIME_UNSET
? (lastSampleTimestampUs + timestampOffsetUs)
: firstSampleTimestampUs != DO_NOT_OFFSET ? firstSampleTimestampUs : C.TIME_UNSET; : firstSampleTimestampUs != DO_NOT_OFFSET ? firstSampleTimestampUs : C.TIME_UNSET;
} }
@ -93,44 +88,47 @@ public final class TimestampAdjuster {
* be offset. * be offset.
*/ */
public long getTimestampOffsetUs() { public long getTimestampOffsetUs() {
return firstSampleTimestampUs == DO_NOT_OFFSET ? 0 return firstSampleTimestampUs == DO_NOT_OFFSET
: lastSampleTimestamp == C.TIME_UNSET ? C.TIME_UNSET : timestampOffsetUs; ? 0
: lastSampleTimestampUs == C.TIME_UNSET ? C.TIME_UNSET : timestampOffsetUs;
} }
/** /**
* Resets the instance to its initial state. * Resets the instance to its initial state.
*/ */
public void reset() { public void reset() {
lastSampleTimestamp = C.TIME_UNSET; lastSampleTimestampUs = C.TIME_UNSET;
} }
/** /**
* Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound. * 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. * @return The adjusted timestamp in microseconds.
*/ */
public long adjustTsTimestamp(long pts) { public long adjustTsTimestamp(long pts90Khz) {
if (pts == C.TIME_UNSET) { if (pts90Khz == C.TIME_UNSET) {
return 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), // The wrap count for the current PTS may be closestWrapCount or (closestWrapCount - 1),
// and we need to snap to the one closest to lastSampleTimestamp. // and we need to snap to the one closest to lastSampleTimestampUs.
long lastPts = usToPts(lastSampleTimestamp); long lastPts = usToPts(lastSampleTimestampUs);
long closestWrapCount = (lastPts + (MAX_PTS_PLUS_ONE / 2)) / MAX_PTS_PLUS_ONE; long closestWrapCount = (lastPts + (MAX_PTS_PLUS_ONE / 2)) / MAX_PTS_PLUS_ONE;
long ptsWrapBelow = pts + (MAX_PTS_PLUS_ONE * (closestWrapCount - 1)); long ptsWrapBelow = pts90Khz + (MAX_PTS_PLUS_ONE * (closestWrapCount - 1));
long ptsWrapAbove = pts + (MAX_PTS_PLUS_ONE * closestWrapCount); long ptsWrapAbove = pts90Khz + (MAX_PTS_PLUS_ONE * closestWrapCount);
pts = Math.abs(ptsWrapBelow - lastPts) < Math.abs(ptsWrapAbove - lastPts) pts90Khz =
? ptsWrapBelow : ptsWrapAbove; 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. * @return The adjusted timestamp in microseconds.
*/ */
public long adjustSampleTimestamp(long timeUs) { public long adjustSampleTimestamp(long timeUs) {
@ -138,15 +136,15 @@ public final class TimestampAdjuster {
return C.TIME_UNSET; return C.TIME_UNSET;
} }
// Record the adjusted PTS to adjust for wraparound next time. // Record the adjusted PTS to adjust for wraparound next time.
if (lastSampleTimestamp != C.TIME_UNSET) { if (lastSampleTimestampUs != C.TIME_UNSET) {
lastSampleTimestamp = timeUs; lastSampleTimestampUs = timeUs;
} else { } else {
if (firstSampleTimestampUs != DO_NOT_OFFSET) { if (firstSampleTimestampUs != DO_NOT_OFFSET) {
// Calculate the timestamp offset. // Calculate the timestamp offset.
timestampOffsetUs = firstSampleTimestampUs - timeUs; timestampOffsetUs = firstSampleTimestampUs - timeUs;
} }
synchronized (this) { synchronized (this) {
lastSampleTimestamp = timeUs; lastSampleTimestampUs = timeUs;
// Notify threads waiting for this adjuster to be initialized. // Notify threads waiting for this adjuster to be initialized.
notifyAll(); notifyAll();
} }
@ -160,15 +158,15 @@ public final class TimestampAdjuster {
* @throws InterruptedException If the thread was interrupted. * @throws InterruptedException If the thread was interrupted.
*/ */
public synchronized void waitUntilInitialized() throws InterruptedException { public synchronized void waitUntilInitialized() throws InterruptedException {
while (lastSampleTimestamp == C.TIME_UNSET) { while (lastSampleTimestampUs == C.TIME_UNSET) {
wait(); 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. * @return The corresponding value in microseconds.
*/ */
public static long ptsToUs(long pts) { 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. * @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) { public static long usToPts(long us) {
return (us * 90000) / C.MICROS_PER_SECOND; return (us * 90000) / C.MICROS_PER_SECOND;