Commit 37908dd4 by ibaker Committed by Ian Baker

Remove TTML package from null-checking blacklist

PiperOrigin-RevId: 290629644
parent 8a2a5271
...@@ -16,11 +16,13 @@ ...@@ -16,11 +16,13 @@
package com.google.android.exoplayer2.text.ttml; package com.google.android.exoplayer2.text.ttml;
import android.text.Layout; import android.text.Layout;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder; import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
import com.google.android.exoplayer2.text.Subtitle; import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.text.SubtitleDecoderException; import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.ColorParser; import com.google.android.exoplayer2.util.ColorParser;
import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Log;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
...@@ -32,6 +34,7 @@ import java.util.HashMap; ...@@ -32,6 +34,7 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.checkerframework.checker.nullness.qual.PolyNull;
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory; import org.xmlpull.v1.XmlPullParserFactory;
...@@ -110,18 +113,18 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -110,18 +113,18 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
Map<String, TtmlStyle> globalStyles = new HashMap<>(); Map<String, TtmlStyle> globalStyles = new HashMap<>();
Map<String, TtmlRegion> regionMap = new HashMap<>(); Map<String, TtmlRegion> regionMap = new HashMap<>();
Map<String, String> imageMap = new HashMap<>(); Map<String, String> imageMap = new HashMap<>();
regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion(null)); regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion(TtmlNode.ANONYMOUS_REGION_ID));
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length); ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length);
xmlParser.setInput(inputStream, null); xmlParser.setInput(inputStream, null);
TtmlSubtitle ttmlSubtitle = null; @Nullable TtmlSubtitle ttmlSubtitle = null;
ArrayDeque<TtmlNode> nodeStack = new ArrayDeque<>(); ArrayDeque<TtmlNode> nodeStack = new ArrayDeque<>();
int unsupportedNodeDepth = 0; int unsupportedNodeDepth = 0;
int eventType = xmlParser.getEventType(); int eventType = xmlParser.getEventType();
FrameAndTickRate frameAndTickRate = DEFAULT_FRAME_AND_TICK_RATE; FrameAndTickRate frameAndTickRate = DEFAULT_FRAME_AND_TICK_RATE;
CellResolution cellResolution = DEFAULT_CELL_RESOLUTION; CellResolution cellResolution = DEFAULT_CELL_RESOLUTION;
TtsExtent ttsExtent = null; @Nullable TtsExtent ttsExtent = null;
while (eventType != XmlPullParser.END_DOCUMENT) { while (eventType != XmlPullParser.END_DOCUMENT) {
TtmlNode parent = nodeStack.peek(); @Nullable TtmlNode parent = nodeStack.peek();
if (unsupportedNodeDepth == 0) { if (unsupportedNodeDepth == 0) {
String name = xmlParser.getName(); String name = xmlParser.getName();
if (eventType == XmlPullParser.START_TAG) { if (eventType == XmlPullParser.START_TAG) {
...@@ -149,10 +152,12 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -149,10 +152,12 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
} }
} }
} else if (eventType == XmlPullParser.TEXT) { } else if (eventType == XmlPullParser.TEXT) {
parent.addChild(TtmlNode.buildTextNode(xmlParser.getText())); Assertions.checkNotNull(parent).addChild(TtmlNode.buildTextNode(xmlParser.getText()));
} else if (eventType == XmlPullParser.END_TAG) { } else if (eventType == XmlPullParser.END_TAG) {
if (xmlParser.getName().equals(TtmlNode.TAG_TT)) { if (xmlParser.getName().equals(TtmlNode.TAG_TT)) {
ttmlSubtitle = new TtmlSubtitle(nodeStack.peek(), globalStyles, regionMap, imageMap); ttmlSubtitle =
new TtmlSubtitle(
Assertions.checkNotNull(nodeStack.peek()), globalStyles, regionMap, imageMap);
} }
nodeStack.pop(); nodeStack.pop();
} }
...@@ -166,7 +171,11 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -166,7 +171,11 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
xmlParser.next(); xmlParser.next();
eventType = xmlParser.getEventType(); eventType = xmlParser.getEventType();
} }
if (ttmlSubtitle != null) {
return ttmlSubtitle; return ttmlSubtitle;
} else {
throw new SubtitleDecoderException("No TTML subtitles found");
}
} catch (XmlPullParserException xppe) { } catch (XmlPullParserException xppe) {
throw new SubtitleDecoderException("Unable to decode source", xppe); throw new SubtitleDecoderException("Unable to decode source", xppe);
} catch (IOException e) { } catch (IOException e) {
...@@ -221,8 +230,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -221,8 +230,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
return defaultValue; return defaultValue;
} }
try { try {
int columns = Integer.parseInt(cellResolutionMatcher.group(1)); int columns = Integer.parseInt(Assertions.checkNotNull(cellResolutionMatcher.group(1)));
int rows = Integer.parseInt(cellResolutionMatcher.group(2)); int rows = Integer.parseInt(Assertions.checkNotNull(cellResolutionMatcher.group(2)));
if (columns == 0 || rows == 0) { if (columns == 0 || rows == 0) {
throw new SubtitleDecoderException("Invalid cell resolution " + columns + " " + rows); throw new SubtitleDecoderException("Invalid cell resolution " + columns + " " + rows);
} }
...@@ -233,7 +242,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -233,7 +242,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
} }
} }
@Nullable
private TtsExtent parseTtsExtent(XmlPullParser xmlParser) { private TtsExtent parseTtsExtent(XmlPullParser xmlParser) {
@Nullable
String ttsExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT); String ttsExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
if (ttsExtent == null) { if (ttsExtent == null) {
return null; return null;
...@@ -245,8 +256,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -245,8 +256,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
return null; return null;
} }
try { try {
int width = Integer.parseInt(extentMatcher.group(1)); int width = Integer.parseInt(Assertions.checkNotNull(extentMatcher.group(1)));
int height = Integer.parseInt(extentMatcher.group(2)); int height = Integer.parseInt(Assertions.checkNotNull(extentMatcher.group(2)));
return new TtsExtent(width, height); return new TtsExtent(width, height);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.w(TAG, "Ignoring malformed tts extent: " + ttsExtent); Log.w(TAG, "Ignoring malformed tts extent: " + ttsExtent);
...@@ -258,24 +269,26 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -258,24 +269,26 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
XmlPullParser xmlParser, XmlPullParser xmlParser,
Map<String, TtmlStyle> globalStyles, Map<String, TtmlStyle> globalStyles,
CellResolution cellResolution, CellResolution cellResolution,
TtsExtent ttsExtent, @Nullable TtsExtent ttsExtent,
Map<String, TtmlRegion> globalRegions, Map<String, TtmlRegion> globalRegions,
Map<String, String> imageMap) Map<String, String> imageMap)
throws IOException, XmlPullParserException { throws IOException, XmlPullParserException {
do { do {
xmlParser.next(); xmlParser.next();
if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_STYLE)) { if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_STYLE)) {
String parentStyleId = XmlPullParserUtil.getAttributeValue(xmlParser, ATTR_STYLE); @Nullable String parentStyleId = XmlPullParserUtil.getAttributeValue(xmlParser, ATTR_STYLE);
TtmlStyle style = parseStyleAttributes(xmlParser, new TtmlStyle()); TtmlStyle style = parseStyleAttributes(xmlParser, new TtmlStyle());
if (parentStyleId != null) { if (parentStyleId != null) {
for (String id : parseStyleIds(parentStyleId)) { for (String id : parseStyleIds(parentStyleId)) {
style.chain(globalStyles.get(id)); style.chain(globalStyles.get(id));
} }
} }
if (style.getId() != null) { String styleId = style.getId();
globalStyles.put(style.getId(), style); if (styleId != null) {
globalStyles.put(styleId, style);
} }
} else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) { } else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) {
@Nullable
TtmlRegion ttmlRegion = parseRegionAttributes(xmlParser, cellResolution, ttsExtent); TtmlRegion ttmlRegion = parseRegionAttributes(xmlParser, cellResolution, ttsExtent);
if (ttmlRegion != null) { if (ttmlRegion != null) {
globalRegions.put(ttmlRegion.id, ttmlRegion); globalRegions.put(ttmlRegion.id, ttmlRegion);
...@@ -292,7 +305,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -292,7 +305,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
do { do {
xmlParser.next(); xmlParser.next();
if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_IMAGE)) { if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_IMAGE)) {
String id = XmlPullParserUtil.getAttributeValue(xmlParser, "id"); @Nullable String id = XmlPullParserUtil.getAttributeValue(xmlParser, "id");
if (id != null) { if (id != null) {
String encodedBitmapData = xmlParser.nextText(); String encodedBitmapData = xmlParser.nextText();
imageMap.put(id, encodedBitmapData); imageMap.put(id, encodedBitmapData);
...@@ -309,9 +322,10 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -309,9 +322,10 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
* fractions. In case of missing tts:extent the pixel defined regions can't be parsed, and null is * fractions. In case of missing tts:extent the pixel defined regions can't be parsed, and null is
* returned. * returned.
*/ */
@Nullable
private TtmlRegion parseRegionAttributes( private TtmlRegion parseRegionAttributes(
XmlPullParser xmlParser, CellResolution cellResolution, TtsExtent ttsExtent) { XmlPullParser xmlParser, CellResolution cellResolution, @Nullable TtsExtent ttsExtent) {
String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID); @Nullable String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
if (regionId == null) { if (regionId == null) {
return null; return null;
} }
...@@ -319,14 +333,16 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -319,14 +333,16 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
float position; float position;
float line; float line;
@Nullable
String regionOrigin = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN); String regionOrigin = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
if (regionOrigin != null) { if (regionOrigin != null) {
Matcher originPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin); Matcher originPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin);
Matcher originPixelMatcher = PIXEL_COORDINATES.matcher(regionOrigin); Matcher originPixelMatcher = PIXEL_COORDINATES.matcher(regionOrigin);
if (originPercentageMatcher.matches()) { if (originPercentageMatcher.matches()) {
try { try {
position = Float.parseFloat(originPercentageMatcher.group(1)) / 100f; position =
line = Float.parseFloat(originPercentageMatcher.group(2)) / 100f; Float.parseFloat(Assertions.checkNotNull(originPercentageMatcher.group(1))) / 100f;
line = Float.parseFloat(Assertions.checkNotNull(originPercentageMatcher.group(2))) / 100f;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.w(TAG, "Ignoring region with malformed origin: " + regionOrigin); Log.w(TAG, "Ignoring region with malformed origin: " + regionOrigin);
return null; return null;
...@@ -337,8 +353,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -337,8 +353,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
return null; return null;
} }
try { try {
int width = Integer.parseInt(originPixelMatcher.group(1)); int width = Integer.parseInt(Assertions.checkNotNull(originPixelMatcher.group(1)));
int height = Integer.parseInt(originPixelMatcher.group(2)); int height = Integer.parseInt(Assertions.checkNotNull(originPixelMatcher.group(2)));
// Convert pixel values to fractions. // Convert pixel values to fractions.
position = width / (float) ttsExtent.width; position = width / (float) ttsExtent.width;
line = height / (float) ttsExtent.height; line = height / (float) ttsExtent.height;
...@@ -362,14 +378,17 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -362,14 +378,17 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
float width; float width;
float height; float height;
@Nullable
String regionExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT); String regionExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
if (regionExtent != null) { if (regionExtent != null) {
Matcher extentPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent); Matcher extentPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent);
Matcher extentPixelMatcher = PIXEL_COORDINATES.matcher(regionExtent); Matcher extentPixelMatcher = PIXEL_COORDINATES.matcher(regionExtent);
if (extentPercentageMatcher.matches()) { if (extentPercentageMatcher.matches()) {
try { try {
width = Float.parseFloat(extentPercentageMatcher.group(1)) / 100f; width =
height = Float.parseFloat(extentPercentageMatcher.group(2)) / 100f; Float.parseFloat(Assertions.checkNotNull(extentPercentageMatcher.group(1))) / 100f;
height =
Float.parseFloat(Assertions.checkNotNull(extentPercentageMatcher.group(2))) / 100f;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
Log.w(TAG, "Ignoring region with malformed extent: " + regionOrigin); Log.w(TAG, "Ignoring region with malformed extent: " + regionOrigin);
return null; return null;
...@@ -380,8 +399,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -380,8 +399,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
return null; return null;
} }
try { try {
int extentWidth = Integer.parseInt(extentPixelMatcher.group(1)); int extentWidth = Integer.parseInt(Assertions.checkNotNull(extentPixelMatcher.group(1)));
int extentHeight = Integer.parseInt(extentPixelMatcher.group(2)); int extentHeight = Integer.parseInt(Assertions.checkNotNull(extentPixelMatcher.group(2)));
// Convert pixel values to fractions. // Convert pixel values to fractions.
width = extentWidth / (float) ttsExtent.width; width = extentWidth / (float) ttsExtent.width;
height = extentHeight / (float) ttsExtent.height; height = extentHeight / (float) ttsExtent.height;
...@@ -404,8 +423,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -404,8 +423,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
} }
@Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START; @Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START;
String displayAlign = XmlPullParserUtil.getAttributeValue(xmlParser, @Nullable
TtmlNode.ATTR_TTS_DISPLAY_ALIGN); String displayAlign =
XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_DISPLAY_ALIGN);
if (displayAlign != null) { if (displayAlign != null) {
switch (Util.toLowerInvariant(displayAlign)) { switch (Util.toLowerInvariant(displayAlign)) {
case "center": case "center":
...@@ -440,7 +460,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -440,7 +460,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
return parentStyleIds.isEmpty() ? new String[0] : Util.split(parentStyleIds, "\\s+"); return parentStyleIds.isEmpty() ? new String[0] : Util.split(parentStyleIds, "\\s+");
} }
private TtmlStyle parseStyleAttributes(XmlPullParser parser, TtmlStyle style) { @PolyNull
private TtmlStyle parseStyleAttributes(XmlPullParser parser, @PolyNull TtmlStyle style) {
int attributeCount = parser.getAttributeCount(); int attributeCount = parser.getAttributeCount();
for (int i = 0; i < attributeCount; i++) { for (int i = 0; i < attributeCount; i++) {
String attributeValue = parser.getAttributeValue(i); String attributeValue = parser.getAttributeValue(i);
...@@ -527,21 +548,24 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -527,21 +548,24 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
return style; return style;
} }
private TtmlStyle createIfNull(TtmlStyle style) { private TtmlStyle createIfNull(@Nullable TtmlStyle style) {
return style == null ? new TtmlStyle() : style; return style == null ? new TtmlStyle() : style;
} }
private TtmlNode parseNode(XmlPullParser parser, TtmlNode parent, private TtmlNode parseNode(
Map<String, TtmlRegion> regionMap, FrameAndTickRate frameAndTickRate) XmlPullParser parser,
@Nullable TtmlNode parent,
Map<String, TtmlRegion> regionMap,
FrameAndTickRate frameAndTickRate)
throws SubtitleDecoderException { throws SubtitleDecoderException {
long duration = C.TIME_UNSET; long duration = C.TIME_UNSET;
long startTime = C.TIME_UNSET; long startTime = C.TIME_UNSET;
long endTime = C.TIME_UNSET; long endTime = C.TIME_UNSET;
String regionId = TtmlNode.ANONYMOUS_REGION_ID; String regionId = TtmlNode.ANONYMOUS_REGION_ID;
String imageId = null; @Nullable String imageId = null;
String[] styleIds = null; @Nullable String[] styleIds = null;
int attributeCount = parser.getAttributeCount(); int attributeCount = parser.getAttributeCount();
TtmlStyle style = parseStyleAttributes(parser, null); @Nullable TtmlStyle style = parseStyleAttributes(parser, null);
for (int i = 0; i < attributeCount; i++) { for (int i = 0; i < attributeCount; i++) {
String attr = parser.getAttributeName(i); String attr = parser.getAttributeName(i);
String value = parser.getAttributeValue(i); String value = parser.getAttributeValue(i);
...@@ -636,7 +660,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -636,7 +660,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
} }
if (matcher.matches()) { if (matcher.matches()) {
String unit = matcher.group(3); String unit = Assertions.checkNotNull(matcher.group(3));
switch (unit) { switch (unit) {
case "px": case "px":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PIXEL); out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PIXEL);
...@@ -650,7 +674,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -650,7 +674,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
default: default:
throw new SubtitleDecoderException("Invalid unit for fontSize: '" + unit + "'."); throw new SubtitleDecoderException("Invalid unit for fontSize: '" + unit + "'.");
} }
out.setFontSize(Float.valueOf(matcher.group(1))); out.setFontSize(Float.parseFloat(Assertions.checkNotNull(matcher.group(1))));
} else { } else {
throw new SubtitleDecoderException("Invalid expression for fontSize: '" + expression + "'."); throw new SubtitleDecoderException("Invalid expression for fontSize: '" + expression + "'.");
} }
...@@ -671,18 +695,18 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -671,18 +695,18 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
throws SubtitleDecoderException { throws SubtitleDecoderException {
Matcher matcher = CLOCK_TIME.matcher(time); Matcher matcher = CLOCK_TIME.matcher(time);
if (matcher.matches()) { if (matcher.matches()) {
String hours = matcher.group(1); String hours = Assertions.checkNotNull(matcher.group(1));
double durationSeconds = Long.parseLong(hours) * 3600; double durationSeconds = Long.parseLong(hours) * 3600;
String minutes = matcher.group(2); String minutes = Assertions.checkNotNull(matcher.group(2));
durationSeconds += Long.parseLong(minutes) * 60; durationSeconds += Long.parseLong(minutes) * 60;
String seconds = matcher.group(3); String seconds = Assertions.checkNotNull(matcher.group(3));
durationSeconds += Long.parseLong(seconds); durationSeconds += Long.parseLong(seconds);
String fraction = matcher.group(4); @Nullable String fraction = matcher.group(4);
durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0; durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0;
String frames = matcher.group(5); @Nullable String frames = matcher.group(5);
durationSeconds += (frames != null) durationSeconds += (frames != null)
? Long.parseLong(frames) / frameAndTickRate.effectiveFrameRate : 0; ? Long.parseLong(frames) / frameAndTickRate.effectiveFrameRate : 0;
String subframes = matcher.group(6); @Nullable String subframes = matcher.group(6);
durationSeconds += (subframes != null) durationSeconds += (subframes != null)
? ((double) Long.parseLong(subframes)) / frameAndTickRate.subFrameRate ? ((double) Long.parseLong(subframes)) / frameAndTickRate.subFrameRate
/ frameAndTickRate.effectiveFrameRate / frameAndTickRate.effectiveFrameRate
...@@ -691,9 +715,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder { ...@@ -691,9 +715,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
} }
matcher = OFFSET_TIME.matcher(time); matcher = OFFSET_TIME.matcher(time);
if (matcher.matches()) { if (matcher.matches()) {
String timeValue = matcher.group(1); String timeValue = Assertions.checkNotNull(matcher.group(1));
double offsetSeconds = Double.parseDouble(timeValue); double offsetSeconds = Double.parseDouble(timeValue);
String unit = matcher.group(2); String unit = Assertions.checkNotNull(matcher.group(2));
switch (unit) { switch (unit) {
case "h": case "h":
offsetSeconds *= 3600; offsetSeconds *= 3600;
......
...@@ -31,6 +31,7 @@ import java.util.Map; ...@@ -31,6 +31,7 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* A package internal representation of TTML node. * A package internal representation of TTML node.
...@@ -93,7 +94,7 @@ import java.util.TreeSet; ...@@ -93,7 +94,7 @@ import java.util.TreeSet;
private final HashMap<String, Integer> nodeStartsByRegion; private final HashMap<String, Integer> nodeStartsByRegion;
private final HashMap<String, Integer> nodeEndsByRegion; private final HashMap<String, Integer> nodeEndsByRegion;
private List<TtmlNode> children; @MonotonicNonNull private List<TtmlNode> children;
public static TtmlNode buildTextNode(String text) { public static TtmlNode buildTextNode(String text) {
return new TtmlNode( return new TtmlNode(
...@@ -196,6 +197,7 @@ import java.util.TreeSet; ...@@ -196,6 +197,7 @@ import java.util.TreeSet;
} }
} }
@Nullable
public String[] getStyleIds() { public String[] getStyleIds() {
return styleIds; return styleIds;
} }
...@@ -217,7 +219,7 @@ import java.util.TreeSet; ...@@ -217,7 +219,7 @@ import java.util.TreeSet;
// Create image based cues. // Create image based cues.
for (Pair<String, String> regionImagePair : regionImageOutputs) { for (Pair<String, String> regionImagePair : regionImageOutputs) {
String encodedBitmapData = imageMap.get(regionImagePair.second); @Nullable String encodedBitmapData = imageMap.get(regionImagePair.second);
if (encodedBitmapData == null) { if (encodedBitmapData == null) {
// Image reference points to an invalid image. Do nothing. // Image reference points to an invalid image. Do nothing.
continue; continue;
...@@ -225,7 +227,7 @@ import java.util.TreeSet; ...@@ -225,7 +227,7 @@ import java.util.TreeSet;
byte[] bitmapData = Base64.decode(encodedBitmapData, Base64.DEFAULT); byte[] bitmapData = Base64.decode(encodedBitmapData, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, /* offset= */ 0, bitmapData.length); Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, /* offset= */ 0, bitmapData.length);
TtmlRegion region = regionMap.get(regionImagePair.first); TtmlRegion region = Assertions.checkNotNull(regionMap.get(regionImagePair.first));
cues.add( cues.add(
new Cue.Builder() new Cue.Builder()
...@@ -241,7 +243,7 @@ import java.util.TreeSet; ...@@ -241,7 +243,7 @@ import java.util.TreeSet;
// Create text based cues. // Create text based cues.
for (Entry<String, SpannableStringBuilder> entry : regionTextOutputs.entrySet()) { for (Entry<String, SpannableStringBuilder> entry : regionTextOutputs.entrySet()) {
TtmlRegion region = regionMap.get(entry.getKey()); TtmlRegion region = Assertions.checkNotNull(regionMap.get(entry.getKey()));
cues.add( cues.add(
new Cue( new Cue(
cleanUpText(entry.getValue()), cleanUpText(entry.getValue()),
...@@ -286,7 +288,7 @@ import java.util.TreeSet; ...@@ -286,7 +288,7 @@ import java.util.TreeSet;
String resolvedRegionId = ANONYMOUS_REGION_ID.equals(regionId) ? inheritedRegion : regionId; String resolvedRegionId = ANONYMOUS_REGION_ID.equals(regionId) ? inheritedRegion : regionId;
if (isTextNode && descendsPNode) { if (isTextNode && descendsPNode) {
getRegionOutput(resolvedRegionId, regionOutputs).append(text); getRegionOutput(resolvedRegionId, regionOutputs).append(Assertions.checkNotNull(text));
} else if (TAG_BR.equals(tag) && descendsPNode) { } else if (TAG_BR.equals(tag) && descendsPNode) {
getRegionOutput(resolvedRegionId, regionOutputs).append('\n'); getRegionOutput(resolvedRegionId, regionOutputs).append('\n');
} else if (isActive(timeUs)) { } else if (isActive(timeUs)) {
...@@ -330,7 +332,7 @@ import java.util.TreeSet; ...@@ -330,7 +332,7 @@ import java.util.TreeSet;
int start = nodeStartsByRegion.containsKey(regionId) ? nodeStartsByRegion.get(regionId) : 0; int start = nodeStartsByRegion.containsKey(regionId) ? nodeStartsByRegion.get(regionId) : 0;
int end = entry.getValue(); int end = entry.getValue();
if (start != end) { if (start != end) {
SpannableStringBuilder regionOutput = regionOutputs.get(regionId); SpannableStringBuilder regionOutput = Assertions.checkNotNull(regionOutputs.get(regionId));
applyStyleToOutput(globalStyles, regionOutput, start, end); applyStyleToOutput(globalStyles, regionOutput, start, end);
} }
} }
...@@ -344,7 +346,7 @@ import java.util.TreeSet; ...@@ -344,7 +346,7 @@ import java.util.TreeSet;
SpannableStringBuilder regionOutput, SpannableStringBuilder regionOutput,
int start, int start,
int end) { int end) {
TtmlStyle resolvedStyle = TtmlRenderUtil.resolveStyle(style, styleIds, globalStyles); @Nullable TtmlStyle resolvedStyle = TtmlRenderUtil.resolveStyle(style, styleIds, globalStyles);
if (resolvedStyle != null) { if (resolvedStyle != null) {
TtmlRenderUtil.applyStylesToSpan(regionOutput, start, end, resolvedStyle); TtmlRenderUtil.applyStylesToSpan(regionOutput, start, end, resolvedStyle);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
*/ */
package com.google.android.exoplayer2.text.ttml; package com.google.android.exoplayer2.text.ttml;
import android.text.Layout.Alignment;
import android.text.SpannableStringBuilder; import android.text.SpannableStringBuilder;
import android.text.Spanned; import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan; import android.text.style.AbsoluteSizeSpan;
...@@ -26,6 +27,7 @@ import android.text.style.StrikethroughSpan; ...@@ -26,6 +27,7 @@ import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan; import android.text.style.StyleSpan;
import android.text.style.TypefaceSpan; import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan; import android.text.style.UnderlineSpan;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.text.SpanUtil; import com.google.android.exoplayer2.text.SpanUtil;
import java.util.Map; import java.util.Map;
...@@ -34,31 +36,36 @@ import java.util.Map; ...@@ -34,31 +36,36 @@ import java.util.Map;
*/ */
/* package */ final class TtmlRenderUtil { /* package */ final class TtmlRenderUtil {
public static TtmlStyle resolveStyle(TtmlStyle style, String[] styleIds, @Nullable
Map<String, TtmlStyle> globalStyles) { public static TtmlStyle resolveStyle(
if (style == null && styleIds == null) { @Nullable TtmlStyle style, @Nullable String[] styleIds, Map<String, TtmlStyle> globalStyles) {
if (style == null) {
if (styleIds == null) {
// No styles at all. // No styles at all.
return null; return null;
} else if (style == null && styleIds.length == 1) { } else if (styleIds.length == 1) {
// Only one single referential style present. // Only one single referential style present.
return globalStyles.get(styleIds[0]); return globalStyles.get(styleIds[0]);
} else if (style == null && styleIds.length > 1) { } else if (styleIds.length > 1) {
// Only multiple referential styles present. // Only multiple referential styles present.
TtmlStyle chainedStyle = new TtmlStyle(); TtmlStyle chainedStyle = new TtmlStyle();
for (String id : styleIds) { for (String id : styleIds) {
chainedStyle.chain(globalStyles.get(id)); chainedStyle.chain(globalStyles.get(id));
} }
return chainedStyle; return chainedStyle;
} else if (style != null && styleIds != null && styleIds.length == 1) { }
} else /* style != null */ {
if (styleIds != null && styleIds.length == 1) {
// Merge a single referential style into inline style. // Merge a single referential style into inline style.
return style.chain(globalStyles.get(styleIds[0])); return style.chain(globalStyles.get(styleIds[0]));
} else if (style != null && styleIds != null && styleIds.length > 1) { } else if (styleIds != null && styleIds.length > 1) {
// Merge multiple referential styles into inline style. // Merge multiple referential styles into inline style.
for (String id : styleIds) { for (String id : styleIds) {
style.chain(globalStyles.get(id)); style.chain(globalStyles.get(id));
} }
return style; return style;
} }
}
// Only inline styles available. // Only inline styles available.
return style; return style;
} }
...@@ -100,10 +107,11 @@ import java.util.Map; ...@@ -100,10 +107,11 @@ import java.util.Map;
end, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
if (style.getTextAlign() != null) { @Nullable Alignment textAlign = style.getTextAlign();
if (textAlign != null) {
SpanUtil.addOrReplaceSpan( SpanUtil.addOrReplaceSpan(
builder, builder,
new AlignmentSpan.Standard(style.getTextAlign()), new AlignmentSpan.Standard(textAlign),
start, start,
end, end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
......
...@@ -18,9 +18,11 @@ package com.google.android.exoplayer2.text.ttml; ...@@ -18,9 +18,11 @@ package com.google.android.exoplayer2.text.ttml;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.text.Layout; import android.text.Layout;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/** /**
* Style object of a <code>TtmlNode</code> * Style object of a <code>TtmlNode</code>
...@@ -58,7 +60,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -58,7 +60,7 @@ import java.lang.annotation.RetentionPolicy;
private static final int OFF = 0; private static final int OFF = 0;
private static final int ON = 1; private static final int ON = 1;
private String fontFamily; private @MonotonicNonNull String fontFamily;
private int fontColor; private int fontColor;
private boolean hasFontColor; private boolean hasFontColor;
private int backgroundColor; private int backgroundColor;
...@@ -69,8 +71,8 @@ import java.lang.annotation.RetentionPolicy; ...@@ -69,8 +71,8 @@ import java.lang.annotation.RetentionPolicy;
@OptionalBoolean private int italic; @OptionalBoolean private int italic;
@FontSizeUnit private int fontSizeUnit; @FontSizeUnit private int fontSizeUnit;
private float fontSize; private float fontSize;
private String id; private @MonotonicNonNull String id;
private Layout.Alignment textAlign; private Layout.@MonotonicNonNull Alignment textAlign;
public TtmlStyle() { public TtmlStyle() {
linethrough = UNSPECIFIED; linethrough = UNSPECIFIED;
...@@ -122,6 +124,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -122,6 +124,7 @@ import java.lang.annotation.RetentionPolicy;
return this; return this;
} }
@Nullable
public String getFontFamily() { public String getFontFamily() {
return fontFamily; return fontFamily;
} }
...@@ -171,7 +174,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -171,7 +174,7 @@ import java.lang.annotation.RetentionPolicy;
* *
* @param ancestor the referential style to inherit from * @param ancestor the referential style to inherit from
*/ */
public TtmlStyle chain(TtmlStyle ancestor) { public TtmlStyle chain(@Nullable TtmlStyle ancestor) {
return inherit(ancestor, true); return inherit(ancestor, true);
} }
...@@ -182,11 +185,11 @@ import java.lang.annotation.RetentionPolicy; ...@@ -182,11 +185,11 @@ import java.lang.annotation.RetentionPolicy;
* *
* @param ancestor the ancestor style to inherit from * @param ancestor the ancestor style to inherit from
*/ */
public TtmlStyle inherit(TtmlStyle ancestor) { public TtmlStyle inherit(@Nullable TtmlStyle ancestor) {
return inherit(ancestor, false); return inherit(ancestor, false);
} }
private TtmlStyle inherit(TtmlStyle ancestor, boolean chaining) { private TtmlStyle inherit(@Nullable TtmlStyle ancestor, boolean chaining) {
if (ancestor != null) { if (ancestor != null) {
if (!hasFontColor && ancestor.hasFontColor) { if (!hasFontColor && ancestor.hasFontColor) {
setFontColor(ancestor.fontColor); setFontColor(ancestor.fontColor);
...@@ -197,7 +200,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -197,7 +200,7 @@ import java.lang.annotation.RetentionPolicy;
if (italic == UNSPECIFIED) { if (italic == UNSPECIFIED) {
italic = ancestor.italic; italic = ancestor.italic;
} }
if (fontFamily == null) { if (fontFamily == null && ancestor.fontFamily != null) {
fontFamily = ancestor.fontFamily; fontFamily = ancestor.fontFamily;
} }
if (linethrough == UNSPECIFIED) { if (linethrough == UNSPECIFIED) {
...@@ -206,7 +209,7 @@ import java.lang.annotation.RetentionPolicy; ...@@ -206,7 +209,7 @@ import java.lang.annotation.RetentionPolicy;
if (underline == UNSPECIFIED) { if (underline == UNSPECIFIED) {
underline = ancestor.underline; underline = ancestor.underline;
} }
if (textAlign == null) { if (textAlign == null && ancestor.textAlign != null) {
textAlign = ancestor.textAlign; textAlign = ancestor.textAlign;
} }
if (fontSizeUnit == UNSPECIFIED) { if (fontSizeUnit == UNSPECIFIED) {
...@@ -226,10 +229,12 @@ import java.lang.annotation.RetentionPolicy; ...@@ -226,10 +229,12 @@ import java.lang.annotation.RetentionPolicy;
return this; return this;
} }
@Nullable
public String getId() { public String getId() {
return id; return id;
} }
@Nullable
public Layout.Alignment getTextAlign() { public Layout.Alignment getTextAlign() {
return textAlign; return textAlign;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment