diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index bc3e4b6d7f..74ceadc863 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -41,6 +41,11 @@
(rendering is coming later).
* Fix `SubtitlePainter` to render `EDGE_TYPE_OUTLINE` using the correct color
([#6724](https://github.com/google/ExoPlayer/pull/6724)).
+ * Add support for WebVTT default
+ [text](https://www.w3.org/TR/webvtt1/#default-text-color) and
+ [background](https://www.w3.org/TR/webvtt1/#default-text-background) colors
+ ([PR #4178](https://github.com/google/ExoPlayer/pull/4178),
+ [issue #6581](https://github.com/google/ExoPlayer/issues/6581)).
* DRM: Add support for attaching DRM sessions to clear content in the demo app.
* Downloads: Merge downloads in `SegmentDownloader` to improve overall download
speed ([#5978](https://github.com/google/ExoPlayer/issues/5978)).
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 f62b073f60..c57d9d2164 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
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.text.webvtt;
import static com.google.android.exoplayer2.text.SpanUtil.addOrReplaceSpan;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.graphics.Color;
import android.graphics.Typeface;
import android.text.Layout;
import android.text.SpannableStringBuilder;
@@ -48,7 +49,9 @@ import java.lang.annotation.Retention;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@@ -137,6 +140,44 @@ public final class WebvttCueParser {
private static final String TAG = "WebvttCueParser";
+ /**
+ * See WebVTT's default text
+ * colors.
+ */
+ private static final Map DEFAULT_TEXT_COLORS;
+
+ static {
+ Map defaultColors = new HashMap<>();
+ defaultColors.put("white", Color.rgb(255, 255, 255));
+ defaultColors.put("lime", Color.rgb(0, 255, 0));
+ defaultColors.put("cyan", Color.rgb(0, 255, 255));
+ defaultColors.put("red", Color.rgb(255, 0, 0));
+ defaultColors.put("yellow", Color.rgb(255, 255, 0));
+ defaultColors.put("magenta", Color.rgb(255, 0, 255));
+ defaultColors.put("blue", Color.rgb(0, 0, 255));
+ defaultColors.put("black", Color.rgb(0, 0, 0));
+ DEFAULT_TEXT_COLORS = Collections.unmodifiableMap(defaultColors);
+ }
+
+ /**
+ * See WebVTT's default text
+ * background colors.
+ */
+ private static final Map DEFAULT_BACKGROUND_COLORS;
+
+ static {
+ Map defaultBackgroundColors = new HashMap<>();
+ defaultBackgroundColors.put("bg_white", Color.rgb(255, 255, 255));
+ defaultBackgroundColors.put("bg_lime", Color.rgb(0, 255, 0));
+ defaultBackgroundColors.put("bg_cyan", Color.rgb(0, 255, 255));
+ defaultBackgroundColors.put("bg_red", Color.rgb(255, 0, 0));
+ defaultBackgroundColors.put("bg_yellow", Color.rgb(255, 255, 0));
+ defaultBackgroundColors.put("bg_magenta", Color.rgb(255, 0, 255));
+ defaultBackgroundColors.put("bg_blue", Color.rgb(0, 0, 255));
+ defaultBackgroundColors.put("bg_black", Color.rgb(0, 0, 0));
+ DEFAULT_BACKGROUND_COLORS = Collections.unmodifiableMap(defaultBackgroundColors);
+ }
+
/**
* Parses the next valid WebVTT cue in a parsable array, including timestamps, settings and text.
*
@@ -514,6 +555,8 @@ public final class WebvttCueParser {
text.setSpan(new UnderlineSpan(), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
case TAG_CLASS:
+ applyDefaultColors(text, startTag.classes, start, end);
+ break;
case TAG_LANG:
case TAG_VOICE:
case "": // Case of the "whole cue" virtual tag.
@@ -529,6 +572,26 @@ public final class WebvttCueParser {
}
}
+ /**
+ * Adds {@link ForegroundColorSpan}s and {@link BackgroundColorSpan}s to {@code text} for entries
+ * in {@code classes} that match WebVTT's default text colors or default text background
+ * colors.
+ */
+ private static void applyDefaultColors(
+ SpannableStringBuilder text, String[] classes, int start, int end) {
+ for (String className : classes) {
+ if (DEFAULT_TEXT_COLORS.containsKey(className)) {
+ int color = DEFAULT_TEXT_COLORS.get(className);
+ text.setSpan(new ForegroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (DEFAULT_BACKGROUND_COLORS.containsKey(className)) {
+ int color = DEFAULT_BACKGROUND_COLORS.get(className);
+ text.setSpan(new BackgroundColorSpan(color), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ }
+
private static void applyStyleToText(SpannableStringBuilder spannedText, WebvttCssStyle style,
int start, int end) {
if (style == null) {
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 aa83fbc8ed..e308300d07 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
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.text.webvtt;
import static com.google.android.exoplayer2.testutil.truth.SpannedSubject.assertThat;
import static com.google.common.truth.Truth.assertThat;
+import android.graphics.Color;
import android.text.Spanned;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.android.exoplayer2.text.span.RubySpan;
@@ -79,6 +80,47 @@ public final class WebvttCueParserTest {
.withTextAndPosition("", RubySpan.POSITION_OVER);
}
+ @Test
+ public void testParseDefaultTextColor() throws Exception {
+ Spanned text = parseCueText("In this sentence this text is red");
+
+ assertThat(text.toString()).isEqualTo("In this sentence this text is red");
+ assertThat(text)
+ .hasForegroundColorSpanBetween(
+ "In this sentence ".length(), "In this sentence this text".length())
+ .withColor(Color.RED);
+ }
+
+ @Test
+ public void testParseUnsupportedDefaultTextColor() throws Exception {
+ Spanned text = parseCueText("In this sentence this text is not papaya");
+
+ assertThat(text.toString()).isEqualTo("In this sentence this text is not papaya");
+ assertThat(text).hasNoSpans();
+ }
+
+ @Test
+ public void testParseDefaultBackgroundColor() throws Exception {
+ Spanned text = parseCueText("In this sentence this text has a cyan background");
+
+ assertThat(text.toString()).isEqualTo("In this sentence this text has a cyan background");
+ assertThat(text)
+ .hasBackgroundColorSpanBetween(
+ "In this sentence ".length(), "In this sentence this text".length())
+ .withColor(Color.CYAN);
+ }
+
+ @Test
+ public void testParseUnsupportedDefaultBackgroundColor() throws Exception {
+ Spanned text =
+ parseCueText(
+ "In this sentence this text doesn't have a papaya background");
+
+ assertThat(text.toString())
+ .isEqualTo("In this sentence this text doesn't have a papaya background");
+ assertThat(text).hasNoSpans();
+ }
+
@Test
public void testParseWellFormedUnclosedEndAtCueEnd() throws Exception {
Spanned text = parseCueText("An unclosed u tag with "