提交 85741ff2 编写于 作者: O olly 提交者: Oliver Woodman

TTML: Parse and use frameRate/Multiplier + tickRate

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=126792535
上级 2426907f
<tt xmlns="http://www.w3.org/ns/ttml"
xmlns:ttp="http://www.w3.org/ns/ttml#parameter"
ttp:frameRate="50"
ttp:frameRateMultiplier="1000 1001"
ttp:tickRate="100">
<head>
<styling>
</styling>
</head>
<body>
<div>
<p begin="100t" end="101t">text 1</p>
</div>
<div>
<p begin="50000f" end="100000f">text 2</p>
</div>
</body>
</tt>
......@@ -58,6 +58,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
private static final String FONT_SIZE_MISSING_UNIT_TTML_FILE = "ttml/font_size_no_unit.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 FRAME_RATE_TTML_FILE = "ttml/frame_rate.xml";
public void testInlineAttributes() throws IOException {
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
......@@ -364,6 +365,15 @@ public final class TtmlParserTest extends InstrumentationTestCase {
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length);
}
public void testFrameRate() throws IOException {
TtmlSubtitle subtitle = getSubtitle(FRAME_RATE_TTML_FILE);
assertEquals(4, subtitle.getEventTimeCount());
assertEquals(1_000_000, subtitle.getEventTime(0));
assertEquals(1_010_000, subtitle.getEventTime(1));
assertEquals(1_001_000_000, subtitle.getEventTime(2), 1000);
assertEquals(2_002_000_000, subtitle.getEventTime(3), 2000);
}
private void assertSpans(TtmlSubtitle subtitle, int second,
String text, String font, int fontStyle,
int backgroundColor, int color, boolean isUnderline,
......
......@@ -64,6 +64,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
private static final String TAG = "TtmlParser";
private static final String TTP = "http://www.w3.org/ns/ttml#parameter";
private static final String ATTR_BEGIN = "begin";
private static final String ATTR_DURATION = "dur";
private static final String ATTR_END = "end";
......@@ -79,10 +81,10 @@ public final class TtmlParser extends SimpleSubtitleParser {
private static final Pattern PERCENTAGE_COORDINATES =
Pattern.compile("^(\\d+\\.?\\d*?)% (\\d+\\.?\\d*?)%$");
// TODO: read and apply the following attributes if specified.
private static final int DEFAULT_FRAMERATE = 30;
private static final int DEFAULT_SUBFRAMERATE = 1;
private static final int DEFAULT_TICKRATE = 1;
private static final int DEFAULT_FRAME_RATE = 30;
private static final FrameAndTickRate DEFAULT_FRAME_AND_TICK_RATE =
new FrameAndTickRate(DEFAULT_FRAME_RATE, 1, 1);
private final XmlPullParserFactory xmlParserFactory;
......@@ -109,11 +111,15 @@ public final class TtmlParser extends SimpleSubtitleParser {
LinkedList<TtmlNode> nodeStack = new LinkedList<>();
int unsupportedNodeDepth = 0;
int eventType = xmlParser.getEventType();
FrameAndTickRate frameAndTickRate = DEFAULT_FRAME_AND_TICK_RATE;
while (eventType != XmlPullParser.END_DOCUMENT) {
TtmlNode parent = nodeStack.peekLast();
if (unsupportedNodeDepth == 0) {
String name = xmlParser.getName();
if (eventType == XmlPullParser.START_TAG) {
if (TtmlNode.TAG_TT.equals(name)) {
frameAndTickRate = parseFrameAndTickRates(xmlParser);
}
if (!isSupportedTag(name)) {
Log.i(TAG, "Ignoring unsupported tag: " + xmlParser.getName());
unsupportedNodeDepth++;
......@@ -121,7 +127,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
parseHeader(xmlParser, globalStyles, regionMap);
} else {
try {
TtmlNode node = parseNode(xmlParser, parent, regionMap);
TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate);
nodeStack.addLast(node);
if (parent != null) {
parent.addChild(node);
......@@ -158,6 +164,39 @@ public final class TtmlParser extends SimpleSubtitleParser {
}
}
private FrameAndTickRate parseFrameAndTickRates(XmlPullParser xmlParser) throws ParserException {
int frameRate = DEFAULT_FRAME_RATE;
String frameRateStr = xmlParser.getAttributeValue(TTP, "frameRate");
if (frameRateStr != null) {
frameRate = Integer.parseInt(frameRateStr);
}
float frameRateMultiplier = 1;
String frameRateMultiplierStr = xmlParser.getAttributeValue(TTP, "frameRateMultiplier");
if (frameRateMultiplierStr != null) {
String[] parts = frameRateMultiplierStr.split(" ");
if (parts.length != 2) {
throw new ParserException("frameRateMultiplier doesn't have 2 parts");
}
float numerator = Integer.parseInt(parts[0]);
float denominator = Integer.parseInt(parts[1]);
frameRateMultiplier = numerator / denominator;
}
int subFrameRate = DEFAULT_FRAME_AND_TICK_RATE.subFrameRate;
String subFrameRateStr = xmlParser.getAttributeValue(TTP, "subFrameRate");
if (subFrameRateStr != null) {
subFrameRate = Integer.parseInt(subFrameRateStr);
}
int tickRate = DEFAULT_FRAME_AND_TICK_RATE.tickRate;
String tickRateStr = xmlParser.getAttributeValue(TTP, "tickRate");
if (tickRateStr != null) {
tickRate = Integer.parseInt(tickRateStr);
}
return new FrameAndTickRate(frameRate * frameRateMultiplier, subFrameRate, tickRate);
}
private Map<String, TtmlStyle> parseHeader(XmlPullParser xmlParser,
Map<String, TtmlStyle> globalStyles, Map<String, TtmlRegion> globalRegions)
throws IOException, XmlPullParserException {
......@@ -319,7 +358,7 @@ public final class TtmlParser extends SimpleSubtitleParser {
}
private TtmlNode parseNode(XmlPullParser parser, TtmlNode parent,
Map<String, TtmlRegion> regionMap) throws ParserException {
Map<String, TtmlRegion> regionMap, FrameAndTickRate frameAndTickRate) throws ParserException {
long duration = 0;
long startTime = TtmlNode.UNDEFINED_TIME;
long endTime = TtmlNode.UNDEFINED_TIME;
......@@ -332,16 +371,13 @@ public final class TtmlParser extends SimpleSubtitleParser {
String value = parser.getAttributeValue(i);
switch (attr) {
case ATTR_BEGIN:
startTime = parseTimeExpression(value,
DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, DEFAULT_TICKRATE);
startTime = parseTimeExpression(value, frameAndTickRate);
break;
case ATTR_END:
endTime = parseTimeExpression(value,
DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, DEFAULT_TICKRATE);
endTime = parseTimeExpression(value, frameAndTickRate);
break;
case ATTR_DURATION:
duration = parseTimeExpression(value,
DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, DEFAULT_TICKRATE);
duration = parseTimeExpression(value, frameAndTickRate);
break;
case ATTR_STYLE:
// IDREFS: potentially multiple space delimited ids
......@@ -442,14 +478,12 @@ public final class TtmlParser extends SimpleSubtitleParser {
* <a href="http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a>
*
* @param time A string that includes the time expression.
* @param frameRate The frame rate of the stream.
* @param subframeRate The sub-frame rate of the stream
* @param tickRate The tick rate of the stream.
* @param frameAndTickRate The effective frame and tick rates of the stream.
* @return The parsed timestamp in microseconds.
* @throws ParserException If the given string does not contain a valid time expression.
*/
private static long parseTimeExpression(String time, int frameRate, int subframeRate,
int tickRate) throws ParserException {
private static long parseTimeExpression(String time, FrameAndTickRate frameAndTickRate)
throws ParserException {
Matcher matcher = CLOCK_TIME.matcher(time);
if (matcher.matches()) {
String hours = matcher.group(1);
......@@ -461,10 +495,13 @@ public final class TtmlParser extends SimpleSubtitleParser {
String fraction = matcher.group(4);
durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0;
String frames = matcher.group(5);
durationSeconds += (frames != null) ? ((double) Long.parseLong(frames)) / frameRate : 0;
durationSeconds += (frames != null)
? Long.parseLong(frames) / frameAndTickRate.effectiveFrameRate : 0;
String subframes = matcher.group(6);
durationSeconds += (subframes != null) ?
((double) Long.parseLong(subframes)) / subframeRate / frameRate : 0;
durationSeconds += (subframes != null)
? ((double) Long.parseLong(subframes)) / frameAndTickRate.subFrameRate
/ frameAndTickRate.effectiveFrameRate
: 0;
return (long) (durationSeconds * C.MICROS_PER_SECOND);
}
matcher = OFFSET_TIME.matcher(time);
......@@ -486,10 +523,10 @@ public final class TtmlParser extends SimpleSubtitleParser {
offsetSeconds /= 1000;
break;
case "f":
offsetSeconds /= frameRate;
offsetSeconds /= frameAndTickRate.effectiveFrameRate;
break;
case "t":
offsetSeconds /= tickRate;
offsetSeconds /= frameAndTickRate.tickRate;
break;
}
return (long) (offsetSeconds * C.MICROS_PER_SECOND);
......@@ -497,4 +534,15 @@ public final class TtmlParser extends SimpleSubtitleParser {
throw new ParserException("Malformed time expression: " + time);
}
private static final class FrameAndTickRate {
final float effectiveFrameRate;
final int subFrameRate;
final int tickRate;
FrameAndTickRate(float effectiveFrameRate, int subFrameRate, int tickRate) {
this.effectiveFrameRate = effectiveFrameRate;
this.subFrameRate = subFrameRate;
this.tickRate = tickRate;
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册