Migrate SubtitleParser tests to incremental parse() methods

All the production code is already calling these new incremental
methods, migrating the tests allows us to remove the old
`List`-returning methods in a follow-up change.

#minor-release

PiperOrigin-RevId: 572822828
This commit is contained in:
ibaker 2023-10-12 01:40:05 -07:00 committed by Copybara-Service
parent d1fc15f207
commit a12bde4f57
4 changed files with 179 additions and 114 deletions

View File

@ -24,6 +24,8 @@ import android.text.Layout;
import android.text.Spanned; import android.text.Spanned;
import androidx.media3.common.text.Cue; import androidx.media3.common.text.Cue;
import androidx.media3.extractor.text.CuesWithTiming; import androidx.media3.extractor.text.CuesWithTiming;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.text.SubtitleParser.OutputOptions;
import androidx.media3.test.utils.TestUtil; import androidx.media3.test.utils.TestUtil;
import androidx.media3.test.utils.truth.SpannedSubject; import androidx.media3.test.utils.truth.SpannedSubject;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
@ -71,7 +73,7 @@ public final class SsaParserTest {
public void parseEmpty() throws IOException { public void parseEmpty() throws IOException {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), EMPTY); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), EMPTY);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).isEmpty(); assertThat(allCues).isEmpty();
} }
@ -81,7 +83,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), EMPTY_STYLE_LINE); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), EMPTY_STYLE_LINE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(1); assertThat(allCues).hasSize(1);
Cue cue = Iterables.getOnlyElement(allCues.get(0).cues); Cue cue = Iterables.getOnlyElement(allCues.get(0).cues);
@ -101,7 +103,7 @@ public final class SsaParserTest {
public void parseTypical() throws IOException { public void parseTypical() throws IOException {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
// Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center). // Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center).
@ -122,6 +124,34 @@ public final class SsaParserTest {
assertTypicalCue3(allCues.get(2)); assertTypicalCue3(allCues.get(2));
} }
@Test
public void parseTypical_onlyCuesAfterTime() throws IOException {
SsaParser parser = new SsaParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL);
List<CuesWithTiming> cues = new ArrayList<>();
parser.parse(bytes, OutputOptions.onlyCuesAfter(/* startTimeUs= */ 1_000_000), cues::add);
assertThat(cues).hasSize(2);
assertTypicalCue2(cues.get(0));
assertTypicalCue3(cues.get(1));
}
@Test
public void parseTypical_cuesAfterTimeThenCuesBefore() throws IOException {
SsaParser parser = new SsaParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL);
List<CuesWithTiming> cues = new ArrayList<>();
parser.parse(
bytes,
OutputOptions.cuesAfterThenRemainingCuesBefore(/* startTimeUs= */ 1_000_000),
cues::add);
assertThat(cues).hasSize(3);
assertTypicalCue2(cues.get(0));
assertTypicalCue3(cues.get(1));
assertTypicalCue1(cues.get(2));
}
@Test @Test
public void parseTypicalWithInitializationData() throws IOException { public void parseTypicalWithInitializationData() throws IOException {
byte[] headerBytes = byte[] headerBytes =
@ -134,7 +164,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(initializationData); SsaParser parser = new SsaParser(initializationData);
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_DIALOGUE_ONLY); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_DIALOGUE_ONLY);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -155,8 +185,13 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(initializationData); SsaParser parser = new SsaParser(initializationData);
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_DIALOGUE_ONLY); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_DIALOGUE_ONLY);
ImmutableList<CuesWithTiming> allCues = List<CuesWithTiming> allCues = new ArrayList<>();
parser.parse(bytes, /* offset= */ 10, /* length= */ bytes.length - 30); parser.parse(
bytes,
/* offset= */ 10,
/* length= */ bytes.length - 30,
OutputOptions.allCues(),
allCues::add);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
// Because of the offset, we skip the first line of dialogue // Because of the offset, we skip the first line of dialogue
@ -173,7 +208,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16LE); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16LE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
// Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center). // Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center).
@ -199,7 +234,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16BE); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16BE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
// Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center). // Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center).
@ -225,7 +260,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), OVERLAPPING_TIMECODES); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), OVERLAPPING_TIMECODES);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
String firstSubtitleText = "First subtitle - end overlaps second"; String firstSubtitleText = "First subtitle - end overlaps second";
String secondSubtitleText = "Second subtitle - beginning overlaps first"; String secondSubtitleText = "Second subtitle - beginning overlaps first";
@ -289,7 +324,7 @@ public final class SsaParserTest {
public void parsePositions() throws IOException { public void parsePositions() throws IOException {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), POSITIONS); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), POSITIONS);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
// Check \pos() sets position & line // Check \pos() sets position & line
Cue firstCue = Iterables.getOnlyElement(allCues.get(0).cues); Cue firstCue = Iterables.getOnlyElement(allCues.get(0).cues);
@ -343,7 +378,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), INVALID_POSITIONS); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), INVALID_POSITIONS);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
// Negative parameter to \pos() - fall back to the positions implied by middle-left alignment. // Negative parameter to \pos() - fall back to the positions implied by middle-left alignment.
Cue firstCue = Iterables.getOnlyElement(allCues.get(0).cues); Cue firstCue = Iterables.getOnlyElement(allCues.get(0).cues);
@ -380,7 +415,7 @@ public final class SsaParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), POSITIONS_WITHOUT_PLAYRES); ApplicationProvider.getApplicationContext(), POSITIONS_WITHOUT_PLAYRES);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
// The dialogue line has a valid \pos() override, but it's ignored because PlayResY isn't // The dialogue line has a valid \pos() override, but it's ignored because PlayResY isn't
// set (so we don't know the denominator). // set (so we don't know the denominator).
@ -396,7 +431,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), INVALID_TIMECODES); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), INVALID_TIMECODES);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(1); assertThat(allCues).hasSize(1);
assertTypicalCue3(Iterables.getOnlyElement(allCues)); assertTypicalCue3(Iterables.getOnlyElement(allCues));
@ -407,7 +442,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_PRIMARY_COLOR); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_PRIMARY_COLOR);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(7); assertThat(allCues).hasSize(7);
// &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB) // &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB)
Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text; Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text;
@ -449,7 +484,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_OUTLINE_COLOR); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_OUTLINE_COLOR);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text; Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text;
SpannedSubject.assertThat(firstCueText) SpannedSubject.assertThat(firstCueText)
@ -467,7 +502,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_FONT_SIZE); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_FONT_SIZE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
Cue firstCue = Iterables.getOnlyElement(allCues.get(0).cues); Cue firstCue = Iterables.getOnlyElement(allCues.get(0).cues);
@ -483,7 +518,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_BOLD_ITALIC); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_BOLD_ITALIC);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text; Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text;
@ -499,7 +534,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_UNDERLINE); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_UNDERLINE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text; Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text;
@ -513,7 +548,7 @@ public final class SsaParserTest {
SsaParser parser = new SsaParser(); SsaParser parser = new SsaParser();
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_STRIKEOUT); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_STRIKEOUT);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text; Spanned firstCueText = (Spanned) Iterables.getOnlyElement(allCues.get(0).cues).text;
@ -523,6 +558,12 @@ public final class SsaParserTest {
.hasNoStrikethroughSpanBetween(0, secondCueText.length()); .hasNoStrikethroughSpanBetween(0, secondCueText.length());
} }
private static ImmutableList<CuesWithTiming> parseAllCues(SubtitleParser parser, byte[] data) {
ImmutableList.Builder<CuesWithTiming> cues = ImmutableList.builder();
parser.parse(data, OutputOptions.allCues(), cues::add);
return cues.build();
}
private static void assertTypicalCue1(CuesWithTiming cuesWithTiming) { private static void assertTypicalCue1(CuesWithTiming cuesWithTiming) {
assertThat(cuesWithTiming.startTimeUs).isEqualTo(0); assertThat(cuesWithTiming.startTimeUs).isEqualTo(0);
assertThat(cuesWithTiming.durationUs).isEqualTo(1230000); assertThat(cuesWithTiming.durationUs).isEqualTo(1230000);

View File

@ -20,11 +20,14 @@ import static com.google.common.truth.Truth.assertThat;
import androidx.media3.common.text.Cue; import androidx.media3.common.text.Cue;
import androidx.media3.extractor.text.CuesWithTiming; import androidx.media3.extractor.text.CuesWithTiming;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.text.SubtitleParser.OutputOptions;
import androidx.media3.test.utils.TestUtil; import androidx.media3.test.utils.TestUtil;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -60,7 +63,7 @@ public final class SubripParserTest {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), EMPTY_FILE); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), EMPTY_FILE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).isEmpty(); assertThat(allCues).isEmpty();
} }
@ -70,7 +73,7 @@ public final class SubripParserTest {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -83,7 +86,8 @@ public final class SubripParserTest {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE);
ImmutableList<CuesWithTiming> allCues = parser.parse(bytes, 10, bytes.length - 15); List<CuesWithTiming> allCues = new ArrayList<>();
parser.parse(bytes, 10, bytes.length - 15, OutputOptions.allCues(), allCues::add);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
// Because of the offset, we skip the first line of dialogue // Because of the offset, we skip the first line of dialogue
@ -93,6 +97,36 @@ public final class SubripParserTest {
assertThat(thirdCue.text.toString()).isEqualTo("This is the third subti"); assertThat(thirdCue.text.toString()).isEqualTo("This is the third subti");
} }
@Test
public void parseTypical_onlyCuesAfterTime() throws IOException {
SubripParser parser = new SubripParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE);
List<CuesWithTiming> cues = new ArrayList<>();
parser.parse(bytes, OutputOptions.onlyCuesAfter(/* startTimeUs= */ 1_000_000), cues::add);
assertThat(cues).hasSize(2);
assertTypicalCue2(cues.get(0));
assertTypicalCue3(cues.get(1));
}
@Test
public void parseTypical_cuesAfterTimeThenCuesBefore() throws IOException {
SubripParser parser = new SubripParser();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_FILE);
List<CuesWithTiming> cues = new ArrayList<>();
parser.parse(
bytes,
OutputOptions.cuesAfterThenRemainingCuesBefore(/* startTimeUs= */ 1_000_000),
cues::add);
assertThat(cues).hasSize(3);
assertTypicalCue2(cues.get(0));
assertTypicalCue3(cues.get(1));
assertTypicalCue1(cues.get(2));
}
@Test @Test
public void parseTypicalWithByteOrderMark() throws IOException { public void parseTypicalWithByteOrderMark() throws IOException {
SubripParser parser = new SubripParser(); SubripParser parser = new SubripParser();
@ -100,7 +134,7 @@ public final class SubripParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), TYPICAL_WITH_BYTE_ORDER_MARK); ApplicationProvider.getApplicationContext(), TYPICAL_WITH_BYTE_ORDER_MARK);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -115,7 +149,7 @@ public final class SubripParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), TYPICAL_EXTRA_BLANK_LINE); ApplicationProvider.getApplicationContext(), TYPICAL_EXTRA_BLANK_LINE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -131,7 +165,7 @@ public final class SubripParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), TYPICAL_MISSING_TIMECODE); ApplicationProvider.getApplicationContext(), TYPICAL_MISSING_TIMECODE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -146,7 +180,7 @@ public final class SubripParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), TYPICAL_MISSING_SEQUENCE); ApplicationProvider.getApplicationContext(), TYPICAL_MISSING_SEQUENCE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -161,7 +195,7 @@ public final class SubripParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), TYPICAL_NEGATIVE_TIMESTAMPS); ApplicationProvider.getApplicationContext(), TYPICAL_NEGATIVE_TIMESTAMPS);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(1); assertThat(allCues).hasSize(1);
assertTypicalCue3(allCues.get(0)); assertTypicalCue3(allCues.get(0));
@ -174,7 +208,7 @@ public final class SubripParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UNEXPECTED_END); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UNEXPECTED_END);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(2); assertThat(allCues).hasSize(2);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -187,7 +221,7 @@ public final class SubripParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16LE); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16LE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -201,7 +235,7 @@ public final class SubripParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16BE); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_UTF16BE);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -215,7 +249,7 @@ public final class SubripParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_WITH_TAGS); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), TYPICAL_WITH_TAGS);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).isNotNull(); assertThat(allCues).isNotNull();
assertThat(allCues.get(0).cues.get(0).text.toString()).isEqualTo("This is the first subtitle."); assertThat(allCues.get(0).cues.get(0).text.toString()).isEqualTo("This is the first subtitle.");
@ -244,7 +278,7 @@ public final class SubripParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), TYPICAL_NO_HOURS_AND_MILLIS); ApplicationProvider.getApplicationContext(), TYPICAL_NO_HOURS_AND_MILLIS);
List<CuesWithTiming> allCues = parser.parse(bytes); ImmutableList<CuesWithTiming> allCues = parseAllCues(parser, bytes);
assertThat(allCues).hasSize(3); assertThat(allCues).hasSize(3);
assertTypicalCue1(allCues.get(0)); assertTypicalCue1(allCues.get(0));
@ -254,6 +288,12 @@ public final class SubripParserTest {
assertTypicalCue3(allCues.get(2)); assertTypicalCue3(allCues.get(2));
} }
private static ImmutableList<CuesWithTiming> parseAllCues(SubtitleParser parser, byte[] data) {
ImmutableList.Builder<CuesWithTiming> cues = ImmutableList.builder();
parser.parse(data, OutputOptions.allCues(), cues::add);
return cues.build();
}
private static void assertTypicalCue1(CuesWithTiming cuesWithTiming) { private static void assertTypicalCue1(CuesWithTiming cuesWithTiming) {
assertThat(cuesWithTiming.startTimeUs).isEqualTo(0); assertThat(cuesWithTiming.startTimeUs).isEqualTo(0);
assertThat(cuesWithTiming.cues.get(0).text.toString()).isEqualTo("This is the first subtitle."); assertThat(cuesWithTiming.cues.get(0).text.toString()).isEqualTo("This is the first subtitle.");

View File

@ -20,16 +20,20 @@ import static androidx.media3.test.utils.truth.SpannedSubject.assertThat;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import android.graphics.Color; import android.graphics.Color;
import android.text.Spanned;
import android.text.SpannedString; import android.text.SpannedString;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.text.Cue; import androidx.media3.common.text.Cue;
import androidx.media3.extractor.text.CuesWithTiming; import androidx.media3.extractor.text.CuesWithTiming;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.test.utils.TestUtil; import androidx.media3.test.utils.TestUtil;
import androidx.test.core.app.ApplicationProvider; import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -67,7 +71,9 @@ public final class Tx3gParserTest {
Tx3gParser parser = new Tx3gParser(ImmutableList.of()); Tx3gParser parser = new Tx3gParser(ImmutableList.of());
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), NO_SUBTITLE); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), NO_SUBTITLE);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); List<CuesWithTiming> allCues = new ArrayList<>(/* initialCapacity= */ 1);
parser.parse(bytes, SubtitleParser.OutputOptions.allCues(), allCues::add);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(allCues);
assertThat(cuesWithTiming.cues).isEmpty(); assertThat(cuesWithTiming.cues).isEmpty();
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET); assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
@ -81,13 +87,9 @@ public final class Tx3gParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_JUST_TEXT); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_JUST_TEXT);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues);
SpannedString text = new SpannedString(singleCue.text); SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("CC Test"); assertThat(text.toString()).isEqualTo("CC Test");
assertThat(text).hasNoSpans(); assertThat(text).hasNoSpans();
assertFractionalLinePosition(singleCue, 0.85f); assertFractionalLinePosition(singleCue, 0.85f);
@ -99,13 +101,9 @@ public final class Tx3gParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("CC Test"); assertThat(text.toString()).isEqualTo("CC Test");
assertThat(text).hasBoldItalicSpanBetween(0, 6); assertThat(text).hasBoldItalicSpanBetween(0, 6);
assertThat(text).hasUnderlineSpanBetween(0, 6); assertThat(text).hasUnderlineSpanBetween(0, 6);
@ -127,9 +125,8 @@ public final class Tx3gParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_START_TOO_LARGE); ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_START_TOO_LARGE);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(text.toString()).isEqualTo("CC 🙂"); assertThat(text.toString()).isEqualTo("CC 🙂");
assertThat(text).hasNoSpans(); assertThat(text).hasNoSpans();
@ -150,9 +147,8 @@ public final class Tx3gParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_END_TOO_LARGE); ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_END_TOO_LARGE);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(text.toString()).isEqualTo("CC 🙂"); assertThat(text.toString()).isEqualTo("CC 🙂");
assertThat(text).hasBoldItalicSpanBetween(0, 5); assertThat(text).hasBoldItalicSpanBetween(0, 5);
@ -168,13 +164,9 @@ public final class Tx3gParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_ALL_DEFAULTS); ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL_ALL_DEFAULTS);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("CC Test"); assertThat(text.toString()).isEqualTo("CC Test");
assertThat(text).hasNoSpans(); assertThat(text).hasNoSpans();
assertFractionalLinePosition(singleCue, 0.85f); assertFractionalLinePosition(singleCue, 0.85f);
@ -186,13 +178,9 @@ public final class Tx3gParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_UTF16_BE_NO_STYL); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_UTF16_BE_NO_STYL);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("你好"); assertThat(text.toString()).isEqualTo("你好");
assertThat(text).hasNoSpans(); assertThat(text).hasNoSpans();
assertFractionalLinePosition(singleCue, 0.85f); assertFractionalLinePosition(singleCue, 0.85f);
@ -203,13 +191,8 @@ public final class Tx3gParserTest {
Tx3gParser parser = new Tx3gParser(ImmutableList.of()); Tx3gParser parser = new Tx3gParser(ImmutableList.of());
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_UTF16_LE_NO_STYL); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_UTF16_LE_NO_STYL);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("你好"); assertThat(text.toString()).isEqualTo("你好");
assertThat(text).hasNoSpans(); assertThat(text).hasNoSpans();
@ -223,13 +206,9 @@ public final class Tx3gParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), SAMPLE_WITH_MULTIPLE_STYL); ApplicationProvider.getApplicationContext(), SAMPLE_WITH_MULTIPLE_STYL);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("Line 2\nLine 3"); assertThat(text.toString()).isEqualTo("Line 2\nLine 3");
assertThat(text).hasItalicSpanBetween(0, 5); assertThat(text).hasItalicSpanBetween(0, 5);
assertThat(text).hasUnderlineSpanBetween(7, 12); assertThat(text).hasUnderlineSpanBetween(7, 12);
@ -245,13 +224,9 @@ public final class Tx3gParserTest {
TestUtil.getByteArray( TestUtil.getByteArray(
ApplicationProvider.getApplicationContext(), SAMPLE_WITH_OTHER_EXTENSION); ApplicationProvider.getApplicationContext(), SAMPLE_WITH_OTHER_EXTENSION);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("CC Test"); assertThat(text.toString()).isEqualTo("CC Test");
assertThat(text).hasBoldSpanBetween(0, 6); assertThat(text).hasBoldSpanBetween(0, 6);
assertThat(text).hasForegroundColorSpanBetween(0, 6).withColor(Color.GREEN); assertThat(text).hasForegroundColorSpanBetween(0, 6).withColor(Color.GREEN);
@ -266,13 +241,9 @@ public final class Tx3gParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("CC Test"); assertThat(text.toString()).isEqualTo("CC Test");
assertThat(text).hasBoldItalicSpanBetween(0, 7); assertThat(text).hasBoldItalicSpanBetween(0, 7);
assertThat(text).hasUnderlineSpanBetween(0, 7); assertThat(text).hasUnderlineSpanBetween(0, 7);
@ -291,13 +262,9 @@ public final class Tx3gParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_TBOX); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_TBOX);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("CC Test"); assertThat(text.toString()).isEqualTo("CC Test");
assertThat(text).hasBoldItalicSpanBetween(0, 7); assertThat(text).hasBoldItalicSpanBetween(0, 7);
assertThat(text).hasUnderlineSpanBetween(0, 7); assertThat(text).hasUnderlineSpanBetween(0, 7);
@ -315,13 +282,9 @@ public final class Tx3gParserTest {
byte[] bytes = byte[] bytes =
TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL); TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), SAMPLE_WITH_STYL);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(parser.parse(bytes)); Cue singleCue = parseToSingleCue(parser, bytes);
Cue singleCue = Iterables.getOnlyElement(cuesWithTiming.cues); Spanned text = (Spanned) singleCue.text;
SpannedString text = new SpannedString(singleCue.text);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(text.toString()).isEqualTo("CC Test"); assertThat(text.toString()).isEqualTo("CC Test");
assertThat(text).hasBoldItalicSpanBetween(0, 6); assertThat(text).hasBoldItalicSpanBetween(0, 6);
assertThat(text).hasUnderlineSpanBetween(0, 6); assertThat(text).hasUnderlineSpanBetween(0, 6);
@ -334,4 +297,16 @@ public final class Tx3gParserTest {
assertThat(cue.lineAnchor).isEqualTo(Cue.ANCHOR_TYPE_START); assertThat(cue.lineAnchor).isEqualTo(Cue.ANCHOR_TYPE_START);
assertThat(cue.line).isWithin(1e-6f).of(expectedFraction); assertThat(cue.line).isWithin(1e-6f).of(expectedFraction);
} }
private static Cue parseToSingleCue(SubtitleParser parser, byte[] data) {
List<CuesWithTiming> result = new ArrayList<>(/* initialCapacity= */ 1);
parser.parse(data, SubtitleParser.OutputOptions.allCues(), result::add);
CuesWithTiming cuesWithTiming = Iterables.getOnlyElement(result);
assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTiming.endTimeUs).isEqualTo(C.TIME_UNSET);
return Iterables.getOnlyElement(cuesWithTiming.cues);
}
} }

