mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add support for TTML Regions extent
This allows limiting the horizontal extension of the cues. NOTE: So far, we only support percentages for size magnitudes. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=121682404
This commit is contained in:
parent
642b2009f3
commit
99c0ba005a
5 changed files with 47 additions and 18 deletions
|
|
@ -8,7 +8,7 @@
|
||||||
<layout>
|
<layout>
|
||||||
<region xml:id="region1" ttm:origin="10% 10%" extent="20% 20%"/>
|
<region xml:id="region1" ttm:origin="10% 10%" extent="20% 20%"/>
|
||||||
<region xml:id="region2" ttm:origin="40% 40%" extent="20% 20%"/>
|
<region xml:id="region2" ttm:origin="40% 40%" extent="20% 20%"/>
|
||||||
<region xml:id="region3" ttm:origin="10% 80%" extent="10% 10%"/>
|
<region xml:id="region3" ttm:origin="10% 80%"/>
|
||||||
<region xml:id="region4" ttm:origin="60% 10%" extent="20% 20%"/>
|
<region xml:id="region4" ttm:origin="60% 10%" extent="20% 20%"/>
|
||||||
<region xml:id="ultimate" ttm:origin="45% 45%" extent="35% 35%"/>
|
<region xml:id="ultimate" ttm:origin="45% 45%" extent="35% 35%"/>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
||||||
|
|
@ -156,10 +156,13 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||||
assertEquals("lorem", ttmlCue.text.toString());
|
assertEquals("lorem", ttmlCue.text.toString());
|
||||||
assertEquals(10.f / 100.f, ttmlCue.position);
|
assertEquals(10.f / 100.f, ttmlCue.position);
|
||||||
assertEquals(10.f / 100.f, ttmlCue.line);
|
assertEquals(10.f / 100.f, ttmlCue.line);
|
||||||
|
assertEquals(20.f / 100.f, ttmlCue.size);
|
||||||
|
|
||||||
ttmlCue = output.get(1);
|
ttmlCue = output.get(1);
|
||||||
assertEquals("amet", ttmlCue.text.toString());
|
assertEquals("amet", ttmlCue.text.toString());
|
||||||
assertEquals(60.f / 100.f, ttmlCue.position);
|
assertEquals(60.f / 100.f, ttmlCue.position);
|
||||||
assertEquals(10.f / 100.f, ttmlCue.line);
|
assertEquals(10.f / 100.f, ttmlCue.line);
|
||||||
|
assertEquals(20.f / 100.f, ttmlCue.size);
|
||||||
|
|
||||||
output = subtitle.getCues(5000000);
|
output = subtitle.getCues(5000000);
|
||||||
assertEquals(1, output.size());
|
assertEquals(1, output.size());
|
||||||
|
|
@ -167,6 +170,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||||
assertEquals("ipsum", ttmlCue.text.toString());
|
assertEquals("ipsum", ttmlCue.text.toString());
|
||||||
assertEquals(40.f / 100.f, ttmlCue.position);
|
assertEquals(40.f / 100.f, ttmlCue.position);
|
||||||
assertEquals(40.f / 100.f, ttmlCue.line);
|
assertEquals(40.f / 100.f, ttmlCue.line);
|
||||||
|
assertEquals(20.f / 100.f, ttmlCue.size);
|
||||||
|
|
||||||
output = subtitle.getCues(9000000);
|
output = subtitle.getCues(9000000);
|
||||||
assertEquals(1, output.size());
|
assertEquals(1, output.size());
|
||||||
|
|
@ -174,6 +178,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||||
assertEquals("dolor", ttmlCue.text.toString());
|
assertEquals("dolor", ttmlCue.text.toString());
|
||||||
assertEquals(10.f / 100.f, ttmlCue.position);
|
assertEquals(10.f / 100.f, ttmlCue.position);
|
||||||
assertEquals(80.f / 100.f, ttmlCue.line);
|
assertEquals(80.f / 100.f, ttmlCue.line);
|
||||||
|
assertEquals(Cue.DIMEN_UNSET, ttmlCue.size);
|
||||||
|
|
||||||
output = subtitle.getCues(21000000);
|
output = subtitle.getCues(21000000);
|
||||||
assertEquals(1, output.size());
|
assertEquals(1, output.size());
|
||||||
|
|
@ -181,6 +186,7 @@ public final class TtmlParserTest extends InstrumentationTestCase {
|
||||||
assertEquals("She first said this", ttmlCue.text.toString());
|
assertEquals("She first said this", ttmlCue.text.toString());
|
||||||
assertEquals(45.f / 100.f, ttmlCue.position);
|
assertEquals(45.f / 100.f, ttmlCue.position);
|
||||||
assertEquals(45.f / 100.f, ttmlCue.line);
|
assertEquals(45.f / 100.f, ttmlCue.line);
|
||||||
|
assertEquals(35.f / 100.f, ttmlCue.size);
|
||||||
output = subtitle.getCues(25000000);
|
output = subtitle.getCues(25000000);
|
||||||
ttmlCue = output.get(0);
|
ttmlCue = output.get(0);
|
||||||
assertEquals("She first said this\nThen this", ttmlCue.text.toString());
|
assertEquals("She first said this\nThen this", ttmlCue.text.toString());
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ import java.util.TreeSet;
|
||||||
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";
|
||||||
public static final String ATTR_TTS_BACKGROUND_COLOR = "backgroundColor";
|
public static final String ATTR_TTS_BACKGROUND_COLOR = "backgroundColor";
|
||||||
|
public static final String ATTR_TTS_EXTENT = "extent";
|
||||||
public static final String ATTR_TTS_FONT_STYLE = "fontStyle";
|
public static final String ATTR_TTS_FONT_STYLE = "fontStyle";
|
||||||
public static final String ATTR_TTS_FONT_SIZE = "fontSize";
|
public static final String ATTR_TTS_FONT_SIZE = "fontSize";
|
||||||
public static final String ATTR_TTS_FONT_FAMILY = "fontFamily";
|
public static final String ATTR_TTS_FONT_FAMILY = "fontFamily";
|
||||||
|
|
@ -180,7 +181,7 @@ import java.util.TreeSet;
|
||||||
for (Entry<String, SpannableStringBuilder> entry : regionOutputs.entrySet()) {
|
for (Entry<String, SpannableStringBuilder> entry : regionOutputs.entrySet()) {
|
||||||
TtmlRegion region = regionMap.get(entry.getKey());
|
TtmlRegion region = regionMap.get(entry.getKey());
|
||||||
cues.add(new Cue(cleanUpText(entry.getValue()), null, region.line, Cue.TYPE_UNSET,
|
cues.add(new Cue(cleanUpText(entry.getValue()), null, region.line, Cue.TYPE_UNSET,
|
||||||
Cue.TYPE_UNSET, region.position, Cue.TYPE_UNSET, Cue.DIMEN_UNSET));
|
Cue.TYPE_UNSET, region.position, Cue.TYPE_UNSET, region.width));
|
||||||
}
|
}
|
||||||
return cues;
|
return cues;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer.text.ttml;
|
||||||
|
|
||||||
import com.google.android.exoplayer.C;
|
import com.google.android.exoplayer.C;
|
||||||
import com.google.android.exoplayer.ParserException;
|
import com.google.android.exoplayer.ParserException;
|
||||||
|
import com.google.android.exoplayer.text.Cue;
|
||||||
import com.google.android.exoplayer.text.SimpleSubtitleParser;
|
import com.google.android.exoplayer.text.SimpleSubtitleParser;
|
||||||
import com.google.android.exoplayer.util.ColorParser;
|
import com.google.android.exoplayer.util.ColorParser;
|
||||||
import com.google.android.exoplayer.util.ParserUtil;
|
import com.google.android.exoplayer.util.ParserUtil;
|
||||||
|
|
@ -74,9 +75,8 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||||
+ "(?:(\\.[0-9]+)|:([0-9][0-9])(?:\\.([0-9]+))?)?$");
|
+ "(?:(\\.[0-9]+)|:([0-9][0-9])(?:\\.([0-9]+))?)?$");
|
||||||
private static final Pattern OFFSET_TIME =
|
private static final Pattern OFFSET_TIME =
|
||||||
Pattern.compile("^([0-9]+(?:\\.[0-9]+)?)(h|m|s|ms|f|t)$");
|
Pattern.compile("^([0-9]+(?:\\.[0-9]+)?)(h|m|s|ms|f|t)$");
|
||||||
private static final Pattern FONT_SIZE =
|
private static final Pattern FONT_SIZE = Pattern.compile("^(([0-9]*.)?[0-9]+)(px|em|%)$");
|
||||||
Pattern.compile("^(([0-9]*.)?[0-9]+)(px|em|%)$");
|
private static final Pattern PERCENTAGE_COORDINATES =
|
||||||
private static final Pattern ORIGIN_COORDINATES =
|
|
||||||
Pattern.compile("^(\\d+\\.?\\d*?)% (\\d+\\.?\\d*?)%$");
|
Pattern.compile("^(\\d+\\.?\\d*?)% (\\d+\\.?\\d*?)%$");
|
||||||
|
|
||||||
// TODO: read and apply the following attributes if specified.
|
// TODO: read and apply the following attributes if specified.
|
||||||
|
|
@ -183,23 +183,42 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||||
return globalStyles;
|
return globalStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a region declaration. Supports origin and extent definition but only when defined in
|
||||||
|
* terms of percentage of the viewport. Regions that do not correctly declare origin are ignored.
|
||||||
|
*/
|
||||||
private Pair<String, TtmlRegion> parseRegionAttributes(XmlPullParser xmlParser) {
|
private Pair<String, TtmlRegion> parseRegionAttributes(XmlPullParser xmlParser) {
|
||||||
String regionId = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
String regionId = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
|
||||||
String regionOrigin = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
|
String regionOrigin = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
|
||||||
|
String regionExtent = ParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
|
||||||
if (regionOrigin == null || regionId == null) {
|
if (regionOrigin == null || regionId == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Matcher originMatcher = ORIGIN_COORDINATES.matcher(regionOrigin);
|
float position = Cue.DIMEN_UNSET;
|
||||||
|
float line = Cue.DIMEN_UNSET;
|
||||||
|
Matcher originMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin);
|
||||||
if (originMatcher.matches()) {
|
if (originMatcher.matches()) {
|
||||||
try {
|
try {
|
||||||
float position = Float.parseFloat(originMatcher.group(1)) / 100.f;
|
position = Float.parseFloat(originMatcher.group(1)) / 100.f;
|
||||||
float line = Float.parseFloat(originMatcher.group(2)) / 100.f;
|
line = Float.parseFloat(originMatcher.group(2)) / 100.f;
|
||||||
return new Pair<>(regionId, new TtmlRegion(position, line));
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.w(TAG, "Ignoring malformed region declaration: '" + regionOrigin + "'", e);
|
Log.w(TAG, "Ignoring region with malformed origin: '" + regionOrigin + "'", e);
|
||||||
|
position = Cue.DIMEN_UNSET;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
float width = Cue.DIMEN_UNSET;
|
||||||
|
if (regionExtent != null) {
|
||||||
|
Matcher extentMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent);
|
||||||
|
if (extentMatcher.matches()) {
|
||||||
|
try {
|
||||||
|
width = Float.parseFloat(extentMatcher.group(1)) / 100.f;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.w(TAG, "Ignoring malformed region extent: '" + regionExtent + "'", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return position != Cue.DIMEN_UNSET ? new Pair<>(regionId, new TtmlRegion(position, line, width))
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String[] parseStyleIds(String parentStyleIds) {
|
private String[] parseStyleIds(String parentStyleIds) {
|
||||||
|
|
@ -387,10 +406,11 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||||
matcher = FONT_SIZE.matcher(expression);
|
matcher = FONT_SIZE.matcher(expression);
|
||||||
} else if (expressions.length == 2){
|
} else if (expressions.length == 2){
|
||||||
matcher = FONT_SIZE.matcher(expressions[1]);
|
matcher = FONT_SIZE.matcher(expressions[1]);
|
||||||
Log.w(TAG, "multiple values in fontSize attribute. Picking the second "
|
Log.w(TAG, "Multiple values in fontSize attribute. Picking the second value for vertical font"
|
||||||
+ "value for vertical font size and ignoring the first.");
|
+ " size and ignoring the first.");
|
||||||
} else {
|
} else {
|
||||||
throw new ParserException();
|
throw new ParserException("Invalid number of entries for fontSize: " + expressions.length
|
||||||
|
+ ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matcher.matches()) {
|
if (matcher.matches()) {
|
||||||
|
|
@ -406,11 +426,11 @@ public final class TtmlParser extends SimpleSubtitleParser {
|
||||||
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
|
out.setFontSizeUnit(TtmlStyle.FONT_SIZE_UNIT_PERCENT);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ParserException();
|
throw new ParserException("Invalid unit for fontSize: '" + unit + "'.");
|
||||||
}
|
}
|
||||||
out.setFontSize(Float.valueOf(matcher.group(1)));
|
out.setFontSize(Float.valueOf(matcher.group(1)));
|
||||||
} else {
|
} else {
|
||||||
throw new ParserException();
|
throw new ParserException("Invalid expression for fontSize: '" + expression + "'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,14 +24,16 @@ import com.google.android.exoplayer.text.Cue;
|
||||||
|
|
||||||
public final float position;
|
public final float position;
|
||||||
public final float line;
|
public final float line;
|
||||||
|
public final float width;
|
||||||
|
|
||||||
public TtmlRegion() {
|
public TtmlRegion() {
|
||||||
this(Cue.DIMEN_UNSET, Cue.DIMEN_UNSET);
|
this(Cue.DIMEN_UNSET, Cue.DIMEN_UNSET, Cue.DIMEN_UNSET);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TtmlRegion(float position, float line) {
|
public TtmlRegion(float position, float line, float width) {
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.line = line;
|
this.line = line;
|
||||||
|
this.width = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue