mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Fix SCTE-35 timestamp adjustment
Issue:#4573 ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=206737252
This commit is contained in:
parent
e2bf474c1e
commit
9ec14d1ada
@ -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:
|
||||||
|
@ -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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user