1ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/* 2ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Copyright (C) 2014 The Android Open Source Project 3ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 4ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Licensed under the Apache License, Version 2.0 (the "License"); 5ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * you may not use this file except in compliance with the License. 6ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * You may obtain a copy of the License at 7ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 8ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * http://www.apache.org/licenses/LICENSE-2.0 9ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 10ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Unless required by applicable law or agreed to in writing, software 11ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * distributed under the License is distributed on an "AS IS" BASIS, 12ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * See the License for the specific language governing permissions and 14ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * limitations under the License. 15ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 16ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 17ba3699b568e656910641b246b27448d92632a389Sungsoo Limpackage android.media; 18ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 19ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.content.Context; 20ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.text.TextUtils; 21ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.util.AttributeSet; 22ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.util.Log; 23ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.view.Gravity; 24ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.view.View; 25ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.view.accessibility.CaptioningManager; 26ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.widget.LinearLayout; 27ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport android.widget.TextView; 28ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 29ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.io.IOException; 30ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.io.StringReader; 31ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.util.ArrayList; 32ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.util.LinkedList; 33ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.util.List; 34ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.util.TreeSet; 35ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.util.Vector; 36ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.util.regex.Matcher; 37ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport java.util.regex.Pattern; 38ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 39ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport org.xmlpull.v1.XmlPullParser; 40ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport org.xmlpull.v1.XmlPullParserException; 41ba3699b568e656910641b246b27448d92632a389Sungsoo Limimport org.xmlpull.v1.XmlPullParserFactory; 42ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 43ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** @hide */ 44ba3699b568e656910641b246b27448d92632a389Sungsoo Limpublic class TtmlRenderer extends SubtitleController.Renderer { 45ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private final Context mContext; 46ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 47ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static final String MEDIA_MIMETYPE_TEXT_TTML = "application/ttml+xml"; 48ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 49ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private TtmlRenderingWidget mRenderingWidget; 50ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 51ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlRenderer(Context context) { 52ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mContext = context; 53ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 54ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 55ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 56ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public boolean supports(MediaFormat format) { 57ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (format.containsKey(MediaFormat.KEY_MIME)) { 58ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return format.getString(MediaFormat.KEY_MIME).equals(MEDIA_MIMETYPE_TEXT_TTML); 59ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 60ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return false; 61ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 62ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 63ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 64ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public SubtitleTrack createTrack(MediaFormat format) { 65ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (mRenderingWidget == null) { 66ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mRenderingWidget = new TtmlRenderingWidget(mContext); 67ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 68ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return new TtmlTrack(mRenderingWidget, format); 69ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 70ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 71ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 72ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** 73ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * A class which provides utillity methods for TTML parsing. 74ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 75ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @hide 76ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 77ba3699b568e656910641b246b27448d92632a389Sungsoo Limfinal class TtmlUtils { 78ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_TT = "tt"; 79ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_HEAD = "head"; 80ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_BODY = "body"; 81ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_DIV = "div"; 82ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_P = "p"; 83ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_SPAN = "span"; 84ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_BR = "br"; 85ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_STYLE = "style"; 86ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_STYLING = "styling"; 87ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_LAYOUT = "layout"; 88ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_REGION = "region"; 89ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_METADATA = "metadata"; 90ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_SMPTE_IMAGE = "smpte:image"; 91ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_SMPTE_DATA = "smpte:data"; 92ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String TAG_SMPTE_INFORMATION = "smpte:information"; 93ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String PCDATA = "#pcdata"; 94ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String ATTR_BEGIN = "begin"; 95ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String ATTR_DURATION = "dur"; 96ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final String ATTR_END = "end"; 97ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static final long INVALID_TIMESTAMP = Long.MAX_VALUE; 98ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 99ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 100ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Time expression RE according to the spec: 101ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression 102ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 103ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static final Pattern CLOCK_TIME = Pattern.compile( 104ba3699b568e656910641b246b27448d92632a389Sungsoo Lim "^([0-9][0-9]+):([0-9][0-9]):([0-9][0-9])" 105ba3699b568e656910641b246b27448d92632a389Sungsoo Lim + "(?:(\\.[0-9]+)|:([0-9][0-9])(?:\\.([0-9]+))?)?$"); 106ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 107ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static final Pattern OFFSET_TIME = Pattern.compile( 108ba3699b568e656910641b246b27448d92632a389Sungsoo Lim "^([0-9]+(?:\\.[0-9]+)?)(h|m|s|ms|f|t)$"); 109ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 110ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private TtmlUtils() { 111ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 112ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 113ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 114ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Parses the given time expression and returns a timestamp in millisecond. 115ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <p> 116ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * For the format of the time expression, please refer <a href= 117ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * "http://www.w3.org/TR/ttaf1-dfxp/#timing-value-timeExpression">timeExpression</a> 118ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 119ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param time A string which includes time expression. 120ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param frameRate the framerate of the stream. 121ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param subframeRate the sub-framerate of the stream 122ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param tickRate the tick rate of the stream. 123ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @return the parsed timestamp in micro-second. 124ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @throws NumberFormatException if the given string does not match to the 125ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * format. 126ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 127ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static long parseTimeExpression(String time, int frameRate, int subframeRate, 128ba3699b568e656910641b246b27448d92632a389Sungsoo Lim int tickRate) throws NumberFormatException { 129ba3699b568e656910641b246b27448d92632a389Sungsoo Lim Matcher matcher = CLOCK_TIME.matcher(time); 130ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (matcher.matches()) { 131ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String hours = matcher.group(1); 132ba3699b568e656910641b246b27448d92632a389Sungsoo Lim double durationSeconds = Long.parseLong(hours) * 3600; 133ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String minutes = matcher.group(2); 134ba3699b568e656910641b246b27448d92632a389Sungsoo Lim durationSeconds += Long.parseLong(minutes) * 60; 135ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String seconds = matcher.group(3); 136ba3699b568e656910641b246b27448d92632a389Sungsoo Lim durationSeconds += Long.parseLong(seconds); 137ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String fraction = matcher.group(4); 138ba3699b568e656910641b246b27448d92632a389Sungsoo Lim durationSeconds += (fraction != null) ? Double.parseDouble(fraction) : 0; 139ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String frames = matcher.group(5); 140ba3699b568e656910641b246b27448d92632a389Sungsoo Lim durationSeconds += (frames != null) ? ((double)Long.parseLong(frames)) / frameRate : 0; 141ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String subframes = matcher.group(6); 142ba3699b568e656910641b246b27448d92632a389Sungsoo Lim durationSeconds += (subframes != null) ? ((double)Long.parseLong(subframes)) 143ba3699b568e656910641b246b27448d92632a389Sungsoo Lim / subframeRate / frameRate 144ba3699b568e656910641b246b27448d92632a389Sungsoo Lim : 0; 145ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return (long)(durationSeconds * 1000); 146ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 147ba3699b568e656910641b246b27448d92632a389Sungsoo Lim matcher = OFFSET_TIME.matcher(time); 148ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (matcher.matches()) { 149ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String timeValue = matcher.group(1); 150ba3699b568e656910641b246b27448d92632a389Sungsoo Lim double value = Double.parseDouble(timeValue); 151ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String unit = matcher.group(2); 152ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (unit.equals("h")) { 153ba3699b568e656910641b246b27448d92632a389Sungsoo Lim value *= 3600L * 1000000L; 154ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (unit.equals("m")) { 155ba3699b568e656910641b246b27448d92632a389Sungsoo Lim value *= 60 * 1000000; 156ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (unit.equals("s")) { 157ba3699b568e656910641b246b27448d92632a389Sungsoo Lim value *= 1000000; 158ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (unit.equals("ms")) { 159ba3699b568e656910641b246b27448d92632a389Sungsoo Lim value *= 1000; 160ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (unit.equals("f")) { 161ba3699b568e656910641b246b27448d92632a389Sungsoo Lim value = value / frameRate * 1000000; 162ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (unit.equals("t")) { 163ba3699b568e656910641b246b27448d92632a389Sungsoo Lim value = value / tickRate * 1000000; 164ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 165ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return (long)value; 166ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 167ba3699b568e656910641b246b27448d92632a389Sungsoo Lim throw new NumberFormatException("Malformed time expression : " + time); 168ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 169ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 170ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 171ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Applies <a href 172ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * src="http://www.w3.org/TR/ttaf1-dfxp/#content-attribute-space">the 173ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * default space policy</a> to the given string. 174ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 175ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param in A string to apply the policy. 176ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 177ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static String applyDefaultSpacePolicy(String in) { 178ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return applySpacePolicy(in, true); 179ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 180ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 181ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 182ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Applies the space policy to the given string. This applies <a href 183ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * src="http://www.w3.org/TR/ttaf1-dfxp/#content-attribute-space">the 184ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * default space policy</a> with linefeed-treatment as treat-as-space 185ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * or preserve. 186ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 187ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param in A string to apply the policy. 188ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param treatLfAsSpace Whether convert line feeds to spaces or not. 189ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 190ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static String applySpacePolicy(String in, boolean treatLfAsSpace) { 191ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // Removes CR followed by LF. ref: 192ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // http://www.w3.org/TR/xml/#sec-line-ends 193ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String crRemoved = in.replaceAll("\r\n", "\n"); 194ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // Apply suppress-at-line-break="auto" and 195ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // white-space-treatment="ignore-if-surrounding-linefeed" 196ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String spacesNeighboringLfRemoved = crRemoved.replaceAll(" *\n *", "\n"); 197ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // Apply linefeed-treatment="treat-as-space" 198ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String lfToSpace = treatLfAsSpace ? spacesNeighboringLfRemoved.replaceAll("\n", " ") 199ba3699b568e656910641b246b27448d92632a389Sungsoo Lim : spacesNeighboringLfRemoved; 200ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // Apply white-space-collapse="true" 201ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String spacesCollapsed = lfToSpace.replaceAll("[ \t\\x0B\f\r]+", " "); 202ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return spacesCollapsed; 203ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 204ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 205ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 206ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Returns the timed text for the given time period. 207ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 208ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param root The root node of the TTML document. 209ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param startUs The start time of the time period in microsecond. 210ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param endUs The end time of the time period in microsecond. 211ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 212ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static String extractText(TtmlNode root, long startUs, long endUs) { 213ba3699b568e656910641b246b27448d92632a389Sungsoo Lim StringBuilder text = new StringBuilder(); 214ba3699b568e656910641b246b27448d92632a389Sungsoo Lim extractText(root, startUs, endUs, text, false); 215ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return text.toString().replaceAll("\n$", ""); 216ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 217ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 218ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static void extractText(TtmlNode node, long startUs, long endUs, StringBuilder out, 219ba3699b568e656910641b246b27448d92632a389Sungsoo Lim boolean inPTag) { 220ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (node.mName.equals(TtmlUtils.PCDATA) && inPTag) { 221ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(node.mText); 222ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (node.mName.equals(TtmlUtils.TAG_BR) && inPTag) { 223ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append("\n"); 224ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (node.mName.equals(TtmlUtils.TAG_METADATA)) { 225ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // do nothing. 226ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (node.isActive(startUs, endUs)) { 227ba3699b568e656910641b246b27448d92632a389Sungsoo Lim boolean pTag = node.mName.equals(TtmlUtils.TAG_P); 228ba3699b568e656910641b246b27448d92632a389Sungsoo Lim int length = out.length(); 229ba3699b568e656910641b246b27448d92632a389Sungsoo Lim for (int i = 0; i < node.mChildren.size(); ++i) { 230ba3699b568e656910641b246b27448d92632a389Sungsoo Lim extractText(node.mChildren.get(i), startUs, endUs, out, pTag || inPTag); 231ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 232ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (pTag && length != out.length()) { 233ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append("\n"); 234ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 235ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 236ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 237ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 238ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 239ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Returns a TTML fragment string for the given time period. 240ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 241ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param root The root node of the TTML document. 242ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param startUs The start time of the time period in microsecond. 243ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param endUs The end time of the time period in microsecond. 244ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 245ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public static String extractTtmlFragment(TtmlNode root, long startUs, long endUs) { 246ba3699b568e656910641b246b27448d92632a389Sungsoo Lim StringBuilder fragment = new StringBuilder(); 247ba3699b568e656910641b246b27448d92632a389Sungsoo Lim extractTtmlFragment(root, startUs, endUs, fragment); 248ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return fragment.toString(); 249ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 250ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 251ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static void extractTtmlFragment(TtmlNode node, long startUs, long endUs, 252ba3699b568e656910641b246b27448d92632a389Sungsoo Lim StringBuilder out) { 253ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (node.mName.equals(TtmlUtils.PCDATA)) { 254ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(node.mText); 255ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (node.mName.equals(TtmlUtils.TAG_BR)) { 256ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append("<br/>"); 257ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (node.isActive(startUs, endUs)) { 258ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append("<"); 259ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(node.mName); 260ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(node.mAttributes); 261ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(">"); 262ba3699b568e656910641b246b27448d92632a389Sungsoo Lim for (int i = 0; i < node.mChildren.size(); ++i) { 263ba3699b568e656910641b246b27448d92632a389Sungsoo Lim extractTtmlFragment(node.mChildren.get(i), startUs, endUs, out); 264ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 265ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append("</"); 266ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(node.mName); 267ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(">"); 268ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 269ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 270ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 271ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 272ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** 273ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * A container class which represents a cue in TTML. 274ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @hide 275ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 276ba3699b568e656910641b246b27448d92632a389Sungsoo Limclass TtmlCue extends SubtitleTrack.Cue { 277ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public String mText; 278ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public String mTtmlFragment; 279ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 280ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlCue(long startTimeMs, long endTimeMs, String text, String ttmlFragment) { 281ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mStartTimeMs = startTimeMs; 282ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mEndTimeMs = endTimeMs; 283ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mText = text; 284ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mTtmlFragment = ttmlFragment; 285ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 286ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 287ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 288ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** 289ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * A container class which represents a node in TTML. 290ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 291ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @hide 292ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 293ba3699b568e656910641b246b27448d92632a389Sungsoo Limclass TtmlNode { 294ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final String mName; 295ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final String mAttributes; 296ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final TtmlNode mParent; 297ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final String mText; 298ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final List<TtmlNode> mChildren = new ArrayList<TtmlNode>(); 299ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final long mRunId; 300ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final long mStartTimeMs; 301ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public final long mEndTimeMs; 302ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 303ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlNode(String name, String attributes, String text, long startTimeMs, long endTimeMs, 304ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlNode parent, long runId) { 305ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mName = name; 306ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mAttributes = attributes; 307ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mText = text; 308ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mStartTimeMs = startTimeMs; 309ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mEndTimeMs = endTimeMs; 310ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mParent = parent; 311ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this.mRunId = runId; 312ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 313ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 314ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 315ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Check if this node is active in the given time range. 316ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 317ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param startTimeMs The start time of the range to check in microsecond. 318ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param endTimeMs The end time of the range to check in microsecond. 319ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @return return true if the given range overlaps the time range of this 320ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * node. 321ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 322ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public boolean isActive(long startTimeMs, long endTimeMs) { 323ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return this.mEndTimeMs > startTimeMs && this.mStartTimeMs < endTimeMs; 324ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 325ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 326ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 327ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** 328ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * A simple TTML parser (http://www.w3.org/TR/ttaf1-dfxp/) which supports DFXP 329ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * presentation profile. 330ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <p> 331ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Supported features in this parser are: 332ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <ul> 333ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>content 334ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>core 335ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>presentation 336ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>profile 337ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>structure 338ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>time-offset 339ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>timing 340ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>tickRate 341ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>time-clock-with-frames 342ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>time-clock 343ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>time-offset-with-frames 344ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * <li>time-offset-with-ticks 345ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * </ul> 346ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * </p> 347ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 348ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @hide 349ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 350ba3699b568e656910641b246b27448d92632a389Sungsoo Limclass TtmlParser { 351ba3699b568e656910641b246b27448d92632a389Sungsoo Lim static final String TAG = "TtmlParser"; 352ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 353ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // TODO: read and apply the following attributes if specified. 354ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static final int DEFAULT_FRAMERATE = 30; 355ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static final int DEFAULT_SUBFRAMERATE = 1; 356ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static final int DEFAULT_TICKRATE = 1; 357ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 358ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private XmlPullParser mParser; 359ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private final TtmlNodeListener mListener; 360ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private long mCurrentRunId; 361ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 362ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlParser(TtmlNodeListener listener) { 363ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mListener = listener; 364ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 365ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 366ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 367ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Parse TTML data. Once this is called, all the previous data are 368ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * reset and it starts parsing for the given text. 369ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 370ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @param ttmlText TTML text to parse. 371ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @throws XmlPullParserException 372ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @throws IOException 373ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 374ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void parse(String ttmlText, long runId) throws XmlPullParserException, IOException { 375ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mParser = null; 376ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mCurrentRunId = runId; 377ba3699b568e656910641b246b27448d92632a389Sungsoo Lim loadParser(ttmlText); 378ba3699b568e656910641b246b27448d92632a389Sungsoo Lim parseTtml(); 379ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 380ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 381ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private void loadParser(String ttmlFragment) throws XmlPullParserException { 382ba3699b568e656910641b246b27448d92632a389Sungsoo Lim XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 383ba3699b568e656910641b246b27448d92632a389Sungsoo Lim factory.setNamespaceAware(false); 384ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mParser = factory.newPullParser(); 385ba3699b568e656910641b246b27448d92632a389Sungsoo Lim StringReader in = new StringReader(ttmlFragment); 386ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mParser.setInput(in); 387ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 388ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 389ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private void extractAttribute(XmlPullParser parser, int i, StringBuilder out) { 390ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(" "); 391ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(parser.getAttributeName(i)); 392ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append("=\""); 393ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append(parser.getAttributeValue(i)); 394ba3699b568e656910641b246b27448d92632a389Sungsoo Lim out.append("\""); 395ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 396ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 397ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private void parseTtml() throws XmlPullParserException, IOException { 398ba3699b568e656910641b246b27448d92632a389Sungsoo Lim LinkedList<TtmlNode> nodeStack = new LinkedList<TtmlNode>(); 399ba3699b568e656910641b246b27448d92632a389Sungsoo Lim int depthInUnsupportedTag = 0; 400ba3699b568e656910641b246b27448d92632a389Sungsoo Lim boolean active = true; 401ba3699b568e656910641b246b27448d92632a389Sungsoo Lim while (!isEndOfDoc()) { 402ba3699b568e656910641b246b27448d92632a389Sungsoo Lim int eventType = mParser.getEventType(); 403ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlNode parent = nodeStack.peekLast(); 404ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (active) { 405ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (eventType == XmlPullParser.START_TAG) { 406ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (!isSupportedTag(mParser.getName())) { 407ba3699b568e656910641b246b27448d92632a389Sungsoo Lim Log.w(TAG, "Unsupported tag " + mParser.getName() + " is ignored."); 408ba3699b568e656910641b246b27448d92632a389Sungsoo Lim depthInUnsupportedTag++; 409ba3699b568e656910641b246b27448d92632a389Sungsoo Lim active = false; 410ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else { 411ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlNode node = parseNode(parent); 412ba3699b568e656910641b246b27448d92632a389Sungsoo Lim nodeStack.addLast(node); 413ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (parent != null) { 414ba3699b568e656910641b246b27448d92632a389Sungsoo Lim parent.mChildren.add(node); 415ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 416ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 417ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (eventType == XmlPullParser.TEXT) { 418ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String text = TtmlUtils.applyDefaultSpacePolicy(mParser.getText()); 419ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (!TextUtils.isEmpty(text)) { 420ba3699b568e656910641b246b27448d92632a389Sungsoo Lim parent.mChildren.add(new TtmlNode( 421ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlUtils.PCDATA, "", text, 0, TtmlUtils.INVALID_TIMESTAMP, 422ba3699b568e656910641b246b27448d92632a389Sungsoo Lim parent, mCurrentRunId)); 423ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 424ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 425ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (eventType == XmlPullParser.END_TAG) { 426ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (mParser.getName().equals(TtmlUtils.TAG_P)) { 427ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mListener.onTtmlNodeParsed(nodeStack.getLast()); 428ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (mParser.getName().equals(TtmlUtils.TAG_TT)) { 429ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mListener.onRootNodeParsed(nodeStack.getLast()); 430ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 431ba3699b568e656910641b246b27448d92632a389Sungsoo Lim nodeStack.removeLast(); 432ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 433ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else { 434ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (eventType == XmlPullParser.START_TAG) { 435ba3699b568e656910641b246b27448d92632a389Sungsoo Lim depthInUnsupportedTag++; 436ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (eventType == XmlPullParser.END_TAG) { 437ba3699b568e656910641b246b27448d92632a389Sungsoo Lim depthInUnsupportedTag--; 438ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (depthInUnsupportedTag == 0) { 439ba3699b568e656910641b246b27448d92632a389Sungsoo Lim active = true; 440ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 441ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 442ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 443ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mParser.next(); 444ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 445ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 446ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 447ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private TtmlNode parseNode(TtmlNode parent) throws XmlPullParserException, IOException { 448ba3699b568e656910641b246b27448d92632a389Sungsoo Lim int eventType = mParser.getEventType(); 449ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (!(eventType == XmlPullParser.START_TAG)) { 450ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return null; 451ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 452ba3699b568e656910641b246b27448d92632a389Sungsoo Lim StringBuilder attrStr = new StringBuilder(); 453ba3699b568e656910641b246b27448d92632a389Sungsoo Lim long start = 0; 454ba3699b568e656910641b246b27448d92632a389Sungsoo Lim long end = TtmlUtils.INVALID_TIMESTAMP; 455ba3699b568e656910641b246b27448d92632a389Sungsoo Lim long dur = 0; 456ba3699b568e656910641b246b27448d92632a389Sungsoo Lim for (int i = 0; i < mParser.getAttributeCount(); ++i) { 457ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String attr = mParser.getAttributeName(i); 458ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String value = mParser.getAttributeValue(i); 459ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // TODO: check if it's safe to ignore the namespace of attributes as follows. 460ba3699b568e656910641b246b27448d92632a389Sungsoo Lim attr = attr.replaceFirst("^.*:", ""); 461ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (attr.equals(TtmlUtils.ATTR_BEGIN)) { 462ba3699b568e656910641b246b27448d92632a389Sungsoo Lim start = TtmlUtils.parseTimeExpression(value, DEFAULT_FRAMERATE, 463ba3699b568e656910641b246b27448d92632a389Sungsoo Lim DEFAULT_SUBFRAMERATE, DEFAULT_TICKRATE); 464ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (attr.equals(TtmlUtils.ATTR_END)) { 465ba3699b568e656910641b246b27448d92632a389Sungsoo Lim end = TtmlUtils.parseTimeExpression(value, DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, 466ba3699b568e656910641b246b27448d92632a389Sungsoo Lim DEFAULT_TICKRATE); 467ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else if (attr.equals(TtmlUtils.ATTR_DURATION)) { 468ba3699b568e656910641b246b27448d92632a389Sungsoo Lim dur = TtmlUtils.parseTimeExpression(value, DEFAULT_FRAMERATE, DEFAULT_SUBFRAMERATE, 469ba3699b568e656910641b246b27448d92632a389Sungsoo Lim DEFAULT_TICKRATE); 470ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else { 471ba3699b568e656910641b246b27448d92632a389Sungsoo Lim extractAttribute(mParser, i, attrStr); 472ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 473ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 474ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (parent != null) { 475ba3699b568e656910641b246b27448d92632a389Sungsoo Lim start += parent.mStartTimeMs; 476ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (end != TtmlUtils.INVALID_TIMESTAMP) { 477ba3699b568e656910641b246b27448d92632a389Sungsoo Lim end += parent.mStartTimeMs; 478ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 479ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 480ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (dur > 0) { 481ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (end != TtmlUtils.INVALID_TIMESTAMP) { 482ba3699b568e656910641b246b27448d92632a389Sungsoo Lim Log.e(TAG, "'dur' and 'end' attributes are defined at the same time." + 483ba3699b568e656910641b246b27448d92632a389Sungsoo Lim "'end' value is ignored."); 484ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 485ba3699b568e656910641b246b27448d92632a389Sungsoo Lim end = start + dur; 486ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 487ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (parent != null) { 488ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // If the end time remains unspecified, then the end point is 489ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // interpreted as the end point of the external time interval. 490ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (end == TtmlUtils.INVALID_TIMESTAMP && 491ba3699b568e656910641b246b27448d92632a389Sungsoo Lim parent.mEndTimeMs != TtmlUtils.INVALID_TIMESTAMP && 492ba3699b568e656910641b246b27448d92632a389Sungsoo Lim end > parent.mEndTimeMs) { 493ba3699b568e656910641b246b27448d92632a389Sungsoo Lim end = parent.mEndTimeMs; 494ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 495ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 496ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlNode node = new TtmlNode(mParser.getName(), attrStr.toString(), null, start, end, 497ba3699b568e656910641b246b27448d92632a389Sungsoo Lim parent, mCurrentRunId); 498ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return node; 499ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 500ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 501ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private boolean isEndOfDoc() throws XmlPullParserException { 502ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return (mParser.getEventType() == XmlPullParser.END_DOCUMENT); 503ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 504ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 505ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static boolean isSupportedTag(String tag) { 506ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (tag.equals(TtmlUtils.TAG_TT) || tag.equals(TtmlUtils.TAG_HEAD) || 507ba3699b568e656910641b246b27448d92632a389Sungsoo Lim tag.equals(TtmlUtils.TAG_BODY) || tag.equals(TtmlUtils.TAG_DIV) || 508ba3699b568e656910641b246b27448d92632a389Sungsoo Lim tag.equals(TtmlUtils.TAG_P) || tag.equals(TtmlUtils.TAG_SPAN) || 509ba3699b568e656910641b246b27448d92632a389Sungsoo Lim tag.equals(TtmlUtils.TAG_BR) || tag.equals(TtmlUtils.TAG_STYLE) || 510ba3699b568e656910641b246b27448d92632a389Sungsoo Lim tag.equals(TtmlUtils.TAG_STYLING) || tag.equals(TtmlUtils.TAG_LAYOUT) || 511ba3699b568e656910641b246b27448d92632a389Sungsoo Lim tag.equals(TtmlUtils.TAG_REGION) || tag.equals(TtmlUtils.TAG_METADATA) || 512ba3699b568e656910641b246b27448d92632a389Sungsoo Lim tag.equals(TtmlUtils.TAG_SMPTE_IMAGE) || tag.equals(TtmlUtils.TAG_SMPTE_DATA) || 513ba3699b568e656910641b246b27448d92632a389Sungsoo Lim tag.equals(TtmlUtils.TAG_SMPTE_INFORMATION)) { 514ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return true; 515ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 516ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return false; 517ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 518ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 519ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 520ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** @hide */ 521ba3699b568e656910641b246b27448d92632a389Sungsoo Liminterface TtmlNodeListener { 522ba3699b568e656910641b246b27448d92632a389Sungsoo Lim void onTtmlNodeParsed(TtmlNode node); 523ba3699b568e656910641b246b27448d92632a389Sungsoo Lim void onRootNodeParsed(TtmlNode node); 524ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 525ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 526ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** @hide */ 527ba3699b568e656910641b246b27448d92632a389Sungsoo Limclass TtmlTrack extends SubtitleTrack implements TtmlNodeListener { 528ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private static final String TAG = "TtmlTrack"; 529ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 530ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private final TtmlParser mParser = new TtmlParser(this); 531ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private final TtmlRenderingWidget mRenderingWidget; 532ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private String mParsingData; 533ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private Long mCurrentRunID; 534ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 535ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private final LinkedList<TtmlNode> mTtmlNodes; 536ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private final TreeSet<Long> mTimeEvents; 537ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private TtmlNode mRootNode; 538ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 539ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlTrack(TtmlRenderingWidget renderingWidget, MediaFormat format) { 540ba3699b568e656910641b246b27448d92632a389Sungsoo Lim super(format); 541ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 542ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTtmlNodes = new LinkedList<TtmlNode>(); 543ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTimeEvents = new TreeSet<Long>(); 544ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mRenderingWidget = renderingWidget; 545ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mParsingData = ""; 546ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 547ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 548ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 549ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlRenderingWidget getRenderingWidget() { 550ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return mRenderingWidget; 551ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 552ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 553ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 554079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang public void onData(byte[] data, boolean eos, long runID) { 555079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang try { 556079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang // TODO: handle UTF-8 conversion properly 557079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang String str = new String(data, "UTF-8"); 558079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang 559079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang // implement intermixing restriction for TTML. 560079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang synchronized(mParser) { 561079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang if (mCurrentRunID != null && runID != mCurrentRunID) { 562079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang throw new IllegalStateException( 563079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang "Run #" + mCurrentRunID + 564079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang " in progress. Cannot process run #" + runID); 565079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang } 566079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang mCurrentRunID = runID; 567079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang mParsingData += str; 568079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang if (eos) { 569079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang try { 570079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang mParser.parse(mParsingData, mCurrentRunID); 571079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang } catch (XmlPullParserException e) { 572079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang e.printStackTrace(); 573079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang } catch (IOException e) { 574079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang e.printStackTrace(); 575079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang } 576079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang finishedRun(runID); 577079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang mParsingData = ""; 578079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang mCurrentRunID = null; 579ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 580ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 581079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang } catch (java.io.UnsupportedEncodingException e) { 582079fa9683dc062f154333a661f5e84a5ac5e43c7Chong Zhang Log.w(TAG, "subtitle data is not UTF-8 encoded: " + e); 583ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 584ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 585ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 586ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 587ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void onTtmlNodeParsed(TtmlNode node) { 588ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTtmlNodes.addLast(node); 589ba3699b568e656910641b246b27448d92632a389Sungsoo Lim addTimeEvents(node); 590ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 591ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 592ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 593ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void onRootNodeParsed(TtmlNode node) { 594ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mRootNode = node; 595ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlCue cue = null; 596ba3699b568e656910641b246b27448d92632a389Sungsoo Lim while ((cue = getNextResult()) != null) { 597ba3699b568e656910641b246b27448d92632a389Sungsoo Lim addCue(cue); 598ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 599ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mRootNode = null; 600ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTtmlNodes.clear(); 601ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTimeEvents.clear(); 602ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 603ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 604ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 605ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void updateView(Vector<SubtitleTrack.Cue> activeCues) { 606ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (!mVisible) { 607ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // don't keep the state if we are not visible 608ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return; 609ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 610ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 611ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (DEBUG && mTimeProvider != null) { 612ba3699b568e656910641b246b27448d92632a389Sungsoo Lim try { 613ba3699b568e656910641b246b27448d92632a389Sungsoo Lim Log.d(TAG, "at " + 614ba3699b568e656910641b246b27448d92632a389Sungsoo Lim (mTimeProvider.getCurrentTimeUs(false, true) / 1000) + 615ba3699b568e656910641b246b27448d92632a389Sungsoo Lim " ms the active cues are:"); 616ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } catch (IllegalStateException e) { 617ba3699b568e656910641b246b27448d92632a389Sungsoo Lim Log.d(TAG, "at (illegal state) the active cues are:"); 618ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 619ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 620ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 621ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mRenderingWidget.setActiveCues(activeCues); 622ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 623ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 624ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** 625ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Returns a {@link TtmlCue} in the presentation time order. 626ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * {@code null} is returned if there is no more timed text to show. 627ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 628ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlCue getNextResult() { 629ba3699b568e656910641b246b27448d92632a389Sungsoo Lim while (mTimeEvents.size() >= 2) { 630ba3699b568e656910641b246b27448d92632a389Sungsoo Lim long start = mTimeEvents.pollFirst(); 631ba3699b568e656910641b246b27448d92632a389Sungsoo Lim long end = mTimeEvents.first(); 632ba3699b568e656910641b246b27448d92632a389Sungsoo Lim List<TtmlNode> activeCues = getActiveNodes(start, end); 633ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (!activeCues.isEmpty()) { 634ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return new TtmlCue(start, end, 635ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlUtils.applySpacePolicy(TtmlUtils.extractText( 636ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mRootNode, start, end), false), 637ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlUtils.extractTtmlFragment(mRootNode, start, end)); 638ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 639ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 640ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return null; 641ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 642ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 643ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private void addTimeEvents(TtmlNode node) { 644ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTimeEvents.add(node.mStartTimeMs); 645ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTimeEvents.add(node.mEndTimeMs); 646ba3699b568e656910641b246b27448d92632a389Sungsoo Lim for (int i = 0; i < node.mChildren.size(); ++i) { 647ba3699b568e656910641b246b27448d92632a389Sungsoo Lim addTimeEvents(node.mChildren.get(i)); 648ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 649ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 650ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 651ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private List<TtmlNode> getActiveNodes(long startTimeUs, long endTimeUs) { 652ba3699b568e656910641b246b27448d92632a389Sungsoo Lim List<TtmlNode> activeNodes = new ArrayList<TtmlNode>(); 653ba3699b568e656910641b246b27448d92632a389Sungsoo Lim for (int i = 0; i < mTtmlNodes.size(); ++i) { 654ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlNode node = mTtmlNodes.get(i); 655ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (node.isActive(startTimeUs, endTimeUs)) { 656ba3699b568e656910641b246b27448d92632a389Sungsoo Lim activeNodes.add(node); 657ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 658ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 659ba3699b568e656910641b246b27448d92632a389Sungsoo Lim return activeNodes; 660ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 661ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 662ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 663ba3699b568e656910641b246b27448d92632a389Sungsoo Lim/** 664ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * Widget capable of rendering TTML captions. 665ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * 666ba3699b568e656910641b246b27448d92632a389Sungsoo Lim * @hide 667ba3699b568e656910641b246b27448d92632a389Sungsoo Lim */ 668ba3699b568e656910641b246b27448d92632a389Sungsoo Limclass TtmlRenderingWidget extends LinearLayout implements SubtitleTrack.RenderingWidget { 669ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 670ba3699b568e656910641b246b27448d92632a389Sungsoo Lim /** Callback for rendering changes. */ 671ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private OnChangedListener mListener; 672ba3699b568e656910641b246b27448d92632a389Sungsoo Lim private final TextView mTextView; 673ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 674ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlRenderingWidget(Context context) { 675ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this(context, null); 676ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 677ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 678ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlRenderingWidget(Context context, AttributeSet attrs) { 679ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this(context, attrs, 0); 680ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 681ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 682ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr) { 683ba3699b568e656910641b246b27448d92632a389Sungsoo Lim this(context, attrs, defStyleAttr, 0); 684ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 685ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 686ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public TtmlRenderingWidget(Context context, AttributeSet attrs, int defStyleAttr, 687ba3699b568e656910641b246b27448d92632a389Sungsoo Lim int defStyleRes) { 688ba3699b568e656910641b246b27448d92632a389Sungsoo Lim super(context, attrs, defStyleAttr, defStyleRes); 689ba3699b568e656910641b246b27448d92632a389Sungsoo Lim // Cannot render text over video when layer type is hardware. 690ba3699b568e656910641b246b27448d92632a389Sungsoo Lim setLayerType(View.LAYER_TYPE_SOFTWARE, null); 691ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 692ba3699b568e656910641b246b27448d92632a389Sungsoo Lim CaptioningManager captionManager = (CaptioningManager) context.getSystemService( 693ba3699b568e656910641b246b27448d92632a389Sungsoo Lim Context.CAPTIONING_SERVICE); 694ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTextView = new TextView(context); 695ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTextView.setTextColor(captionManager.getUserStyle().foregroundColor); 696ba3699b568e656910641b246b27448d92632a389Sungsoo Lim addView(mTextView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 697ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTextView.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL); 698ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 699ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 700ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 701ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void setOnChangedListener(OnChangedListener listener) { 702ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mListener = listener; 703ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 704ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 705ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 706ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void setSize(int width, int height) { 707ba3699b568e656910641b246b27448d92632a389Sungsoo Lim final int widthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY); 708ba3699b568e656910641b246b27448d92632a389Sungsoo Lim final int heightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 709ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 710ba3699b568e656910641b246b27448d92632a389Sungsoo Lim measure(widthSpec, heightSpec); 711ba3699b568e656910641b246b27448d92632a389Sungsoo Lim layout(0, 0, width, height); 712ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 713ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 714ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 715ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void setVisible(boolean visible) { 716ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (visible) { 717ba3699b568e656910641b246b27448d92632a389Sungsoo Lim setVisibility(View.VISIBLE); 718ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } else { 719ba3699b568e656910641b246b27448d92632a389Sungsoo Lim setVisibility(View.GONE); 720ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 721ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 722ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 723ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 724ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void onAttachedToWindow() { 725ba3699b568e656910641b246b27448d92632a389Sungsoo Lim super.onAttachedToWindow(); 726ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 727ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 728ba3699b568e656910641b246b27448d92632a389Sungsoo Lim @Override 729ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void onDetachedFromWindow() { 730ba3699b568e656910641b246b27448d92632a389Sungsoo Lim super.onDetachedFromWindow(); 731ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 732ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 733ba3699b568e656910641b246b27448d92632a389Sungsoo Lim public void setActiveCues(Vector<SubtitleTrack.Cue> activeCues) { 734ba3699b568e656910641b246b27448d92632a389Sungsoo Lim final int count = activeCues.size(); 735ba3699b568e656910641b246b27448d92632a389Sungsoo Lim String subtitleText = ""; 736ba3699b568e656910641b246b27448d92632a389Sungsoo Lim for (int i = 0; i < count; i++) { 737ba3699b568e656910641b246b27448d92632a389Sungsoo Lim TtmlCue cue = (TtmlCue) activeCues.get(i); 738ba3699b568e656910641b246b27448d92632a389Sungsoo Lim subtitleText += cue.mText + "\n"; 739ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 740ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mTextView.setText(subtitleText); 741ba3699b568e656910641b246b27448d92632a389Sungsoo Lim 742ba3699b568e656910641b246b27448d92632a389Sungsoo Lim if (mListener != null) { 743ba3699b568e656910641b246b27448d92632a389Sungsoo Lim mListener.onChanged(this); 744ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 745ba3699b568e656910641b246b27448d92632a389Sungsoo Lim } 746ba3699b568e656910641b246b27448d92632a389Sungsoo Lim} 747