Update WebvttParser
to implement new partial-output method
This requires adapting the 'to `CuesWithTiming` list' logic to work with
the new partial-output API, and that needs a private method so it's no
longer a good fit for a default method on `Subtitle` - hence moving it
to a new utility class.
Also update the implementation to never return `UNSET` duration (this is
an equivalent change to the `SsaParser` change in 9631923440
).
PiperOrigin-RevId: 566598094
This commit is contained in:
parent
24e700c216
commit
92a3f3a8cd
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.extractor.text;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.common.util.Consumer;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import java.util.List;
|
||||
|
||||
/** Utility methods for working with legacy {@link Subtitle} objects. */
|
||||
@UnstableApi
|
||||
public class LegacySubtitleUtil {
|
||||
|
||||
private LegacySubtitleUtil() {}
|
||||
|
||||
/**
|
||||
* Converts a {@link Subtitle} to a list of {@link CuesWithTiming} representing it, emitted to
|
||||
* {@code output}.
|
||||
*
|
||||
* <p>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.
|
||||
*/
|
||||
public static void toCuesWithTiming(
|
||||
Subtitle subtitle,
|
||||
SubtitleParser.OutputOptions outputOptions,
|
||||
Consumer<CuesWithTiming> output) {
|
||||
int startIndex =
|
||||
outputOptions.startTimeUs != C.TIME_UNSET
|
||||
? max(subtitle.getNextEventTimeIndex(outputOptions.startTimeUs) - 1, 0)
|
||||
: 0;
|
||||
for (int i = startIndex; i < subtitle.getEventTimeCount(); i++) {
|
||||
outputSubtitleEvent(subtitle, i, output);
|
||||
}
|
||||
if (outputOptions.outputAllCues) {
|
||||
for (int i = 0; i < startIndex; i++) {
|
||||
outputSubtitleEvent(subtitle, i, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void outputSubtitleEvent(
|
||||
Subtitle subtitle, int eventIndex, Consumer<CuesWithTiming> output) {
|
||||
long startTimeUs = subtitle.getEventTime(eventIndex);
|
||||
List<Cue> cuesForThisStartTime = subtitle.getCues(startTimeUs);
|
||||
if (cuesForThisStartTime.isEmpty()) {
|
||||
// An empty cue list has already been implicitly encoded in the duration of the previous
|
||||
// sample.
|
||||
return;
|
||||
} else if (eventIndex == subtitle.getEventTimeCount() - 1) {
|
||||
// The last cue list must be empty
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// It's safe to inspect element i+1, because we already exited the loop above if
|
||||
// i == getEventTimeCount() - 1.
|
||||
long durationUs = subtitle.getEventTime(eventIndex + 1) - subtitle.getEventTime(eventIndex);
|
||||
output.accept(new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs));
|
||||
}
|
||||
}
|
@ -18,7 +18,6 @@ package androidx.media3.extractor.text;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
|
||||
/** A subtitle consisting of timed {@link Cue}s. */
|
||||
@ -57,24 +56,4 @@ public interface Subtitle {
|
||||
* @return A list of cues that should be displayed, possibly empty.
|
||||
*/
|
||||
List<Cue> getCues(long timeUs);
|
||||
|
||||
/** Converts the current instance to a list of {@link CuesWithTiming} representing it. */
|
||||
// TODO(b/181312195): Remove this when TtmlDecoder has been migrated to TtmlParser (and in-line it
|
||||
// in DelegatingSubtitleDecoderTtmlParserTest).
|
||||
default ImmutableList<CuesWithTiming> toCuesWithTimingList() {
|
||||
ImmutableList.Builder<CuesWithTiming> allCues = ImmutableList.builder();
|
||||
for (int i = 0; i < getEventTimeCount(); i++) {
|
||||
long startTimeUs = getEventTime(i);
|
||||
List<Cue> cuesForThisStartTime = getCues(startTimeUs);
|
||||
if (cuesForThisStartTime.isEmpty() && i != 0) {
|
||||
// An empty cue list has already been implicitly encoded in the duration of the previous
|
||||
// sample (unless there was no previous sample).
|
||||
continue;
|
||||
}
|
||||
long durationUs =
|
||||
i < getEventTimeCount() - 1 ? getEventTime(i + 1) - getEventTime(i) : C.TIME_UNSET;
|
||||
allCues.add(new CuesWithTiming(cuesForThisStartTime, startTimeUs, durationUs));
|
||||
}
|
||||
return allCues.build();
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,11 @@ package androidx.media3.extractor.text.webvtt;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.ParserException;
|
||||
import androidx.media3.common.util.Consumer;
|
||||
import androidx.media3.common.util.ParsableByteArray;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.extractor.text.CuesWithTiming;
|
||||
import androidx.media3.extractor.text.LegacySubtitleUtil;
|
||||
import androidx.media3.extractor.text.SubtitleParser;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
@ -53,6 +55,18 @@ public final class WebvttParser implements SubtitleParser {
|
||||
|
||||
@Override
|
||||
public ImmutableList<CuesWithTiming> parse(byte[] data, int offset, int length) {
|
||||
ImmutableList.Builder<CuesWithTiming> result = ImmutableList.builder();
|
||||
parse(data, offset, length, OutputOptions.allCues(), result::add);
|
||||
return result.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parse(
|
||||
byte[] data,
|
||||
int offset,
|
||||
int length,
|
||||
OutputOptions outputOptions,
|
||||
Consumer<CuesWithTiming> output) {
|
||||
parsableWebvttData.reset(data, /* limit= */ offset + length);
|
||||
parsableWebvttData.setPosition(offset);
|
||||
List<WebvttCssStyle> definedStyles = new ArrayList<>();
|
||||
@ -85,7 +99,7 @@ public final class WebvttParser implements SubtitleParser {
|
||||
}
|
||||
}
|
||||
WebvttSubtitle subtitle = new WebvttSubtitle(cueInfos);
|
||||
return subtitle.toCuesWithTimingList();
|
||||
LegacySubtitleUtil.toCuesWithTiming(subtitle, outputOptions, output);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,13 +25,11 @@ import androidx.media3.common.text.TextAnnotation;
|
||||
import androidx.media3.common.text.TextEmphasisSpan;
|
||||
import androidx.media3.common.util.Assertions;
|
||||
import androidx.media3.common.util.ColorParser;
|
||||
import androidx.media3.extractor.text.CuesWithTiming;
|
||||
import androidx.media3.extractor.text.Subtitle;
|
||||
import androidx.media3.extractor.text.SubtitleDecoderException;
|
||||
import androidx.media3.test.utils.TestUtil;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.junit.Test;
|
||||
@ -877,39 +875,6 @@ public final class TtmlDecoderTest {
|
||||
assertThat(eighthCue.shearDegrees).isWithin(0.01f).of(90f);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTimingConversion() throws IOException, SubtitleDecoderException {
|
||||
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList = subtitle.toCuesWithTimingList();
|
||||
|
||||
assertThat(subtitle.getEventTimeCount()).isEqualTo(4);
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(1))).isEmpty();
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(3))).isEmpty();
|
||||
// cuesWithTimingsList has 2 fewer the events because it skips the empty cues
|
||||
assertThat(cuesWithTimingsList).hasSize(2);
|
||||
|
||||
subtitle = getSubtitle(INHERIT_STYLE_TTML_FILE);
|
||||
cuesWithTimingsList = subtitle.toCuesWithTimingList();
|
||||
|
||||
assertThat(subtitle.getEventTimeCount()).isEqualTo(2);
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(1))).isEmpty();
|
||||
// cuesWithTimingsList has 1 fewer events because it skips the empty cues
|
||||
assertThat(cuesWithTimingsList).hasSize(1);
|
||||
|
||||
subtitle = getSubtitle(INHERIT_MULTIPLE_STYLES_TTML_FILE);
|
||||
cuesWithTimingsList = subtitle.toCuesWithTimingList();
|
||||
|
||||
assertThat(subtitle.getEventTimeCount()).isEqualTo(12);
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(1))).isEmpty();
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(3))).isEmpty();
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(5))).isEmpty();
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(7))).isEmpty();
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(9))).isEmpty();
|
||||
assertThat(subtitle.getCues(subtitle.getEventTime(11))).isEmpty();
|
||||
// cuesWithTimingsList has 6 fewer events because it skips the empty cues
|
||||
assertThat(cuesWithTimingsList).hasSize(6);
|
||||
}
|
||||
|
||||
private static Spanned getOnlyCueTextAtTimeUs(Subtitle subtitle, long timeUs) {
|
||||
Cue cue = getOnlyCueAtTimeUs(subtitle, timeUs);
|
||||
assertThat(cue.text).isInstanceOf(Spanned.class);
|
||||
|
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.media3.extractor.text.webvtt;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import androidx.media3.extractor.text.CuesWithTiming;
|
||||
import androidx.media3.extractor.text.LegacySubtitleUtil;
|
||||
import androidx.media3.extractor.text.Subtitle;
|
||||
import androidx.media3.extractor.text.SubtitleParser;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.Arrays;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/**
|
||||
* Tests for {@link LegacySubtitleUtil} using {@link WebvttSubtitle}.
|
||||
*
|
||||
* <p>This is in the webvtt package so we don't need to increase the visibility of {@link
|
||||
* WebvttSubtitle}.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
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 simpleSubtitle =
|
||||
new WebvttSubtitle(
|
||||
Arrays.asList(
|
||||
new WebvttCueInfo(
|
||||
WebvttCueParser.newCueForText(FIRST_SUBTITLE_STRING),
|
||||
/* startTimeUs= */ 1_000_000,
|
||||
/* endTimeUs= */ 2_000_000),
|
||||
new WebvttCueInfo(
|
||||
WebvttCueParser.newCueForText(SECOND_SUBTITLE_STRING),
|
||||
/* startTimeUs= */ 3_000_000,
|
||||
/* endTimeUs= */ 4_000_000)));
|
||||
|
||||
private static final WebvttSubtitle overlappingSubtitle =
|
||||
new WebvttSubtitle(
|
||||
Arrays.asList(
|
||||
new WebvttCueInfo(
|
||||
WebvttCueParser.newCueForText(FIRST_SUBTITLE_STRING),
|
||||
/* startTimeUs= */ 1_000_000,
|
||||
/* endTimeUs= */ 3_000_000),
|
||||
new WebvttCueInfo(
|
||||
WebvttCueParser.newCueForText(SECOND_SUBTITLE_STRING),
|
||||
/* startTimeUs= */ 2_000_000,
|
||||
/* endTimeUs= */ 4_000_000)));
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_allCues_simpleSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(simpleSubtitle, SubtitleParser.OutputOptions.allCues());
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(2);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(1_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING);
|
||||
assertThat(cuesWithTimingsList.get(1).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(1).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(1).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_allCues_overlappingSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(overlappingSubtitle, SubtitleParser.OutputOptions.allCues());
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(3);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(1_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING);
|
||||
assertThat(cuesWithTimingsList.get(1).startTimeUs).isEqualTo(2_000_000);
|
||||
assertThat(cuesWithTimingsList.get(1).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(1).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING, SECOND_SUBTITLE_STRING)
|
||||
.inOrder();
|
||||
assertThat(cuesWithTimingsList.get(2).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(2).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(2).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_onlyEmitCuesAfterStartTime_startBetweenCues_simpleSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(simpleSubtitle, SubtitleParser.OutputOptions.onlyCuesAfter(2_500_000));
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(1);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_onlyEmitCuesAfterStartTime_startAtCueEnd_simpleSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(simpleSubtitle, SubtitleParser.OutputOptions.onlyCuesAfter(2_000_000));
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(1);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_onlyEmitCuesAfterStartTime_startAtCueStart_simpleSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(simpleSubtitle, SubtitleParser.OutputOptions.onlyCuesAfter(3_000_000));
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(1);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_onlyEmitCuesAfterStartTime_startInMiddleOfCue_simpleSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(simpleSubtitle, SubtitleParser.OutputOptions.onlyCuesAfter(1_500_000));
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(2);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(1_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING);
|
||||
assertThat(cuesWithTimingsList.get(1).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(1).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(1).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_onlyEmitCuesAfterStartTime_overlappingSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(
|
||||
overlappingSubtitle, SubtitleParser.OutputOptions.onlyCuesAfter(2_500_000));
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(2);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(2_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING, SECOND_SUBTITLE_STRING)
|
||||
.inOrder();
|
||||
assertThat(cuesWithTimingsList.get(1).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(1).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(1).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_emitCuesAfterStartTimeThenThoseBefore_simpleSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(
|
||||
simpleSubtitle,
|
||||
SubtitleParser.OutputOptions.cuesAfterThenRemainingCuesBefore(2_500_000));
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(2);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
assertThat(cuesWithTimingsList.get(1).startTimeUs).isEqualTo(1_000_000);
|
||||
assertThat(cuesWithTimingsList.get(1).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(1).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTiming_emitCuesAfterStartTimeThenThoseBefore_overlappingSubtitle() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList =
|
||||
toCuesWithTimingList(
|
||||
overlappingSubtitle,
|
||||
SubtitleParser.OutputOptions.cuesAfterThenRemainingCuesBefore(2_500_000));
|
||||
|
||||
assertThat(cuesWithTimingsList).hasSize(3);
|
||||
assertThat(cuesWithTimingsList.get(0).startTimeUs).isEqualTo(2_000_000);
|
||||
assertThat(cuesWithTimingsList.get(0).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(0).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING, SECOND_SUBTITLE_STRING)
|
||||
.inOrder();
|
||||
assertThat(cuesWithTimingsList.get(1).startTimeUs).isEqualTo(3_000_000);
|
||||
assertThat(cuesWithTimingsList.get(1).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(1).cues, c -> c.text))
|
||||
.containsExactly(SECOND_SUBTITLE_STRING);
|
||||
assertThat(cuesWithTimingsList.get(2).startTimeUs).isEqualTo(1_000_000);
|
||||
assertThat(cuesWithTimingsList.get(2).durationUs).isEqualTo(1_000_000);
|
||||
assertThat(Lists.transform(cuesWithTimingsList.get(2).cues, c -> c.text))
|
||||
.containsExactly(FIRST_SUBTITLE_STRING);
|
||||
}
|
||||
|
||||
private static ImmutableList<CuesWithTiming> toCuesWithTimingList(
|
||||
Subtitle subtitle, SubtitleParser.OutputOptions outputOptions) {
|
||||
ImmutableList.Builder<CuesWithTiming> result = ImmutableList.builder();
|
||||
LegacySubtitleUtil.toCuesWithTiming(subtitle, outputOptions, result::add);
|
||||
return result.build();
|
||||
}
|
||||
}
|
@ -20,9 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static java.lang.Long.MAX_VALUE;
|
||||
|
||||
import androidx.media3.common.text.Cue;
|
||||
import androidx.media3.extractor.text.CuesWithTiming;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@ -299,31 +297,6 @@ public class WebvttSubtitleTest {
|
||||
assertThat(nestedSubtitle.getCues(Long.MAX_VALUE)).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toCuesWithTimingConversion() {
|
||||
ImmutableList<CuesWithTiming> cuesWithTimingsList = simpleSubtitle.toCuesWithTimingList();
|
||||
|
||||
assertThat(simpleSubtitle.getEventTimeCount()).isEqualTo(4);
|
||||
assertThat(simpleSubtitle.getCues(simpleSubtitle.getEventTime(1))).isEmpty();
|
||||
assertThat(simpleSubtitle.getCues(simpleSubtitle.getEventTime(3))).isEmpty();
|
||||
// cuesWithTimingsList has half the events because it skips the empty cues
|
||||
assertThat(cuesWithTimingsList).hasSize(2);
|
||||
|
||||
cuesWithTimingsList = overlappingSubtitle.toCuesWithTimingList();
|
||||
|
||||
assertThat(overlappingSubtitle.getEventTimeCount()).isEqualTo(4);
|
||||
assertThat(overlappingSubtitle.getCues(overlappingSubtitle.getEventTime(3))).isEmpty();
|
||||
// cuesWithTimingsList has one fewer events because it skips the empty cues
|
||||
assertThat(cuesWithTimingsList).hasSize(3);
|
||||
|
||||
cuesWithTimingsList = nestedSubtitle.toCuesWithTimingList();
|
||||
|
||||
assertThat(nestedSubtitle.getEventTimeCount()).isEqualTo(4);
|
||||
assertThat(nestedSubtitle.getCues(nestedSubtitle.getEventTime(3))).isEmpty();
|
||||
// cuesWithTimingsList has one fewer events because it skips the empty cues
|
||||
assertThat(cuesWithTimingsList).hasSize(3);
|
||||
}
|
||||
|
||||
private static List<String> getCueTexts(List<Cue> cues) {
|
||||
List<String> cueTexts = new ArrayList<>();
|
||||
for (int i = 0; i < cues.size(); i++) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user