From 06132c6778cef2cac3d9238478046d7b2ec1ec4e Mon Sep 17 00:00:00 2001 From: ibaker Date: Thu, 13 Jul 2023 19:07:09 +0100 Subject: [PATCH] Update `DelegatingSubtitleDecoder` to handle `startTimeUs = TIME_UNSET` Also add a test to enforce this when `Tx3gDecoder` is migrated to `Tx3gParser`. PiperOrigin-RevId: 547856968 --- .../exoplayer/text/DelegatingSubtitleDecoder.java | 15 +++++++++++---- .../extractor/text/tx3g/Tx3gDecoderTest.java | 11 +++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/DelegatingSubtitleDecoder.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/DelegatingSubtitleDecoder.java index d6578f980e..b70cb60d95 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/DelegatingSubtitleDecoder.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/text/DelegatingSubtitleDecoder.java @@ -74,8 +74,6 @@ import java.util.List; return new SubtitleFromCuesWithTiming(cuesWithTiming); } - // TODO: ImmutableLongArray is no longer Beta, remove suppression when we upgrade Guava dep - @SuppressWarnings("UnstableApiUsage") private static final class SubtitleFromCuesWithTiming implements Subtitle { private final ImmutableList> cuesListForUniqueStartTimes; @@ -83,7 +81,7 @@ import java.util.List; /** Ordering of two CuesWithTiming objects based on their startTimeUs values. */ private static final Ordering CUES_BY_START_TIME_ASCENDING = - Ordering.natural().onResultOf(c -> c.startTimeUs); + Ordering.natural().onResultOf(c -> normalizeUnsetStartTimeToZero(c.startTimeUs)); SubtitleFromCuesWithTiming(List cuesWithTimingList) { this.cuesStartTimesUs = new long[cuesWithTimingList.size()]; @@ -93,7 +91,8 @@ import java.util.List; ImmutableList.sortedCopyOf(CUES_BY_START_TIME_ASCENDING, cuesWithTimingList); for (int i = 0; i < sortedCuesWithTimingList.size(); i++) { cuesListForUniqueStartTimes.add(sortedCuesWithTimingList.get(i).cues); - cuesStartTimesUs[i] = sortedCuesWithTimingList.get(i).startTimeUs; + cuesStartTimesUs[i] = + normalizeUnsetStartTimeToZero(sortedCuesWithTimingList.get(i).startTimeUs); } this.cuesListForUniqueStartTimes = cuesListForUniqueStartTimes.build(); } @@ -134,5 +133,13 @@ import java.util.List; return cuesListForUniqueStartTimes.get(index); } } + + // SubtitleParser can return CuesWithTiming with startTimeUs == TIME_UNSET, indicating the + // start time should be derived from the surrounding sample timestamp. In the context of the + // Subtitle interface, this means starting at zero, so we can just always interpret TIME_UNSET + // as zero here. + private static long normalizeUnsetStartTimeToZero(long startTime) { + return startTime == C.TIME_UNSET ? 0 : startTime; + } } } diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/tx3g/Tx3gDecoderTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/tx3g/Tx3gDecoderTest.java index a5adafaa12..e782f73db0 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/tx3g/Tx3gDecoderTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/tx3g/Tx3gDecoderTest.java @@ -78,6 +78,17 @@ public final class Tx3gDecoderTest { assertFractionalLinePosition(subtitle.getCues(0).get(0), 0.85f); } + @Test + public void decode_noCuesBeforeStartTIme() throws Exception { + Tx3gDecoder decoder = new Tx3gDecoder(ImmutableList.of()); + byte[] bytes = + TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_JUST_TEXT); + + Subtitle subtitle = decoder.decode(bytes, bytes.length, /* reset= */ false); + + assertThat(subtitle.getCues(/* timeUs= */ -1)).isEmpty(); + } + @Test public void decodeWithStyl() throws Exception { Tx3gDecoder decoder = new Tx3gDecoder(ImmutableList.of());