mirror of
https://github.com/androidx/media.git
synced 2025-05-16 20:19:57 +08:00
Merge pull request #5066 from szaboa:feature/1583_support_png_ttml
PiperOrigin-RevId: 225531695
This commit is contained in:
commit
254589cbe8
@ -10,6 +10,9 @@
|
|||||||
([#3314](https://github.com/google/ExoPlayer/issues/3314)).
|
([#3314](https://github.com/google/ExoPlayer/issues/3314)).
|
||||||
* Update `TrackSelection.Factory` interface to support creating all track
|
* Update `TrackSelection.Factory` interface to support creating all track
|
||||||
selections together.
|
selections together.
|
||||||
|
* Captions:
|
||||||
|
* Support PNG subtitles in SMPTE-TT
|
||||||
|
([#1583](https://github.com/google/ExoPlayer/issues/1583)).
|
||||||
* Do not retry failed loads whose error is `FileNotFoundException`.
|
* Do not retry failed loads whose error is `FileNotFoundException`.
|
||||||
* Prevent Cea608Decoder from generating Subtitles with null Cues list.
|
* Prevent Cea608Decoder from generating Subtitles with null Cues list.
|
||||||
* Offline:
|
* Offline:
|
||||||
|
@ -68,6 +68,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
private static final String ATTR_END = "end";
|
private static final String ATTR_END = "end";
|
||||||
private static final String ATTR_STYLE = "style";
|
private static final String ATTR_STYLE = "style";
|
||||||
private static final String ATTR_REGION = "region";
|
private static final String ATTR_REGION = "region";
|
||||||
|
private static final String ATTR_IMAGE = "backgroundImage";
|
||||||
|
|
||||||
private static final Pattern CLOCK_TIME =
|
private static final Pattern CLOCK_TIME =
|
||||||
Pattern.compile("^([0-9][0-9]+):([0-9][0-9]):([0-9][0-9])"
|
Pattern.compile("^([0-9][0-9]+):([0-9][0-9]):([0-9][0-9])"
|
||||||
@ -77,6 +78,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
private static final Pattern FONT_SIZE = Pattern.compile("^(([0-9]*.)?[0-9]+)(px|em|%)$");
|
private static final Pattern FONT_SIZE = Pattern.compile("^(([0-9]*.)?[0-9]+)(px|em|%)$");
|
||||||
private static final Pattern PERCENTAGE_COORDINATES =
|
private static final Pattern PERCENTAGE_COORDINATES =
|
||||||
Pattern.compile("^(\\d+\\.?\\d*?)% (\\d+\\.?\\d*?)%$");
|
Pattern.compile("^(\\d+\\.?\\d*?)% (\\d+\\.?\\d*?)%$");
|
||||||
|
private static final Pattern PIXEL_COORDINATES =
|
||||||
|
Pattern.compile("^(\\d+\\.?\\d*?)px (\\d+\\.?\\d*?)px$");
|
||||||
private static final Pattern CELL_RESOLUTION = Pattern.compile("^(\\d+) (\\d+)$");
|
private static final Pattern CELL_RESOLUTION = Pattern.compile("^(\\d+) (\\d+)$");
|
||||||
|
|
||||||
private static final int DEFAULT_FRAME_RATE = 30;
|
private static final int DEFAULT_FRAME_RATE = 30;
|
||||||
@ -105,6 +108,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
|
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
|
||||||
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<>();
|
||||||
regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion(null));
|
regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion(null));
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length);
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length);
|
||||||
xmlParser.setInput(inputStream, null);
|
xmlParser.setInput(inputStream, null);
|
||||||
@ -114,6 +118,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
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;
|
||||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||||
TtmlNode parent = nodeStack.peek();
|
TtmlNode parent = nodeStack.peek();
|
||||||
if (unsupportedNodeDepth == 0) {
|
if (unsupportedNodeDepth == 0) {
|
||||||
@ -122,12 +127,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
if (TtmlNode.TAG_TT.equals(name)) {
|
if (TtmlNode.TAG_TT.equals(name)) {
|
||||||
frameAndTickRate = parseFrameAndTickRates(xmlParser);
|
frameAndTickRate = parseFrameAndTickRates(xmlParser);
|
||||||
cellResolution = parseCellResolution(xmlParser, DEFAULT_CELL_RESOLUTION);
|
cellResolution = parseCellResolution(xmlParser, DEFAULT_CELL_RESOLUTION);
|
||||||
|
ttsExtent = parseTtsExtent(xmlParser);
|
||||||
}
|
}
|
||||||
if (!isSupportedTag(name)) {
|
if (!isSupportedTag(name)) {
|
||||||
Log.i(TAG, "Ignoring unsupported tag: " + xmlParser.getName());
|
Log.i(TAG, "Ignoring unsupported tag: " + xmlParser.getName());
|
||||||
unsupportedNodeDepth++;
|
unsupportedNodeDepth++;
|
||||||
} else if (TtmlNode.TAG_HEAD.equals(name)) {
|
} else if (TtmlNode.TAG_HEAD.equals(name)) {
|
||||||
parseHeader(xmlParser, globalStyles, regionMap, cellResolution);
|
parseHeader(xmlParser, globalStyles, cellResolution, ttsExtent, regionMap, imageMap);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate);
|
TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate);
|
||||||
@ -145,7 +151,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
parent.addChild(TtmlNode.buildTextNode(xmlParser.getText()));
|
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);
|
ttmlSubtitle = new TtmlSubtitle(nodeStack.peek(), globalStyles, regionMap, imageMap);
|
||||||
}
|
}
|
||||||
nodeStack.pop();
|
nodeStack.pop();
|
||||||
}
|
}
|
||||||
@ -226,11 +232,34 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TtsExtent parseTtsExtent(XmlPullParser xmlParser) {
|
||||||
|
String ttsExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
|
||||||
|
if (ttsExtent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher extentMatcher = PIXEL_COORDINATES.matcher(ttsExtent);
|
||||||
|
if (!extentMatcher.matches()) {
|
||||||
|
Log.w(TAG, "Ignoring non-pixel tts extent: " + ttsExtent);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int width = Integer.parseInt(extentMatcher.group(1));
|
||||||
|
int height = Integer.parseInt(extentMatcher.group(2));
|
||||||
|
return new TtsExtent(width, height);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Ignoring malformed tts extent: " + ttsExtent);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Map<String, TtmlStyle> parseHeader(
|
private Map<String, TtmlStyle> parseHeader(
|
||||||
XmlPullParser xmlParser,
|
XmlPullParser xmlParser,
|
||||||
Map<String, TtmlStyle> globalStyles,
|
Map<String, TtmlStyle> globalStyles,
|
||||||
|
CellResolution cellResolution,
|
||||||
|
TtsExtent ttsExtent,
|
||||||
Map<String, TtmlRegion> globalRegions,
|
Map<String, TtmlRegion> globalRegions,
|
||||||
CellResolution cellResolution)
|
Map<String, String> imageMap)
|
||||||
throws IOException, XmlPullParserException {
|
throws IOException, XmlPullParserException {
|
||||||
do {
|
do {
|
||||||
xmlParser.next();
|
xmlParser.next();
|
||||||
@ -246,23 +275,41 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
globalStyles.put(style.getId(), style);
|
globalStyles.put(style.getId(), style);
|
||||||
}
|
}
|
||||||
} else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) {
|
} else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) {
|
||||||
TtmlRegion ttmlRegion = parseRegionAttributes(xmlParser, cellResolution);
|
TtmlRegion ttmlRegion = parseRegionAttributes(xmlParser, cellResolution, ttsExtent);
|
||||||
if (ttmlRegion != null) {
|
if (ttmlRegion != null) {
|
||||||
globalRegions.put(ttmlRegion.id, ttmlRegion);
|
globalRegions.put(ttmlRegion.id, ttmlRegion);
|
||||||
}
|
}
|
||||||
|
} else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_METADATA)) {
|
||||||
|
parseMetadata(xmlParser, imageMap);
|
||||||
}
|
}
|
||||||
} while (!XmlPullParserUtil.isEndTag(xmlParser, TtmlNode.TAG_HEAD));
|
} while (!XmlPullParserUtil.isEndTag(xmlParser, TtmlNode.TAG_HEAD));
|
||||||
return globalStyles;
|
return globalStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseMetadata(XmlPullParser xmlParser, Map<String, String> imageMap)
|
||||||
|
throws IOException, XmlPullParserException {
|
||||||
|
do {
|
||||||
|
xmlParser.next();
|
||||||
|
if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_IMAGE)) {
|
||||||
|
String id = XmlPullParserUtil.getAttributeValue(xmlParser, "id");
|
||||||
|
if (id != null) {
|
||||||
|
String encodedBitmapData = xmlParser.nextText();
|
||||||
|
imageMap.put(id, encodedBitmapData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (!XmlPullParserUtil.isEndTag(xmlParser, TtmlNode.TAG_METADATA));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a region declaration.
|
* Parses a region declaration.
|
||||||
*
|
*
|
||||||
* <p>If the region defines an origin and extent, it is required that they're defined as
|
* <p>Supports both percentage and pixel defined regions. In case of pixel defined regions the
|
||||||
* percentages of the viewport. Region declarations that define origin and extent in other formats
|
* passed {@code ttsExtent} is used as a reference window to convert the pixel values to
|
||||||
* are unsupported, and null is returned.
|
* fractions. In case of missing tts:extent the pixel defined regions can't be parsed, and null is
|
||||||
|
* returned.
|
||||||
*/
|
*/
|
||||||
private TtmlRegion parseRegionAttributes(XmlPullParser xmlParser, CellResolution cellResolution) {
|
private TtmlRegion parseRegionAttributes(
|
||||||
|
XmlPullParser xmlParser, CellResolution cellResolution, TtsExtent ttsExtent) {
|
||||||
String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
||||||
if (regionId == null) {
|
if (regionId == null) {
|
||||||
return null;
|
return null;
|
||||||
@ -270,13 +317,30 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
|
|
||||||
float position;
|
float position;
|
||||||
float line;
|
float line;
|
||||||
|
|
||||||
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 originMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin);
|
Matcher originPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin);
|
||||||
if (originMatcher.matches()) {
|
Matcher originPixelMatcher = PIXEL_COORDINATES.matcher(regionOrigin);
|
||||||
|
if (originPercentageMatcher.matches()) {
|
||||||
try {
|
try {
|
||||||
position = Float.parseFloat(originMatcher.group(1)) / 100f;
|
position = Float.parseFloat(originPercentageMatcher.group(1)) / 100f;
|
||||||
line = Float.parseFloat(originMatcher.group(2)) / 100f;
|
line = Float.parseFloat(originPercentageMatcher.group(2)) / 100f;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Ignoring region with malformed origin: " + regionOrigin);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (originPixelMatcher.matches()) {
|
||||||
|
if (ttsExtent == null) {
|
||||||
|
Log.w(TAG, "Ignoring region with missing tts:extent: " + regionOrigin);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int width = Integer.parseInt(originPixelMatcher.group(1));
|
||||||
|
int height = Integer.parseInt(originPixelMatcher.group(2));
|
||||||
|
// Convert pixel values to fractions.
|
||||||
|
position = width / (float) ttsExtent.width;
|
||||||
|
line = height / (float) ttsExtent.height;
|
||||||
} 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;
|
||||||
@ -299,11 +363,27 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
float height;
|
float height;
|
||||||
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 extentMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent);
|
Matcher extentPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent);
|
||||||
if (extentMatcher.matches()) {
|
Matcher extentPixelMatcher = PIXEL_COORDINATES.matcher(regionExtent);
|
||||||
|
if (extentPercentageMatcher.matches()) {
|
||||||
try {
|
try {
|
||||||
width = Float.parseFloat(extentMatcher.group(1)) / 100f;
|
width = Float.parseFloat(extentPercentageMatcher.group(1)) / 100f;
|
||||||
height = Float.parseFloat(extentMatcher.group(2)) / 100f;
|
height = Float.parseFloat(extentPercentageMatcher.group(2)) / 100f;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Ignoring region with malformed extent: " + regionOrigin);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else if (extentPixelMatcher.matches()) {
|
||||||
|
if (ttsExtent == null) {
|
||||||
|
Log.w(TAG, "Ignoring region with missing tts:extent: " + regionOrigin);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
int extentWidth = Integer.parseInt(extentPixelMatcher.group(1));
|
||||||
|
int extentHeight = Integer.parseInt(extentPixelMatcher.group(2));
|
||||||
|
// Convert pixel values to fractions.
|
||||||
|
width = extentWidth / (float) ttsExtent.width;
|
||||||
|
height = extentHeight / (float) ttsExtent.height;
|
||||||
} 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;
|
||||||
@ -457,6 +537,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
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;
|
||||||
String[] styleIds = null;
|
String[] styleIds = null;
|
||||||
int attributeCount = parser.getAttributeCount();
|
int attributeCount = parser.getAttributeCount();
|
||||||
TtmlStyle style = parseStyleAttributes(parser, null);
|
TtmlStyle style = parseStyleAttributes(parser, null);
|
||||||
@ -487,6 +568,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
regionId = value;
|
regionId = value;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ATTR_IMAGE:
|
||||||
|
// Parse URI reference only if refers to an element in the same document (it must start
|
||||||
|
// with '#'). Resolving URIs from external sources is not supported.
|
||||||
|
if (value.startsWith("#")) {
|
||||||
|
imageId = value.substring(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
break;
|
break;
|
||||||
@ -509,7 +597,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
endTime = parent.endTimeUs;
|
endTime = parent.endTimeUs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TtmlNode.buildNode(parser.getName(), startTime, endTime, style, styleIds, regionId);
|
return TtmlNode.buildNode(
|
||||||
|
parser.getName(), startTime, endTime, style, styleIds, regionId, imageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isSupportedTag(String tag) {
|
private static boolean isSupportedTag(String tag) {
|
||||||
@ -525,9 +614,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
|| tag.equals(TtmlNode.TAG_LAYOUT)
|
|| tag.equals(TtmlNode.TAG_LAYOUT)
|
||||||
|| tag.equals(TtmlNode.TAG_REGION)
|
|| tag.equals(TtmlNode.TAG_REGION)
|
||||||
|| tag.equals(TtmlNode.TAG_METADATA)
|
|| tag.equals(TtmlNode.TAG_METADATA)
|
||||||
|| tag.equals(TtmlNode.TAG_SMPTE_IMAGE)
|
|| tag.equals(TtmlNode.TAG_IMAGE)
|
||||||
|| tag.equals(TtmlNode.TAG_SMPTE_DATA)
|
|| tag.equals(TtmlNode.TAG_DATA)
|
||||||
|| tag.equals(TtmlNode.TAG_SMPTE_INFORMATION);
|
|| tag.equals(TtmlNode.TAG_INFORMATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void parseFontSize(String expression, TtmlStyle out) throws
|
private static void parseFontSize(String expression, TtmlStyle out) throws
|
||||||
@ -651,4 +740,15 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
|
|||||||
this.rows = rows;
|
this.rows = rows;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Represents the tts:extent for a TTML file. */
|
||||||
|
private static final class TtsExtent {
|
||||||
|
final int width;
|
||||||
|
final int height;
|
||||||
|
|
||||||
|
TtsExtent(int width, int height) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.text.ttml;
|
package com.google.android.exoplayer2.text.ttml;
|
||||||
|
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Pair;
|
||||||
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.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
@ -44,9 +49,9 @@ import java.util.TreeSet;
|
|||||||
public static final String TAG_LAYOUT = "layout";
|
public static final String TAG_LAYOUT = "layout";
|
||||||
public static final String TAG_REGION = "region";
|
public static final String TAG_REGION = "region";
|
||||||
public static final String TAG_METADATA = "metadata";
|
public static final String TAG_METADATA = "metadata";
|
||||||
public static final String TAG_SMPTE_IMAGE = "smpte:image";
|
public static final String TAG_IMAGE = "image";
|
||||||
public static final String TAG_SMPTE_DATA = "smpte:data";
|
public static final String TAG_DATA = "data";
|
||||||
public static final String TAG_SMPTE_INFORMATION = "smpte:information";
|
public static final String TAG_INFORMATION = "information";
|
||||||
|
|
||||||
public static final String ANONYMOUS_REGION_ID = "";
|
public static final String ANONYMOUS_REGION_ID = "";
|
||||||
public static final String ATTR_ID = "id";
|
public static final String ATTR_ID = "id";
|
||||||
@ -75,34 +80,57 @@ import java.util.TreeSet;
|
|||||||
public static final String START = "start";
|
public static final String START = "start";
|
||||||
public static final String END = "end";
|
public static final String END = "end";
|
||||||
|
|
||||||
public final String tag;
|
@Nullable public final String tag;
|
||||||
public final String text;
|
@Nullable public final String text;
|
||||||
public final boolean isTextNode;
|
public final boolean isTextNode;
|
||||||
public final long startTimeUs;
|
public final long startTimeUs;
|
||||||
public final long endTimeUs;
|
public final long endTimeUs;
|
||||||
public final TtmlStyle style;
|
@Nullable public final TtmlStyle style;
|
||||||
|
@Nullable private final String[] styleIds;
|
||||||
public final String regionId;
|
public final String regionId;
|
||||||
|
@Nullable public final String imageId;
|
||||||
|
|
||||||
private final String[] styleIds;
|
|
||||||
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;
|
private List<TtmlNode> children;
|
||||||
|
|
||||||
public static TtmlNode buildTextNode(String text) {
|
public static TtmlNode buildTextNode(String text) {
|
||||||
return new TtmlNode(null, TtmlRenderUtil.applyTextElementSpacePolicy(text), C.TIME_UNSET,
|
return new TtmlNode(
|
||||||
C.TIME_UNSET, null, null, ANONYMOUS_REGION_ID);
|
/* tag= */ null,
|
||||||
|
TtmlRenderUtil.applyTextElementSpacePolicy(text),
|
||||||
|
/* startTimeUs= */ C.TIME_UNSET,
|
||||||
|
/* endTimeUs= */ C.TIME_UNSET,
|
||||||
|
/* style= */ null,
|
||||||
|
/* styleIds= */ null,
|
||||||
|
ANONYMOUS_REGION_ID,
|
||||||
|
/* imageId= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TtmlNode buildNode(String tag, long startTimeUs, long endTimeUs,
|
public static TtmlNode buildNode(
|
||||||
TtmlStyle style, String[] styleIds, String regionId) {
|
@Nullable String tag,
|
||||||
return new TtmlNode(tag, null, startTimeUs, endTimeUs, style, styleIds, regionId);
|
long startTimeUs,
|
||||||
|
long endTimeUs,
|
||||||
|
@Nullable TtmlStyle style,
|
||||||
|
@Nullable String[] styleIds,
|
||||||
|
String regionId,
|
||||||
|
@Nullable String imageId) {
|
||||||
|
return new TtmlNode(
|
||||||
|
tag, /* text= */ null, startTimeUs, endTimeUs, style, styleIds, regionId, imageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TtmlNode(String tag, String text, long startTimeUs, long endTimeUs,
|
private TtmlNode(
|
||||||
TtmlStyle style, String[] styleIds, String regionId) {
|
@Nullable String tag,
|
||||||
|
@Nullable String text,
|
||||||
|
long startTimeUs,
|
||||||
|
long endTimeUs,
|
||||||
|
@Nullable TtmlStyle style,
|
||||||
|
@Nullable String[] styleIds,
|
||||||
|
String regionId,
|
||||||
|
@Nullable String imageId) {
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
this.imageId = imageId;
|
||||||
this.style = style;
|
this.style = style;
|
||||||
this.styleIds = styleIds;
|
this.styleIds = styleIds;
|
||||||
this.isTextNode = text != null;
|
this.isTextNode = text != null;
|
||||||
@ -151,7 +179,8 @@ import java.util.TreeSet;
|
|||||||
|
|
||||||
private void getEventTimes(TreeSet<Long> out, boolean descendsPNode) {
|
private void getEventTimes(TreeSet<Long> out, boolean descendsPNode) {
|
||||||
boolean isPNode = TAG_P.equals(tag);
|
boolean isPNode = TAG_P.equals(tag);
|
||||||
if (descendsPNode || isPNode) {
|
boolean isDivNode = TAG_DIV.equals(tag);
|
||||||
|
if (descendsPNode || isPNode || (isDivNode && imageId != null)) {
|
||||||
if (startTimeUs != C.TIME_UNSET) {
|
if (startTimeUs != C.TIME_UNSET) {
|
||||||
out.add(startTimeUs);
|
out.add(startTimeUs);
|
||||||
}
|
}
|
||||||
@ -171,13 +200,46 @@ import java.util.TreeSet;
|
|||||||
return styleIds;
|
return styleIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Cue> getCues(long timeUs, Map<String, TtmlStyle> globalStyles,
|
public List<Cue> getCues(
|
||||||
Map<String, TtmlRegion> regionMap) {
|
long timeUs,
|
||||||
TreeMap<String, SpannableStringBuilder> regionOutputs = new TreeMap<>();
|
Map<String, TtmlStyle> globalStyles,
|
||||||
traverseForText(timeUs, false, regionId, regionOutputs);
|
Map<String, TtmlRegion> regionMap,
|
||||||
traverseForStyle(timeUs, globalStyles, regionOutputs);
|
Map<String, String> imageMap) {
|
||||||
|
|
||||||
|
List<Pair<String, String>> regionImageOutputs = new ArrayList<>();
|
||||||
|
traverseForImage(timeUs, regionId, regionImageOutputs);
|
||||||
|
|
||||||
|
TreeMap<String, SpannableStringBuilder> regionTextOutputs = new TreeMap<>();
|
||||||
|
traverseForText(timeUs, false, regionId, regionTextOutputs);
|
||||||
|
traverseForStyle(timeUs, globalStyles, regionTextOutputs);
|
||||||
|
|
||||||
List<Cue> cues = new ArrayList<>();
|
List<Cue> cues = new ArrayList<>();
|
||||||
for (Entry<String, SpannableStringBuilder> entry : regionOutputs.entrySet()) {
|
|
||||||
|
// Create image based cues.
|
||||||
|
for (Pair<String, String> regionImagePair : regionImageOutputs) {
|
||||||
|
String encodedBitmapData = imageMap.get(regionImagePair.second);
|
||||||
|
if (encodedBitmapData == null) {
|
||||||
|
// Image reference points to an invalid image. Do nothing.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] bitmapData = Base64.decode(encodedBitmapData, Base64.DEFAULT);
|
||||||
|
Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, /* offset= */ 0, bitmapData.length);
|
||||||
|
TtmlRegion region = regionMap.get(regionImagePair.first);
|
||||||
|
|
||||||
|
cues.add(
|
||||||
|
new Cue(
|
||||||
|
bitmap,
|
||||||
|
region.position,
|
||||||
|
Cue.ANCHOR_TYPE_MIDDLE,
|
||||||
|
region.line,
|
||||||
|
region.lineAnchor,
|
||||||
|
region.width,
|
||||||
|
/* height= */ Cue.DIMEN_UNSET));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create text based cues.
|
||||||
|
for (Entry<String, SpannableStringBuilder> entry : regionTextOutputs.entrySet()) {
|
||||||
TtmlRegion region = regionMap.get(entry.getKey());
|
TtmlRegion region = regionMap.get(entry.getKey());
|
||||||
cues.add(
|
cues.add(
|
||||||
new Cue(
|
new Cue(
|
||||||
@ -192,9 +254,22 @@ import java.util.TreeSet;
|
|||||||
region.textSizeType,
|
region.textSizeType,
|
||||||
region.textSize));
|
region.textSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
return cues;
|
return cues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void traverseForImage(
|
||||||
|
long timeUs, String inheritedRegion, List<Pair<String, String>> regionImageList) {
|
||||||
|
String resolvedRegionId = ANONYMOUS_REGION_ID.equals(regionId) ? inheritedRegion : regionId;
|
||||||
|
if (isActive(timeUs) && TAG_DIV.equals(tag) && imageId != null) {
|
||||||
|
regionImageList.add(new Pair<>(resolvedRegionId, imageId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < getChildCount(); ++i) {
|
||||||
|
getChild(i).traverseForImage(timeUs, resolvedRegionId, regionImageList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void traverseForText(
|
private void traverseForText(
|
||||||
long timeUs,
|
long timeUs,
|
||||||
boolean descendsPNode,
|
boolean descendsPNode,
|
||||||
|
@ -33,11 +33,16 @@ import java.util.Map;
|
|||||||
private final long[] eventTimesUs;
|
private final long[] eventTimesUs;
|
||||||
private final Map<String, TtmlStyle> globalStyles;
|
private final Map<String, TtmlStyle> globalStyles;
|
||||||
private final Map<String, TtmlRegion> regionMap;
|
private final Map<String, TtmlRegion> regionMap;
|
||||||
|
private final Map<String, String> imageMap;
|
||||||
|
|
||||||
public TtmlSubtitle(TtmlNode root, Map<String, TtmlStyle> globalStyles,
|
public TtmlSubtitle(
|
||||||
Map<String, TtmlRegion> regionMap) {
|
TtmlNode root,
|
||||||
|
Map<String, TtmlStyle> globalStyles,
|
||||||
|
Map<String, TtmlRegion> regionMap,
|
||||||
|
Map<String, String> imageMap) {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.regionMap = regionMap;
|
this.regionMap = regionMap;
|
||||||
|
this.imageMap = imageMap;
|
||||||
this.globalStyles =
|
this.globalStyles =
|
||||||
globalStyles != null ? Collections.unmodifiableMap(globalStyles) : Collections.emptyMap();
|
globalStyles != null ? Collections.unmodifiableMap(globalStyles) : Collections.emptyMap();
|
||||||
this.eventTimesUs = root.getEventTimesUs();
|
this.eventTimesUs = root.getEventTimesUs();
|
||||||
@ -66,7 +71,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Cue> getCues(long timeUs) {
|
public List<Cue> getCues(long timeUs) {
|
||||||
return root.getCues(timeUs, globalStyles, regionMap);
|
return root.getCues(timeUs, globalStyles, regionMap, imageMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:tts="http://www.w3.org/ns/ttml#styling" xmlns:smpte="http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt" xml:lang="eng">
|
||||||
|
<head>
|
||||||
|
<metadata>
|
||||||
|
<smpte:image imagetype="PNG" encoding="Base64" xml:id="img_0">
|
||||||
|
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
|
||||||
|
</smpte:image>
|
||||||
|
<smpte:image imagetype="PNG" encoding="Base64" xml:id="img_1">
|
||||||
|
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
|
||||||
|
</smpte:image>
|
||||||
|
</metadata>
|
||||||
|
<styling>
|
||||||
|
<style/>
|
||||||
|
</styling>
|
||||||
|
<layout>
|
||||||
|
<region xml:id="region_0" tts:extent="51% 12%" tts:origin="24% 78%"/>
|
||||||
|
<region xml:id="region_1" tts:extent="57% 6%" tts:origin="21% 85%"/>
|
||||||
|
<region xml:id="region_2" tts:extent="51% 12%" tts:origin="24% 28%"/>
|
||||||
|
<region xml:id="region_3" tts:extent="57% 6%" tts:origin="21% 35%"/>
|
||||||
|
</layout>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div begin="00:00:00.200" end="00:00:03.000" region="region_2" smpte:backgroundImage="#img_0"/>
|
||||||
|
<div begin="00:00:03.200" end="00:00:06.937" region="region_3" smpte:backgroundImage="#img_1"/>
|
||||||
|
<div begin="00:00:07.200" end="00:59:03.000" region="region_2" smpte:backgroundImage="#img_0"/>
|
||||||
|
</body>
|
||||||
|
</tt>
|
23
library/core/src/test/assets/ttml/bitmap_pixel_region.xml
Normal file
23
library/core/src/test/assets/ttml/bitmap_pixel_region.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:tts="http://www.w3.org/ns/ttml#styling" xmlns:smpte="http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt" xml:lang="eng" tts:extent="1280px 720px">
|
||||||
|
<head>
|
||||||
|
<metadata>
|
||||||
|
<smpte:image imagetype="PNG" encoding="Base64" xml:id="img_0">
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAAXAAAABICAYAAADvR65LAAAACXBIWXMAAAsTAAALEwEAmpwYAAANIUlEQVR4nO2d/5GjSA+GNVVfALMh4EyucAIXxDiGCWFiwKngukwghA1h74/vxMia/t3qNtjvU7W1u8Ygtbp53agFvL2/v/8hAAAAh+N/j3YAAABAGRBwAAA4KBBwAAA4KC8j4MMwEBHRuq4P9qQ/vdv+yrFOBTECFhxawFNPgnEc6Xw+ExHRPM90u92a+7YXerZ9HEc6nU50Op2IiGhZFprnGSKleOXxCGwxE/BxHLd/uwYkb1+WpeqE1iLBLMuy/XEdX54wRyEW01RCbbeyMQwDnc/nzRYRBfvjWUmN517Ho9V4eDTP0o4YJgLOJ+/pdHLOKMZxpMvlQkRE0zQVn9B8HC3eknmeq2zsBSmI8zw3EUJLG1K85Y/psiyWLu+aHn3WkqP7zzxLO1IwEXB92ezbzsEsQYu3Fge2wX+etcNKaC2iwzDc9cs0TU896wFgL5gKuEug9cldIqxyhk/0c5bNNng7xOMbGYuWcZF9jPgD0IdqAY8JdGx2nkJsYWxdV1rXFcLhoXVcQiktAEA7igScqz+I6MeCotwmt7N4l5ZP+VIntZT4k7NP7Luu7fqKQsc4N3Ytbej+1p9pm67PYrYs4l3SDzlYx7PVeAwdI8f/Hn1Zsm9tP9SOtdz21WYosgVclkAR3QdIpjnkdv77crls4ltaPlU72/N1bKzkTadxUvaRsXItrLrKyfgzPQhLY9fShus4cmzIdms/2Cbvp+NTG2+XDT4Gp3lcNlLbHotDajx7jkcr/3v0Zcm+pf1gNdb02A+NI+mrhO2mjr9sAT+dTj8cldtCAqtn4yVwh5T+ALDvLj9Pp5NTaIdhoMvl4my3r/KGbZ3PZ1qWxbuw6ion89kpzTO3suEbC7IaRbbb98Ovv1cab2lDnsCaZVl+nOjaBlFe6qk0nj3Ho6X/PfqyZN/cdliNtZxxFKqmy52NZws4/0IwoXpWKdhStHMFiG2yLT75SsqE2B9XG/h4evYgO1jvJ39FLXLNXMWhxVHarU0hWdmQcdQlhPrfEletuEyxWcQ71M/yhJPb+XP+k9qfNfFsMR7ZXuo5UeN/bV/6fC3ZN7cd1mONjy3HEJdP8/5sU44/uV8u2QJ+u902Zz4+PojIPe2XjnJgS3N067rSPM/O3JYMXsol2TzPd6I/DAMty7IFWp+4crDI6he5Hw8YCwFf15Wu1+uWS+OT2LK23coGjwV5c1VKX8v+4v/LWbpFvGP9zPblmJEzo9PplJTTJaqLp+V4lNuXZaHr9Rr1vdb/0r6M+Vqyb247rMeaFmk50eRtevLgSjdxW1KoqkKJXR5KR2vFh4/vynHJf8cuH7Wv67pug1BfCukFBtkO/lGR/ozjiEoYig8+LZyMZbxj/ewSDbm9FzXjUZ78epLjmr23ILUvc3zt0c7WY0366JsMuK48mi9iMjIAOemTGm632zawXTnMlEueHF907kzvyyebLwcG3Pgu7y3jbVmp1JKa8ejL70vhaC3gqX2Z42uPdrYea/pHWPooNYy/V9pPxQIuBVrDq7rsrCWy5lteusv8plU6g48nbWtk+3LypsAN4h2G48OTFR0PGb9Hx6fG1x7tbDnWfIKshf3r62ubAJfc9p8s4PohUvJvmUti5Hadd7SaFXAOVua82GavdIbuZNAWxPubI1351fj6qHa2GGvrutI0TbQsy12OnOg7Z9+kjNAl0nKbD520b4Er5wTAM5OSmtxLGqnG1yO1MxVebNV5dpkaJkqraksWcLnSHMofhbbV5HpS/Ou9AEV0/8t8tIF0RBDv/1Nb2dWTGl8f2c6asea6Q1nDQk70fWOPq3IlRLKAy/IcLq/hMhgJp0x4u5x1t+4Ea/HW+Sq9kiwXcvn7oBzEO8yjJikl1Pjao509xlpokVQjq+ykDzHNzFrElHWY7Jg2oJ22EG25KOrLoctLD6vKF70SfT6f70rPdHrIZ9M1EGWbYvSoKOhVtRDCKt57oEU8ZXxC5XMWz0ap9b/GV8t2+tphOdZ4kVXa0Hokt43jGNTOHIpupWeHXY3i7ZYnmMwNuWzLhQAim7pzeSxd6cK29fPJXXWejBbr0JoC0f2g1O2z+mHsYSOXmng/mh7xlPGRN8oxfJ7M85x8I08r/2t8rdk3tR1WY01mHLRNmXom+r5ZjO3IK4GSeBcLuEugLZ79HUM3kn1ipmkyXSzlSxvuJA5+ik3dOVz3mfpL63p8AH+ee3I+0kYONfHeA63j6YsP0f154EoL9Pa/xtfadqa0w3Ks+c5v12STv+9Dp55DFAl4LD1ilcJg5G2orudZsL1QmWLIH+mv63syP6UvjXx3ovF+8upB2tPPEPH5patrSuIaa3utjVj8UvyQlMY7xb6ln759U+LZajzGzoMe/lv5WrNvajtqxpq0JdMxof154it1TB8np+/e3t/f/yR98z94lu0TcIv8W4p9TWzGzy85DT35LNQul+3UqwzffvLzmF+S3Pr21LbX2EiJX8yPmF8p8a7t55R25Prt8qfFeCSyufK18D/lmKXnT+q+OeM6d6yN40hfX19E9D1Lzz2HdMaCKF83swUcAABeHSngn5+fD7vj1eSdmAAAAPoDAQcAgIMCAQcAgIMCAQcAgIMCAQcAgAL2cCcwBBwAADKRVSePfOY6yggBAOCgvP39B/od4p9fvxAgAB7EX79/vz3ahz2DFAoAABwUCDgAABwUCDgAABwUCPhOaP0QsBb08Hnvcdm7f6141XbvDQh4Q1IHOb8Pj4iy3kj9SHr4vPe47N2/Vrxqu/cIBNyYcRzvngvMyGcY+14JR0S7fVGBi5DP/LhRoro62UfFJdX/I/abBa/a7r0BATeEX5cUeuMOvwj6mS89+X2f/D7DPb7+LMTR/QevAwTcCC3erlcpyT+h92cehR4+HzEurwD6ZR9AwA3gGZt8756cZfObN3xv39nLbbk59PD5iHF5BdAv+wECboDrXXhyhr2uK63rGhzsRzwRevh8xLi8AuiXfQABN8KXOkklVLHi25ZbyuX6fk05mO948gdNL+jm2ukRF72vhf8lPliW5vn6JnRs3ifFh979AtxAwI0JLWD6CJVl6W1sw/WW+9DLhF37SH9zy8FcPvNnWgAvl8tmL8dO67j47JX47xP8mA86/Vbit68d7K/2Sy+iy+9LH5Zlcba1d78APxBwY/iEzxXEUFkWb5Mi4bLrqm5JqYzx2S3xWQsB+yavUPYQl5g9fYyY/9qXFB+GYaDL5eK1WVNjLY+p/ZeL6LLiRsM/WqH29uoX4AYCbgDPKHjg8ozKugztdDptthhpU89q9OxOpndcteq1LMtC0zRtbWekvy2qF3Lj4qPG/5K+keKt9+PPa8eObIe8F0GjhViO4VIfrPoF+IGAG7CuK83z7Myd8iC2uGyc5/nuB2EYBlqWhS6Xy2ZTzpakEPC+t9tty/P6Zl6lrOtK1+t1y3XySdp6ppUblxb+1/YN25C2WTyv12t+UP5Djj3+v15gn6Zp+zcR3flQ8yNv1S/ADwTcCB6Irhyq/HfNZbG+fF/XdTtB9YyaRZr3k3a5KsZ6Bv4ocuKyBx9038gfCD0ZqJ2psoiG9tfb2Hei7/FbYn8P/fLsQMANud1u2+DUQk50P6MpEfGc9INv0bL0eHtmD+0o7RseL67jhW78yvErp3ImlLcusQ3aAgE3RtZ8y+oPubBzPp+7XDpKkUCucV9w3/CPuuuuXfn/VuNFVyhZCjhoDwS8Ibfbbcs5E92vzo/jiPwfIKI2C8opyAol1wInRHz/QMA7oPOaODEAk3LjV4tUhBbvaZrurtQ+Pj62xUawXyDgnZCLNwAwehGzF/rGHn01iPz1MYCAd6S3eMsfjNht1KAfe/gxl+sj4LhAwA3gG2aIyFuy5buhphVSJHw3kvQQkNoqikfTwn8up4uVCbZ8doguE9QzcFwpHgMIuAG6bNC1GKTv7GstaLKWl4ju8p1E9TdpxGwzuu1HqIjp4b9cE9F9Q/TdP/M8V93I40Pbkp/pNoP9AgE3Rp/sRPezmWmaur2GikWCxYAfytRjduV6tAB/3kKQrGntP894WbzlA7N0CWGL9Jd8/EPIPtg3EHAD+GTU9d46ZRK6nT6UUolt4+36e/I2adet/UTuhzelEvNLV96UpI1axCXVbor/NT7Iu3ddKbaaxy/E2sxjY1mWH1eP8imCJcdv2S/gnre///x5tA+75p9fv7IC5Mstxy69+SW6vsd3+rZJmyEb8iW97M/5fN5KxT4/P7Pr0lP9kljasIhLiBT/LXxw2alN1cT8cn1X2ib6FvDc2Fv2y1+/f79F3H9pIOARcgX8SIzjSF9fX0RUJuAAtAYCHgYplBcGuU4Ajg0E/ImRl8b6clU/EQ8AcDwg4E+MrECRz2Ym+vnSAIg4AMcDAv7E6OdK88KRrpDBm1EAOCYQ8CeGH6JF9PNFE0Q/X/QAADgWqEKJ8AxVKJzv1nXgR7grErw2qEIJAwGP8AwCDsBRgYCH+RdHEwkWLXE/8gAAAABJRU5ErkJggg==
|
||||||
|
</smpte:image>
|
||||||
|
<smpte:image imagetype="PNG" encoding="Base64" xml:id="img_1">
|
||||||
|
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
|
||||||
|
</smpte:image>
|
||||||
|
</metadata>
|
||||||
|
<styling>
|
||||||
|
<style/>
|
||||||
|
</styling>
|
||||||
|
<layout>
|
||||||
|
<region xml:id="region_0" tts:extent="653px 86px" tts:origin="307px 562px"/>
|
||||||
|
<region xml:id="region_1" tts:extent="730px 43px" tts:origin="269px 612px"/>
|
||||||
|
</layout>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div begin="00:00:00.200" end="00:00:03.000" region="region_0" smpte:backgroundImage="#img_0"/>
|
||||||
|
<div begin="00:00:03.200" end="00:00:06.937" region="region_1" smpte:backgroundImage="#img_1"/>
|
||||||
|
</body>
|
||||||
|
</tt>
|
@ -0,0 +1,23 @@
|
|||||||
|
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:tts="http://www.w3.org/ns/ttml#styling" xmlns:smpte="http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt" xml:lang="eng">
|
||||||
|
<head>
|
||||||
|
<metadata>
|
||||||
|
<smpte:image imagetype="PNG" encoding="Base64" xml:id="img_0">
|
||||||
|
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
|
||||||
|
</smpte:image>
|
||||||
|
<smpte:image imagetype="PNG" encoding="Base64" xml:id="img_1">
|
||||||
|
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
|
||||||
|
</smpte:image>
|
||||||
|
</metadata>
|
||||||
|
<styling>
|
||||||
|
<style/>
|
||||||
|
</styling>
|
||||||
|
<layout>
|
||||||
|
<region xml:id="region_0" tts:extent="653px 86px" tts:origin="307px 562px"/>
|
||||||
|
<region xml:id="region_1" tts:extent="730px 43px" tts:origin="269px 612px"/>
|
||||||
|
</layout>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div begin="00:00:00.200" end="00:00:03.000" region="region_0" smpte:backgroundImage="#img_0"/>
|
||||||
|
<div begin="00:00:03.200" end="00:00:06.937" region="region_1" smpte:backgroundImage="#img_1"/>
|
||||||
|
</body>
|
||||||
|
</tt>
|
@ -63,6 +63,9 @@ public final class TtmlDecoderTest {
|
|||||||
private static final String FONT_SIZE_INVALID_TTML_FILE = "ttml/font_size_invalid.xml";
|
private static final String FONT_SIZE_INVALID_TTML_FILE = "ttml/font_size_invalid.xml";
|
||||||
private static final String FONT_SIZE_EMPTY_TTML_FILE = "ttml/font_size_empty.xml";
|
private static final String FONT_SIZE_EMPTY_TTML_FILE = "ttml/font_size_empty.xml";
|
||||||
private static final String FRAME_RATE_TTML_FILE = "ttml/frame_rate.xml";
|
private static final String FRAME_RATE_TTML_FILE = "ttml/frame_rate.xml";
|
||||||
|
private static final String BITMAP_REGION_FILE = "ttml/bitmap_percentage_region.xml";
|
||||||
|
private static final String BITMAP_PIXEL_REGION_FILE = "ttml/bitmap_pixel_region.xml";
|
||||||
|
private static final String BITMAP_UNSUPPORTED_REGION_FILE = "ttml/bitmap_unsupported_region.xml";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInlineAttributes() throws IOException, SubtitleDecoderException {
|
public void testInlineAttributes() throws IOException, SubtitleDecoderException {
|
||||||
@ -259,56 +262,56 @@ public final class TtmlDecoderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testMultipleRegions() throws IOException, SubtitleDecoderException {
|
public void testMultipleRegions() throws IOException, SubtitleDecoderException {
|
||||||
TtmlSubtitle subtitle = getSubtitle(MULTIPLE_REGIONS_TTML_FILE);
|
TtmlSubtitle subtitle = getSubtitle(MULTIPLE_REGIONS_TTML_FILE);
|
||||||
List<Cue> output = subtitle.getCues(1000000);
|
List<Cue> cues = subtitle.getCues(1000000);
|
||||||
assertThat(output).hasSize(2);
|
assertThat(cues).hasSize(2);
|
||||||
Cue ttmlCue = output.get(0);
|
Cue cue = cues.get(0);
|
||||||
assertThat(ttmlCue.text.toString()).isEqualTo("lorem");
|
assertThat(cue.text.toString()).isEqualTo("lorem");
|
||||||
assertThat(ttmlCue.position).isEqualTo(10f / 100f);
|
assertThat(cue.position).isEqualTo(10f / 100f);
|
||||||
assertThat(ttmlCue.line).isEqualTo(10f / 100f);
|
assertThat(cue.line).isEqualTo(10f / 100f);
|
||||||
assertThat(ttmlCue.size).isEqualTo(20f / 100f);
|
assertThat(cue.size).isEqualTo(20f / 100f);
|
||||||
|
|
||||||
ttmlCue = output.get(1);
|
cue = cues.get(1);
|
||||||
assertThat(ttmlCue.text.toString()).isEqualTo("amet");
|
assertThat(cue.text.toString()).isEqualTo("amet");
|
||||||
assertThat(ttmlCue.position).isEqualTo(60f / 100f);
|
assertThat(cue.position).isEqualTo(60f / 100f);
|
||||||
assertThat(ttmlCue.line).isEqualTo(10f / 100f);
|
assertThat(cue.line).isEqualTo(10f / 100f);
|
||||||
assertThat(ttmlCue.size).isEqualTo(20f / 100f);
|
assertThat(cue.size).isEqualTo(20f / 100f);
|
||||||
|
|
||||||
output = subtitle.getCues(5000000);
|
cues = subtitle.getCues(5000000);
|
||||||
assertThat(output).hasSize(1);
|
assertThat(cues).hasSize(1);
|
||||||
ttmlCue = output.get(0);
|
cue = cues.get(0);
|
||||||
assertThat(ttmlCue.text.toString()).isEqualTo("ipsum");
|
assertThat(cue.text.toString()).isEqualTo("ipsum");
|
||||||
assertThat(ttmlCue.position).isEqualTo(40f / 100f);
|
assertThat(cue.position).isEqualTo(40f / 100f);
|
||||||
assertThat(ttmlCue.line).isEqualTo(40f / 100f);
|
assertThat(cue.line).isEqualTo(40f / 100f);
|
||||||
assertThat(ttmlCue.size).isEqualTo(20f / 100f);
|
assertThat(cue.size).isEqualTo(20f / 100f);
|
||||||
|
|
||||||
output = subtitle.getCues(9000000);
|
cues = subtitle.getCues(9000000);
|
||||||
assertThat(output).hasSize(1);
|
assertThat(cues).hasSize(1);
|
||||||
ttmlCue = output.get(0);
|
cue = cues.get(0);
|
||||||
assertThat(ttmlCue.text.toString()).isEqualTo("dolor");
|
assertThat(cue.text.toString()).isEqualTo("dolor");
|
||||||
assertThat(ttmlCue.position).isEqualTo(Cue.DIMEN_UNSET);
|
assertThat(cue.position).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
assertThat(ttmlCue.line).isEqualTo(Cue.DIMEN_UNSET);
|
assertThat(cue.line).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
assertThat(ttmlCue.size).isEqualTo(Cue.DIMEN_UNSET);
|
assertThat(cue.size).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
// TODO: Should be as below, once https://github.com/google/ExoPlayer/issues/2953 is fixed.
|
// TODO: Should be as below, once https://github.com/google/ExoPlayer/issues/2953 is fixed.
|
||||||
// assertEquals(10f / 100f, ttmlCue.position);
|
// assertEquals(10f / 100f, cue.position);
|
||||||
// assertEquals(80f / 100f, ttmlCue.line);
|
// assertEquals(80f / 100f, cue.line);
|
||||||
// assertEquals(1f, ttmlCue.size);
|
// assertEquals(1f, cue.size);
|
||||||
|
|
||||||
output = subtitle.getCues(21000000);
|
cues = subtitle.getCues(21000000);
|
||||||
assertThat(output).hasSize(1);
|
assertThat(cues).hasSize(1);
|
||||||
ttmlCue = output.get(0);
|
cue = cues.get(0);
|
||||||
assertThat(ttmlCue.text.toString()).isEqualTo("She first said this");
|
assertThat(cue.text.toString()).isEqualTo("She first said this");
|
||||||
assertThat(ttmlCue.position).isEqualTo(45f / 100f);
|
assertThat(cue.position).isEqualTo(45f / 100f);
|
||||||
assertThat(ttmlCue.line).isEqualTo(45f / 100f);
|
assertThat(cue.line).isEqualTo(45f / 100f);
|
||||||
assertThat(ttmlCue.size).isEqualTo(35f / 100f);
|
assertThat(cue.size).isEqualTo(35f / 100f);
|
||||||
output = subtitle.getCues(25000000);
|
cues = subtitle.getCues(25000000);
|
||||||
ttmlCue = output.get(0);
|
cue = cues.get(0);
|
||||||
assertThat(ttmlCue.text.toString()).isEqualTo("She first said this\nThen this");
|
assertThat(cue.text.toString()).isEqualTo("She first said this\nThen this");
|
||||||
output = subtitle.getCues(29000000);
|
cues = subtitle.getCues(29000000);
|
||||||
assertThat(output).hasSize(1);
|
assertThat(cues).hasSize(1);
|
||||||
ttmlCue = output.get(0);
|
cue = cues.get(0);
|
||||||
assertThat(ttmlCue.text.toString()).isEqualTo("She first said this\nThen this\nFinally this");
|
assertThat(cue.text.toString()).isEqualTo("She first said this\nThen this\nFinally this");
|
||||||
assertThat(ttmlCue.position).isEqualTo(45f / 100f);
|
assertThat(cue.position).isEqualTo(45f / 100f);
|
||||||
assertThat(ttmlCue.line).isEqualTo(45f / 100f);
|
assertThat(cue.line).isEqualTo(45f / 100f);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -499,6 +502,91 @@ public final class TtmlDecoderTest {
|
|||||||
assertThat((double) subtitle.getEventTime(3)).isWithin(2000).of(2_002_000_000);
|
assertThat((double) subtitle.getEventTime(3)).isWithin(2000).of(2_002_000_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBitmapPercentageRegion() throws IOException, SubtitleDecoderException {
|
||||||
|
TtmlSubtitle subtitle = getSubtitle(BITMAP_REGION_FILE);
|
||||||
|
|
||||||
|
List<Cue> cues = subtitle.getCues(1000000);
|
||||||
|
assertThat(cues).hasSize(1);
|
||||||
|
Cue cue = cues.get(0);
|
||||||
|
assertThat(cue.text).isNull();
|
||||||
|
assertThat(cue.bitmap).isNotNull();
|
||||||
|
assertThat(cue.position).isEqualTo(24f / 100f);
|
||||||
|
assertThat(cue.line).isEqualTo(28f / 100f);
|
||||||
|
assertThat(cue.size).isEqualTo(51f / 100f);
|
||||||
|
assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
|
||||||
|
cues = subtitle.getCues(4000000);
|
||||||
|
assertThat(cues).hasSize(1);
|
||||||
|
cue = cues.get(0);
|
||||||
|
assertThat(cue.text).isNull();
|
||||||
|
assertThat(cue.bitmap).isNotNull();
|
||||||
|
assertThat(cue.position).isEqualTo(21f / 100f);
|
||||||
|
assertThat(cue.line).isEqualTo(35f / 100f);
|
||||||
|
assertThat(cue.size).isEqualTo(57f / 100f);
|
||||||
|
assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
|
||||||
|
cues = subtitle.getCues(7500000);
|
||||||
|
assertThat(cues).hasSize(1);
|
||||||
|
cue = cues.get(0);
|
||||||
|
assertThat(cue.text).isNull();
|
||||||
|
assertThat(cue.bitmap).isNotNull();
|
||||||
|
assertThat(cue.position).isEqualTo(24f / 100f);
|
||||||
|
assertThat(cue.line).isEqualTo(28f / 100f);
|
||||||
|
assertThat(cue.size).isEqualTo(51f / 100f);
|
||||||
|
assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBitmapPixelRegion() throws IOException, SubtitleDecoderException {
|
||||||
|
TtmlSubtitle subtitle = getSubtitle(BITMAP_PIXEL_REGION_FILE);
|
||||||
|
|
||||||
|
List<Cue> cues = subtitle.getCues(1000000);
|
||||||
|
assertThat(cues).hasSize(1);
|
||||||
|
Cue cue = cues.get(0);
|
||||||
|
assertThat(cue.text).isNull();
|
||||||
|
assertThat(cue.bitmap).isNotNull();
|
||||||
|
assertThat(cue.position).isEqualTo(307f / 1280f);
|
||||||
|
assertThat(cue.line).isEqualTo(562f / 720f);
|
||||||
|
assertThat(cue.size).isEqualTo(653f / 1280f);
|
||||||
|
assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
|
||||||
|
cues = subtitle.getCues(4000000);
|
||||||
|
assertThat(cues).hasSize(1);
|
||||||
|
cue = cues.get(0);
|
||||||
|
assertThat(cue.text).isNull();
|
||||||
|
assertThat(cue.bitmap).isNotNull();
|
||||||
|
assertThat(cue.position).isEqualTo(269f / 1280f);
|
||||||
|
assertThat(cue.line).isEqualTo(612f / 720f);
|
||||||
|
assertThat(cue.size).isEqualTo(730f / 1280f);
|
||||||
|
assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBitmapUnsupportedRegion() throws IOException, SubtitleDecoderException {
|
||||||
|
TtmlSubtitle subtitle = getSubtitle(BITMAP_UNSUPPORTED_REGION_FILE);
|
||||||
|
|
||||||
|
List<Cue> cues = subtitle.getCues(1000000);
|
||||||
|
assertThat(cues).hasSize(1);
|
||||||
|
Cue cue = cues.get(0);
|
||||||
|
assertThat(cue.text).isNull();
|
||||||
|
assertThat(cue.bitmap).isNotNull();
|
||||||
|
assertThat(cue.position).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
assertThat(cue.line).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
assertThat(cue.size).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
|
||||||
|
cues = subtitle.getCues(4000000);
|
||||||
|
assertThat(cues).hasSize(1);
|
||||||
|
cue = cues.get(0);
|
||||||
|
assertThat(cue.text).isNull();
|
||||||
|
assertThat(cue.bitmap).isNotNull();
|
||||||
|
assertThat(cue.position).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
assertThat(cue.line).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
assertThat(cue.size).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
assertThat(cue.bitmapHeight).isEqualTo(Cue.DIMEN_UNSET);
|
||||||
|
}
|
||||||
|
|
||||||
private void assertSpans(
|
private void assertSpans(
|
||||||
TtmlSubtitle subtitle,
|
TtmlSubtitle subtitle,
|
||||||
int second,
|
int second,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user