Subrip cleanup
This commit is contained in:
parent
4d6b008ef6
commit
4347bf0416
@ -8,6 +8,8 @@
|
|||||||
* Fix issue where subtitles have a wrong position if SubtitleView has a non-zero
|
* Fix issue where subtitles have a wrong position if SubtitleView has a non-zero
|
||||||
offset to its parent
|
offset to its parent
|
||||||
([#4788](https://github.com/google/ExoPlayer/issues/4788)).
|
([#4788](https://github.com/google/ExoPlayer/issues/4788)).
|
||||||
|
* SubRip: Add support for alignment tags, and remove tags from the displayed
|
||||||
|
captions ([#4306](https://github.com/google/ExoPlayer/issues/4306)).
|
||||||
|
|
||||||
### 2.9.0 ###
|
### 2.9.0 ###
|
||||||
|
|
||||||
|
@ -15,9 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.text.subrip;
|
package com.google.android.exoplayer2.text.subrip;
|
||||||
|
|
||||||
import android.support.annotation.StringDef;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.Html;
|
import android.text.Html;
|
||||||
import android.text.Layout;
|
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import com.google.android.exoplayer2.text.Cue;
|
import com.google.android.exoplayer2.text.Cue;
|
||||||
@ -25,9 +24,6 @@ import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
|
|||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import com.google.android.exoplayer2.util.LongArray;
|
import com.google.android.exoplayer2.util.LongArray;
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -37,6 +33,11 @@ import java.util.regex.Pattern;
|
|||||||
*/
|
*/
|
||||||
public final class SubripDecoder extends SimpleSubtitleDecoder {
|
public final class SubripDecoder extends SimpleSubtitleDecoder {
|
||||||
|
|
||||||
|
// Fractional positions for use when alignment tags are present.
|
||||||
|
/* package */ static final float START_FRACTION = 0.08f;
|
||||||
|
/* package */ static final float END_FRACTION = 1 - START_FRACTION;
|
||||||
|
/* package */ static final float MID_FRACTION = 0.5f;
|
||||||
|
|
||||||
private static final String TAG = "SubripDecoder";
|
private static final String TAG = "SubripDecoder";
|
||||||
|
|
||||||
private static final String SUBRIP_TIMECODE = "(?:(\\d+):)?(\\d+):(\\d+),(\\d+)";
|
private static final String SUBRIP_TIMECODE = "(?:(\\d+):)?(\\d+):(\\d+),(\\d+)";
|
||||||
@ -46,35 +47,24 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
|||||||
private static final Pattern SUBRIP_TAG_PATTERN = Pattern.compile("\\{\\\\.*?\\}");
|
private static final Pattern SUBRIP_TAG_PATTERN = Pattern.compile("\\{\\\\.*?\\}");
|
||||||
private static final String SUBRIP_ALIGNMENT_TAG = "\\{\\\\an[1-9]\\}";
|
private static final String SUBRIP_ALIGNMENT_TAG = "\\{\\\\an[1-9]\\}";
|
||||||
|
|
||||||
private static final float DEFAULT_START_FRACTION = 0.08f;
|
// Alignment tags for SSA V4+.
|
||||||
private static final float DEFAULT_END_FRACTION = 1 - DEFAULT_START_FRACTION;
|
private static final String ALIGN_BOTTOM_LEFT = "{\\an1}";
|
||||||
private static final float DEFAULT_MID_FRACTION = 0.5f;
|
private static final String ALIGN_BOTTOM_MID = "{\\an2}";
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
@StringDef({
|
|
||||||
ALIGN_BOTTOM_LEFT, ALIGN_BOTTOM_MID, ALIGN_BOTTOM_RIGHT,
|
|
||||||
ALIGN_MID_LEFT, ALIGN_MID_MID, ALIGN_MID_RIGHT,
|
|
||||||
ALIGN_TOP_LEFT, ALIGN_TOP_MID, ALIGN_TOP_RIGHT
|
|
||||||
})
|
|
||||||
|
|
||||||
private @interface SubRipTag {}
|
|
||||||
|
|
||||||
// Possible valid alignment tags based on SSA v4+ specs
|
|
||||||
private static final String ALIGN_BOTTOM_LEFT = "{\\an1}";
|
|
||||||
private static final String ALIGN_BOTTOM_MID = "{\\an2}";
|
|
||||||
private static final String ALIGN_BOTTOM_RIGHT = "{\\an3}";
|
private static final String ALIGN_BOTTOM_RIGHT = "{\\an3}";
|
||||||
private static final String ALIGN_MID_LEFT = "{\\an4}";
|
private static final String ALIGN_MID_LEFT = "{\\an4}";
|
||||||
private static final String ALIGN_MID_MID = "{\\an5}";
|
private static final String ALIGN_MID_MID = "{\\an5}";
|
||||||
private static final String ALIGN_MID_RIGHT = "{\\an6}";
|
private static final String ALIGN_MID_RIGHT = "{\\an6}";
|
||||||
private static final String ALIGN_TOP_LEFT = "{\\an7}";
|
private static final String ALIGN_TOP_LEFT = "{\\an7}";
|
||||||
private static final String ALIGN_TOP_MID = "{\\an8}";
|
private static final String ALIGN_TOP_MID = "{\\an8}";
|
||||||
private static final String ALIGN_TOP_RIGHT = "{\\an9}";
|
private static final String ALIGN_TOP_RIGHT = "{\\an9}";
|
||||||
|
|
||||||
private final StringBuilder textBuilder;
|
private final StringBuilder textBuilder;
|
||||||
|
private final ArrayList<String> tags;
|
||||||
|
|
||||||
public SubripDecoder() {
|
public SubripDecoder() {
|
||||||
super("SubripDecoder");
|
super("SubripDecoder");
|
||||||
textBuilder = new StringBuilder();
|
textBuilder = new StringBuilder();
|
||||||
|
tags = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -118,9 +108,9 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and parse the text.
|
// Read and parse the text and tags.
|
||||||
ArrayList<String> tags = new ArrayList<>();
|
|
||||||
textBuilder.setLength(0);
|
textBuilder.setLength(0);
|
||||||
|
tags.clear();
|
||||||
while (!TextUtils.isEmpty(currentLine = subripData.readLine())) {
|
while (!TextUtils.isEmpty(currentLine = subripData.readLine())) {
|
||||||
if (textBuilder.length() > 0) {
|
if (textBuilder.length() > 0) {
|
||||||
textBuilder.append("<br>");
|
textBuilder.append("<br>");
|
||||||
@ -129,21 +119,17 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Spanned text = Html.fromHtml(textBuilder.toString());
|
Spanned text = Html.fromHtml(textBuilder.toString());
|
||||||
Cue cue = null;
|
|
||||||
|
|
||||||
// At end of this loop the clue must be created with the applied tags
|
String alignmentTag = null;
|
||||||
for (String tag : tags) {
|
for (int i = 0; i < tags.size(); i++) {
|
||||||
|
String tag = tags.get(i);
|
||||||
// Check if the tag is an alignment tag
|
|
||||||
if (tag.matches(SUBRIP_ALIGNMENT_TAG)) {
|
if (tag.matches(SUBRIP_ALIGNMENT_TAG)) {
|
||||||
cue = buildCue(text, tag);
|
alignmentTag = tag;
|
||||||
|
// Subsequent alignment tags should be ignored.
|
||||||
// Based on the specs, in case of alignment tags only the first appearance counts, so break
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cues.add(buildCue(text, alignmentTag));
|
||||||
cues.add(cue == null ? new Cue(text) : cue);
|
|
||||||
|
|
||||||
if (haveEndTimecode) {
|
if (haveEndTimecode) {
|
||||||
cues.add(null);
|
cues.add(null);
|
||||||
@ -157,108 +143,93 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the given line by first trimming it then extracting the tags from it
|
* Trims and removes tags from the given line. The removed tags are added to {@code tags}.
|
||||||
* <p>
|
|
||||||
* The pattern that is used to extract the tags is specified in SSA v4+ specs and
|
|
||||||
* has the following form: "{\...}".
|
|
||||||
* <p>
|
|
||||||
* "All override codes appear within braces {}"
|
|
||||||
* "All override codes are always preceded by a backslash \"
|
|
||||||
*
|
*
|
||||||
* @param currentLine Current line
|
* @param line The line to process.
|
||||||
* @param tags Extracted tags will be stored in this array list
|
* @param tags A list to which removed tags will be added.
|
||||||
* @return Processed line
|
* @return The processed line.
|
||||||
*/
|
*/
|
||||||
private String processLine(String currentLine, ArrayList<String> tags) {
|
private String processLine(String line, ArrayList<String> tags) {
|
||||||
// Trim line
|
line = line.trim();
|
||||||
String trimmedLine = currentLine.trim();
|
|
||||||
|
|
||||||
// Extract tags
|
|
||||||
int replacedCharacters = 0;
|
|
||||||
StringBuilder processedLine = new StringBuilder(trimmedLine);
|
|
||||||
Matcher matcher = SUBRIP_TAG_PATTERN.matcher(trimmedLine);
|
|
||||||
|
|
||||||
|
int removedCharacterCount = 0;
|
||||||
|
StringBuilder processedLine = new StringBuilder(line);
|
||||||
|
Matcher matcher = SUBRIP_TAG_PATTERN.matcher(line);
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String tag = matcher.group();
|
String tag = matcher.group();
|
||||||
tags.add(tag);
|
tags.add(tag);
|
||||||
processedLine.replace(matcher.start() - replacedCharacters, matcher.end() - replacedCharacters, "");
|
int start = matcher.start() - removedCharacterCount;
|
||||||
replacedCharacters += tag.length();
|
int tagLength = tag.length();
|
||||||
|
processedLine.replace(start, /* end= */ start + tagLength, /* str= */ "");
|
||||||
|
removedCharacterCount += tagLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
return processedLine.toString();
|
return processedLine.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a {@link Cue} based on the given text and tag
|
* Build a {@link Cue} based on the given text and alignment tag.
|
||||||
* <p>
|
|
||||||
* Match the alignment tag and calculate the line, position, position anchor accordingly
|
|
||||||
* <p>
|
|
||||||
* Based on SSA v4+ specs the alignment tag can have the following form: {\an[1-9},
|
|
||||||
* where the number specifies the direction (based on the numpad layout).
|
|
||||||
* Note. older SSA scripts may contain tags like {\a1[1-9]} but these are based on
|
|
||||||
* other direction rules, but multiple sources says that these are deprecated, so no support here either
|
|
||||||
*
|
*
|
||||||
* @param alignmentTag Alignment tag
|
* @param text The text.
|
||||||
|
* @param alignmentTag The alignment tag, or {@code null} if no alignment tag is available.
|
||||||
* @return Built cue
|
* @return Built cue
|
||||||
*/
|
*/
|
||||||
private Cue buildCue(Spanned text, String alignmentTag) {
|
private Cue buildCue(Spanned text, @Nullable String alignmentTag) {
|
||||||
float line, position;
|
if (alignmentTag == null) {
|
||||||
@Cue.AnchorType int positionAnchor;
|
return new Cue(text);
|
||||||
@Cue.AnchorType int lineAnchor;
|
}
|
||||||
|
|
||||||
// Set position and position anchor (horizontal alignment)
|
// Horizontal alignment.
|
||||||
|
@Cue.AnchorType int positionAnchor;
|
||||||
switch (alignmentTag) {
|
switch (alignmentTag) {
|
||||||
case ALIGN_BOTTOM_LEFT:
|
case ALIGN_BOTTOM_LEFT:
|
||||||
case ALIGN_MID_LEFT:
|
case ALIGN_MID_LEFT:
|
||||||
case ALIGN_TOP_LEFT:
|
case ALIGN_TOP_LEFT:
|
||||||
position = DEFAULT_START_FRACTION;
|
|
||||||
positionAnchor = Cue.ANCHOR_TYPE_START;
|
positionAnchor = Cue.ANCHOR_TYPE_START;
|
||||||
break;
|
break;
|
||||||
case ALIGN_BOTTOM_MID:
|
|
||||||
case ALIGN_MID_MID:
|
|
||||||
case ALIGN_TOP_MID:
|
|
||||||
position = DEFAULT_MID_FRACTION;
|
|
||||||
positionAnchor = Cue.ANCHOR_TYPE_MIDDLE;
|
|
||||||
break;
|
|
||||||
case ALIGN_BOTTOM_RIGHT:
|
case ALIGN_BOTTOM_RIGHT:
|
||||||
case ALIGN_MID_RIGHT:
|
case ALIGN_MID_RIGHT:
|
||||||
case ALIGN_TOP_RIGHT:
|
case ALIGN_TOP_RIGHT:
|
||||||
position = DEFAULT_END_FRACTION;
|
|
||||||
positionAnchor = Cue.ANCHOR_TYPE_END;
|
positionAnchor = Cue.ANCHOR_TYPE_END;
|
||||||
break;
|
break;
|
||||||
|
case ALIGN_BOTTOM_MID:
|
||||||
|
case ALIGN_MID_MID:
|
||||||
|
case ALIGN_TOP_MID:
|
||||||
default:
|
default:
|
||||||
position = DEFAULT_MID_FRACTION;
|
|
||||||
positionAnchor = Cue.ANCHOR_TYPE_MIDDLE;
|
positionAnchor = Cue.ANCHOR_TYPE_MIDDLE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set line and line anchor (vertical alignment)
|
// Vertical alignment.
|
||||||
|
@Cue.AnchorType int lineAnchor;
|
||||||
switch (alignmentTag) {
|
switch (alignmentTag) {
|
||||||
case ALIGN_BOTTOM_LEFT:
|
case ALIGN_BOTTOM_LEFT:
|
||||||
case ALIGN_BOTTOM_MID:
|
case ALIGN_BOTTOM_MID:
|
||||||
case ALIGN_BOTTOM_RIGHT:
|
case ALIGN_BOTTOM_RIGHT:
|
||||||
line = DEFAULT_END_FRACTION;
|
|
||||||
lineAnchor = Cue.ANCHOR_TYPE_END;
|
lineAnchor = Cue.ANCHOR_TYPE_END;
|
||||||
break;
|
break;
|
||||||
case ALIGN_MID_LEFT:
|
|
||||||
case ALIGN_MID_MID:
|
|
||||||
case ALIGN_MID_RIGHT:
|
|
||||||
line = DEFAULT_MID_FRACTION;
|
|
||||||
lineAnchor = Cue.ANCHOR_TYPE_MIDDLE;
|
|
||||||
break;
|
|
||||||
case ALIGN_TOP_LEFT:
|
case ALIGN_TOP_LEFT:
|
||||||
case ALIGN_TOP_MID:
|
case ALIGN_TOP_MID:
|
||||||
case ALIGN_TOP_RIGHT:
|
case ALIGN_TOP_RIGHT:
|
||||||
line = DEFAULT_START_FRACTION;
|
|
||||||
lineAnchor = Cue.ANCHOR_TYPE_START;
|
lineAnchor = Cue.ANCHOR_TYPE_START;
|
||||||
break;
|
break;
|
||||||
|
case ALIGN_MID_LEFT:
|
||||||
|
case ALIGN_MID_MID:
|
||||||
|
case ALIGN_MID_RIGHT:
|
||||||
default:
|
default:
|
||||||
line = DEFAULT_END_FRACTION;
|
lineAnchor = Cue.ANCHOR_TYPE_MIDDLE;
|
||||||
lineAnchor = Cue.ANCHOR_TYPE_END;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Cue(text, null, line, Cue.LINE_TYPE_FRACTION, lineAnchor, position, positionAnchor, Cue.DIMEN_UNSET);
|
return new Cue(
|
||||||
|
text,
|
||||||
|
/* textAlignment= */ null,
|
||||||
|
getFractionalPositionForAnchorType(lineAnchor),
|
||||||
|
Cue.LINE_TYPE_FRACTION,
|
||||||
|
lineAnchor,
|
||||||
|
getFractionalPositionForAnchorType(positionAnchor),
|
||||||
|
positionAnchor,
|
||||||
|
Cue.DIMEN_UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long parseTimecode(Matcher matcher, int groupOffset) {
|
private static long parseTimecode(Matcher matcher, int groupOffset) {
|
||||||
@ -268,4 +239,16 @@ public final class SubripDecoder extends SimpleSubtitleDecoder {
|
|||||||
timestampMs += Long.parseLong(matcher.group(groupOffset + 4));
|
timestampMs += Long.parseLong(matcher.group(groupOffset + 4));
|
||||||
return timestampMs * 1000;
|
return timestampMs * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* package */ static float getFractionalPositionForAnchorType(@Cue.AnchorType int anchorType) {
|
||||||
|
switch (anchorType) {
|
||||||
|
case Cue.ANCHOR_TYPE_START:
|
||||||
|
return SubripDecoder.START_FRACTION;
|
||||||
|
case Cue.ANCHOR_TYPE_MIDDLE:
|
||||||
|
return SubripDecoder.MID_FRACTION;
|
||||||
|
case Cue.ANCHOR_TYPE_END:
|
||||||
|
default:
|
||||||
|
return SubripDecoder.END_FRACTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ This {\an2} is the third {\ tag} subtitle.
|
|||||||
|
|
||||||
4
|
4
|
||||||
00:00:09,567 --> 00:00:12,901
|
00:00:09,567 --> 00:00:12,901
|
||||||
This { \an2} is the fourth subtitle.
|
This { \an2} is not a valid tag due to the space after the opening bracket.
|
||||||
|
|
||||||
5
|
5
|
||||||
00:00:013,567 --> 00:00:14,901
|
00:00:013,567 --> 00:00:14,901
|
||||||
@ -53,4 +53,4 @@ This {\an8} is a line.
|
|||||||
|
|
||||||
14
|
14
|
||||||
00:00:024,567 --> 00:00:24,901
|
00:00:024,567 --> 00:00:24,901
|
||||||
This {\an9} is a line.
|
This {\an9} is a line.
|
||||||
|
@ -19,7 +19,6 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
import com.google.android.exoplayer2.text.Cue;
|
import com.google.android.exoplayer2.text.Cue;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -158,89 +157,30 @@ public final class SubripDecoderTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDecodeCueWithTag() throws IOException{
|
public void testDecodeCueWithTag() throws IOException {
|
||||||
SubripDecoder decoder = new SubripDecoder();
|
SubripDecoder decoder = new SubripDecoder();
|
||||||
byte[] bytes = TestUtil.getByteArray(RuntimeEnvironment.application, TYPICAL_WITH_TAGS);
|
byte[] bytes = TestUtil.getByteArray(RuntimeEnvironment.application, TYPICAL_WITH_TAGS);
|
||||||
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
|
SubripSubtitle subtitle = decoder.decode(bytes, bytes.length, false);
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(0)).get(0).text.toString())
|
|
||||||
.isEqualTo("This is the first subtitle.");
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(2)).get(0).text.toString())
|
|
||||||
.isEqualTo("This is the second subtitle.\nSecond subtitle with second line.");
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(4)).get(0).text.toString())
|
|
||||||
.isEqualTo("This is the third subtitle.");
|
|
||||||
|
|
||||||
// Based on the SSA v4+ specs the curly bracket must be followed by a backslash, so this is
|
assertTypicalCue1(subtitle, 0);
|
||||||
// not a valid tag (won't be parsed / replaced)
|
assertTypicalCue2(subtitle, 2);
|
||||||
|
assertTypicalCue3(subtitle, 4);
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(6)).get(0).text.toString())
|
assertThat(subtitle.getCues(subtitle.getEventTime(6)).get(0).text.toString())
|
||||||
.isEqualTo("This { \\an2} is the fourth subtitle.");
|
.isEqualTo("This { \\an2} is not a valid tag due to the space after the opening bracket.");
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(8)).get(0).text.toString())
|
assertThat(subtitle.getCues(subtitle.getEventTime(8)).get(0).text.toString())
|
||||||
.isEqualTo("This is the fifth subtitle with multiple valid tags.");
|
.isEqualTo("This is the fifth subtitle with multiple valid tags.");
|
||||||
|
|
||||||
// Verify positions
|
assertAlignmentCue(subtitle, 10, Cue.ANCHOR_TYPE_END, Cue.ANCHOR_TYPE_START); // {/an1}
|
||||||
|
assertAlignmentCue(subtitle, 12, Cue.ANCHOR_TYPE_END, Cue.ANCHOR_TYPE_MIDDLE); // {/an2}
|
||||||
// {/an1}
|
assertAlignmentCue(subtitle, 14, Cue.ANCHOR_TYPE_END, Cue.ANCHOR_TYPE_END); // {/an3}
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(10)).get(0).positionAnchor)
|
assertAlignmentCue(subtitle, 16, Cue.ANCHOR_TYPE_MIDDLE, Cue.ANCHOR_TYPE_START); // {/an4}
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_START);
|
assertAlignmentCue(subtitle, 18, Cue.ANCHOR_TYPE_MIDDLE, Cue.ANCHOR_TYPE_MIDDLE); // {/an5}
|
||||||
|
assertAlignmentCue(subtitle, 20, Cue.ANCHOR_TYPE_MIDDLE, Cue.ANCHOR_TYPE_END); // {/an6}
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(10)).get(0).lineAnchor)
|
assertAlignmentCue(subtitle, 22, Cue.ANCHOR_TYPE_START, Cue.ANCHOR_TYPE_START); // {/an7}
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_END);
|
assertAlignmentCue(subtitle, 24, Cue.ANCHOR_TYPE_START, Cue.ANCHOR_TYPE_MIDDLE); // {/an8}
|
||||||
|
assertAlignmentCue(subtitle, 26, Cue.ANCHOR_TYPE_START, Cue.ANCHOR_TYPE_END); // {/an9}
|
||||||
// {/an2}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(12)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_MIDDLE);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(12)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_END);
|
|
||||||
|
|
||||||
// {/an3}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(14)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_END);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(14)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_END);
|
|
||||||
|
|
||||||
// {/an4}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(16)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_START);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(16)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_MIDDLE);
|
|
||||||
|
|
||||||
// {/an5}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(18)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_MIDDLE);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(18)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_MIDDLE);
|
|
||||||
|
|
||||||
// {/an6}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(20)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_END);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(20)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_MIDDLE);
|
|
||||||
|
|
||||||
// {/an7}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(22)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_START);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(22)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_START);
|
|
||||||
|
|
||||||
// {/an8}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(24)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_MIDDLE);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(24)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_START);
|
|
||||||
|
|
||||||
// {/an9}
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(26)).get(0).positionAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_END);
|
|
||||||
|
|
||||||
assertThat(subtitle.getCues(subtitle.getEventTime(26)).get(0).lineAnchor)
|
|
||||||
.isEqualTo(Cue.ANCHOR_TYPE_START);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertTypicalCue1(SubripSubtitle subtitle, int eventIndex) {
|
private static void assertTypicalCue1(SubripSubtitle subtitle, int eventIndex) {
|
||||||
@ -263,4 +203,19 @@ public final class SubripDecoderTest {
|
|||||||
.isEqualTo("This is the third subtitle.");
|
.isEqualTo("This is the third subtitle.");
|
||||||
assertThat(subtitle.getEventTime(eventIndex + 1)).isEqualTo(8901000);
|
assertThat(subtitle.getEventTime(eventIndex + 1)).isEqualTo(8901000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void assertAlignmentCue(
|
||||||
|
SubripSubtitle subtitle,
|
||||||
|
int eventIndex,
|
||||||
|
@Cue.AnchorType int lineAnchor,
|
||||||
|
@Cue.AnchorType int positionAnchor) {
|
||||||
|
long eventTimeUs = subtitle.getEventTime(eventIndex);
|
||||||
|
Cue cue = subtitle.getCues(eventTimeUs).get(0);
|
||||||
|
assertThat(cue.lineType).isEqualTo(Cue.LINE_TYPE_FRACTION);
|
||||||
|
assertThat(cue.lineAnchor).isEqualTo(lineAnchor);
|
||||||
|
assertThat(cue.line).isEqualTo(SubripDecoder.getFractionalPositionForAnchorType(lineAnchor));
|
||||||
|
assertThat(cue.positionAnchor).isEqualTo(positionAnchor);
|
||||||
|
assertThat(cue.position)
|
||||||
|
.isEqualTo(SubripDecoder.getFractionalPositionForAnchorType(positionAnchor));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user