use of com.google.android.exoplayer2.text.SubtitleDecoderException in project ExoPlayer by google.
the class TtmlDecoder method parseTimeExpression.
/**
* Parses a time expression, returning the parsed timestamp.
* <p>
* For the format of a time expression, see:
* <a href="http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a>
*
* @param time A string that includes the time expression.
* @param frameAndTickRate The effective frame and tick rates of the stream.
* @return The parsed timestamp in microseconds.
* @throws SubtitleDecoderException If the given string does not contain a valid time expression.
*/
private static long parseTimeExpression(String time, FrameAndTickRate frameAndTickRate) throws SubtitleDecoderException {
Matcher matcher = CLOCK_TIME.matcher(time);
if (matcher.matches()) {
String hours = matcher.group(1);
double durationSeconds = Long.parseLong(hours) * 3600;
String minutes = matcher.group(2);
durationSeconds += Long.parseLong(minutes) * 60;
String seconds = matcher.group(3);
durationSeconds += Long.parseLong(seconds);
String fraction = matcher.group(4);
durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0;
String frames = matcher.group(5);
durationSeconds += (frames != null) ? Long.parseLong(frames) / frameAndTickRate.effectiveFrameRate : 0;
String subframes = matcher.group(6);
durationSeconds += (subframes != null) ? ((double) Long.parseLong(subframes)) / frameAndTickRate.subFrameRate / frameAndTickRate.effectiveFrameRate : 0;
return (long) (durationSeconds * C.MICROS_PER_SECOND);
}
matcher = OFFSET_TIME.matcher(time);
if (matcher.matches()) {
String timeValue = matcher.group(1);
double offsetSeconds = Double.parseDouble(timeValue);
String unit = matcher.group(2);
switch(unit) {
case "h":
offsetSeconds *= 3600;
break;
case "m":
offsetSeconds *= 60;
break;
case "s":
// Do nothing.
break;
case "ms":
offsetSeconds /= 1000;
break;
case "f":
offsetSeconds /= frameAndTickRate.effectiveFrameRate;
break;
case "t":
offsetSeconds /= frameAndTickRate.tickRate;
break;
}
return (long) (offsetSeconds * C.MICROS_PER_SECOND);
}
throw new SubtitleDecoderException("Malformed time expression: " + time);
}
use of com.google.android.exoplayer2.text.SubtitleDecoderException in project ExoPlayer by google.
the class TtmlDecoder method decode.
@Override
protected TtmlSubtitle decode(byte[] bytes, int length) throws SubtitleDecoderException {
try {
XmlPullParser xmlParser = xmlParserFactory.newPullParser();
Map<String, TtmlStyle> globalStyles = new HashMap<>();
Map<String, TtmlRegion> regionMap = new HashMap<>();
regionMap.put(TtmlNode.ANONYMOUS_REGION_ID, new TtmlRegion());
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes, 0, length);
xmlParser.setInput(inputStream, null);
TtmlSubtitle ttmlSubtitle = null;
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++;
} else if (TtmlNode.TAG_HEAD.equals(name)) {
parseHeader(xmlParser, globalStyles, regionMap);
} else {
try {
TtmlNode node = parseNode(xmlParser, parent, regionMap, frameAndTickRate);
nodeStack.addLast(node);
if (parent != null) {
parent.addChild(node);
}
} catch (SubtitleDecoderException e) {
Log.w(TAG, "Suppressing parser error", e);
// Treat the node (and by extension, all of its children) as unsupported.
unsupportedNodeDepth++;
}
}
} else if (eventType == XmlPullParser.TEXT) {
parent.addChild(TtmlNode.buildTextNode(xmlParser.getText()));
} else if (eventType == XmlPullParser.END_TAG) {
if (xmlParser.getName().equals(TtmlNode.TAG_TT)) {
ttmlSubtitle = new TtmlSubtitle(nodeStack.getLast(), globalStyles, regionMap);
}
nodeStack.removeLast();
}
} else {
if (eventType == XmlPullParser.START_TAG) {
unsupportedNodeDepth++;
} else if (eventType == XmlPullParser.END_TAG) {
unsupportedNodeDepth--;
}
}
xmlParser.next();
eventType = xmlParser.getEventType();
}
return ttmlSubtitle;
} catch (XmlPullParserException xppe) {
throw new SubtitleDecoderException("Unable to decode source", xppe);
} catch (IOException e) {
throw new IllegalStateException("Unexpected error when reading input.", e);
}
}
use of com.google.android.exoplayer2.text.SubtitleDecoderException in project ExoPlayer by google.
the class TtmlDecoder method parseFontSize.
private static void parseFontSize(String expression, TtmlStyle out) throws SubtitleDecoderException {
String[] expressions = expression.split("\\s+");
Matcher matcher;
if (expressions.length == 1) {
matcher = FONT_SIZE.matcher(expression);
} else if (expressions.length == 2) {
matcher = FONT_SIZE.matcher(expressions[1]);
Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font" + " size and ignoring the first.");
} else {
throw new SubtitleDecoderException("Invalid number of entries for fontSize: " + expressions.length + ".");
}
if (matcher.matches()) {
String unit = matcher.group(3);
switch(unit) {
case "px":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PIXEL);
break;
case "em":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_EM);
break;
case "%":
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
break;
default:
throw new SubtitleDecoderException("Invalid unit for fontSize: '" + unit + "'.");
}
out.setFontSize(Float.valueOf(matcher.group(1)));
} else {
throw new SubtitleDecoderException("Invalid expression for fontSize: '" + expression + "'.");
}
}
use of com.google.android.exoplayer2.text.SubtitleDecoderException in project ExoPlayer by google.
the class TtmlDecoderTest method testMultipleRegions.
public void testMultipleRegions() throws IOException, SubtitleDecoderException {
TtmlSubtitle subtitle = getSubtitle(MULTIPLE_REGIONS_TTML_FILE);
List<Cue> output = subtitle.getCues(1000000);
assertEquals(2, output.size());
Cue ttmlCue = output.get(0);
assertEquals("lorem", ttmlCue.text.toString());
assertEquals(10.f / 100.f, ttmlCue.position);
assertEquals(10.f / 100.f, ttmlCue.line);
assertEquals(20.f / 100.f, ttmlCue.size);
ttmlCue = output.get(1);
assertEquals("amet", ttmlCue.text.toString());
assertEquals(60.f / 100.f, ttmlCue.position);
assertEquals(10.f / 100.f, ttmlCue.line);
assertEquals(20.f / 100.f, ttmlCue.size);
output = subtitle.getCues(5000000);
assertEquals(1, output.size());
ttmlCue = output.get(0);
assertEquals("ipsum", ttmlCue.text.toString());
assertEquals(40.f / 100.f, ttmlCue.position);
assertEquals(40.f / 100.f, ttmlCue.line);
assertEquals(20.f / 100.f, ttmlCue.size);
output = subtitle.getCues(9000000);
assertEquals(1, output.size());
ttmlCue = output.get(0);
assertEquals("dolor", ttmlCue.text.toString());
assertEquals(10.f / 100.f, ttmlCue.position);
assertEquals(80.f / 100.f, ttmlCue.line);
assertEquals(Cue.DIMEN_UNSET, ttmlCue.size);
output = subtitle.getCues(21000000);
assertEquals(1, output.size());
ttmlCue = output.get(0);
assertEquals("She first said this", ttmlCue.text.toString());
assertEquals(45.f / 100.f, ttmlCue.position);
assertEquals(45.f / 100.f, ttmlCue.line);
assertEquals(35.f / 100.f, ttmlCue.size);
output = subtitle.getCues(25000000);
ttmlCue = output.get(0);
assertEquals("She first said this\nThen this", ttmlCue.text.toString());
output = subtitle.getCues(29000000);
assertEquals(1, output.size());
ttmlCue = output.get(0);
assertEquals("She first said this\nThen this\nFinally this", ttmlCue.text.toString());
assertEquals(45.f / 100.f, ttmlCue.position);
assertEquals(45.f / 100.f, ttmlCue.line);
}
use of com.google.android.exoplayer2.text.SubtitleDecoderException in project ExoPlayer by google.
the class TtmlDecoderTest method testFontSizeWithInvalidValueIsIgnored.
public void testFontSizeWithInvalidValueIsIgnored() throws IOException, SubtitleDecoderException {
TtmlSubtitle subtitle = getSubtitle(FONT_SIZE_INVALID_TTML_FILE);
assertEquals(6, subtitle.getEventTimeCount());
List<Cue> cues = subtitle.getCues(10 * 1000000);
assertEquals(1, cues.size());
SpannableStringBuilder spannable = (SpannableStringBuilder) cues.get(0).text;
assertEquals("invalid", String.valueOf(spannable));
assertEquals(0, spannable.getSpans(0, spannable.length(), RelativeSizeSpan.class).length);
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length);
cues = subtitle.getCues(20 * 1000000);
assertEquals(1, cues.size());
spannable = (SpannableStringBuilder) cues.get(0).text;
assertEquals("invalid", String.valueOf(spannable));
assertEquals(0, spannable.getSpans(0, spannable.length(), RelativeSizeSpan.class).length);
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length);
cues = subtitle.getCues(30 * 1000000);
assertEquals(1, cues.size());
spannable = (SpannableStringBuilder) cues.get(0).text;
assertEquals("invalid dot", String.valueOf(spannable));
assertEquals(0, spannable.getSpans(0, spannable.length(), RelativeSizeSpan.class).length);
assertEquals(0, spannable.getSpans(0, spannable.length(), AbsoluteSizeSpan.class).length);
}
Aggregations