2424import android .text .Spanned ;
2525import androidx .media3 .common .text .Cue ;
2626import androidx .media3 .extractor .text .CuesWithTiming ;
27+ import androidx .media3 .extractor .text .SubtitleParser ;
28+ import androidx .media3 .extractor .text .SubtitleParser .OutputOptions ;
2729import androidx .media3 .test .utils .TestUtil ;
2830import androidx .media3 .test .utils .truth .SpannedSubject ;
2931import androidx .test .core .app .ApplicationProvider ;
@@ -71,7 +73,7 @@ public void cuesReplacementBehaviorIsMerge() throws IOException {
7173 public void parseEmpty () throws IOException {
7274 SsaParser parser = new SsaParser ();
7375 byte [] bytes = TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), EMPTY );
74- List <CuesWithTiming > allCues = parser . parse ( bytes );
76+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
7577
7678 assertThat (allCues ).isEmpty ();
7779 }
@@ -81,7 +83,7 @@ public void parseEmptyStyleLine() throws IOException {
8183 SsaParser parser = new SsaParser ();
8284 byte [] bytes =
8385 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), EMPTY_STYLE_LINE );
84- List <CuesWithTiming > allCues = parser . parse ( bytes );
86+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
8587
8688 assertThat (allCues ).hasSize (1 );
8789 Cue cue = Iterables .getOnlyElement (allCues .get (0 ).cues );
@@ -101,7 +103,7 @@ public void parseEmptyStyleLine() throws IOException {
101103 public void parseTypical () throws IOException {
102104 SsaParser parser = new SsaParser ();
103105 byte [] bytes = TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), TYPICAL );
104- List <CuesWithTiming > allCues = parser . parse ( bytes );
106+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
105107
106108 assertThat (allCues ).hasSize (3 );
107109 // Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center).
@@ -122,6 +124,34 @@ public void parseTypical() throws IOException {
122124 assertTypicalCue3 (allCues .get (2 ));
123125 }
124126
127+ @ Test
128+ public void parseTypical_onlyCuesAfterTime () throws IOException {
129+ SsaParser parser = new SsaParser ();
130+ byte [] bytes = TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), TYPICAL );
131+ List <CuesWithTiming > cues = new ArrayList <>();
132+ parser .parse (bytes , OutputOptions .onlyCuesAfter (/* startTimeUs= */ 1_000_000 ), cues ::add );
133+
134+ assertThat (cues ).hasSize (2 );
135+ assertTypicalCue2 (cues .get (0 ));
136+ assertTypicalCue3 (cues .get (1 ));
137+ }
138+
139+ @ Test
140+ public void parseTypical_cuesAfterTimeThenCuesBefore () throws IOException {
141+ SsaParser parser = new SsaParser ();
142+ byte [] bytes = TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), TYPICAL );
143+ List <CuesWithTiming > cues = new ArrayList <>();
144+ parser .parse (
145+ bytes ,
146+ OutputOptions .cuesAfterThenRemainingCuesBefore (/* startTimeUs= */ 1_000_000 ),
147+ cues ::add );
148+
149+ assertThat (cues ).hasSize (3 );
150+ assertTypicalCue2 (cues .get (0 ));
151+ assertTypicalCue3 (cues .get (1 ));
152+ assertTypicalCue1 (cues .get (2 ));
153+ }
154+
125155 @ Test
126156 public void parseTypicalWithInitializationData () throws IOException {
127157 byte [] headerBytes =
@@ -134,7 +164,7 @@ public void parseTypicalWithInitializationData() throws IOException {
134164 SsaParser parser = new SsaParser (initializationData );
135165 byte [] bytes =
136166 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), TYPICAL_DIALOGUE_ONLY );
137- List <CuesWithTiming > allCues = parser . parse ( bytes );
167+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
138168
139169 assertThat (allCues ).hasSize (3 );
140170 assertTypicalCue1 (allCues .get (0 ));
@@ -155,8 +185,13 @@ public void parseTypicalWithInitializationDataAtOffsetIntoDialogueAndRestrictedL
155185 SsaParser parser = new SsaParser (initializationData );
156186 byte [] bytes =
157187 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), TYPICAL_DIALOGUE_ONLY );
158- ImmutableList <CuesWithTiming > allCues =
159- parser .parse (bytes , /* offset= */ 10 , /* length= */ bytes .length - 30 );
188+ List <CuesWithTiming > allCues = new ArrayList <>();
189+ parser .parse (
190+ bytes ,
191+ /* offset= */ 10 ,
192+ /* length= */ bytes .length - 30 ,
193+ OutputOptions .allCues (),
194+ allCues ::add );
160195
161196 assertThat (allCues ).hasSize (2 );
162197 // Because of the offset, we skip the first line of dialogue
@@ -173,7 +208,7 @@ public void parseTypicalUtf16le() throws IOException {
173208 SsaParser parser = new SsaParser ();
174209 byte [] bytes =
175210 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), TYPICAL_UTF16LE );
176- List <CuesWithTiming > allCues = parser . parse ( bytes );
211+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
177212
178213 assertThat (allCues ).hasSize (3 );
179214 // Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center).
@@ -199,7 +234,7 @@ public void parseTypicalUtf16be() throws IOException {
199234 SsaParser parser = new SsaParser ();
200235 byte [] bytes =
201236 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), TYPICAL_UTF16BE );
202- List <CuesWithTiming > allCues = parser . parse ( bytes );
237+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
203238
204239 assertThat (allCues ).hasSize (3 );
205240 // Check position, line, anchors & alignment are set from Alignment Style (2 - bottom-center).
@@ -225,7 +260,7 @@ public void parseOverlappingTimecodes() throws IOException {
225260 SsaParser parser = new SsaParser ();
226261 byte [] bytes =
227262 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), OVERLAPPING_TIMECODES );
228- List <CuesWithTiming > allCues = parser . parse ( bytes );
263+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
229264
230265 String firstSubtitleText = "First subtitle - end overlaps second" ;
231266 String secondSubtitleText = "Second subtitle - beginning overlaps first" ;
@@ -289,7 +324,7 @@ public void parseOverlappingTimecodes() throws IOException {
289324 public void parsePositions () throws IOException {
290325 SsaParser parser = new SsaParser ();
291326 byte [] bytes = TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), POSITIONS );
292- List <CuesWithTiming > allCues = parser . parse ( bytes );
327+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
293328
294329 // Check \pos() sets position & line
295330 Cue firstCue = Iterables .getOnlyElement (allCues .get (0 ).cues );
@@ -343,7 +378,7 @@ public void parseInvalidPositions() throws IOException {
343378 SsaParser parser = new SsaParser ();
344379 byte [] bytes =
345380 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), INVALID_POSITIONS );
346- List <CuesWithTiming > allCues = parser . parse ( bytes );
381+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
347382
348383 // Negative parameter to \pos() - fall back to the positions implied by middle-left alignment.
349384 Cue firstCue = Iterables .getOnlyElement (allCues .get (0 ).cues );
@@ -380,7 +415,7 @@ public void parsePositionsWithMissingPlayResY() throws IOException {
380415 byte [] bytes =
381416 TestUtil .getByteArray (
382417 ApplicationProvider .getApplicationContext (), POSITIONS_WITHOUT_PLAYRES );
383- List <CuesWithTiming > allCues = parser . parse ( bytes );
418+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
384419
385420 // The dialogue line has a valid \pos() override, but it's ignored because PlayResY isn't
386421 // set (so we don't know the denominator).
@@ -396,7 +431,7 @@ public void parseInvalidTimecodes() throws IOException {
396431 SsaParser parser = new SsaParser ();
397432 byte [] bytes =
398433 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), INVALID_TIMECODES );
399- List <CuesWithTiming > allCues = parser . parse ( bytes );
434+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
400435
401436 assertThat (allCues ).hasSize (1 );
402437 assertTypicalCue3 (Iterables .getOnlyElement (allCues ));
@@ -407,7 +442,7 @@ public void parsePrimaryColor() throws IOException {
407442 SsaParser parser = new SsaParser ();
408443 byte [] bytes =
409444 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), STYLE_PRIMARY_COLOR );
410- List <CuesWithTiming > allCues = parser . parse ( bytes );
445+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
411446 assertThat (allCues ).hasSize (7 );
412447 // &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB)
413448 Spanned firstCueText = (Spanned ) Iterables .getOnlyElement (allCues .get (0 ).cues ).text ;
@@ -449,7 +484,7 @@ public void parseOutlineColor() throws IOException {
449484 SsaParser parser = new SsaParser ();
450485 byte [] bytes =
451486 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), STYLE_OUTLINE_COLOR );
452- List <CuesWithTiming > allCues = parser . parse ( bytes );
487+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
453488 assertThat (allCues ).hasSize (2 );
454489 Spanned firstCueText = (Spanned ) Iterables .getOnlyElement (allCues .get (0 ).cues ).text ;
455490 SpannedSubject .assertThat (firstCueText )
@@ -467,7 +502,7 @@ public void parseFontSize() throws IOException {
467502 SsaParser parser = new SsaParser ();
468503 byte [] bytes =
469504 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), STYLE_FONT_SIZE );
470- List <CuesWithTiming > allCues = parser . parse ( bytes );
505+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
471506 assertThat (allCues ).hasSize (2 );
472507
473508 Cue firstCue = Iterables .getOnlyElement (allCues .get (0 ).cues );
@@ -483,7 +518,7 @@ public void parseBoldItalic() throws IOException {
483518 SsaParser parser = new SsaParser ();
484519 byte [] bytes =
485520 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), STYLE_BOLD_ITALIC );
486- List <CuesWithTiming > allCues = parser . parse ( bytes );
521+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
487522 assertThat (allCues ).hasSize (3 );
488523
489524 Spanned firstCueText = (Spanned ) Iterables .getOnlyElement (allCues .get (0 ).cues ).text ;
@@ -499,7 +534,7 @@ public void parseUnderline() throws IOException {
499534 SsaParser parser = new SsaParser ();
500535 byte [] bytes =
501536 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), STYLE_UNDERLINE );
502- List <CuesWithTiming > allCues = parser . parse ( bytes );
537+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
503538 assertThat (allCues ).hasSize (2 );
504539
505540 Spanned firstCueText = (Spanned ) Iterables .getOnlyElement (allCues .get (0 ).cues ).text ;
@@ -513,7 +548,7 @@ public void parseStrikeout() throws IOException {
513548 SsaParser parser = new SsaParser ();
514549 byte [] bytes =
515550 TestUtil .getByteArray (ApplicationProvider .getApplicationContext (), STYLE_STRIKEOUT );
516- List <CuesWithTiming > allCues = parser . parse ( bytes );
551+ ImmutableList <CuesWithTiming > allCues = parseAllCues ( parser , bytes );
517552 assertThat (allCues ).hasSize (2 );
518553
519554 Spanned firstCueText = (Spanned ) Iterables .getOnlyElement (allCues .get (0 ).cues ).text ;
@@ -523,6 +558,12 @@ public void parseStrikeout() throws IOException {
523558 .hasNoStrikethroughSpanBetween (0 , secondCueText .length ());
524559 }
525560
561+ private static ImmutableList <CuesWithTiming > parseAllCues (SubtitleParser parser , byte [] data ) {
562+ ImmutableList .Builder <CuesWithTiming > cues = ImmutableList .builder ();
563+ parser .parse (data , OutputOptions .allCues (), cues ::add );
564+ return cues .build ();
565+ }
566+
526567 private static void assertTypicalCue1 (CuesWithTiming cuesWithTiming ) {
527568 assertThat (cuesWithTiming .startTimeUs ).isEqualTo (0 );
528569 assertThat (cuesWithTiming .durationUs ).isEqualTo (1230000 );
0 commit comments