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();