From 2bd348effd6a6f14a892439aa9f0ef9b22789062 Mon Sep 17 00:00:00 2001 From: Egor Neliuba Date: Mon, 4 Apr 2022 18:26:31 +0300 Subject: [PATCH] Consider BorderStyle value before applying OutlineColour as background --- .../exoplayer2/text/ssa/SsaDecoder.java | 2 +- .../android/exoplayer2/text/ssa/SsaStyle.java | 67 ++++++++++++++++++- .../exoplayer2/text/ssa/SsaDecoderTest.java | 6 +- .../src/test/assets/media/ssa/style_colors | 20 +++--- 4 files changed, 82 insertions(+), 13 deletions(-) diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java b/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java index c02e2338ab..ba6b70499e 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java @@ -320,7 +320,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder { /* end= */ spannableText.length(), SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE); } - if (style.outlineColor != null) { + if (style.borderStyle == SsaStyle.SSA_BORDER_STYLE_BOX && style.outlineColor != null) { spannableText.setSpan( new BackgroundColorSpan(style.outlineColor), /* start= */ 0, diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java b/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java index 92821c02d9..4766306961 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/text/ssa/SsaStyle.java @@ -92,6 +92,31 @@ import java.util.regex.Pattern; public static final int SSA_ALIGNMENT_TOP_CENTER = 8; public static final int SSA_ALIGNMENT_TOP_RIGHT = 9; + /** + * The SSA/ASS BorderStyle. + * + *

Allowed values: + * + *

+ */ + @Target(TYPE_USE) + @IntDef({ + SSA_BORDER_STYLE_UNKNOWN, + SSA_BORDER_STYLE_OUTLINE, + SSA_BORDER_STYLE_BOX, + }) + @Documented + @Retention(SOURCE) + public @interface SsaBorderStyle {} + + public static final int SSA_BORDER_STYLE_UNKNOWN = -1; + public static final int SSA_BORDER_STYLE_OUTLINE = 1; + public static final int SSA_BORDER_STYLE_BOX = 3; + public final String name; public final @SsaAlignment int alignment; @Nullable @ColorInt public final Integer primaryColor; @@ -101,6 +126,7 @@ import java.util.regex.Pattern; public final boolean italic; public final boolean underline; public final boolean strikeout; + public final @SsaBorderStyle int borderStyle; private SsaStyle( String name, @@ -111,7 +137,8 @@ import java.util.regex.Pattern; boolean bold, boolean italic, boolean underline, - boolean strikeout) { + boolean strikeout, + int borderStyle) { this.name = name; this.alignment = alignment; this.primaryColor = primaryColor; @@ -121,6 +148,7 @@ import java.util.regex.Pattern; this.italic = italic; this.underline = underline; this.strikeout = strikeout; + this.borderStyle = borderStyle; } @Nullable @@ -157,7 +185,10 @@ import java.util.regex.Pattern; format.underlineIndex != C.INDEX_UNSET && parseBooleanValue(styleValues[format.underlineIndex].trim()), format.strikeoutIndex != C.INDEX_UNSET - && parseBooleanValue(styleValues[format.strikeoutIndex].trim())); + && parseBooleanValue(styleValues[format.strikeoutIndex].trim()), + format.borderStyleIndex != C.INDEX_UNSET + ? parseBorderStyle(styleValues[format.borderStyleIndex].trim()) + : SSA_BORDER_STYLE_UNKNOWN); } catch (RuntimeException e) { Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e); return null; @@ -195,6 +226,30 @@ import java.util.regex.Pattern; } } + private static @SsaBorderStyle int parseBorderStyle(String borderStyleStr) { + try { + @SsaBorderStyle int borderStyle = Integer.parseInt(borderStyleStr.trim()); + if (isValidBorderStyle(borderStyle)) { + return borderStyle; + } + } catch (NumberFormatException e) { + // Swallow the exception and return UNKNOWN below. + } + Log.w(TAG, "Ignoring unknown BorderStyle: " + borderStyleStr); + return SSA_BORDER_STYLE_UNKNOWN; + } + + private static boolean isValidBorderStyle(@SsaBorderStyle int alignment) { + switch (alignment) { + case SSA_BORDER_STYLE_OUTLINE: + case SSA_BORDER_STYLE_BOX: + return true; + case SSA_BORDER_STYLE_UNKNOWN: + default: + return false; + } + } + /** * Parses a SSA V4+ color expression. * @@ -269,6 +324,7 @@ import java.util.regex.Pattern; public final int italicIndex; public final int underlineIndex; public final int strikeoutIndex; + public final int borderStyleIndex; public final int length; private Format( @@ -281,6 +337,7 @@ import java.util.regex.Pattern; int italicIndex, int underlineIndex, int strikeoutIndex, + int borderStyleIndex, int length) { this.nameIndex = nameIndex; this.alignmentIndex = alignmentIndex; @@ -291,6 +348,7 @@ import java.util.regex.Pattern; this.italicIndex = italicIndex; this.underlineIndex = underlineIndex; this.strikeoutIndex = strikeoutIndex; + this.borderStyleIndex = borderStyleIndex; this.length = length; } @@ -310,6 +368,7 @@ import java.util.regex.Pattern; int italicIndex = C.INDEX_UNSET; int underlineIndex = C.INDEX_UNSET; int strikeoutIndex = C.INDEX_UNSET; + int borderStyleIndex = C.INDEX_UNSET; String[] keys = TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ","); for (int i = 0; i < keys.length; i++) { @@ -341,6 +400,9 @@ import java.util.regex.Pattern; case "strikeout": strikeoutIndex = i; break; + case "borderstyle": + borderStyleIndex = i; + break; } } return nameIndex != C.INDEX_UNSET @@ -354,6 +416,7 @@ import java.util.regex.Pattern; italicIndex, underlineIndex, strikeoutIndex, + borderStyleIndex, keys.length) : null; } diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/text/ssa/SsaDecoderTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/text/ssa/SsaDecoderTest.java index a4001a2d4b..60eb6008c9 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/text/ssa/SsaDecoderTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/text/ssa/SsaDecoderTest.java @@ -301,7 +301,7 @@ public final class SsaDecoderTest { SsaDecoder decoder = new SsaDecoder(); byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_COLORS); Subtitle subtitle = decoder.decode(bytes, bytes.length, false); - assertThat(subtitle.getEventTimeCount()).isEqualTo(16); + assertThat(subtitle.getEventTimeCount()).isEqualTo(18); // &H000000FF (AABBGGRR) -> #FFFF0000 (AARRGGBB) Spanned firstCueText = (Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(0))).text; @@ -342,11 +342,15 @@ public final class SsaDecoderTest { (Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(12))).text; SpannedSubject.assertThat(seventhCueText) .hasNoForegroundColorSpanBetween(0, seventhCueText.length()); + // OutlineColour should be treated as background only when BorderStyle=3 Spanned eighthCueText = (Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(14))).text; SpannedSubject.assertThat(eighthCueText) .hasBackgroundColorSpanBetween(0, eighthCueText.length()) .withColor(Color.BLUE); + Spanned ninthCueText = + (Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(16))).text; + SpannedSubject.assertThat(ninthCueText).hasNoBackgroundColorSpanBetween(0, ninthCueText.length()); } @Test diff --git a/testdata/src/test/assets/media/ssa/style_colors b/testdata/src/test/assets/media/ssa/style_colors index fe31e66380..a0ec2240c0 100644 --- a/testdata/src/test/assets/media/ssa/style_colors +++ b/testdata/src/test/assets/media/ssa/style_colors @@ -5,15 +5,16 @@ PlayResX: 1280 PlayResY: 720 [V4+ Styles] -Format: Name ,PrimaryColour,OutlineColour -Style: PrimaryColourStyleHexRed ,&H000000FF ,&H00000000 -Style: PrimaryColourStyleHexYellow ,&H0000FFFF ,&H00000000 -Style: PrimaryColourStyleHexGreen ,&HFF00 ,&H00000000 -Style: PrimaryColourStyleHexAlpha ,&HA00000FF ,&H00000000 -Style: PrimaryColourStyleDecimal ,16711680 ,&H00000000 -Style: PrimaryColourStyleDecimalAlpha,2164195328 ,&H00000000 -Style: PrimaryColourStyleInvalid ,blue ,&H00000000 -Style: OutlineColourStyleBlue ,&H00000000 ,&H00FF0000 +Format: Name ,PrimaryColour,OutlineColour,BorderStyle +Style: PrimaryColourStyleHexRed ,&H000000FF ,&H00000000 ,3 +Style: PrimaryColourStyleHexYellow ,&H0000FFFF ,&H00000000 ,3 +Style: PrimaryColourStyleHexGreen ,&HFF00 ,&H00000000 ,3 +Style: PrimaryColourStyleHexAlpha ,&HA00000FF ,&H00000000 ,3 +Style: PrimaryColourStyleDecimal ,16711680 ,&H00000000 ,3 +Style: PrimaryColourStyleDecimalAlpha,2164195328 ,&H00000000 ,3 +Style: PrimaryColourStyleInvalid ,blue ,&H00000000 ,3 +Style: OutlineColourStyleBlue ,&H00000000 ,&H00FF0000 ,3 +Style: OutlineColourStyleIgnored ,&H00000000 ,&H00FF0000 ,1 [Events] @@ -26,3 +27,4 @@ Dialogue: 0:00:09.00,0:00:10.00,PrimaryColourStyleDecimal ,Fifth line in BLU Dialogue: 0:00:11.00,0:00:12.00,PrimaryColourStyleDecimalAlpha,Sixth line in BLUE with alpha (2164195328). Dialogue: 0:00:13.00,0:00:14.00,PrimaryColourInvalid ,Seventh line with invalid color. Dialogue: 0:00:15.00,0:00:16.00,OutlineColourStyleBlue ,Eighth line with BLUE (&H00FF0000) outline. +Dialogue: 0:00:17.00,0:00:18.00,OutlineColourStyleIgnored ,Ninth line with ignored outline because BorderStyle is not 3.