Search in sources :

Example 6 with SubtitleDecoderException

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);
}
Also used : Matcher(java.util.regex.Matcher) SubtitleDecoderException(com.google.android.exoplayer2.text.SubtitleDecoderException)

Example 7 with SubtitleDecoderException

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);
    }
}
Also used : HashMap(java.util.HashMap) XmlPullParser(org.xmlpull.v1.XmlPullParser) IOException(java.io.IOException) LinkedList(java.util.LinkedList) SubtitleDecoderException(com.google.android.exoplayer2.text.SubtitleDecoderException) ByteArrayInputStream(java.io.ByteArrayInputStream) XmlPullParserException(org.xmlpull.v1.XmlPullParserException)

Example 8 with SubtitleDecoderException

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 + "'.");
    }
}
Also used : Matcher(java.util.regex.Matcher) SubtitleDecoderException(com.google.android.exoplayer2.text.SubtitleDecoderException)

Example 9 with SubtitleDecoderException

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);
}
Also used : Cue(com.google.android.exoplayer2.text.Cue)

Example 10 with SubtitleDecoderException

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);
}
Also used : Cue(com.google.android.exoplayer2.text.Cue) SpannableStringBuilder(android.text.SpannableStringBuilder)

Aggregations

Cue (com.google.android.exoplayer2.text.Cue)7 SubtitleDecoderException (com.google.android.exoplayer2.text.SubtitleDecoderException)5 SpannableStringBuilder (android.text.SpannableStringBuilder)4 Subtitle (com.google.android.exoplayer2.text.Subtitle)4 Matcher (java.util.regex.Matcher)3 ParserException (com.google.android.exoplayer2.ParserException)1 TrackOutput (com.google.android.exoplayer2.extractor.TrackOutput)1 SubtitleInputBuffer (com.google.android.exoplayer2.text.SubtitleInputBuffer)1 SubtitleOutputBuffer (com.google.android.exoplayer2.text.SubtitleOutputBuffer)1 ParsableByteArray (com.google.android.exoplayer2.util.ParsableByteArray)1 ByteArrayInputStream (java.io.ByteArrayInputStream)1 IOException (java.io.IOException)1 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 LinkedList (java.util.LinkedList)1 XmlPullParser (org.xmlpull.v1.XmlPullParser)1 XmlPullParserException (org.xmlpull.v1.XmlPullParserException)1