Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for SSA Bold Italic style #8654

Merged
merged 4 commits into from
Mar 12, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions demos/main/src/main/assets/media.exolist.json
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,13 @@
"subtitle_mime_type": "text/x-ssa",
"subtitle_language": "en"
},
{
"name": "SubStation Alpha Style",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/video-avc-baseline-480.mp4",
"subtitle_uri": "https://drive.google.com/uc?export=download&id=16IrvtynQ6-ANRpRX7hU6xEQeFU91LmXl",
"subtitle_mime_type": "text/x-ssa",
"subtitle_language": "en"
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for including this to make testing easier, it's really helpful - FYI I'll drop it before actually merging the PR though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I didn't realise this exercises more styles than just italic/bold - this does actually look like a useful file to add to the demo app (but probably not as part of this PR).

If you're happy to contribute this file, can you open a separate PR containing just the SSA file and I'll take it from there an upload it to our cloud bucket and add an entry to the demo app. Thanks!

{
"name": "MPEG-4 Timed Text",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mp4/dizzy-with-tx3g.mp4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
import static com.google.android.exoplayer2.text.Cue.LINE_TYPE_FRACTION;
import static com.google.android.exoplayer2.util.Util.castNonNull;

import android.graphics.Typeface;
import android.text.Layout;
import android.text.SpannableString;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.text.Cue;
Expand Down Expand Up @@ -319,6 +321,27 @@ private static Cue createCue(
style.fontSize / screenHeight,
Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING);
}
if (style.bold && style.italic) {
spannableText.setSpan(
new StyleSpan(Typeface.BOLD_ITALIC),
0,
spannableText.length(),
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if(style.bold){
Copy link
Contributor

@szaboa szaboa Mar 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't put the else in new lines, to be consistent with the rest of the code. Also if we want to make it really consistent I'd add these leading comments /* start= */ and /* end= */ at the start of the 2nd and 3rd params of setSpan - similarly how it is done for the primary color span.

And lastly, you have a couple of missing whitespaces, e.g. if(style.bold){ -> if (style.bold) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. I will fix this.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @szaboa - but don't worry too much about the styling/whitespace. When we merge PRs we run them through https://github.com/google/google-java-format (same for all the code we author ourselves), which sorts all this stuff out without needing to think too hard about it.

spannableText.setSpan(
new StyleSpan(Typeface.BOLD),
0,
spannableText.length(),
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
}
else if(style.italic){
spannableText.setSpan(
new StyleSpan(Typeface.ITALIC),
0,
spannableText.length(),
SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}

@SsaStyle.SsaAlignment int alignment;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,22 @@
@SsaAlignment public final int alignment;
@Nullable @ColorInt public final Integer primaryColor;
public final float fontSize;
public final boolean bold;
public final boolean italic;

private SsaStyle(
String name,
@SsaAlignment int alignment,
@Nullable @ColorInt Integer primaryColor,
float fontSize) {
float fontSize,
boolean bold,
boolean italic) {
this.name = name;
this.alignment = alignment;
this.primaryColor = primaryColor;
this.fontSize = fontSize;
this.bold = bold;
this.italic = italic;
}

@Nullable
Expand All @@ -121,7 +127,9 @@ public static SsaStyle fromStyleLine(String styleLine, Format format) {
styleValues[format.nameIndex].trim(),
parseAlignment(styleValues[format.alignmentIndex].trim()),
parseColor(styleValues[format.primaryColorIndex].trim()),
parseFontSize(styleValues[format.fontSizeIndex].trim()));
parseFontSize(styleValues[format.fontSizeIndex].trim()),
parseBold(styleValues[format.boldIndex].trim()),
parseItalic(styleValues[format.italicIndex].trim()));
} catch (RuntimeException e) {
Log.w(TAG, "Skipping malformed 'Style:' line: '" + styleLine + "'", e);
return null;
Expand Down Expand Up @@ -207,6 +215,36 @@ private static float parseFontSize(String fontSize) {
}
}

private static boolean parseBold(String bold) {
try {
int boldFlag = Integer.parseInt(bold);
if(boldFlag == 1 || boldFlag == -1){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the specs, only -1 means true, or am I missing something? Also you could simplify it by inlining the return like return Integer.parseInt(bold) == -1;. Same for parseItalic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah based on the specs it should only be -1 but when I was testing with vlc they took 1 as well. Do you think we should go for a clean implementation or a more forgiving one?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also checked the libass implementation and it seems they are doing the same (-1, 1): https://github.com/libass/libass/blob/82b225b3d6653091d028b39d561d185ed76a7be5/libass/ass_parse.c#L118

So IMO we can go with the more forgiving implementation, but lets wait for @icbaker 's confirmation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to more forgiving

though weirdly for italic it seems libass only consider 1 to mean true (just below the line @szaboa linked), while the spec only allows -1...? P

Maybe it would be easier just to do != 0?

return true;
}
else{
return false;
}
} catch (NumberFormatException e) {
Log.w(TAG, "Failed to parse bold: '" + bold + "'", e);
return false;
}
}

private static boolean parseItalic(String italic) {
try {
int italicFlag = Integer.parseInt(italic);
if(italicFlag == 1 || italicFlag == -1){
return true;
}
else{
return false;
}
} catch (NumberFormatException e) {
Log.w(TAG, "Failed to parse italic: '" + italic + "'", e);
return false;
}
}

/**
* Represents a {@code Format:} line from the {@code [V4+ Styles]} section
*
Expand All @@ -219,18 +257,24 @@ private static float parseFontSize(String fontSize) {
public final int alignmentIndex;
public final int primaryColorIndex;
public final int fontSizeIndex;
public final int boldIndex;
public final int italicIndex;
public final int length;

private Format(
int nameIndex,
int alignmentIndex,
int primaryColorIndex,
int fontSizeIndex,
int boldIndex,
int italicIndex,
int length) {
this.nameIndex = nameIndex;
this.alignmentIndex = alignmentIndex;
this.primaryColorIndex = primaryColorIndex;
this.fontSizeIndex = fontSizeIndex;
this.boldIndex = boldIndex;
this.italicIndex = italicIndex;
this.length = length;
}

Expand All @@ -245,6 +289,8 @@ public static Format fromFormatLine(String styleFormatLine) {
int alignmentIndex = C.INDEX_UNSET;
int primaryColorIndex = C.INDEX_UNSET;
int fontSizeIndex = C.INDEX_UNSET;
int boldIndex = C.INDEX_UNSET;
int italicIndex = C.INDEX_UNSET;
String[] keys =
TextUtils.split(styleFormatLine.substring(SsaDecoder.FORMAT_LINE_PREFIX.length()), ",");
for (int i = 0; i < keys.length; i++) {
Expand All @@ -261,10 +307,16 @@ public static Format fromFormatLine(String styleFormatLine) {
case "fontsize":
fontSizeIndex = i;
break;
case "bold":
boldIndex = i;
break;
case "italic":
italicIndex = i;
break;
}
}
return nameIndex != C.INDEX_UNSET
? new Format(nameIndex, alignmentIndex, primaryColorIndex, fontSizeIndex, keys.length)
? new Format(nameIndex, alignmentIndex, primaryColorIndex, fontSizeIndex, boldIndex, italicIndex, keys.length)
: null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertWithMessage;

import android.graphics.Color;
import android.graphics.Typeface;
import android.text.Layout;
import android.text.Spanned;
import androidx.test.core.app.ApplicationProvider;
Expand Down Expand Up @@ -49,6 +50,7 @@ public final class SsaDecoderTest {
private static final String POSITIONS_WITHOUT_PLAYRES = "media/ssa/positioning_without_playres";
private static final String STYLE_COLORS = "media/ssa/style_colors";
private static final String STYLE_FONT_SIZE = "media/ssa/style_font_size";
private static final String STYLE_BOLD_ITALIC = "media/ssa/style_bold_italic";

@Test
public void decodeEmpty() throws IOException {
Expand Down Expand Up @@ -335,6 +337,24 @@ public void decodeFontSize() throws IOException{
assertThat(secondCue.textSizeType).isEqualTo(Cue.TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING);
}

@Test
public void decodeBoldItalic() throws IOException{
SsaDecoder decoder = new SsaDecoder();
byte[] bytes = TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), STYLE_BOLD_ITALIC);
Subtitle subtitle = decoder.decode(bytes, bytes.length, false);
assertThat(subtitle.getEventTimeCount()).isEqualTo(6);

Spanned firstCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(0))).text;
SpannedSubject.assertThat(firstCueText).hasBoldSpanBetween(0, firstCueText.length());
Spanned secondCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(2))).text;
SpannedSubject.assertThat(secondCueText).hasItalicSpanBetween(0, secondCueText.length());
Spanned thirdCueText =
(Spanned) Iterables.getOnlyElement(subtitle.getCues(subtitle.getEventTime(4))).text;
SpannedSubject.assertThat(thirdCueText).hasBoldItalicSpanBetween(0, thirdCueText.length());
}

private static void assertTypicalCue1(Subtitle subtitle, int eventIndex) {
assertThat(subtitle.getEventTime(eventIndex)).isEqualTo(0);
assertThat(subtitle.getCues(subtitle.getEventTime(eventIndex)).get(0).text.toString())
Expand Down
20 changes: 20 additions & 0 deletions testdata/src/test/assets/media/ssa/style_bold_italic
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[Script Info]
Title: SSA/ASS Test
Original Script: Abel
Script Type: V4.00+
PlayResX: 1280
PlayResY: 720

[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: FontBold ,Roboto,50,&H000000FF,&H000000FF,&H00000000,&H00000000,-1,0,0,0,100,100,0,0,1,3,0,2,50,50,70,1
Style: FontItalic ,Roboto,50,&H000000FF,&H000000FF,&H00000000,&H00000000,0,-1,0,0,100,100,0,0,1,3,0,2,50,50,70,1
Style: FontBoldItalic ,Roboto,50,&H000000FF,&H000000FF,&H00000000,&H00000000,-1,-1,0,0,100,100,0,0,1,3,0,2,50,50,70,1



[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:01.00,0:00:03.00,FontBold ,Abel,0,0,0,,First line with Bold.
Dialogue: 0,0:00:05.00,0:00:07.00,FontItalic ,Abel,0,0,0,,Second line with Italic.
Dialogue: 0,0:00:09.00,0:00:11.00,FontBoldItalic ,Abel,0,0,0,,Third line with Bold Italic.