View File

@ -22,9 +22,13 @@ import static org.junit.Assert.assertThrows;
import androidx.media3.common.C; import androidx.media3.common.C;
import androidx.media3.common.text.Cue; import androidx.media3.common.text.Cue;
import androidx.media3.extractor.text.CuesWithTiming; import androidx.media3.extractor.text.CuesWithTiming;
import androidx.media3.extractor.text.SubtitleParser;
import androidx.media3.extractor.text.SubtitleParser.OutputOptions;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.truth.Expect; import com.google.common.truth.Expect;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@ -171,7 +175,7 @@ public final class Mp4WebvttParserTest {
@Test @Test
public void singleCueSample() { public void singleCueSample() {
Mp4WebvttParser parser = new Mp4WebvttParser(); Mp4WebvttParser parser = new Mp4WebvttParser();
List<CuesWithTiming> result = parser.parse(SINGLE_CUE_SAMPLE); CuesWithTiming result = parseToSingleCuesWithTiming(parser, SINGLE_CUE_SAMPLE);
// Line feed must be trimmed by the decoder // Line feed must be trimmed by the decoder
Cue expectedCue = WebvttCueParser.newCueForText("Hello World"); Cue expectedCue = WebvttCueParser.newCueForText("Hello World");
assertMp4WebvttSubtitleEquals(result, expectedCue); assertMp4WebvttSubtitleEquals(result, expectedCue);
@ -180,7 +184,7 @@ public final class Mp4WebvttParserTest {
@Test @Test
public void twoCuesSample() { public void twoCuesSample() {
Mp4WebvttParser parser = new Mp4WebvttParser(); Mp4WebvttParser parser = new Mp4WebvttParser();
List<CuesWithTiming> result = parser.parse(DOUBLE_CUE_SAMPLE); CuesWithTiming result = parseToSingleCuesWithTiming(parser, DOUBLE_CUE_SAMPLE);
Cue firstExpectedCue = WebvttCueParser.newCueForText("Hello World"); Cue firstExpectedCue = WebvttCueParser.newCueForText("Hello World");
Cue secondExpectedCue = WebvttCueParser.newCueForText("Bye Bye"); Cue secondExpectedCue = WebvttCueParser.newCueForText("Bye Bye");
assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue); assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue);
@ -190,13 +194,12 @@ public final class Mp4WebvttParserTest {
public void noCueSample() { public void noCueSample() {
Mp4WebvttParser parser = new Mp4WebvttParser(); Mp4WebvttParser parser = new Mp4WebvttParser();
List<CuesWithTiming> result = parser.parse(NO_CUE_SAMPLE); CuesWithTiming result = parseToSingleCuesWithTiming(parser, NO_CUE_SAMPLE);
assertThat(result).hasSize(1); assertThat(result.cues).isEmpty();
assertThat(result.get(0).cues).isEmpty(); assertThat(result.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(result.get(0).startTimeUs).isEqualTo(C.TIME_UNSET); assertThat(result.durationUs).isEqualTo(C.TIME_UNSET);
assertThat(result.get(0).durationUs).isEqualTo(C.TIME_UNSET); assertThat(result.endTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(result.get(0).endTimeUs).isEqualTo(C.TIME_UNSET);
} }
// Negative tests. // Negative tests.
@ -204,23 +207,29 @@ public final class Mp4WebvttParserTest {
@Test @Test
public void sampleWithIncompleteHeader() { public void sampleWithIncompleteHeader() {
Mp4WebvttParser parser = new Mp4WebvttParser(); Mp4WebvttParser parser = new Mp4WebvttParser();
assertThrows(IllegalArgumentException.class, () -> parser.parse(INCOMPLETE_HEADER_SAMPLE)); assertThrows(
IllegalArgumentException.class,
() -> parser.parse(INCOMPLETE_HEADER_SAMPLE, OutputOptions.allCues(), c -> {}));
} }
// Util methods // Util methods
private static CuesWithTiming parseToSingleCuesWithTiming(SubtitleParser parser, byte[] data) {
List<CuesWithTiming> result = new ArrayList<>(/* initialCapacity= */ 1);
parser.parse(data, OutputOptions.allCues(), result::add);
return Iterables.getOnlyElement(result);
}
/** /**
* Asserts that the Subtitle's cues (which are all part of the event at t=0) are equal to the * Asserts that the Subtitle's cues (which are all part of the event at t=0) are equal to the
* expected Cues. * expected Cues.
* *
* @param cuesWithTimings The list of {@link CuesWithTiming} to check. * @param cuesWithTiming The {@link CuesWithTiming} to check.
* @param expectedCues The expected {@link Cue}s. * @param expectedCues The expected {@link Cue}s.
*/ */
private void assertMp4WebvttSubtitleEquals( private void assertMp4WebvttSubtitleEquals(CuesWithTiming cuesWithTiming, Cue... expectedCues) {
List<CuesWithTiming> cuesWithTimings, Cue... expectedCues) { assertThat(cuesWithTiming.startTimeUs).isEqualTo(C.TIME_UNSET);
assertThat(cuesWithTimings).hasSize(1); ImmutableList<Cue> allCues = cuesWithTiming.cues;
assertThat(cuesWithTimings.get(0).startTimeUs).isEqualTo(C.TIME_UNSET);
ImmutableList<Cue> allCues = cuesWithTimings.get(0).cues;
assertThat(allCues).hasSize(expectedCues.length); assertThat(allCues).hasSize(expectedCues.length);
for (int i = 0; i < allCues.size(); i++) { for (int i = 0; i < allCues.size(); i++) {
assertCuesEqual(expectedCues[i], allCues.get(i)); assertCuesEqual(expectedCues[i], allCues.get(i));