diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 6ea823fb70..a0957b8ad2 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -106,6 +106,9 @@ * Extractors: * Add support for DTS-UHD in MP4 ([#9163](https://github.com/google/ExoPlayer/issues/9163). +* Text: + * TTML: Inherit the `rubyPosition` value from a containing `` element. * Ad playback: * Support changing ad break positions in the player logic ([#5067](https://github.com/google/ExoPlayer/issues/5067). @@ -128,7 +131,6 @@ `ControlDispatcher` parameter has also been deprecated in all `MediaSessionConnector` listener methods. - ### 2.14.2 (2021-07-20) * Core Library: diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java index b7f4b0f5b1..8d2724622e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRenderUtil.java @@ -178,12 +178,22 @@ import java.util.Map; break; } - // TODO: Get rubyPosition from `textNode` when TTML inheritance is implemented. + @Nullable + TtmlStyle textStyle = resolveStyle(textNode.style, textNode.getStyleIds(), globalStyles); + + // Use position from ruby text node if defined. @TextAnnotation.Position int rubyPosition = - containerNode.style != null - ? containerNode.style.getRubyPosition() - : TextAnnotation.POSITION_UNKNOWN; + textStyle != null ? textStyle.getRubyPosition() : TextAnnotation.POSITION_UNKNOWN; + + if (rubyPosition == TextAnnotation.POSITION_UNKNOWN) { + // If ruby position is not defined, use position info from container node. + @Nullable + TtmlStyle containerStyle = + resolveStyle(containerNode.style, containerNode.getStyleIds(), globalStyles); + rubyPosition = containerStyle != null ? containerStyle.getRubyPosition() : rubyPosition; + } + builder.setSpan( new RubySpan(rubyText, rubyPosition), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); break; diff --git a/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java b/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java index d068ade74b..67b4ec64b7 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java @@ -710,6 +710,18 @@ public final class TtmlDecoderTest { Spanned sixthCue = getOnlyCueTextAtTimeUs(subtitle, 60_000_000); assertThat(sixthCue.toString()).isEqualTo("Cue with annotated text."); assertThat(sixthCue).hasNoRubySpanBetween(0, sixthCue.length()); + + Spanned seventhCue = getOnlyCueTextAtTimeUs(subtitle, 70_000_000); + assertThat(seventhCue.toString()).isEqualTo("Cue with annotated text."); + assertThat(seventhCue) + .hasRubySpanBetween("Cue with ".length(), "Cue with annotated".length()) + .withTextAndPosition("rubies", TextAnnotation.POSITION_BEFORE); + + Spanned eighthCue = getOnlyCueTextAtTimeUs(subtitle, 80_000_000); + assertThat(eighthCue.toString()).isEqualTo("Cue with annotated text."); + assertThat(eighthCue) + .hasRubySpanBetween("Cue with ".length(), "Cue with annotated".length()) + .withTextAndPosition("rubies", TextAnnotation.POSITION_AFTER); } @Test diff --git a/testdata/src/test/assets/media/ttml/rubies.xml b/testdata/src/test/assets/media/ttml/rubies.xml index e149f16dfd..4a655512c2 100644 --- a/testdata/src/test/assets/media/ttml/rubies.xml +++ b/testdata/src/test/assets/media/ttml/rubies.xml @@ -7,6 +7,8 @@