diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b137660096..e11f91e0d1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -24,6 +24,9 @@ * Audio: * Video: * Text: + * Fix an `IllegalArgumentException` from `LegacySubtitleUtil` when a + WebVTT subtitle sample contains no cues, e.g. as part of a DASH stream + ([#1516](https://github.com/androidx/media/issues/1516)). * Metadata: * Image: * DRM: diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/LegacySubtitleUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/LegacySubtitleUtil.java index 5d58569cee..79e3970b36 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/LegacySubtitleUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/LegacySubtitleUtil.java @@ -32,11 +32,14 @@ public class LegacySubtitleUtil { * Converts a {@link Subtitle} to a list of {@link CuesWithTiming} representing it, emitted to * {@code output}. * - *

This may only be called with {@link Subtitle} instances where the first event is non-empty - * and the last event is an empty cue list. + *

This may only be called with empty {@link Subtitle} instances, or those where the first + * event is non-empty and the last event is an empty cue list. */ public static void toCuesWithTiming( Subtitle subtitle, OutputOptions outputOptions, Consumer output) { + if (subtitle.getEventTimeCount() == 0) { + return; + } int startIndex = getStartIndex(subtitle, outputOptions); boolean startedInMiddleOfCue = false; if (outputOptions.startTimeUs != C.TIME_UNSET) { diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/webvtt/LegacySubtitleUtilWebvttTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/webvtt/LegacySubtitleUtilWebvttTest.java index 0afa37bcae..9a9d2ce693 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/webvtt/LegacySubtitleUtilWebvttTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/webvtt/LegacySubtitleUtilWebvttTest.java @@ -39,6 +39,8 @@ public class LegacySubtitleUtilWebvttTest { private static final String FIRST_SUBTITLE_STRING = "This is the first subtitle."; private static final String SECOND_SUBTITLE_STRING = "This is the second subtitle."; + private static final WebvttSubtitle EMPTY_SUBTITLE = new WebvttSubtitle(ImmutableList.of()); + private static final WebvttSubtitle SIMPLE_SUBTITLE = new WebvttSubtitle( Arrays.asList( @@ -135,6 +137,14 @@ public class LegacySubtitleUtilWebvttTest { .containsExactly(SECOND_SUBTITLE_STRING); } + @Test + public void toCuesWithTiming_allCues_emptySubtitle() { + ImmutableList cuesWithTimingsList = + toCuesWithTimingList(EMPTY_SUBTITLE, SubtitleParser.OutputOptions.allCues()); + + assertThat(cuesWithTimingsList).isEmpty(); + } + @Test public void toCuesWithTiming_onlyEmitCuesAfterStartTime_startBetweenCues_simpleSubtitle() { ImmutableList cuesWithTimingsList = @@ -252,6 +262,14 @@ public class LegacySubtitleUtilWebvttTest { .containsExactly(SECOND_SUBTITLE_STRING); } + @Test + public void toCuesWithTiming_onlyEmitCuesAfterStartTime_emptySubtitle() { + ImmutableList cuesWithTimingsList = + toCuesWithTimingList(EMPTY_SUBTITLE, SubtitleParser.OutputOptions.onlyCuesAfter(0)); + + assertThat(cuesWithTimingsList).isEmpty(); + } + @Test public void toCuesWithTiming_emitCuesAfterStartTimeThenThoseBefore_startAtStartOfCue_simpleSubtitle() { @@ -405,6 +423,14 @@ public class LegacySubtitleUtilWebvttTest { .inOrder(); } + @Test + public void toCuesWithTiming_emitCuesAfterStartTimeThenThoseBefore_emptySubtitle() { + ImmutableList cuesWithTimingsList = + toCuesWithTimingList(EMPTY_SUBTITLE, SubtitleParser.OutputOptions.onlyCuesAfter(0)); + + assertThat(cuesWithTimingsList).isEmpty(); + } + private static ImmutableList toCuesWithTimingList( Subtitle subtitle, SubtitleParser.OutputOptions outputOptions) { ImmutableList.Builder result = ImmutableList.builder();