diff --git a/RELEASENOTES.md b/RELEASENOTES.md index cedebcb51b..0946392f96 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -40,6 +40,8 @@ * Improve codec performance checks for software video codecs. This may lead to some additional tracks being marked as `EXCEEDS_CAPABILITIES`. * Text: + * Fix SSA and SubRip to display an in-progress cue when enabling subtitles + ([#2309](https://github.com/androidx/media/issues/2309)). * Metadata: * Image: * DataSource: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java index 277f4a8d44..fe6454f612 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/SubtitleExtractor.java @@ -259,7 +259,7 @@ public class SubtitleExtractor implements Extractor { cuesWithTiming.startTimeUs, cueEncoder.encode(cuesWithTiming.cues, cuesWithTiming.durationUs)); samples.add(sample); - if (seekTimeUs == C.TIME_UNSET || cuesWithTiming.startTimeUs >= seekTimeUs) { + if (seekTimeUs == C.TIME_UNSET || cuesWithTiming.endTimeUs >= seekTimeUs) { writeToOutput(sample); } }); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaParser.java index 6576ca05e7..ded9350678 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ssa/SsaParser.java @@ -167,13 +167,14 @@ public final class SsaParser implements SubtitleParser { } long startTimeUs = startTimesUs.get(i); // It's safe to inspect element i+1, because we already exited the loop above if i=size()-1. - long durationUs = startTimesUs.get(i + 1) - startTimesUs.get(i); - if (outputOptions.startTimeUs == C.TIME_UNSET || startTimeUs >= outputOptions.startTimeUs) { - output.accept(new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs)); - + long endTimeUs = startTimesUs.get(i + 1); + CuesWithTiming cuesWithTiming = + new CuesWithTiming( + cuesForThisStartTime, startTimeUs, /* durationUs= */ endTimeUs - startTimeUs); + if (outputOptions.startTimeUs == C.TIME_UNSET || endTimeUs >= outputOptions.startTimeUs) { + output.accept(cuesWithTiming); } else if (cuesWithTimingBeforeRequestedStartTimeUs != null) { - cuesWithTimingBeforeRequestedStartTimeUs.add( - new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs)); + cuesWithTimingBeforeRequestedStartTimeUs.add(cuesWithTiming); } } if (cuesWithTimingBeforeRequestedStartTimeUs != null) { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/subrip/SubripParser.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/subrip/SubripParser.java index db3a8df9a9..d404de9156 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/subrip/SubripParser.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/subrip/SubripParser.java @@ -166,7 +166,7 @@ public final class SubripParser implements SubtitleParser { break; } } - if (outputOptions.startTimeUs == C.TIME_UNSET || startTimeUs >= outputOptions.startTimeUs) { + if (outputOptions.startTimeUs == C.TIME_UNSET || endTimeUs >= outputOptions.startTimeUs) { output.accept( new CuesWithTiming( ImmutableList.of(buildCue(text, alignmentTag)), diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ssa/SsaParserTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ssa/SsaParserTest.java index ba10f7974b..c8beeaf72d 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ssa/SsaParserTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ssa/SsaParserTest.java @@ -129,7 +129,8 @@ public final class SsaParserTest { SsaParser parser = new SsaParser(); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL); List cues = new ArrayList<>(); - parser.parse(bytes, OutputOptions.onlyCuesAfter(/* startTimeUs= */ 1_000_000), cues::add); + // Choose a start time halfway through the second cue, and expect it to be included. + parser.parse(bytes, OutputOptions.onlyCuesAfter(/* startTimeUs= */ 3_000_000), cues::add); assertThat(cues).hasSize(2); assertTypicalCue2(cues.get(0)); @@ -141,9 +142,10 @@ public final class SsaParserTest { SsaParser parser = new SsaParser(); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL); List cues = new ArrayList<>(); + // Choose a start time halfway through the second cue, and expect it to be considered 'after'. parser.parse( bytes, - OutputOptions.cuesAfterThenRemainingCuesBefore(/* startTimeUs= */ 1_000_000), + OutputOptions.cuesAfterThenRemainingCuesBefore(/* startTimeUs= */ 3_000_000), cues::add); assertThat(cues).hasSize(3); diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/subrip/SubripParserTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/subrip/SubripParserTest.java index 5988c2885f..b8cab4f943 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/subrip/SubripParserTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/subrip/SubripParserTest.java @@ -105,7 +105,8 @@ public final class SubripParserTest { byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE); List cues = new ArrayList<>(); - parser.parse(bytes, OutputOptions.onlyCuesAfter(/* startTimeUs= */ 1_000_000), cues::add); + // Choose a start time halfway through the second cue, and expect it to be included. + parser.parse(bytes, OutputOptions.onlyCuesAfter(/* startTimeUs= */ 3_000_000), cues::add); assertThat(cues).hasSize(2); assertTypicalCue2(cues.get(0)); @@ -118,9 +119,10 @@ public final class SubripParserTest { byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE); List cues = new ArrayList<>(); + // Choose a start time halfway through the second cue, and expect it to be considered 'after'. parser.parse( bytes, - OutputOptions.cuesAfterThenRemainingCuesBefore(/* startTimeUs= */ 1_000_000), + OutputOptions.cuesAfterThenRemainingCuesBefore(/* startTimeUs= */ 3_000_000), cues::add); assertThat(cues).hasSize(3);