Use different wraparound assumptions for duration readers
The timestamp adjuster also estimates the number of wraparounds of the 90Khz TS timestamp. It does that by assuming that a new timestamp is always close to the previous one (in either direction). This logic doesn't always work for duration estimates because the timestamp at the end of the media is not close to the one at the beginning and it may also never be less than the one at the beginning. This can be fixed by introducing a new estimation model that assumes the new timestamp is strictly greater than the previous one without making the assumption that it has to be close to it. Issue: androidx/media#855 #minor-release PiperOrigin-RevId: 590936953
This commit is contained in:
parent
4974f960e7
commit
01578780a6
@ -58,6 +58,8 @@
|
||||
playback because of their higher resolution.
|
||||
* Fix wrong keyframe detection for TS H264 streams
|
||||
([#864](https://github.com/androidx/media/pull/864)).
|
||||
* Fix duration estimation of TS streams that are longer than 47721 seconds
|
||||
([#855](https://github.com/androidx/media/issues/855)).
|
||||
* Audio:
|
||||
* Fix handling of EOS for `SilenceSkippingAudioProcessor` when called
|
||||
multiple times ([#712](https://github.com/androidx/media/issues/712)).
|
||||
|
@ -187,6 +187,9 @@ public final class TimestampAdjuster {
|
||||
/**
|
||||
* Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound.
|
||||
*
|
||||
* <p>When estimating the wraparound, the method assumes that this timestamp is close to the
|
||||
* previous adjusted timestamp.
|
||||
*
|
||||
* @param pts90Khz A 90 kHz clock MPEG-2 TS presentation timestamp.
|
||||
* @return The adjusted timestamp in microseconds.
|
||||
*/
|
||||
@ -209,6 +212,30 @@ public final class TimestampAdjuster {
|
||||
return adjustSampleTimestamp(ptsToUs(pts90Khz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound.
|
||||
*
|
||||
* <p>When estimating the wraparound, the method assumes that the timestamp is strictly greater
|
||||
* than the previous adjusted timestamp.
|
||||
*
|
||||
* @param pts90Khz A 90 kHz clock MPEG-2 TS presentation timestamp.
|
||||
* @return The adjusted timestamp in microseconds.
|
||||
*/
|
||||
public synchronized long adjustTsTimestampGreaterThanPreviousTimestamp(long pts90Khz) {
|
||||
if (pts90Khz == C.TIME_UNSET) {
|
||||
return C.TIME_UNSET;
|
||||
}
|
||||
if (lastUnadjustedTimestampUs != C.TIME_UNSET) {
|
||||
// The wrap count for the current PTS must be same or greater than the previous one.
|
||||
long lastPts = usToNonWrappedPts(lastUnadjustedTimestampUs);
|
||||
long wrapCount = lastPts / MAX_PTS_PLUS_ONE;
|
||||
long ptsSameWrap = pts90Khz + (MAX_PTS_PLUS_ONE * wrapCount);
|
||||
long ptsNextWrap = pts90Khz + (MAX_PTS_PLUS_ONE * (wrapCount + 1));
|
||||
pts90Khz = ptsSameWrap >= lastPts ? ptsSameWrap : ptsNextWrap;
|
||||
}
|
||||
return adjustSampleTimestamp(ptsToUs(pts90Khz));
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets a timestamp in microseconds.
|
||||
*
|
||||
|
@ -82,4 +82,162 @@ public class TimestampAdjusterTest {
|
||||
assertThat(firstAdjustedTimestampUs).isEqualTo(5000);
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(9000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestamp_closeToWraparoundFollowedBySlightlySmallerValue_doesNotAssumeWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit) and close to the next one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L - 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 180_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs - 1_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestamp_closeToWraparoundFollowedBySlightlyLargerValue_doesNotAssumeWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit) and close to the next one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L - 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 45_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs + 500_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustTsTimestamp_closeToWraparoundFollowedByMuchSmallerValue_assumesWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit) and close to the next one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L - 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestamp(90_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs + 2_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestamp_justBeyondWraparoundFollowedBySlightlySmallerValue_doesNotAssumeWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit), just beyond the last one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L + 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestamp(45_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs - 500_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestamp_justBeyondWraparoundFollowedBySlightlyLargerValue_doesNotAssumeWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit), just beyond the last one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L + 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestamp(180_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs + 1_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adjustTsTimestamp_justBeyondWraparoundFollowedByMuchLargerValue_assumesWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit), just beyond the last one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L + 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 90_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs - 2_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestampGreaterThanPreviousTimestamp_closeToWraparoundFollowedBySlightlySmallerValue_assumesWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit) and close to the next one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L - 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 90_000);
|
||||
long secondAdjustedTimestampUs =
|
||||
adjuster.adjustTsTimestampGreaterThanPreviousTimestamp(0x200000000L - 180_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs - firstAdjustedTimestampUs).isGreaterThan(0x100000000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestampGreaterThanPreviousTimestamp_closeToWraparoundFollowedBySlightlyLargerValue_doesNotAssumeWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit) and close to the next one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L - 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 90_000);
|
||||
long secondAdjustedTimestampUs =
|
||||
adjuster.adjustTsTimestampGreaterThanPreviousTimestamp(0x200000000L - 45_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs + 500_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestampGreaterThanPreviousTimestamp_closeToWraparoundFollowedByMuchSmallerValue_assumesWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit) and close to the next one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L - 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(0x200000000L - 90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestampGreaterThanPreviousTimestamp(90_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs + 2_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestampGreaterThanPreviousTimestamp_justBeyondWraparoundFollowedBySlightlySmallerValue_assumesWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit), just beyond the last one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L + 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(90_000);
|
||||
long secondAdjustedTimestampUs = adjuster.adjustTsTimestampGreaterThanPreviousTimestamp(45_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs - firstAdjustedTimestampUs).isGreaterThan(0x100000000L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestampGreaterThanPreviousTimestamp_justBeyondWraparoundFollowedBySlightlyLargerValue_doesNotAssumeWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit), just beyond the last one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L + 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(90_000);
|
||||
long secondAdjustedTimestampUs =
|
||||
adjuster.adjustTsTimestampGreaterThanPreviousTimestamp(180_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs).isEqualTo(firstAdjustedTimestampUs + 1_000_000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
adjustTsTimestampGreaterThanPreviousTimestamp_justBeyondWraparoundFollowedByMuchLargerValue_doesNotAssumeWraparound() {
|
||||
// Init timestamp with a non-zero wraparound (multiple of 33-bit), just beyond the last one.
|
||||
TimestampAdjuster adjuster =
|
||||
new TimestampAdjuster(TimestampAdjuster.ptsToUs(3 * 0x200000000L + 90_000));
|
||||
|
||||
long firstAdjustedTimestampUs = adjuster.adjustTsTimestamp(90_000);
|
||||
long secondAdjustedTimestampUs =
|
||||
adjuster.adjustTsTimestampGreaterThanPreviousTimestamp(0x200000000L - 90_000);
|
||||
|
||||
assertThat(secondAdjustedTimestampUs - firstAdjustedTimestampUs).isGreaterThan(0x100000000L);
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ package androidx.media3.extractor.ts;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.TimestampAdjuster;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -103,12 +102,9 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
long minScrPositionUs = scrTimestampAdjuster.adjustTsTimestamp(firstScrValue);
|
||||
long maxScrPositionUs = scrTimestampAdjuster.adjustTsTimestamp(lastScrValue);
|
||||
long maxScrPositionUs =
|
||||
scrTimestampAdjuster.adjustTsTimestampGreaterThanPreviousTimestamp(lastScrValue);
|
||||
durationUs = maxScrPositionUs - minScrPositionUs;
|
||||
if (durationUs < 0) {
|
||||
Log.w(TAG, "Invalid duration: " + durationUs + ". Using TIME_UNSET instead.");
|
||||
durationUs = C.TIME_UNSET;
|
||||
}
|
||||
return finishReadDuration(input);
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ package androidx.media3.extractor.ts;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.TimestampAdjuster;
|
||||
import androidx.media3.common.util.Util;
|
||||
@ -99,12 +98,9 @@ import java.io.IOException;
|
||||
}
|
||||
|
||||
long minPcrPositionUs = pcrTimestampAdjuster.adjustTsTimestamp(firstPcrValue);
|
||||
long maxPcrPositionUs = pcrTimestampAdjuster.adjustTsTimestamp(lastPcrValue);
|
||||
long maxPcrPositionUs =
|
||||
pcrTimestampAdjuster.adjustTsTimestampGreaterThanPreviousTimestamp(lastPcrValue);
|
||||
durationUs = maxPcrPositionUs - minPcrPositionUs;
|
||||
if (durationUs < 0) {
|
||||
Log.w(TAG, "Invalid duration: " + durationUs + ". Using TIME_UNSET instead.");
|
||||
durationUs = C.TIME_UNSET;
|
||||
}
|
||||
return finishReadDuration(input);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user