Match with ExoPlayer seeking when using CompositionPlayer

In a image sequence, seek to after the image duration should render the final
image frame. But currently it hangs as ConstantRateTimestampIterator doesn't
output any timestamp in this case

PiperOrigin-RevId: 724295475
This commit is contained in:
claincly 2025-02-07 04:24:24 -08:00 committed by Copybara-Service
parent 5e6fb88372
commit babc2dd416
3 changed files with 50 additions and 7 deletions

View File

@ -17,6 +17,7 @@ package androidx.media3.common.util;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState;
import static java.lang.Math.max;
import static java.lang.Math.round;
import androidx.annotation.FloatRange;
@ -70,7 +71,8 @@ public final class ConstantRateTimestampIterator implements TimestampIterator {
this.endPositionUs = endPositionUs;
this.frameRate = frameRate;
float durationSecs = (endPositionUs - startPositionUs) / (float) C.MICROS_PER_SECOND;
this.totalNumberOfFramesToAdd = round(frameRate * durationSecs);
// Generate at least one timestamp so that at least one frame is produced when seeking.
this.totalNumberOfFramesToAdd = max(round(frameRate * durationSecs), 1);
framesDurationUs = C.MICROS_PER_SECOND / frameRate;
}

View File

@ -67,12 +67,12 @@ public class ConstantRateTimestampIteratorTest {
}
@Test
public void timestampIterator_smallDuration_generatesEmptyIterator() {
public void timestampIterator_smallDuration_generatesOneTimestamp() {
ConstantRateTimestampIterator constantRateTimestampIterator =
new ConstantRateTimestampIterator(/* durationUs= */ 1, /* frameRate= */ 2);
assertThat(generateList(constantRateTimestampIterator)).isEmpty();
assertThat(constantRateTimestampIterator.getLastTimestampUs()).isEqualTo(C.TIME_UNSET);
assertThat(generateList(constantRateTimestampIterator)).containsExactly(0L);
assertThat(constantRateTimestampIterator.getLastTimestampUs()).isEqualTo(0L);
}
@Test
@ -110,15 +110,15 @@ public class ConstantRateTimestampIteratorTest {
}
@Test
public void timestampIterator_withNoTimestampsWithinParameters_generatesNoTimestamp() {
public void timestampIterator_withNoTimestampsWithinParameters_generatesOneTimestamp() {
ConstantRateTimestampIterator constantRateTimestampIterator =
new ConstantRateTimestampIterator(
/* startPositionUs= */ 900_000L,
/* endPositionUs= */ C.MICROS_PER_SECOND,
/* frameRate= */ 3);
assertThat(generateList(constantRateTimestampIterator)).isEmpty();
assertThat(constantRateTimestampIterator.getLastTimestampUs()).isEqualTo(C.TIME_UNSET);
assertThat(generateList(constantRateTimestampIterator)).containsExactly(900_000L);
assertThat(constantRateTimestampIterator.getLastTimestampUs()).isEqualTo(900_000L);
}
private static List<Long> generateList(TimestampIterator iterator) {

View File

@ -355,6 +355,47 @@ public class CompositionPlayerSeekTest {
.isEqualTo(sequenceTimestampsUs);
}
@Test
public void seekToEndOfSecondImage_afterPlayingSingleSequenceOfTwoImages() throws Exception {
// Seeks to the end of the second image
long seekTimeMs = usToMs(2 * IMAGE_DURATION_US);
ImmutableList<Long> sequenceTimestampsUs =
new ImmutableList.Builder<Long>()
// Plays the first image
.addAll(IMAGE_TIMESTAMPS_US)
// Plays the second image
.addAll(
transform(IMAGE_TIMESTAMPS_US, timestampUs -> (IMAGE_DURATION_US + timestampUs)))
// Plays the last image frame, which is one microsecond smaller than the total duration.
.add(399_999L)
.build();
assertThat(
playSequenceUntilEndedAndSeekAndGetTimestampsUs(
ImmutableList.of(IMAGE_MEDIA_ITEM, IMAGE_MEDIA_ITEM), seekTimeMs))
.isEqualTo(sequenceTimestampsUs);
}
@Test
public void seekToAfterEndOfSecondImage_afterPlayingSingleSequenceOfTwoImages() throws Exception {
long seekTimeMs = usToMs(3 * IMAGE_DURATION_US);
ImmutableList<Long> sequenceTimestampsUs =
new ImmutableList.Builder<Long>()
// Plays the first image
.addAll(IMAGE_TIMESTAMPS_US)
// Plays the second image
.addAll(
transform(IMAGE_TIMESTAMPS_US, timestampUs -> (IMAGE_DURATION_US + timestampUs)))
// Plays the last image frame, which is one microsecond smaller than the total duration.
.add(399_999L)
.build();
assertThat(
playSequenceUntilEndedAndSeekAndGetTimestampsUs(
ImmutableList.of(IMAGE_MEDIA_ITEM, IMAGE_MEDIA_ITEM), seekTimeMs))
.isEqualTo(sequenceTimestampsUs);
}
@Test
public void seekToZero_afterPlayingSingleSequenceOfVideoAndImage() throws Exception {
maybeSkipTest();