Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for TTML origin and extent attributes defined in style instead of region #1987

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
`DefaultAudioSink` to stall.
* Video:
* Text:
* TTML: Add support for referencing `tts:origin` and `tts:extent` via
`style` ([#2953](https://github.com/google/ExoPlayer/issues/2953)).
* Metadata:
* Image:
* DataSource:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,13 +308,9 @@ public void multipleRegions() throws IOException {

cue = getOnlyCueAtTimeUs(subtitle, 9_000_000);
assertThat(cue.text.toString()).isEqualTo("dolor");
assertThat(cue.position).isEqualTo(Cue.DIMEN_UNSET);
assertThat(cue.line).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.
// assertEquals(10f / 100f, cue.position);
// assertEquals(80f / 100f, cue.line);
// assertEquals(1f, cue.size);
assertThat(cue.position).isEqualTo(10f / 100f);
assertThat(cue.line).isEqualTo(80f / 100f);
assertThat(cue.size).isEqualTo(1f);

cue = getOnlyCueAtTimeUs(subtitle, 21_000_000);
assertThat(cue.text.toString()).isEqualTo("They first said this");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@

public static final String ANONYMOUS_REGION_ID = "";
public static final String ATTR_ID = "id";
public static final String ATTR_STYLE = "style";
public static final String ATTR_TTS_ORIGIN = "origin";
public static final String ATTR_TTS_EXTENT = "extent";
public static final String ATTR_TTS_DISPLAY_ALIGN = "displayAlign";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ private static Map<String, TtmlStyle> parseHeader(
globalStyles.put(styleId, style);
}
} else if (XmlPullParserUtil.isStartTag(xmlParser, TtmlNode.TAG_REGION)) {
@Nullable TtmlRegion ttmlRegion = parseRegionAttributes(xmlParser, cellRows, ttsExtent);
@Nullable
TtmlRegion ttmlRegion = parseRegionAttributes(xmlParser, cellRows, ttsExtent, globalStyles);
if (ttmlRegion != null) {
globalRegions.put(ttmlRegion.id, ttmlRegion);
}
Expand Down Expand Up @@ -350,7 +351,10 @@ private static void parseMetadata(XmlPullParser xmlParser, Map<String, String> i
*/
@Nullable
private static TtmlRegion parseRegionAttributes(
XmlPullParser xmlParser, int cellRows, @Nullable TtsExtent ttsExtent) {
XmlPullParser xmlParser,
int cellRows,
@Nullable TtsExtent ttsExtent,
Map<String, TtmlStyle> globalStyles) {
@Nullable String regionId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_ID);
if (regionId == null) {
return null;
Expand All @@ -361,6 +365,15 @@ private static TtmlRegion parseRegionAttributes(

@Nullable
String regionOrigin = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_ORIGIN);
if (regionOrigin == null) {
String styleId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_STYLE);
if (styleId != null) {
TtmlStyle style = globalStyles.get(styleId);
if (style != null) {
regionOrigin = style.getOrigin();
}
}
}
if (regionOrigin != null) {
Matcher originPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionOrigin);
Matcher originPixelMatcher = PIXEL_COORDINATES.matcher(regionOrigin);
Expand Down Expand Up @@ -393,19 +406,24 @@ private static TtmlRegion parseRegionAttributes(
return null;
}
} else {
Log.w(TAG, "Ignoring region without an origin");
return null;
// TODO: Should default to top left as below in this case, but need to fix
// https://github.com/google/ExoPlayer/issues/2953 first.
// Origin is omitted. Default to top left.
// position = 0;
// line = 0;
position = 0;
line = 0;
}

float width;
float height;
@Nullable
String regionExtent = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_TTS_EXTENT);
if (regionExtent == null) {
String styleId = XmlPullParserUtil.getAttributeValue(xmlParser, TtmlNode.ATTR_STYLE);
if (styleId != null) {
TtmlStyle style = globalStyles.get(styleId);
if (style != null) {
regionExtent = style.getExtent();
}
}
}
if (regionExtent != null) {
Matcher extentPercentageMatcher = PERCENTAGE_COORDINATES.matcher(regionExtent);
Matcher extentPixelMatcher = PIXEL_COORDINATES.matcher(regionExtent);
Expand Down Expand Up @@ -439,13 +457,9 @@ private static TtmlRegion parseRegionAttributes(
return null;
}
} else {
Log.w(TAG, "Ignoring region without an extent");
return null;
// TODO: Should default to extent of parent as below in this case, but need to fix
// https://github.com/google/ExoPlayer/issues/2953 first.
// Extent is omitted. Default to extent of parent.
// width = 1;
// height = 1;
width = 1;
height = 1;
}

@Cue.AnchorType int lineAnchor = Cue.ANCHOR_TYPE_START;
Expand Down Expand Up @@ -626,6 +640,12 @@ private static String[] parseStyleIds(String parentStyleIds) {
case TtmlNode.ATTR_TTS_SHEAR:
style = createIfNull(style).setShearPercentage(parseShear(attributeValue));
break;
case TtmlNode.ATTR_TTS_ORIGIN:
style = createIfNull(style).setOrigin(attributeValue);
break;
case TtmlNode.ATTR_TTS_EXTENT:
style = createIfNull(style).setExtent(attributeValue);
break;
default:
// ignore
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@
private @OptionalBoolean int textCombine;
@Nullable private TextEmphasis textEmphasis;
private float shearPercentage;
@Nullable private String origin;
@Nullable private String extent;

public TtmlStyle() {
linethrough = UNSPECIFIED;
Expand Down Expand Up @@ -277,6 +279,12 @@ private TtmlStyle inherit(@Nullable TtmlStyle ancestor, boolean chaining) {
if (shearPercentage == UNSPECIFIED_SHEAR) {
shearPercentage = ancestor.shearPercentage;
}
if (origin == null) {
origin = ancestor.origin;
}
if (extent == null) {
extent = ancestor.extent;
}
// attributes not inherited as of http://www.w3.org/TR/ttml1/
if (chaining && !hasBackgroundColor && ancestor.hasBackgroundColor) {
setBackgroundColor(ancestor.backgroundColor);
Expand Down Expand Up @@ -382,4 +390,26 @@ public TtmlStyle setFontSizeUnit(int fontSizeUnit) {
public float getFontSize() {
return fontSize;
}

@CanIgnoreReturnValue
public TtmlStyle setOrigin(@Nullable String origin) {
this.origin = origin;
return this;
}

@Nullable
public String getOrigin() {
return origin;
}

@CanIgnoreReturnValue
public TtmlStyle setExtent(@Nullable String extent) {
this.extent = extent;
return this;
}

@Nullable
public String getExtent() {
return extent;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ public final class TtmlParserTest {
private static final String RUBIES_FILE = "media/ttml/rubies.xml";
private static final String TEXT_EMPHASIS_FILE = "media/ttml/text_emphasis.xml";
private static final String SHEAR_FILE = "media/ttml/shear.xml";
private static final String REGION_ATTRS_FROM_STYLE_FILE =
"media/ttml/inherit_region_attributes_from_style.xml";

@Test
public void simple_allCues() throws Exception {
Expand Down Expand Up @@ -485,13 +487,9 @@ public void multipleRegions() throws Exception {

cue = Iterables.getOnlyElement(allCues.get(2).cues);
assertThat(cue.text.toString()).isEqualTo("dolor");
assertThat(cue.position).isEqualTo(Cue.DIMEN_UNSET);
assertThat(cue.line).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.
// assertEquals(10f / 100f, cue.position);
// assertEquals(80f / 100f, cue.line);
// assertEquals(1f, cue.size);
assertThat(cue.position).isEqualTo(10f / 100f);
assertThat(cue.line).isEqualTo(80f / 100f);
assertThat(cue.size).isEqualTo(1f);

cue = Iterables.getOnlyElement(allCues.get(3).cues);
assertThat(cue.text.toString()).isEqualTo("They first said this");
Expand Down Expand Up @@ -1093,6 +1091,26 @@ public void shear() throws Exception {
assertThat(eighthCue.shearDegrees).isWithin(0.01f).of(90f);
}

@Test
public void regionAttrsFromStyle() throws Exception {
ImmutableList<CuesWithTiming> allCues = getAllCues(REGION_ATTRS_FROM_STYLE_FILE);

Cue firstCue = Iterables.getOnlyElement(allCues.get(0).cues);
assertThat(firstCue.position).isEqualTo(10f / 100f);
assertThat(firstCue.line).isEqualTo(10f / 100f);
assertThat(firstCue.size).isEqualTo(20f / 100f);

Cue secondCue = Iterables.getOnlyElement(allCues.get(1).cues);
assertThat(secondCue.position).isEqualTo(30f / 100f);
assertThat(secondCue.line).isEqualTo(30f / 100f);
assertThat(secondCue.size).isEqualTo(40f / 100f);

Cue thirdCue = Iterables.getOnlyElement(allCues.get(2).cues);
assertThat(thirdCue.position).isEqualTo(30f / 100f);
assertThat(thirdCue.line).isEqualTo(30f / 100f);
assertThat(thirdCue.size).isEqualTo(20f / 100f);
}

private static Spanned getOnlyCueTextAtIndex(List<CuesWithTiming> allCues, int index) {
Cue cue = getOnlyCueAtIndex(allCues, index);
assertThat(cue.text).isInstanceOf(Spanned.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<tt xmlns="http://www.w3.org/ns/ttml"
xmlns:tts="http://www.w3.org/2006/10/ttaf1#style">
<head>
<styling>
<style xml:id="style1" tts:extent="20% 20%" />
<style xml:id="style2" tts:origin="30% 30%" />
<style xml:id="style3" style="style1 style2" />
</styling>
<layout>
<region xml:id="region1" style="style1" tts:origin="10% 10%" />
<region xml:id="region2" style="style2" tts:extent="40% 40%" />
<region xml:id="region3" style="style3" />
</layout>
</head>
<body>
<div>
<p begin="2s" end="4s" region="region1">text 1</p>
<p begin="4s" end="6s" region="region2">text 2</p>
<p begin="8s" end="10s" region="region3">text 3</p>
</div>
</body>
</tt>