diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java
index 81ff0fdd65..82023e6c58 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java
@@ -15,6 +15,7 @@
*/
package com.google.android.exoplayer2.text.webvtt;
+import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
import com.google.android.exoplayer2.text.Subtitle;
@@ -41,12 +42,10 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
private static final int TYPE_vttc = 0x76747463;
private final ParsableByteArray sampleData;
- private final WebvttCueInfo.Builder builder;
public Mp4WebvttDecoder() {
super("Mp4WebvttDecoder");
sampleData = new ParsableByteArray();
- builder = new WebvttCueInfo.Builder();
}
@Override
@@ -63,7 +62,7 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
int boxSize = sampleData.readInt();
int boxType = sampleData.readInt();
if (boxType == TYPE_vttc) {
- resultingCueList.add(parseVttCueBox(sampleData, builder, boxSize - BOX_HEADER_SIZE));
+ resultingCueList.add(parseVttCueBox(sampleData, boxSize - BOX_HEADER_SIZE));
} else {
// Peers of the VTTCueBox are still not supported and are skipped.
sampleData.skipBytes(boxSize - BOX_HEADER_SIZE);
@@ -72,10 +71,10 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
return new Mp4WebvttSubtitle(resultingCueList);
}
- private static Cue parseVttCueBox(
- ParsableByteArray sampleData, WebvttCueInfo.Builder builder, int remainingCueBoxBytes)
+ private static Cue parseVttCueBox(ParsableByteArray sampleData, int remainingCueBoxBytes)
throws SubtitleDecoderException {
- builder.reset();
+ @Nullable Cue.Builder cueBuilder = null;
+ @Nullable CharSequence cueText = null;
while (remainingCueBoxBytes > 0) {
if (remainingCueBoxBytes < BOX_HEADER_SIZE) {
throw new SubtitleDecoderException("Incomplete vtt cue box header found.");
@@ -89,14 +88,20 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
sampleData.skipBytes(payloadLength);
remainingCueBoxBytes -= payloadLength;
if (boxType == TYPE_sttg) {
- WebvttCueParser.parseCueSettingsList(boxPayload, builder);
+ cueBuilder = WebvttCueParser.parseCueSettingsList(boxPayload);
} else if (boxType == TYPE_payl) {
- WebvttCueParser.parseCueText(null, boxPayload.trim(), builder, Collections.emptyList());
+ cueText =
+ WebvttCueParser.parseCueText(
+ /* id= */ null, boxPayload.trim(), /* styles= */ Collections.emptyList());
} else {
// Other VTTCueBox children are still not supported and are ignored.
}
}
- return builder.build().cue;
+ if (cueText == null) {
+ cueText = "";
+ }
+ return cueBuilder != null
+ ? cueBuilder.setText(cueText).build()
+ : WebvttCueParser.newCueForText(cueText);
}
-
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueInfo.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueInfo.java
index c57d14ac5c..b04e06d744 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueInfo.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueInfo.java
@@ -15,296 +15,19 @@
*/
package com.google.android.exoplayer2.text.webvtt;
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-import android.text.Layout.Alignment;
-import androidx.annotation.IntDef;
-import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.Cue;
-import com.google.android.exoplayer2.util.Assertions;
-import com.google.android.exoplayer2.util.Log;
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
/** A representation of a WebVTT cue. */
public final class WebvttCueInfo {
- /* package */ static final float DEFAULT_POSITION = 0.5f;
-
public final Cue cue;
public final long startTime;
public final long endTime;
- private WebvttCueInfo(
- long startTime,
- long endTime,
- CharSequence text,
- @Nullable Alignment textAlignment,
- float line,
- @Cue.LineType int lineType,
- @Cue.AnchorType int lineAnchor,
- float position,
- @Cue.AnchorType int positionAnchor,
- float width) {
- this.cue =
- new Cue(text, textAlignment, line, lineType, lineAnchor, position, positionAnchor, width);
- this.startTime = startTime;
- this.endTime = endTime;
- }
-
- /** Builder for WebVTT cues. */
- @SuppressWarnings("hiding")
- public static class Builder {
-
- /**
- * Valid values for {@link #setTextAlignment(int)}.
- *
- *
We use a custom list (and not {@link Alignment} directly) in order to include both {@code
- * START}/{@code LEFT} and {@code END}/{@code RIGHT}. The distinction is important for {@link
- * #derivePosition(int)}.
- *
- *
These correspond to the valid values for the 'align' cue setting in the WebVTT spec.
- */
- @Documented
- @Retention(SOURCE)
- @IntDef({
- TextAlignment.START,
- TextAlignment.CENTER,
- TextAlignment.END,
- TextAlignment.LEFT,
- TextAlignment.RIGHT
- })
- public @interface TextAlignment {
- /**
- * See WebVTT's align:start.
- */
- int START = 1;
- /**
- * See WebVTT's align:center.
- */
- int CENTER = 2;
- /**
- * See WebVTT's align:end.
- */
- int END = 3;
- /**
- * See WebVTT's align:left.
- */
- int LEFT = 4;
- /**
- * See WebVTT's align:right.
- */
- int RIGHT = 5;
- }
-
- private static final String TAG = "WebvttCueBuilder";
-
- private long startTime;
- private long endTime;
- @Nullable private CharSequence text;
- @TextAlignment private int textAlignment;
- private float line;
- // Equivalent to WebVTT's snap-to-lines flag:
- // https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag
- @Cue.LineType private int lineType;
- @Cue.AnchorType private int lineAnchor;
- private float position;
- @Cue.AnchorType private int positionAnchor;
- private float width;
-
- // Initialization methods
-
- // Calling reset() is forbidden because `this` isn't initialized. This can be safely
- // suppressed because reset() only assigns fields, it doesn't read any.
- @SuppressWarnings("nullness:method.invocation.invalid")
- public Builder() {
- reset();
- }
-
- public void reset() {
- startTime = 0;
- endTime = 0;
- text = null;
- // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment
- textAlignment = TextAlignment.CENTER;
- line = Cue.DIMEN_UNSET;
- // Defaults to NUMBER (true): https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag
- lineType = Cue.LINE_TYPE_NUMBER;
- // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-line-alignment
- lineAnchor = Cue.ANCHOR_TYPE_START;
- position = Cue.DIMEN_UNSET;
- positionAnchor = Cue.TYPE_UNSET;
- // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-size
- width = 1.0f;
- }
-
- // Construction methods.
-
- public WebvttCueInfo build() {
- line = computeLine(line, lineType);
-
- if (position == Cue.DIMEN_UNSET) {
- position = derivePosition(textAlignment);
- }
-
- if (positionAnchor == Cue.TYPE_UNSET) {
- positionAnchor = derivePositionAnchor(textAlignment);
- }
-
- width = Math.min(width, deriveMaxSize(positionAnchor, position));
-
- return new WebvttCueInfo(
- startTime,
- endTime,
- Assertions.checkNotNull(text),
- convertTextAlignment(textAlignment),
- line,
- lineType,
- lineAnchor,
- position,
- positionAnchor,
- width);
- }
-
- public Builder setStartTime(long time) {
- startTime = time;
- return this;
- }
-
- public Builder setEndTime(long time) {
- endTime = time;
- return this;
- }
-
- public Builder setText(CharSequence text) {
- this.text = text;
- return this;
- }
-
- public Builder setTextAlignment(@TextAlignment int textAlignment) {
- this.textAlignment = textAlignment;
- return this;
- }
-
- public Builder setLine(float line) {
- this.line = line;
- return this;
- }
-
- public Builder setLineType(@Cue.LineType int lineType) {
- this.lineType = lineType;
- return this;
- }
-
- public Builder setLineAnchor(@Cue.AnchorType int lineAnchor) {
- this.lineAnchor = lineAnchor;
- return this;
- }
-
- public Builder setPosition(float position) {
- this.position = position;
- return this;
- }
-
- public Builder setPositionAnchor(@Cue.AnchorType int positionAnchor) {
- this.positionAnchor = positionAnchor;
- return this;
- }
-
- public Builder setWidth(float width) {
- this.width = width;
- return this;
- }
-
- // https://www.w3.org/TR/webvtt1/#webvtt-cue-line
- private static float computeLine(float line, @Cue.LineType int lineType) {
- if (line != Cue.DIMEN_UNSET
- && lineType == Cue.LINE_TYPE_FRACTION
- && (line < 0.0f || line > 1.0f)) {
- return 1.0f; // Step 1
- } else if (line != Cue.DIMEN_UNSET) {
- // Step 2: Do nothing, line is already correct.
- return line;
- } else if (lineType == Cue.LINE_TYPE_FRACTION) {
- return 1.0f; // Step 3
- } else {
- // Steps 4 - 10 (stacking multiple simultaneous cues) are handled by
- // WebvttSubtitle.getCues(long) and WebvttSubtitle.isNormal(Cue).
- return Cue.DIMEN_UNSET;
- }
- }
-
- // https://www.w3.org/TR/webvtt1/#webvtt-cue-position
- private static float derivePosition(@TextAlignment int textAlignment) {
- switch (textAlignment) {
- case TextAlignment.LEFT:
- return 0.0f;
- case TextAlignment.RIGHT:
- return 1.0f;
- case TextAlignment.START:
- case TextAlignment.CENTER:
- case TextAlignment.END:
- default:
- return DEFAULT_POSITION;
- }
- }
-
- // https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment
- @Cue.AnchorType
- private static int derivePositionAnchor(@TextAlignment int textAlignment) {
- switch (textAlignment) {
- case TextAlignment.LEFT:
- case TextAlignment.START:
- return Cue.ANCHOR_TYPE_START;
- case TextAlignment.RIGHT:
- case TextAlignment.END:
- return Cue.ANCHOR_TYPE_END;
- case TextAlignment.CENTER:
- default:
- return Cue.ANCHOR_TYPE_MIDDLE;
- }
- }
-
- @Nullable
- private static Alignment convertTextAlignment(@TextAlignment int textAlignment) {
- switch (textAlignment) {
- case TextAlignment.START:
- case TextAlignment.LEFT:
- return Alignment.ALIGN_NORMAL;
- case TextAlignment.CENTER:
- return Alignment.ALIGN_CENTER;
- case TextAlignment.END:
- case TextAlignment.RIGHT:
- return Alignment.ALIGN_OPPOSITE;
- default:
- Log.w(TAG, "Unknown textAlignment: " + textAlignment);
- return null;
- }
- }
-
- // Step 2 here: https://www.w3.org/TR/webvtt1/#processing-cue-settings
- private static float deriveMaxSize(@Cue.AnchorType int positionAnchor, float position) {
- switch (positionAnchor) {
- case Cue.ANCHOR_TYPE_START:
- return 1.0f - position;
- case Cue.ANCHOR_TYPE_END:
- return position;
- case Cue.ANCHOR_TYPE_MIDDLE:
- if (position <= 0.5f) {
- return position * 2;
- } else {
- return (1.0f - position) * 2;
- }
- case Cue.TYPE_UNSET:
- default:
- throw new IllegalStateException(String.valueOf(positionAnchor));
- }
- }
+ public WebvttCueInfo(Cue cue, long startTimeUs, long endTimeUs) {
+ this.cue = cue;
+ this.startTime = startTimeUs;
+ this.endTime = endTimeUs;
}
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java
index e6fa78ca65..565d324828 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java
@@ -15,11 +15,14 @@
*/
package com.google.android.exoplayer2.text.webvtt;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.graphics.Typeface;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
+import android.text.SpannedString;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
@@ -30,6 +33,7 @@ import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.Cue;
@@ -37,19 +41,70 @@ import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** Parser for WebVTT cues. (https://w3c.github.io/webvtt/#cues) */
public final class WebvttCueParser {
+ /**
+ * Valid values for {@link WebvttCueInfoBuilder#textAlignment}.
+ *
+ *
We use a custom list (and not {@link Layout.Alignment} directly) in order to include both
+ * {@code START}/{@code LEFT} and {@code END}/{@code RIGHT}. The distinction is important for
+ * {@link WebvttCueInfoBuilder#derivePosition(int)}.
+ *
+ *
These correspond to the valid values for the 'align' cue setting in the WebVTT spec.
+ */
+ @Documented
+ @Retention(SOURCE)
+ @IntDef({
+ TEXT_ALIGNMENT_START,
+ TEXT_ALIGNMENT_CENTER,
+ TEXT_ALIGNMENT_END,
+ TEXT_ALIGNMENT_LEFT,
+ TEXT_ALIGNMENT_RIGHT
+ })
+ private @interface TextAlignment {}
+
+ /**
+ * See WebVTT's align:start.
+ */
+ private static final int TEXT_ALIGNMENT_START = 1;
+
+ /**
+ * See WebVTT's align:center.
+ */
+ private static final int TEXT_ALIGNMENT_CENTER = 2;
+
+ /**
+ * See WebVTT's align:end.
+ */
+ private static final int TEXT_ALIGNMENT_END = 3;
+
+ /**
+ * See WebVTT's align:left.
+ */
+ private static final int TEXT_ALIGNMENT_LEFT = 4;
+
+ /**
+ * See WebVTT's align:right.
+ */
+ private static final int TEXT_ALIGNMENT_RIGHT = 5;
+
public static final Pattern CUE_HEADER_PATTERN = Pattern
.compile("^(\\S+)\\s+-->\\s+(\\S+)(.*)?$");
-
private static final Pattern CUE_SETTING_PATTERN = Pattern.compile("(\\S+?):(\\S+)");
private static final char CHAR_LESS_THAN = '<';
@@ -74,92 +129,70 @@ public final class WebvttCueParser {
private static final int STYLE_BOLD = Typeface.BOLD;
private static final int STYLE_ITALIC = Typeface.ITALIC;
+ /* package */ static final float DEFAULT_POSITION = 0.5f;
+
private static final String TAG = "WebvttCueParser";
- private final StringBuilder textBuilder;
-
- public WebvttCueParser() {
- textBuilder = new StringBuilder();
- }
-
/**
* Parses the next valid WebVTT cue in a parsable array, including timestamps, settings and text.
*
* @param webvttData Parsable WebVTT file data.
- * @param builder Builder for WebVTT Cues (output parameter).
* @param styles List of styles defined by the CSS style blocks preceding the cues.
- * @return Whether a valid Cue was found.
+ * @return The parsed cue info, or null if no valid cue was found.
*/
- public boolean parseCue(
- ParsableByteArray webvttData, WebvttCueInfo.Builder builder, List styles) {
+ @Nullable
+ public static WebvttCueInfo parseCue(ParsableByteArray webvttData, List styles) {
@Nullable String firstLine = webvttData.readLine();
if (firstLine == null) {
- return false;
+ return null;
}
Matcher cueHeaderMatcher = WebvttCueParser.CUE_HEADER_PATTERN.matcher(firstLine);
if (cueHeaderMatcher.matches()) {
// We have found the timestamps in the first line. No id present.
- return parseCue(null, cueHeaderMatcher, webvttData, builder, textBuilder, styles);
+ return parseCue(null, cueHeaderMatcher, webvttData, styles);
}
// The first line is not the timestamps, but could be the cue id.
@Nullable String secondLine = webvttData.readLine();
if (secondLine == null) {
- return false;
+ return null;
}
cueHeaderMatcher = WebvttCueParser.CUE_HEADER_PATTERN.matcher(secondLine);
if (cueHeaderMatcher.matches()) {
// We can do the rest of the parsing, including the id.
- return parseCue(firstLine.trim(), cueHeaderMatcher, webvttData, builder, textBuilder,
- styles);
+ return parseCue(firstLine.trim(), cueHeaderMatcher, webvttData, styles);
}
- return false;
+ return null;
}
/**
* Parses a string containing a list of cue settings.
*
* @param cueSettingsList String containing the settings for a given cue.
- * @param builder The {@link WebvttCueInfo.Builder} where incremental construction takes place.
+ * @return The cue settings parsed into a {@link Cue.Builder}.
*/
- /* package */ static void parseCueSettingsList(
- String cueSettingsList, WebvttCueInfo.Builder builder) {
- // Parse the cue settings list.
- Matcher cueSettingMatcher = CUE_SETTING_PATTERN.matcher(cueSettingsList);
- while (cueSettingMatcher.find()) {
- String name = cueSettingMatcher.group(1);
- String value = cueSettingMatcher.group(2);
- try {
- if ("line".equals(name)) {
- parseLineAttribute(value, builder);
- } else if ("align".equals(name)) {
- builder.setTextAlignment(parseTextAlignment(value));
- } else if ("position".equals(name)) {
- parsePositionAttribute(value, builder);
- } else if ("size".equals(name)) {
- builder.setWidth(WebvttParserUtil.parsePercentage(value));
- } else {
- Log.w(TAG, "Unknown cue setting " + name + ":" + value);
- }
- } catch (NumberFormatException e) {
- Log.w(TAG, "Skipping bad cue setting: " + cueSettingMatcher.group());
- }
- }
+ /* package */ static Cue.Builder parseCueSettingsList(String cueSettingsList) {
+ WebvttCueInfoBuilder builder = new WebvttCueInfoBuilder();
+ parseCueSettingsList(cueSettingsList, builder);
+ return builder.toCueBuilder();
+ }
+
+ /** Create a new {@link Cue} containing {@code text} and with WebVTT default values. */
+ /* package */ static Cue newCueForText(CharSequence text) {
+ WebvttCueInfoBuilder infoBuilder = new WebvttCueInfoBuilder();
+ infoBuilder.text = text;
+ return infoBuilder.toCueBuilder().build();
}
/**
- * Parses the text payload of a WebVTT Cue and applies modifications on {@link
- * WebvttCueInfo.Builder}.
+ * Parses the text payload of a WebVTT Cue and returns it as a styled {@link SpannedString}.
*
- * @param id Id of the cue, {@code null} if it is not present.
+ * @param id ID of the cue, {@code null} if it is not present.
* @param markup The markup text to be parsed.
* @param styles List of styles defined by the CSS style blocks preceding the cues.
- * @param builder Output builder.
+ * @return The styled cue text.
*/
- /* package */ static void parseCueText(
- @Nullable String id,
- String markup,
- WebvttCueInfo.Builder builder,
- List styles) {
+ /* package */ static SpannedString parseCueText(
+ @Nullable String id, String markup, List styles) {
SpannableStringBuilder spannedText = new SpannableStringBuilder();
ArrayDeque startTagStack = new ArrayDeque<>();
List scratchStyleMatches = new ArrayList<>();
@@ -227,29 +260,31 @@ public final class WebvttCueParser {
}
applySpansForTag(id, StartTag.buildWholeCueVirtualTag(), spannedText, styles,
scratchStyleMatches);
- builder.setText(spannedText);
+ return SpannedString.valueOf(spannedText);
}
- private static boolean parseCue(
+ // Internal methods
+
+ @Nullable
+ private static WebvttCueInfo parseCue(
@Nullable String id,
Matcher cueHeaderMatcher,
ParsableByteArray webvttData,
- WebvttCueInfo.Builder builder,
- StringBuilder textBuilder,
List styles) {
+ WebvttCueInfoBuilder builder = new WebvttCueInfoBuilder();
try {
// Parse the cue start and end times.
- builder.setStartTime(WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(1)))
- .setEndTime(WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(2)));
+ builder.startTimeUs = WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(1));
+ builder.endTimeUs = WebvttParserUtil.parseTimestampUs(cueHeaderMatcher.group(2));
} catch (NumberFormatException e) {
Log.w(TAG, "Skipping cue with bad header: " + cueHeaderMatcher.group());
- return false;
+ return null;
}
parseCueSettingsList(cueHeaderMatcher.group(3), builder);
// Parse the cue text.
- textBuilder.setLength(0);
+ StringBuilder textBuilder = new StringBuilder();
for (String line = webvttData.readLine();
!TextUtils.isEmpty(line);
line = webvttData.readLine()) {
@@ -258,20 +293,44 @@ public final class WebvttCueParser {
}
textBuilder.append(line.trim());
}
- parseCueText(id, textBuilder.toString(), builder, styles);
- return true;
+ builder.text = parseCueText(id, textBuilder.toString(), styles);
+ return builder.build();
}
- // Internal methods
+ private static void parseCueSettingsList(String cueSettingsList, WebvttCueInfoBuilder builder) {
+ // Parse the cue settings list.
+ Matcher cueSettingMatcher = CUE_SETTING_PATTERN.matcher(cueSettingsList);
- private static void parseLineAttribute(String s, WebvttCueInfo.Builder builder) {
+ while (cueSettingMatcher.find()) {
+ String name = cueSettingMatcher.group(1);
+ String value = cueSettingMatcher.group(2);
+ try {
+ if ("line".equals(name)) {
+ parseLineAttribute(value, builder);
+ } else if ("align".equals(name)) {
+ builder.textAlignment = parseTextAlignment(value);
+ } else if ("position".equals(name)) {
+ parsePositionAttribute(value, builder);
+ } else if ("size".equals(name)) {
+ builder.size = WebvttParserUtil.parsePercentage(value);
+ } else {
+ Log.w(TAG, "Unknown cue setting " + name + ":" + value);
+ }
+ } catch (NumberFormatException e) {
+ Log.w(TAG, "Skipping bad cue setting: " + cueSettingMatcher.group());
+ }
+ }
+ }
+
+ private static void parseLineAttribute(String s, WebvttCueInfoBuilder builder) {
int commaIndex = s.indexOf(',');
if (commaIndex != -1) {
- builder.setLineAnchor(parsePositionAnchor(s.substring(commaIndex + 1)));
+ builder.lineAnchor = parsePositionAnchor(s.substring(commaIndex + 1));
s = s.substring(0, commaIndex);
}
if (s.endsWith("%")) {
- builder.setLine(WebvttParserUtil.parsePercentage(s)).setLineType(Cue.LINE_TYPE_FRACTION);
+ builder.line = WebvttParserUtil.parsePercentage(s);
+ builder.lineType = Cue.LINE_TYPE_FRACTION;
} else {
int lineNumber = Integer.parseInt(s);
if (lineNumber < 0) {
@@ -279,17 +338,18 @@ public final class WebvttCueParser {
// Cue defines it to be the first row that's not visible.
lineNumber--;
}
- builder.setLine(lineNumber).setLineType(Cue.LINE_TYPE_NUMBER);
+ builder.line = lineNumber;
+ builder.lineType = Cue.LINE_TYPE_NUMBER;
}
}
- private static void parsePositionAttribute(String s, WebvttCueInfo.Builder builder) {
+ private static void parsePositionAttribute(String s, WebvttCueInfoBuilder builder) {
int commaIndex = s.indexOf(',');
if (commaIndex != -1) {
- builder.setPositionAnchor(parsePositionAnchor(s.substring(commaIndex + 1)));
+ builder.positionAnchor = parsePositionAnchor(s.substring(commaIndex + 1));
s = s.substring(0, commaIndex);
}
- builder.setPosition(WebvttParserUtil.parsePercentage(s));
+ builder.position = WebvttParserUtil.parsePercentage(s);
}
@Cue.AnchorType
@@ -308,24 +368,24 @@ public final class WebvttCueParser {
}
}
- @WebvttCueInfo.Builder.TextAlignment
+ @TextAlignment
private static int parseTextAlignment(String s) {
switch (s) {
case "start":
- return WebvttCueInfo.Builder.TextAlignment.START;
+ return TEXT_ALIGNMENT_START;
case "left":
- return WebvttCueInfo.Builder.TextAlignment.LEFT;
+ return TEXT_ALIGNMENT_LEFT;
case "center":
case "middle":
- return WebvttCueInfo.Builder.TextAlignment.CENTER;
+ return TEXT_ALIGNMENT_CENTER;
case "end":
- return WebvttCueInfo.Builder.TextAlignment.END;
+ return TEXT_ALIGNMENT_END;
case "right":
- return WebvttCueInfo.Builder.TextAlignment.RIGHT;
+ return TEXT_ALIGNMENT_RIGHT;
default:
Log.w(TAG, "Invalid alignment value: " + s);
// Default value: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment
- return WebvttCueInfo.Builder.TextAlignment.CENTER;
+ return TEXT_ALIGNMENT_CENTER;
}
}
@@ -490,6 +550,151 @@ public final class WebvttCueParser {
Collections.sort(output);
}
+ private static final class WebvttCueInfoBuilder {
+
+ public long startTimeUs;
+ public long endTimeUs;
+ public @MonotonicNonNull CharSequence text;
+ @TextAlignment public int textAlignment;
+ public float line;
+ // Equivalent to WebVTT's snap-to-lines flag:
+ // https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag
+ @Cue.LineType public int lineType;
+ @Cue.AnchorType public int lineAnchor;
+ public float position;
+ @Cue.AnchorType public int positionAnchor;
+ public float size;
+
+ public WebvttCueInfoBuilder() {
+ startTimeUs = 0;
+ endTimeUs = 0;
+ // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-text-alignment
+ textAlignment = TEXT_ALIGNMENT_CENTER;
+ line = Cue.DIMEN_UNSET;
+ // Defaults to NUMBER (true): https://www.w3.org/TR/webvtt1/#webvtt-cue-snap-to-lines-flag
+ lineType = Cue.LINE_TYPE_NUMBER;
+ // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-line-alignment
+ lineAnchor = Cue.ANCHOR_TYPE_START;
+ position = Cue.DIMEN_UNSET;
+ positionAnchor = Cue.TYPE_UNSET;
+ // Default: https://www.w3.org/TR/webvtt1/#webvtt-cue-size
+ size = 1.0f;
+ }
+
+ public WebvttCueInfo build() {
+ return new WebvttCueInfo(toCueBuilder().build(), startTimeUs, endTimeUs);
+ }
+
+ public Cue.Builder toCueBuilder() {
+ float position =
+ this.position != Cue.DIMEN_UNSET ? this.position : derivePosition(textAlignment);
+ @Cue.AnchorType
+ int positionAnchor =
+ this.positionAnchor != Cue.TYPE_UNSET
+ ? this.positionAnchor
+ : derivePositionAnchor(textAlignment);
+ Cue.Builder cueBuilder =
+ new Cue.Builder()
+ .setTextAlignment(convertTextAlignment(textAlignment))
+ .setLine(computeLine(line, lineType), lineType)
+ .setLineAnchor(lineAnchor)
+ .setPosition(position)
+ .setPositionAnchor(positionAnchor)
+ .setSize(Math.min(size, deriveMaxSize(positionAnchor, position)));
+
+ if (text != null) {
+ cueBuilder.setText(text);
+ }
+
+ return cueBuilder;
+ }
+
+ // https://www.w3.org/TR/webvtt1/#webvtt-cue-line
+ private static float computeLine(float line, @Cue.LineType int lineType) {
+ if (line != Cue.DIMEN_UNSET
+ && lineType == Cue.LINE_TYPE_FRACTION
+ && (line < 0.0f || line > 1.0f)) {
+ return 1.0f; // Step 1
+ } else if (line != Cue.DIMEN_UNSET) {
+ // Step 2: Do nothing, line is already correct.
+ return line;
+ } else if (lineType == Cue.LINE_TYPE_FRACTION) {
+ return 1.0f; // Step 3
+ } else {
+ // Steps 4 - 10 (stacking multiple simultaneous cues) are handled by
+ // WebvttSubtitle.getCues(long) and WebvttSubtitle.isNormal(Cue).
+ return Cue.DIMEN_UNSET;
+ }
+ }
+
+ // https://www.w3.org/TR/webvtt1/#webvtt-cue-position
+ private static float derivePosition(@TextAlignment int textAlignment) {
+ switch (textAlignment) {
+ case TEXT_ALIGNMENT_LEFT:
+ return 0.0f;
+ case TEXT_ALIGNMENT_RIGHT:
+ return 1.0f;
+ case TEXT_ALIGNMENT_START:
+ case TEXT_ALIGNMENT_CENTER:
+ case TEXT_ALIGNMENT_END:
+ default:
+ return DEFAULT_POSITION;
+ }
+ }
+
+ // https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment
+ @Cue.AnchorType
+ private static int derivePositionAnchor(@TextAlignment int textAlignment) {
+ switch (textAlignment) {
+ case TEXT_ALIGNMENT_LEFT:
+ case TEXT_ALIGNMENT_START:
+ return Cue.ANCHOR_TYPE_START;
+ case TEXT_ALIGNMENT_RIGHT:
+ case TEXT_ALIGNMENT_END:
+ return Cue.ANCHOR_TYPE_END;
+ case TEXT_ALIGNMENT_CENTER:
+ default:
+ return Cue.ANCHOR_TYPE_MIDDLE;
+ }
+ }
+
+ @Nullable
+ private static Layout.Alignment convertTextAlignment(@TextAlignment int textAlignment) {
+ switch (textAlignment) {
+ case TEXT_ALIGNMENT_START:
+ case TEXT_ALIGNMENT_LEFT:
+ return Layout.Alignment.ALIGN_NORMAL;
+ case TEXT_ALIGNMENT_CENTER:
+ return Layout.Alignment.ALIGN_CENTER;
+ case TEXT_ALIGNMENT_END:
+ case TEXT_ALIGNMENT_RIGHT:
+ return Layout.Alignment.ALIGN_OPPOSITE;
+ default:
+ Log.w(TAG, "Unknown textAlignment: " + textAlignment);
+ return null;
+ }
+ }
+
+ // Step 2 here: https://www.w3.org/TR/webvtt1/#processing-cue-settings
+ private static float deriveMaxSize(@Cue.AnchorType int positionAnchor, float position) {
+ switch (positionAnchor) {
+ case Cue.ANCHOR_TYPE_START:
+ return 1.0f - position;
+ case Cue.ANCHOR_TYPE_END:
+ return position;
+ case Cue.ANCHOR_TYPE_MIDDLE:
+ if (position <= 0.5f) {
+ return position * 2;
+ } else {
+ return (1.0f - position) * 2;
+ }
+ case Cue.TYPE_UNSET:
+ default:
+ throw new IllegalStateException(String.valueOf(positionAnchor));
+ }
+ }
+ }
+
private static final class StyleMatch implements Comparable {
public final int score;
@@ -550,5 +755,4 @@ public final class WebvttCueParser {
}
}
-
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoder.java
index 6c1d61d126..fe36770aee 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoder.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoder.java
@@ -16,6 +16,7 @@
package com.google.android.exoplayer2.text.webvtt;
import android.text.TextUtils;
+import androidx.annotation.Nullable;
import com.google.android.exoplayer2.ParserException;
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
import com.google.android.exoplayer2.text.Subtitle;
@@ -40,28 +41,20 @@ public final class WebvttDecoder extends SimpleSubtitleDecoder {
private static final String COMMENT_START = "NOTE";
private static final String STYLE_START = "STYLE";
- private final WebvttCueParser cueParser;
private final ParsableByteArray parsableWebvttData;
- private final WebvttCueInfo.Builder webvttCueBuilder;
private final CssParser cssParser;
- private final List definedStyles;
public WebvttDecoder() {
super("WebvttDecoder");
- cueParser = new WebvttCueParser();
parsableWebvttData = new ParsableByteArray();
- webvttCueBuilder = new WebvttCueInfo.Builder();
cssParser = new CssParser();
- definedStyles = new ArrayList<>();
}
@Override
protected Subtitle decode(byte[] bytes, int length, boolean reset)
throws SubtitleDecoderException {
parsableWebvttData.reset(bytes, length);
- // Initialization for consistent starting state.
- webvttCueBuilder.reset();
- definedStyles.clear();
+ List definedStyles = new ArrayList<>();
// Validate the first line of the header, and skip the remainder.
try {
@@ -83,9 +76,10 @@ public final class WebvttDecoder extends SimpleSubtitleDecoder {
parsableWebvttData.readLine(); // Consume the "STYLE" header.
definedStyles.addAll(cssParser.parseBlock(parsableWebvttData));
} else if (event == EVENT_CUE) {
- if (cueParser.parseCue(parsableWebvttData, webvttCueBuilder, definedStyles)) {
- cueInfos.add(webvttCueBuilder.build());
- webvttCueBuilder.reset();
+ @Nullable
+ WebvttCueInfo cueInfo = WebvttCueParser.parseCue(parsableWebvttData, definedStyles);
+ if (cueInfo != null) {
+ cueInfos.add(cueInfo);
}
}
}
diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitle.java b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitle.java
index 83c588fb77..49ee73ea5a 100644
--- a/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitle.java
+++ b/library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitle.java
@@ -80,9 +80,9 @@ import java.util.List;
// individual cues, but tweaking their `line` value):
// https://www.w3.org/TR/webvtt1/#cue-computed-line
if (isNormal(cue)) {
- // we want to merge all of the normal cues into a single cue to ensure they are drawn
+ // We want to merge all of the normal cues into a single cue to ensure they are drawn
// correctly (i.e. don't overlap) and to emulate roll-up, but only if there are multiple
- // normal cues, otherwise we can just append the single normal cue
+ // normal cues, otherwise we can just append the single normal cue.
if (firstNormalCue == null) {
firstNormalCue = cue;
} else if (normalCueTextBuilder == null) {
@@ -100,10 +100,10 @@ import java.util.List;
}
}
if (normalCueTextBuilder != null) {
- // there were multiple normal cues, so create a new cue with all of the text
- list.add(new WebvttCueInfo.Builder().setText(normalCueTextBuilder).build().cue);
+ // There were multiple normal cues, so create a new cue with all of the text.
+ list.add(WebvttCueParser.newCueForText(normalCueTextBuilder));
} else if (firstNormalCue != null) {
- // there was only a single normal cue, so just add it to the list
+ // There was only a single normal cue, so just add it to the list.
list.add(firstNormalCue);
}
return list;
@@ -116,6 +116,6 @@ import java.util.List;
* @return Whether this cue should be placed in the default position.
*/
private static boolean isNormal(Cue cue) {
- return (cue.line == Cue.DIMEN_UNSET && cue.position == WebvttCueInfo.DEFAULT_POSITION);
+ return (cue.line == Cue.DIMEN_UNSET && cue.position == WebvttCueParser.DEFAULT_POSITION);
}
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoderTest.java
index 69d7caa832..5f91193699 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoderTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoderTest.java
@@ -92,7 +92,7 @@ public final class Mp4WebvttDecoderTest {
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Subtitle result = decoder.decode(SINGLE_CUE_SAMPLE, SINGLE_CUE_SAMPLE.length, false);
// Line feed must be trimmed by the decoder
- Cue expectedCue = new WebvttCueInfo.Builder().setText("Hello World").build().cue;
+ Cue expectedCue = WebvttCueParser.newCueForText("Hello World");
assertMp4WebvttSubtitleEquals(result, expectedCue);
}
@@ -100,8 +100,8 @@ public final class Mp4WebvttDecoderTest {
public void testTwoCuesSample() throws SubtitleDecoderException {
Mp4WebvttDecoder decoder = new Mp4WebvttDecoder();
Subtitle result = decoder.decode(DOUBLE_CUE_SAMPLE, DOUBLE_CUE_SAMPLE.length, false);
- Cue firstExpectedCue = new WebvttCueInfo.Builder().setText("Hello World").build().cue;
- Cue secondExpectedCue = new WebvttCueInfo.Builder().setText("Bye Bye").build().cue;
+ Cue firstExpectedCue = WebvttCueParser.newCueForText("Hello World");
+ Cue secondExpectedCue = WebvttCueParser.newCueForText("Bye Bye");
assertMp4WebvttSubtitleEquals(result, firstExpectedCue, secondExpectedCue);
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java
index ebaec594f1..d23ed00e95 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParserTest.java
@@ -217,13 +217,7 @@ public final class WebvttCueParserTest {
}
private static Spanned parseCueText(String string) {
- WebvttCueInfo.Builder builder = new WebvttCueInfo.Builder();
- WebvttCueParser.parseCueText(null, string, builder, Collections.emptyList());
- return (Spanned) builder.build().cue.text;
+ return WebvttCueParser.parseCueText(
+ /* id= */ null, string, /* styles= */ Collections.emptyList());
}
-
- private static T[] getSpans(Spanned text, Class spanType) {
- return text.getSpans(0, text.length(), spanType);
- }
-
}
diff --git a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitleTest.java b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitleTest.java
index 621751db94..61c6394db4 100644
--- a/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitleTest.java
+++ b/library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/WebvttSubtitleTest.java
@@ -21,7 +21,7 @@ import static java.lang.Long.MAX_VALUE;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.text.Cue;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
@@ -38,68 +38,41 @@ public class WebvttSubtitleTest {
private static final WebvttSubtitle emptySubtitle = new WebvttSubtitle(Collections.emptyList());
- private static final WebvttSubtitle simpleSubtitle;
+ 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)));
- static {
- ArrayList simpleSubtitleCues = new ArrayList<>();
- WebvttCueInfo firstCue =
- new WebvttCueInfo.Builder()
- .setStartTime(1000000)
- .setEndTime(2000000)
- .setText(FIRST_SUBTITLE_STRING)
- .build();
- simpleSubtitleCues.add(firstCue);
- WebvttCueInfo secondCue =
- new WebvttCueInfo.Builder()
- .setStartTime(3000000)
- .setEndTime(4000000)
- .setText(SECOND_SUBTITLE_STRING)
- .build();
- simpleSubtitleCues.add(secondCue);
- simpleSubtitle = new WebvttSubtitle(simpleSubtitleCues);
- }
+ 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)));
- private static final WebvttSubtitle overlappingSubtitle;
-
- static {
- ArrayList overlappingSubtitleCues = new ArrayList<>();
- WebvttCueInfo firstCue =
- new WebvttCueInfo.Builder()
- .setStartTime(1000000)
- .setEndTime(3000000)
- .setText(FIRST_SUBTITLE_STRING)
- .build();
- overlappingSubtitleCues.add(firstCue);
- WebvttCueInfo secondCue =
- new WebvttCueInfo.Builder()
- .setStartTime(2000000)
- .setEndTime(4000000)
- .setText(SECOND_SUBTITLE_STRING)
- .build();
- overlappingSubtitleCues.add(secondCue);
- overlappingSubtitle = new WebvttSubtitle(overlappingSubtitleCues);
- }
-
- private static final WebvttSubtitle nestedSubtitle;
-
- static {
- ArrayList nestedSubtitleCues = new ArrayList<>();
- WebvttCueInfo firstCue =
- new WebvttCueInfo.Builder()
- .setStartTime(1000000)
- .setEndTime(4000000)
- .setText(FIRST_SUBTITLE_STRING)
- .build();
- nestedSubtitleCues.add(firstCue);
- WebvttCueInfo secondCue =
- new WebvttCueInfo.Builder()
- .setStartTime(2000000)
- .setEndTime(3000000)
- .setText(SECOND_SUBTITLE_STRING)
- .build();
- nestedSubtitleCues.add(secondCue);
- nestedSubtitle = new WebvttSubtitle(nestedSubtitleCues);
- }
+ private static final WebvttSubtitle nestedSubtitle =
+ new WebvttSubtitle(
+ Arrays.asList(
+ new WebvttCueInfo(
+ WebvttCueParser.newCueForText(FIRST_SUBTITLE_STRING),
+ /* startTimeUs= */ 1_000_000,
+ /* endTimeUs= */ 4_000_000),
+ new WebvttCueInfo(
+ WebvttCueParser.newCueForText(SECOND_SUBTITLE_STRING),
+ /* startTimeUs= */ 2_000_000,
+ /* endTimeUs= */ 3_000_000)));
@Test
public void testEventCount() {
@@ -123,27 +96,27 @@ public class WebvttSubtitleTest {
public void testSimpleSubtitleText() {
// Test before first subtitle
assertSingleCueEmpty(simpleSubtitle.getCues(0));
- assertSingleCueEmpty(simpleSubtitle.getCues(500000));
- assertSingleCueEmpty(simpleSubtitle.getCues(999999));
+ assertSingleCueEmpty(simpleSubtitle.getCues(500_000));
+ assertSingleCueEmpty(simpleSubtitle.getCues(999_999));
// Test first subtitle
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getCues(1000000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getCues(1500000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getCues(1999999));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getCues(1_000_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getCues(1_500_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, simpleSubtitle.getCues(1_999_999));
// Test after first subtitle, before second subtitle
- assertSingleCueEmpty(simpleSubtitle.getCues(2000000));
- assertSingleCueEmpty(simpleSubtitle.getCues(2500000));
- assertSingleCueEmpty(simpleSubtitle.getCues(2999999));
+ assertSingleCueEmpty(simpleSubtitle.getCues(2_000_000));
+ assertSingleCueEmpty(simpleSubtitle.getCues(2_500_000));
+ assertSingleCueEmpty(simpleSubtitle.getCues(2_999_999));
// Test second subtitle
- assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getCues(3000000));
- assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getCues(3500000));
- assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getCues(3999999));
+ assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getCues(3_000_000));
+ assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getCues(3_500_000));
+ assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, simpleSubtitle.getCues(3_999_999));
// Test after second subtitle
- assertSingleCueEmpty(simpleSubtitle.getCues(4000000));
- assertSingleCueEmpty(simpleSubtitle.getCues(4500000));
+ assertSingleCueEmpty(simpleSubtitle.getCues(4_000_000));
+ assertSingleCueEmpty(simpleSubtitle.getCues(4_500_000));
assertSingleCueEmpty(simpleSubtitle.getCues(Long.MAX_VALUE));
}
@@ -161,30 +134,30 @@ public class WebvttSubtitleTest {
public void testOverlappingSubtitleText() {
// Test before first subtitle
assertSingleCueEmpty(overlappingSubtitle.getCues(0));
- assertSingleCueEmpty(overlappingSubtitle.getCues(500000));
- assertSingleCueEmpty(overlappingSubtitle.getCues(999999));
+ assertSingleCueEmpty(overlappingSubtitle.getCues(500_000));
+ assertSingleCueEmpty(overlappingSubtitle.getCues(999_999));
// Test first subtitle
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getCues(1000000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getCues(1500000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getCues(1999999));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getCues(1_000_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getCues(1_500_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, overlappingSubtitle.getCues(1_999_999));
// Test after first and second subtitle
- assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING,
- overlappingSubtitle.getCues(2000000));
- assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING,
- overlappingSubtitle.getCues(2500000));
- assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING,
- overlappingSubtitle.getCues(2999999));
+ assertSingleCueTextEquals(
+ FIRST_AND_SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(2_000_000));
+ assertSingleCueTextEquals(
+ FIRST_AND_SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(2_500_000));
+ assertSingleCueTextEquals(
+ FIRST_AND_SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(2_999_999));
// Test second subtitle
- assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(3000000));
- assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(3500000));
- assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(3999999));
+ assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(3_000_000));
+ assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(3_500_000));
+ assertSingleCueTextEquals(SECOND_SUBTITLE_STRING, overlappingSubtitle.getCues(3_999_999));
// Test after second subtitle
- assertSingleCueEmpty(overlappingSubtitle.getCues(4000000));
- assertSingleCueEmpty(overlappingSubtitle.getCues(4500000));
+ assertSingleCueEmpty(overlappingSubtitle.getCues(4_000_000));
+ assertSingleCueEmpty(overlappingSubtitle.getCues(4_500_000));
assertSingleCueEmpty(overlappingSubtitle.getCues(Long.MAX_VALUE));
}
@@ -202,61 +175,61 @@ public class WebvttSubtitleTest {
public void testNestedSubtitleText() {
// Test before first subtitle
assertSingleCueEmpty(nestedSubtitle.getCues(0));
- assertSingleCueEmpty(nestedSubtitle.getCues(500000));
- assertSingleCueEmpty(nestedSubtitle.getCues(999999));
+ assertSingleCueEmpty(nestedSubtitle.getCues(500_000));
+ assertSingleCueEmpty(nestedSubtitle.getCues(999_999));
// Test first subtitle
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(1000000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(1500000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(1999999));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(1_000_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(1_500_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(1_999_999));
// Test after first and second subtitle
- assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getCues(2000000));
- assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getCues(2500000));
- assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getCues(2999999));
+ assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getCues(2_000_000));
+ assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getCues(2_500_000));
+ assertSingleCueTextEquals(FIRST_AND_SECOND_SUBTITLE_STRING, nestedSubtitle.getCues(2_999_999));
// Test first subtitle
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(3000000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(3500000));
- assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(3999999));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(3_000_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(3_500_000));
+ assertSingleCueTextEquals(FIRST_SUBTITLE_STRING, nestedSubtitle.getCues(3_999_999));
// Test after second subtitle
- assertSingleCueEmpty(nestedSubtitle.getCues(4000000));
- assertSingleCueEmpty(nestedSubtitle.getCues(4500000));
+ assertSingleCueEmpty(nestedSubtitle.getCues(4_000_000));
+ assertSingleCueEmpty(nestedSubtitle.getCues(4_500_000));
assertSingleCueEmpty(nestedSubtitle.getCues(Long.MAX_VALUE));
}
private void testSubtitleEventTimesHelper(WebvttSubtitle subtitle) {
- assertThat(subtitle.getEventTime(0)).isEqualTo(1000000);
- assertThat(subtitle.getEventTime(1)).isEqualTo(2000000);
- assertThat(subtitle.getEventTime(2)).isEqualTo(3000000);
- assertThat(subtitle.getEventTime(3)).isEqualTo(4000000);
+ assertThat(subtitle.getEventTime(0)).isEqualTo(1_000_000);
+ assertThat(subtitle.getEventTime(1)).isEqualTo(2_000_000);
+ assertThat(subtitle.getEventTime(2)).isEqualTo(3_000_000);
+ assertThat(subtitle.getEventTime(3)).isEqualTo(4_000_000);
}
private void testSubtitleEventIndicesHelper(WebvttSubtitle subtitle) {
// Test first event
assertThat(subtitle.getNextEventTimeIndex(0)).isEqualTo(0);
- assertThat(subtitle.getNextEventTimeIndex(500000)).isEqualTo(0);
- assertThat(subtitle.getNextEventTimeIndex(999999)).isEqualTo(0);
+ assertThat(subtitle.getNextEventTimeIndex(500_000)).isEqualTo(0);
+ assertThat(subtitle.getNextEventTimeIndex(999_999)).isEqualTo(0);
// Test second event
- assertThat(subtitle.getNextEventTimeIndex(1000000)).isEqualTo(1);
- assertThat(subtitle.getNextEventTimeIndex(1500000)).isEqualTo(1);
- assertThat(subtitle.getNextEventTimeIndex(1999999)).isEqualTo(1);
+ assertThat(subtitle.getNextEventTimeIndex(1_000_000)).isEqualTo(1);
+ assertThat(subtitle.getNextEventTimeIndex(1_500_000)).isEqualTo(1);
+ assertThat(subtitle.getNextEventTimeIndex(1_999_999)).isEqualTo(1);
// Test third event
- assertThat(subtitle.getNextEventTimeIndex(2000000)).isEqualTo(2);
- assertThat(subtitle.getNextEventTimeIndex(2500000)).isEqualTo(2);
- assertThat(subtitle.getNextEventTimeIndex(2999999)).isEqualTo(2);
+ assertThat(subtitle.getNextEventTimeIndex(2_000_000)).isEqualTo(2);
+ assertThat(subtitle.getNextEventTimeIndex(2_500_000)).isEqualTo(2);
+ assertThat(subtitle.getNextEventTimeIndex(2_999_999)).isEqualTo(2);
// Test fourth event
- assertThat(subtitle.getNextEventTimeIndex(3000000)).isEqualTo(3);
- assertThat(subtitle.getNextEventTimeIndex(3500000)).isEqualTo(3);
- assertThat(subtitle.getNextEventTimeIndex(3999999)).isEqualTo(3);
+ assertThat(subtitle.getNextEventTimeIndex(3_000_000)).isEqualTo(3);
+ assertThat(subtitle.getNextEventTimeIndex(3_500_000)).isEqualTo(3);
+ assertThat(subtitle.getNextEventTimeIndex(3_999_999)).isEqualTo(3);
// Test null event (i.e. look for events after the last event)
- assertThat(subtitle.getNextEventTimeIndex(4000000)).isEqualTo(INDEX_UNSET);
- assertThat(subtitle.getNextEventTimeIndex(4500000)).isEqualTo(INDEX_UNSET);
+ assertThat(subtitle.getNextEventTimeIndex(4_000_000)).isEqualTo(INDEX_UNSET);
+ assertThat(subtitle.getNextEventTimeIndex(4_500_000)).isEqualTo(INDEX_UNSET);
assertThat(subtitle.getNextEventTimeIndex(MAX_VALUE)).isEqualTo(INDEX_UNSET);
}
@@ -268,5 +241,4 @@ public class WebvttSubtitleTest {
assertThat(cues).hasSize(1);
assertThat(cues.get(0).text.toString()).isEqualTo(expected);
}
-
}